add-to-app 场景下 Flutter 模块使用插件时需要做哪些引擎创建时的初始化?
【免费下载链接】samplesA collection of Flutter examples and demos项目地址: https://gitcode.com/GitHub_Trending/sam/samples
如果你要把一个依赖原生插件的 Flutter module 嵌入现有 Android 或 iOS 应用(add-to-app 场景),光把 module 构建进宿主工程是不够的:宿主应用必须在创建 FlutterEngine 的时候就完成引擎的启动和插件注册,否则url_launcher这类插件在 Dart 侧调用时拿不到原生实现。本文基于 samples 仓库的 add_to_app/plugin 示例 梳理这条初始化路径:它演示了把使用url_launcher、sensors_plus等插件的 module 嵌入现有原生 App 后,Android 和 iOS 各自在引擎创建时写的原生代码,以及如何验证插件确实生效。
准备条件
add_to_app/plugin/README.md 列出的要求:
- Flutter SDK;
- Android:Android Studio;
- iOS:Xcode、Cocoapods(在 add_to_app/README.md 中说明用
sudo gem install cocoapods安装最新 Cocoapods,此命令会以管理员权限修改 macOS 上的 Ruby gem 环境)。
module 侧的依赖在 pubspec.yaml 中声明,包括url_launcher: ^6.0.20、sensors_plus: ^6.1.1、provider: ^6.0.2,并通过flutter: module:段声明androidX: true、androidPackage与iosBundleIdentifier。
引擎创建时的初始化:Android 侧
Android 宿主应用把引擎创建放在Application.onCreate()里,见 MyApplication.kt,核心是四步:
override fun onCreate() { super.onCreate() // 1. 手动创建引擎 val flutterEngine = FlutterEngine(this) // 2. 立即执行 Dart 入口,引擎开始运行 flutterEngine .dartExecutor .executeDartEntrypoint( DartExecutor.DartEntrypoint.createDefault() ) // 3. 以固定 ID 缓存引擎,供后续 Activity 复用 FlutterEngineCache.getInstance().put(ENGINE_ID, flutterEngine) // 4. 在引擎的 dartExecutor 上建立 MethodChannel 并处理 Dart 侧调用 channel = MethodChannel(flutterEngine.dartExecutor, "dev.flutter.example/counter") channel.setMethodCallHandler { call, _ -> when (call.method) { "incrementCounter" -> { count++ reportCounter() } "requestCounter" -> { reportCounter() } } } }其中ENGINE_ID = "1"定义在同文件顶部。原生侧通过channel.invokeMethod("reportCounter", count)把计数回传给 Dart。
之后原生界面不重复创建引擎,而是从缓存取出,见 MainActivity.kt:
button.setOnClickListener { val intent = FlutterActivity .withCachedEngine(ENGINE_ID) .build(this) startActivity(intent) }也就是说,Android 侧的“引擎创建时初始化”= 创建FlutterEngine+ 执行 Dart entrypoint + 放入FlutterEngineCache+ 在dartExecutor上注册 MethodChannel handler;插件本身由 module 的构建产物(implementation project(':flutter')引入的 Flutter 构建输出)在运行时自动注册,宿主需要显式补的主要是上面这套引擎生命周期代码。
引擎创建时的初始化:iOS 侧
iOS 宿主的初始化在 AppDelegate.swift 中,application(_:didFinishLaunchingWithOptions:)里完成三件事:
// Instantiate Flutter engine self.flutterEngine = FlutterEngine(name: "io.flutter", project: nil) self.flutterEngine?.run(withEntrypoint: nil) GeneratedPluginRegistrant.register(with: self.flutterEngine!)与 Android 的差别在于,iOS 侧显式调用了GeneratedPluginRegistrant.register(with:),把 module 用到的原生插件注册到刚创建并启动的引擎上——这是 iOS 宿主使用插件 module 时最关键的一行。引擎以"io.flutter"命名创建、以run(withEntrypoint: nil)启动默认入口。
ViewController.swift 展示了对这个共享引擎的两类复用:
- 用
flutterEngine.binaryMessenger创建FlutterMethodChannel(name: "dev.flutter.example/counter", ...)并处理incrementCounter/requestCounter; - 点击按钮时用同一个引擎创建
FlutterViewController(engine:nibName:bundle:)并present出来,不再新建引擎。
构建与运行
README 给出的最短路径(默认同时准备 Android 和 iOS,且两套工具链都已安装):
#!/bin/bash set -e cd flutter_module_using_plugin flutter pub get # For Android builds: open -a "Android Studio" ../android_using_plugin # macOS only # Or open the ../android_using_plugin folder in Android Studio for other platforms. # For iOS builds: cd ../ios_using_plugin pod install open IOSUsingPlugin.xcworkspace按 README 的说明,open -a "Android Studio" ../android_using_plugin仅限 macOS,其他平台改为手动用 Android Studio 打开../android_using_plugin目录。iOS 侧先pod install再打开IOSUsingPlugin.xcworkspace。只构建其中一个平台时,跳过另一平台对应的命令即可。
如何验证插件初始化成功
示例本身设计了两个可观察的信号:
- 插件行为:Flutter 界面里有一个 "Open Flutter Docs" 按钮(见 main.dart),它调用
launcher.canLaunchUrl/launcher.launchUrl用url_launcher插件在设备浏览器中打开文档页。点击后浏览器被拉起,说明引擎创建时的插件注册已生效;这是 plugin 示例与 fullscreen 示例的差异之一(fullscreen 示例不含插件初始化代码和这个按钮)。 - MethodChannel 通信:原生界面显示
Current count: N(Android)/Current counter: N(iOS),在 Flutter 侧点 "Tap me!" 后原生计数同步变化,反向的reportCounter也驱动 Flutter 界面Taps: N更新,说明dartExecutor/binaryMessenger上建立的 channel 工作正常。
调试方面,add_to_app/README.md 说明可以对正在运行的宿主应用执行flutter attach,获得热重载、断点和 DevTools 等能力,用于排查 Dart 侧问题。
限制与适用条件
- 本示例是全屏嵌入场景;模块级 view 嵌入(如
add_to_app/android_view)的初始化代码不同,不在此范围内。 - iOS 侧依赖
GeneratedPluginRegistrant,该文件由 tooling 依据 module 的pubspec.yaml生成,插件依赖变更后需重新生成/构建;README 未给出更多生成细节,遇到注册缺失时先确认 module 侧flutter pub get与宿主侧pod install已执行。 - 示例中
MyApplication.kt里的 MethodChannel(dev.flutter.example/counter)是示例业务通信,与插件初始化无耦合;你自己的应用需要替换为自己的 channel 名和 handler 逻辑。 - 更多问题可参考 add_to_app/README.md 的 Debugging 与 Troubleshooting 部分。
【免费下载链接】samplesA collection of Flutter examples and demos项目地址: https://gitcode.com/GitHub_Trending/sam/samples
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考