原生跳转FlutterViewController的几种方式
在将Flutter项目接入到iOS原生项目中,需要使用FlutterViewController进行跳转展示,目前官方给出了三种跳转方式:
- 单个Flutter页面
官方推荐在程序启动时,先预热FlutterEngine,也就是在AppDelegate中先初始化FlutterEnging,具体的代码是: OC代码:
objectivec
self.flutterEngine = [[FlutterEngine alloc] initWithName:@"my flutter engine"];//FlutterEngine 名,每个Flutter对应的名称
// Runs the default Dart entrypoint with a default Flutter route.
[self.flutterEngine run];//启动FlutterEngine
// Connects plugins with iOS platform code to this app.
[GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];//如果使用了插件,则需要使用对应的FlutterEngine注册
Swift代码:
php
lazy var flutterEngine = FlutterEngine(name: "my flutter engine")
flutterEngine.run();
// Connects plugins with iOS platform code to this app.
GeneratedPluginRegistrant.register(with: self.flutterEngine);
return super.application(application, didFinishLaunchingWithOptions: launchOptions);
然后在需要跳转到Flutter界面的时候直接将已经加载好的FlutterEngine传入进FlutterViewController中即可: OC代码:
ini
FlutterEngine *flutterEngine = ((AppDelegate *)UIApplication.sharedApplication.delegate).flutterEngine;
FlutterViewController *flutterViewController = [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];
[self presentViewController:flutterViewController animated:YES completion:nil];
Swift代码:
swift
let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine
let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
present(flutterViewController, animated: true, completion: nil)
这种的好处是,由于FlutterEngine已经提前预热加载,在进行跳转的时候几乎和原生界面跳转无差异,用户体验良好。
- 隐式FlutterEngine创建FlutterViewController
OC代码:
ini
FlutterViewController *flutterViewController =
[[FlutterViewController alloc] initWithProject:nil nibName:nil bundle:nil];
[self presentViewController:flutterViewController animated:YES completion:nil];
Swift代码:
php
let flutterViewController = FlutterViewController(project: nil, nibName: nil, bundle: nil)
present(flutterViewController, animated: true, completion: nil)
这种情况下,Flutter会在跳转FlutterViewController的时候创建FlutterEngine,虽然操作方便了,但是在跳转的时候会有明显的卡顿情况,用户体验不友好。
- 多个Flutter页面时,使用FlutterEngineGroup 代码:
ini
创建EngineGroup
self.flutterEngineGroup = [[FlutterEngineGroup alloc] initWithName:@"flutterGroupName" project:NULL];
使用时:
objectivec
FlutterEngine *engine = [[SVFlutterEngine shared].engineGroup makeEngineWithEntrypoint:nil libraryURI:nil initialRoute:nil];//使用Group创建一个FlutterEngine
FlutterViewController *flutterVC = [[FlutterViewController alloc]initWithEngine:engine nibName:nil bundle:nil];
[self presentViewController:flutterVC animated:YES completion:nil];
这种效果比第二种好一些,但是也会有稍微的卡顿,感觉就是不彻底。App要商用,肯定不能给这种让用户去体验。效果肯定是第一种最佳,但是第一种会引入新的问题,如果app中仅跳转一个固定页面作为根页面那么到还好,如果需要改变initialRoute页面,则需要寻找额外的方式。
Flutter 设置initialRoute的几种方式
目前Flutter给出这么几种方式设置跳转到FlutterView的方式,
- 在进行FlutterEngine初始化的时候进行设置,方法如下:
ini
self.flutterEngine = [[FlutterEngine alloc]initWithName:@"flutter_engine"];
[self.flutterEngine runWithEntrypoint:nil libraryURI:nil initialRoute:@"/home"];
设置路由为/home,需要先在Flutter 的MaterialApp的参数getPages
中设置路由 2. 隐式创建FlutterEngine的时候使用FlutterViewController初始化方法设置initialRoute
go
FlutterViewController *flutterVC = [[FlutterViewController alloc]initWithProject:nil initialRoute:@"/home" nibName:nil bundle:nil];
- 使用EngineGroup创建Flutter Engine的时候也有对应的方法
go
[engineGroup makeEngineWithEntrypoint:nil libraryURI:nil initialRoute:@"/home"];
但是需要注意的是,每个FlutterEngine仅能设置一次initialRoute,如果你的app有多个需要从原生直接跳Flutter页面的话,那么按照目前的情况你可能得维护多个FlutterEngine的缓存和生命周期。
这里顺带提一下,Flutter中的Entrypoint参数和libraryURI参数,这两个参数可以让你在启动Flutter界面是指定启动入口,其中Entrypoint指定启动参数名,libraryURI指定启动文件地址:
csharp
//正常不设置Entrypoint和libraryURI情况下
void main() => runApp(const MyApp());
设置Entrypoint:routeParse libraryURI:nil时进这里,如果这个入口写在其他的文件里则在libraryURI参数中传入文件路径
@pragma('vm:entry-point')
void routeParse(){
debugPrint("ssl current route1:routeParse");
runApp(const MyApp());
}
最终解决方案
1、用空间换时间:提前缓存多个与FlutterEngine,需要跳转的时候使用对应的FlutterEngine跳转 2、使用FlutterViewController 自带的pushRoute,每次进入的时候传入对应需要push的路由,比如:
定义路由:
csharp
static const root_router = '/home';
static const route_add = '/add';
static List<GetPage> pages = [
GetPage(name: root_router, page: ()=> const SVHome()),
GetPage(name: route_add, page: ()=> const SVAdd()),
];
//初始化引擎根视图
_flutterEngine = [[FlutterEngine alloc]initWithName:@"flutter_engine"];
[self.flutterEngine runWithEntrypoint:nil libraryURI:nil initialRoute:@"/home"];
//在FlutterViewController 中调用,跳转到add路由
[self pushRoute:@"/add"];
这种方法和直接跳转到根视图从感官上几乎无感,可以很好的解决需要缓存多个FlutterEngine的问题,但是需要注意的是:当你从add界面直接退出Flutter界面时,需要先返回根视图,否则下次会从add界面push到新页面。 这个需要自己维护了。