likes
comments
collection
share

Flutter中多module集成

作者站长头像
站长
· 阅读数 3

1. FlutterEngine

flutter 作为模块嵌入iOS工程或者android工程。我们可以参考下官方文档, FlutterEngine 充当 Dart VM 和 Flutter 运行时的主机; FlutterViewController 依附于 FlutterEngine,给 Flutter 传递 UIKit 的输入事件,并展示被 FlutterEngine 渲染的每一帧画面。

FlutterEngine文档

创建FlutterEngine:

import UIKit
import Flutter
// Used to connect plugins (only if you have plugins with iOS platform code).
import FlutterPluginRegistrant

@UIApplicationMain
class AppDelegate: FlutterAppDelegate { // More on the FlutterAppDelegate.
  lazy var flutterEngine = FlutterEngine(name: "my flutter engine")

  override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Runs the default Dart entrypoint with a default Flutter route.
    flutterEngine.run();
    // Used to connect plugins (only if you have plugins with iOS platform code).
    GeneratedPluginRegistrant.register(with: self.flutterEngine);
    return super.application(application, didFinishLaunchingWithOptions: launchOptions);
  }
}

使用

  let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine
  let flutterViewController =
       FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
   present(flutterViewController, animated: true, completion: nil)

2. 加载多个modules

我们想要加载多个modules的时候,尝试使用在podfile文件中

flutter_application_path = '../../mall/mall_flutter_module'

flutter_application_path1 = '../../testModule'

load File.join(flutter_application_path,'.ios','Flutter','podhelper.rb')
load File.join(flutter_application_path1,'.ios','Flutter','podhelper.rb')

target 'xxx' do
install_all_flutter_pods(flutter_application_path)
install_all_flutter_pods(flutter_application_path1)

end

pod install

Flutter中多module集成

报错,已经导入了,之后我尝试使用数组的形式inatll,也是无法install,之后进行下图操作

Flutter中多module集成

覆盖了之前的mallModul

  • 手动添加 我们添加flutter Module的时候也有手动的效果
flutter build ios-framework --output=some/path/MyApp/Flutter/

效果

some/path/MyApp/
└── Flutter/
    ├── Debug/
    │   ├── Flutter.xcframework
    │   ├── App.xcframework
    │   ├── FlutterPluginRegistrant.xcframework (only if you have plugins with iOS platform code)
    │   └── example_plugin.xcframework (each plugin is a separate framework)
    ├── Profile/
    │   ├── Flutter.xcframework
    │   ├── App.xcframework
    │   ├── FlutterPluginRegistrant.xcframework
    │   └── example_plugin.xcframework
    └── Release/
        ├── Flutter.xcframework
        ├── App.xcframework
        ├── FlutterPluginRegistrant.xcframework
        └── example_plugin.xcframework

始终使用相同目录下的 Flutter.framework 和 App.framework。混合使用不同目录(例如 Profile/Flutter.framework 以及 Debug/App.framework)将会导致运行失败。

Flutter中多module集成

Xcode工程添加

Flutter中多module集成

不论是使用framework还是pods导入,实际上是导入上面的2个framework,包含flutter环境和app实际的代码

3. 解决方式

最后,与我的同事讨论了所有这些,他们建议尝试将所有模块导入一个类似伞的项目中,然后导入到本机应用程序中。因此,我尝试将这两个模块作为第三个模块的包导入,这里称为umbrella

umbrella/pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^0.1.2
  login:
    path: ../flutter_modules/login
  register:
    path: ../flutter_modules/register

那行得通,但是您必须在模块中创建路由umbrella以映射loginregister小部件。

因此需要使用一个胶水项目,来管理这些module

创建一个胶水项目


void main() => runApp(UmbrellaApp(route: window.defaultRouteName));

class UmbrellaApp extends StatelessWidget {
  final String route;

  UmbrellaApp(this.route);

  @override
  Widget build(BuildContext context) {
    switch (route) {
      // UmbrellaModule is class holding static strings.
      case UmbrellaModules.login:  // UmbrellaModules.login = 'login_module'
        return LoginModule();
      case UmbrellaModules.register:
        return RegisterModule();
      default:
        return ErrorRoute();
    }
  }
}

iOS 原生应用:

let flutterViewController = UmbrellaViewController()
flutterViewController.setInitialRoute("oobe_module")
self.present(flutterViewController, animated: false, completion: nil)
// UmbrellaViewController
class UmbrellaViewController: FlutterViewController

4.优化

在 FlutterEngine 上调用 run,默认将会调用你的 lib/main.dart 文件里的 main() 函数。

你也可以使用另一个入口方法 runWithEntrypoint,并使用 NSString 字符串指定一个不同的 Dart 入口。

使用 main() 以外的 Dart 入口函数,必须使用下面的注解,防止被 tree-shaken 优化掉,而没有编译。

  @pragma('vm:entry-point')
  void myOtherEntrypoint() { ... };

另外,在指定 Dart 函数时,你可以指定特定文件的特定函数。

下面的例子使用 lib/other_file.dart 文件的 myOtherEntrypoint() 函数取代 lib/main.dart 的 main() 函数:

flutterEngine.run(withEntrypoint: "myOtherEntrypoint", libraryURI: "other_file.dart")

但是报错

**Dart Error: Dart_LookupLibrary: library 'other_file.dart' not found.**

我们看官方注释 Flutter中多module集成

flutterEngine.run(withEntrypoint: entryPoint, libraryURI: ":package:multiple_flutters_module/other_file.dart")

但是还是有点问题

2022-09-08 16:20:48.674755+0800 MultipleFluttersIos[17128:42337128] [VERBOSE-2:shell.cc(93)] Dart Error: Dart_LookupLibrary: library ':package:multiple_flutters_module/other_file.dart' not found.

最终我们使用在main函数中执行不同的入口


void main() => runApp(const MyApp(color: Colors.blue));

@pragma('vm:entry-point')
void topMain() => runApp(const MyApp(color: Colors.green));

@pragma('vm:entry-point')
void bottomMain() => runApp(const MyApp(color: Colors.purple));

在 Android 和 iOS 上添加多个 Flutter 实例的主要 API 是基于新的 FlutterEngineGroup 类 (Android APIiOS API) 来创建 FlutterEngine 的,而不是通过以前的 FlutterEngine 构造。

尽管 FlutterEngine API 的用法简洁明了,但从 FlutterEngineGroup 生成的 FlutterEngine 具有常用共享资源(例如 GPU 上下文、字体度量和隔离线程的快照)的性能优势,从而加快首次渲染的速度、降低延迟并降低内存占用。

  • 由 FlutterEngineGroup 生成的 FlutterEngine 可以用来关联 UI 相关的类,例如 FlutterActivity 或 FlutterViewController,与通常构造缓存的 FlutterEngine 类似。

  • 第一个 FlutterEngineGroup 生成的 FlutterEngine 不需要持续保活,只要有 1 个可用的 FlutterEngine,就可以随时在各个 FlutterEngine 之间共享资源。

  • 通过 FlutterEngineGroup 生成的首个 FlutterEngine 与使用先前的构造方法构造的 FlutterEngine 有相同的性能特征

  • 当所有由 FlutterEngineGroup 构造的 FlutterEngine 都被销毁后,下一个创建的 FlutterEngine 与首个创造的性能特征相同。

  • FlutterEngineGroup 本身不需要持续保活。将其销毁后,已生成的 FlutterEngine 不受影响,但无法继续在现有共享的基础上创建新引擎。

在delegate

let engines = FlutterEngineGroup(name: "multiple-flutters", project: nil)

初始化

class SingleFlutterViewController: FlutterViewController {

  private var channel: FlutterMethodChannel?


  init(withEntrypoint entryPoint: String?) {

    let appDelegate: AppDelegate = UIApplication.shared.delegate as! AppDelegate

    let newEngine = appDelegate.engines.makeEngine(withEntrypoint: entryPoint, libraryURI:nil)

    GeneratedPluginRegistrant.register(with: newEngine)

    super.init(engine: newEngine, nibName: nil, bundle: nil)


  }
  }

Flutter中多module集成

5 实际中的表现

展示flutter商城,并随意点击

Flutter中多module集成

最终稳定在430左右,最高441

Flutter中多module集成

初始化Engine,不展示flutter商城页面

Flutter中多module集成

首页正常使用后增加内存

Flutter中多module集成

使用FlutterEngineGroup加载测试模块

Flutter中多module集成

使用group 加载模块2

Flutter中多module集成

打开商城使用独立的Engine

FlutterEngine(name: "io.flutter", project: nil)

Flutter中多module集成

内存使用情况会变大,此时内存中加载了2个Engine,3个渲染的FlutterViewController

模块全部使用group

Flutter中多module集成

切换模块一

Flutter中多module集成

切换模块三

Flutter中多module集成

没有明显的增加,因为flutter模块共用了一个渲染引擎,减少了内存压力,同时也方便处理以前的模块和新模块的关系。