3、Flutter Module创建:使用flutter create --template module创建模块、模块目录结构解析、与原生工程的依赖关系

好,咱们直接进入正题。这一章讲的是Flutter Module的创建,说白了就是怎么在原生项目里塞进一个Flutter模块。我刚开始搞混合开发时,也踩过不少坑,尤其是目录结构和依赖关系这块,容易让人懵。今天咱们一步步拆开来看。

3.1 使用flutter create --template module创建模块

创建Flutter Module,官方推荐用这条命令:

flutter create --template module my_flutter_module

这条命令会生成一个名为my_flutter_module的目录。它跟普通的Flutter项目不一样,不是用来独立运行的,而是作为原生工程的依赖库存在。

我个人习惯把模块名起得短一点,比如flutter_module,因为后面在原生工程里引用时,名字太长容易写错。嗯,这里要注意:模块名不要用驼峰,建议全小写加下划线,否则在Android的Gradle配置里可能会报错。

核心区别:普通Flutter项目有lib/main.dart作为入口,而Module项目没有main.dart,取而代之的是lib/main.dart(其实也有,但通常只放一个空壳)。真正的入口由原生工程通过FlutterEngine来启动。

3.2 模块目录结构解析

创建完成后,我们看看目录里都有什么。我直接列出来:

my_flutter_module/
├── .android/          # Android原生壳工程(隐藏,自动生成)
├── .ios/              # iOS原生壳工程(隐藏,自动生成)
├── lib/               # Dart代码目录
│   └── main.dart      # 模块入口(通常只做导出)
├── test/              # 测试代码
├── pubspec.yaml       # Flutter模块的依赖配置
├── build.gradle       # Android构建配置(用于发布AAR)
└── CMakeLists.txt     # 桌面端支持(可选)

这里有几个关键点,我当年第一次看到.android.ios目录时,还以为是项目出错了。其实它们是Flutter自动生成的壳工程,用来在开发阶段调试用的。你想想看,如果没有这两个壳,你怎么在模拟器里跑Flutter代码?

但注意:这两个目录不要手动修改。每次执行flutter pub getflutter build时,它们会被重新生成。我曾经手贱改过.android里的Gradle配置,结果下次构建时全被覆盖了,白忙活一场。

避坑指南:如果你需要自定义Android原生配置(比如修改minSdkVersion),应该在android/目录(如果有)或通过pubspec.yaml中的flutter:配置项来设置,而不是直接改.android

3.3 与原生工程的依赖关系

这是最核心的部分。Flutter Module怎么跟原生工程关联起来?说白了就两种方式:

方式一:本地依赖(开发阶段)

在原生工程的settings.gradle里添加:

// Android
setBinding(new Binding([gradle: this]))
evaluate(new File(
    settingsDir.parentFile,
    'my_flutter_module/.android/include_flutter.groovy'
))

include ':my_flutter_module'
project(':my_flutter_module').projectDir = new File('../my_flutter_module')

然后在app/build.gradle里添加依赖:

implementation project(':flutter')

这种方式的好处是:你改Flutter代码后,热重载直接生效,不用重新打包。我平时开发都用这个方式,效率高。

方式二:AAR发布(生产阶段)

用命令打包成AAR:

flutter build aar

然后原生工程通过Maven仓库引用:

repositories {
    maven {
        url 'my_flutter_module/build/host/outputs/repo'
    }
}

dependencies {
    implementation 'com.example.flutter_module:flutter_release:1.0'
}

这种方式适合CI/CD流水线,或者给其他团队使用。但注意:每次Flutter代码更新,都需要重新打包AAR并更新版本号。

重要提醒:两种方式不要混用。如果你在开发阶段用了本地依赖,发布时又改成AAR,很容易出现版本不一致的问题。我建议:开发用本地依赖,发布前统一切到AAR并做一次完整回归测试。

3.4 依赖关系图

为了让你更直观地理解,我画了一张图:

原生工程 Android / iOS Main Activity / AppDelegate Flutter Module lib/ pubspec.yaml 依赖 FlutterEngine Dart VM + Platform Channels 原生工程通过 FlutterEngine 加载 Flutter Module 两者通过 Platform Channel 通信

这张图很清楚地展示了:原生工程不直接跟Flutter Module的Dart代码打交道,而是通过FlutterEngine这个中间层。所有的UI渲染、事件传递,都经过这个引擎。这也是为什么Flutter混合开发性能好的原因——它有自己的渲染引擎,不依赖原生控件。

3.5 实战建议

最后,我分享几个实战中的小经验:

  • 模块位置:我建议把Flutter Module放在原生工程的同级目录,而不是子目录。这样两个项目各自独立,互不干扰。
  • 版本管理:Flutter Module和原生工程最好在同一个Git仓库里,用submodule或者monorepo的方式管理。否则版本同步起来很痛苦。
  • 依赖版本:Flutter Module的pubspec.yaml里不要写死版本号,用^范围依赖。这样升级Flutter SDK时,依赖库能自动适配。

个人经验:我曾经在一个项目里,Flutter Module和原生工程分开两个Git仓库,结果每次发版都要手动确认版本对应关系,出了好几次线上事故。后来改成monorepo,用同一个pubspec.yaml管理版本,再也没出过问题。

好了,这一章的内容就到这里。创建Flutter Module其实不难,关键是理解它的目录结构和依赖关系。下一章我们会讲如何把Flutter Module集成到Android原生工程中,包括Gradle配置、FlutterEngine初始化等细节。


公众号:蓝海资料掘金营,微信deep3321