2、第一个嵌入式Flutter应用:创建项目、理解结构、运行到设备

好,咱们直接动手。这一章我会带你从零创建一个嵌入式Flutter项目,然后把它跑在真实的嵌入式设备上。你可能会觉得「不就是flutter create吗?」——嗯,没那么简单。嵌入式环境有它自己的脾气,咱们得摸清楚。

2.1 创建Flutter项目

先打开终端,找个干净的目录。我个人习惯把嵌入式项目统一放在 ~/embedded_flutter/ 下,方便管理。

flutter create --org com.embflutter --platforms=linux first_embedded_app

这里注意几个关键点:

  • --platforms=linux:嵌入式Flutter目前主要跑在Linux上,所以只生成Linux平台代码。别加android或ios,否则编译时会多出很多不必要的依赖。
  • --org:包名,我习惯用 com.embflutter,方便区分嵌入式项目。

创建完成后,你会看到这样的目录结构:

first_embedded_app/
├── lib/
│   └── main.dart
├── linux/
│   ├── CMakeLists.txt
│   ├── flutter/
│   │   └── CMakeLists.txt
│   └── main.cc
├── test/
├── pubspec.yaml
└── ...

嗯,这里要特别说一下 linux/ 目录。它里面藏着嵌入式设备启动Flutter引擎的C++入口。说白了,你的Dart代码最终是通过这个C++壳子跑起来的。

核心理解:嵌入式Flutter应用 = Dart UI层 + C++引擎层。linux/main.cc就是引擎的启动入口,一般不需要改,但出了问题排查时你得知道它在哪。

2.2 理解项目结构

咱们挑几个关键文件说说:

文件/目录 作用 嵌入式特别说明
lib/main.dart Dart入口,你的UI代码 和移动端一样,但要注意内存
linux/main.cc C++入口,启动Flutter引擎 嵌入式设备可能需修改窗口大小
linux/CMakeLists.txt 构建配置 嵌入式交叉编译时需改这里
pubspec.yaml 依赖管理 嵌入式慎用大包,比如video_player

我记得第一次做嵌入式项目时,盯着 linux/main.cc 看了半天。里面有个 flutter::DartProject 的初始化,我当时以为要改很多,其实大部分场景下保持默认就行。但有一个参数你可能会用到——窗口尺寸:

// linux/main.cc 中
flutter::ViewProperties view_properties;
view_properties.width = 800;
view_properties.height = 480;  // 嵌入式屏幕常见分辨率

如果你的设备是7寸屏、10寸屏,记得在这里改。我踩过这个坑——默认1024x768,结果在800x480的屏上只显示了一半。

2.3 运行到嵌入式设备

好,项目创建好了,结构也懂了。接下来是最激动人心的部分——跑起来。

嵌入式设备和手机不一样,没有USB调试那一套。你得先确定目标设备的架构。常见的有:

  • armv7l:老一点的ARM板子,比如树莓派3B
  • aarch64:64位ARM,比如树莓派4B、RK3588
  • x86_64:少数工控机用这个

假设你用的是树莓派4B(aarch64),编译命令是这样的:

flutter build linux --release --target-arch=arm64

然后你会得到一个 build/linux/arm64/release/bundle/ 目录。把这个目录整个拷贝到嵌入式设备上,比如用scp:

scp -r build/linux/arm64/release/bundle/ pi@192.168.1.100:~/my_app/

在设备上执行:

cd ~/my_app/
./first_embedded_app

如果一切顺利,屏幕上会出现Flutter默认的计数器应用。嗯,那一刻的感觉还是很爽的。

我曾经遇到的坑:第一次运行时,设备上缺少libflutter_engine.so。后来发现是编译时没有把引擎库打包进去。解决方案是在CMakeLists.txt里加上 flutter_engine 的链接路径。如果你也遇到类似问题,先检查bundle目录里有没有 libflutter_engine.so

2.4 热重载与热重启在嵌入式上的表现

这是很多人关心的问题。在手机上,热重载几乎是秒级的。但在嵌入式设备上,情况有点不一样。

首先,嵌入式Flutter 支持热重载,但前提是你得用debug模式运行:

flutter run -d linux --target-arch=arm64

然后你会看到类似这样的输出:

Flutter run key commands.
r Hot reload.
R Hot restart.
h List all available interactive commands.

r 触发热重载。你会发现:

  • 热重载速度:在树莓派4B上,大约2-5秒。比手机慢,但还能接受。
  • 热重启速度:大约10-15秒。因为要重新初始化引擎。
  • 首次启动:最慢,可能要30秒以上,因为要加载引擎库。

我的建议:嵌入式开发时,尽量用热重载。热重启太慢了,每次等得心焦。我一般改UI逻辑时用热重载,改状态管理或初始化代码时才用热重启。

为什么会这样?说白了,嵌入式设备的CPU和内存都有限。树莓派4B的4核Cortex-A72,跟手机上的骁龙8系比,差距还是明显的。所以你要有心理准备——热重载不是秒级的,但比重新编译快多了。

另外,有个小技巧:如果你改了 pubspec.yaml 添加了依赖,必须 flutter pub get 然后热重启,热重载不管用。我刚开始不知道,每次改了依赖就按 r,结果半天没反应,还以为是设备坏了。

2.5 本章小结

这一章我们做了三件事:创建项目、理解结构、跑起来。你可能会觉得内容不多,但嵌入式Flutter的入门门槛就在这里——环境搭好了,后面就顺了。

下一章我们会深入嵌入式设备的硬件交互,比如GPIO、I2C这些。到时候你会发现,Flutter不只是做UI,还能直接控制硬件。嗯,那才是嵌入式开发的真正乐趣所在。