thrio 是一个支持 flutter 嵌入原生应用的路由库,目前只有 iOS 版本可看,Android 版本在开发中。
thrio 的诞生主要是为了解决我们自身的业务问题。
我们目前积累了将近 10 万行 Dart 业务代码,早期的时候采用 flutter_boost 提供的解决方案来实现将 Flutter 嵌入原生应用,使用过程中也积累了很多对 flutter_boost 改造的需求,但因为 flutter_boost 的路线图短期或者长期都看不到能满足我们这些需求的可能,所以我们只好自己造了一个轮子。
- 三端统一的打开页面的接口,至少支持 push,支持多开页面实例,flutter_boost 支持
- 三端统一的关闭页面的接口,至少支持关闭顶层页面,关闭特定页面,关闭到特定页面,flutter_boost 支持前两点,第三点不支持
- 三端统一的页面间通知的接口,一定要支持特定页面间的通知传递,flutter_boost 支持不好,无法满足特定页面间的通知传递
- dart 页面导航栏自动隐藏,且不影响原生页面的导航栏,flutter_boost 不支持,需要自行扩展
- iOS 的 FlutterViewController 要支持侧滑返回,flutter_boost 不支持
- 原生页面和 dart 页面要支持页面禁止关闭,flutter_boost 支持了 dart 页面,但页面动画缺失
- 支持 FlutterViewController 内嵌套 Dart 页面,flutter_boost 的支持非常弱
以上对 flutter_boost 的一些判断可能不准确,仅做对比。
- dart 中注册页面路由
class Module with ThrioModule {
@override
void onPageRegister() {
registerPageBuilder(
'flutter3',
(settings) => Page3(index: settings.index, params: settings.params),
);
registerPageBuilder(
'flutter4',
(settings) => Page4(index: settings.index, params: settings.params),
);
}
}
- iOS 中注册页面路由
- (void)onPageRegister {
[self registerNativeViewControllerBuilder:^UIViewController * _Nullable(NSDictionary<NSString *,id> * _Nonnull params) {
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
return [sb instantiateViewControllerWithIdentifier:@"ThrioViewController"];
} forUrl:@"native1"];
}
- Dart 端打开页面
ThrioNavigator.push(url: 'flutter1');
// 传入参数
ThrioNavigator.push(url: 'native1', params: { '1': {'2': '3'}});
// 是否动画,目前在内嵌的dart页面中动画无法取消,原生iOS页面有效果
ThrioNavigator.push(url: 'native1', animated:true);
- iOS 端打开页面
[ThrioNavigator pushUrl:@"flutter1"];
- dart 端关闭顶层页面
// 默认动画开启
ThrioNavigator.pop();
// 不开启动画,原生和dart页面都生效
ThrioNavigator.pop(animated: false);
- iOS 端关闭顶层页面
// 默认动画开启
[ThrioNavigator pop];
// 关闭动画
[ThrioNavigator popAnimated:NO];
- dart 端关闭到页面
// 默认动画开启
ThrioNavigator.popTo(url: 'flutter1');
// 不开启动画,原生和dart页面都生效
ThrioNavigator.popTo(url: 'flutter1', animated: false);
- iOS 端关闭到页面
// 默认动画开启
[ThrioNavigator popToUrl:@"flutter1"];
// 关闭动画
[ThrioNavigator popToUrl:@"flutter1" animated:NO];
- dart 端关闭特定页面
ThrioNavigator.remove(url: 'flutter1');
// 只有当页面是顶层页面时,animated参数才会生效
ThrioNavigator.remove(url: 'flutter1', animated: true);
- iOS 端关闭特定页面
[ThrioNavigator removeUrl:@"flutter1"];
// 只有当页面是顶层页面时,animated参数才会生效
[ThrioNavigator removeUrl:@"flutter1" animated:NO];
给一个页面发送通知,只有当页面呈现之后才会收到该通知。
- dart 端给特定页面发通知
ThrioNavigator.notify(url: 'flutter1', name: 'reload');
- iOS 端给特定页面发通知
[ThrioNavigator notifyUrl:@"flutter1" name:@"reload"];
- dart 端接收页面通知
使用NavigatorPageNotify
这个Widget来实现在任何地方接收当前页面收到的通知。
NavigatorPageNotify(
name: 'page1Notify',
onPageNotify: (params) =>
ThrioLogger().v('flutter1 receive notify: $params'),
child: Xxxx());
- iOS 端接收页面通知
UIViewController
实现协议NavigatorNotifyProtocol
,通过该协议定义的方法来接收页面通知
- (void)onNotify:(NSString *)name params:(NSDictionary *)params {
ThrioLogV(@"native1 onNotify: %@, %@", name, params);
}
实际上实现了 UIViewController 的分类扩展,FlutterViewController 强制设为 YES,原生页面设置导航栏隐藏,也很简单
viewController.thrio_hidesNavigationBar = NO;
FlutterViewController 默认是不支持侧滑返回的,因为 thrio 支持一个 FlutterViewController 可以打开任意多个 dart 页面,dart 页面本身也是要支持侧滑返回的,手势上存在一定的冲突,在这里 thrio 做了一些特殊处理,基本上支持无缝切换。有兴趣可以参看源码实现。
- dart 端禁止特定页面关闭
ThrioNavigator.setPopDisabled(url: 'flutter1');
- iOS 端禁止特定页面关闭
[ThrioNavigator setPopDisabledUrl:@"flutter1" disabled:NO];
在 dart 端依然支持通过 WillPopScope 来设置禁止页面返回。
这是谷歌推荐的实现方式,导航栈中不被原生页面分隔的所有Flutter页面共用一个原生的容器页面,有效减少内存消耗量。
- iOS端
- 在 iOS 中,打开一个新的 FlutterViewController,内存消耗大概是12M+,打开一个内嵌的Flutter页面,内存消耗不会超过2M,目前在我们的商家App上,95%的页面都不会需要从Flutter页面跳转到原生页面,整个App的内存占用将会减少很多。