runApp之后会做两件事情。
1)注册widget跟对应的key
FlutterBoost.singleton.registerPageBuilders({
'first': (pageName, params, _) => FirstRouteWidget(),
'second': (pageName, params, _) => SecondRouteWidget(),
},
});
2)初始化widget
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Boost example',
builder: FlutterBoost.init(postPush: _onRoutePushed),
home: Container());
}
首先我们来看FlutterBoost单例实现FlutterBoost.singleton。 首先创建BoostChannel,BoostChannel内创建flutter_boost 的MethodChannel。设置MethodCallHandler。 然后通过channel初始化ContainerCoordinator(协调者)
ContainerCoordinator(BoostChannel channel) {
assert(_instance == null);
_instance = this;
//添加生命周期监听
channel.addEventListener("lifecycle",
(String name, Map arguments) => _onChannelEvent(arguments));
//添加事件监听
channel.addMethodHandler((MethodCall call) => _onMethodCall(call));
}
在iOS端会invoke以下几种类型。 生命周期:
- (void)didEnterBackground
{
[BoostMessageChannel sendEvent:@"lifecycle"
arguments:@{@"type":@"background"}];
}
- (void)willEnterForeground
{
[BoostMessageChannel sendEvent:@"lifecycle"
arguments:@{@"type":@"foreground"}];
}
//最终都会以__event__为method传递到dart
+ (void)sendEvent:(NSString *)eventName
arguments:(NSDictionary *)arguments
{
if(!eventName) return;
NSMutableDictionary *msg = [NSMutableDictionary new];
msg[@"name"] = eventName;
msg[@"arguments"] = arguments;
[self.methodChannel invokeMethod:@"__event__"
arguments:msg
result:^(id r){}];
}
事件:
[self.methodChannel invokeMethod:@"onNativePageResult" arguments:tmp result:^(id tTesult) {
if (result) {
result(tTesult);
}
}];
[self.methodChannel invokeMethod:@"didShowPageContainer" arguments:tmp result:^(id tTesult) {
if (result) {
result(tTesult);
}
}];
//类似的event还有很多,就不一一列举了
//dart端都有自定义的类型与之对应
enum ContainerLifeCycle {
Init,
Appear,
WillDisappear,
Disappear,
Destroy,
Background,
Foreground
}
在MethodCallHandler闭包回调的时候会根据是call.method是否是event来判断执行EventListener的监听闭包_onChannelEvent,还是MethodHandler的的监听闭包_onMethodCall。
接下来让我们来认识下MaterialApp。 MaterialApp的child为WidgetsApp,WidgetsApp内创建了Navigator。
FlutterBoost.init()的时候创建了BoostContainerManager,BoostContainerManager的widget是Overlay。Overlay作用是创建叠加层。
@override
Widget build(BuildContext context) {
return Overlay(
key: _overlayKey,
initialEntries: const <OverlayEntry>[],
);
}
ContainerManagerState内主要创建了以下内容:
class ContainerManagerState extends State<BoostContainerManager> {
//定义了globalKey _overlayKey可以通过_overlayKey.currentState 更新widget,达到界面更换的效果
final GlobalKey<OverlayState> _overlayKey = GlobalKey<OverlayState>();
//不在当前页面的容器
final List<BoostContainer> _offstage = <BoostContainer>[];
//最近的overlayEntry 数组 显示的页面 _ContainerOverlayEntry的builder为每一个页面对应的BoostContainer。所以通过修改_leastEntries可以达到想要的页面切换效果。
List<_ContainerOverlayEntry> _leastEntries;
//当前展示的容器内容
BoostContainer _onstage;
}
接下来我们简单根据iOS唤起的逻辑来看一下flutter_boost如果指定widget进行显示。 当iOS FLBFlutterViewContainer 调用viewWillAppear的时候。
[BoostMessageChannel willShowPageContainer:^(NSNumber *result) {}
pageName:_name
params:_params
uniqueId:self.uniqueIDString];
+ (void)willShowPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId
{
if ([pageName isEqualToString:kIgnoreMessageWithName]) {
return;
}
NSMutableDictionary *tmp = [NSMutableDictionary dictionary];
if(pageName) tmp[@"pageName"] = pageName;
if(params) tmp[@"params"] = params;
if(uniqueId) tmp[@"uniqueId"] = uniqueId;
[self.methodChannel invokeMethod:@"willShowPageContainer" arguments:tmp result:^(id tTesult) {
if (result) {
result(tTesult);
}
}];
}
当flutter端监听到时候会根据method调用到
bool _nativeContainerWillShow(String name, Map params, String pageId) {
if (FlutterBoost.containerManager?.containsContainer(pageId) != true) {
FlutterBoost.containerManager
?.pushContainer(_createContainerSettings(name, params, pageId));
}
void pushContainer(BoostContainerSettings settings) {
//添加新的容器 确保新的容器不在_onstage跟_offstage中
assert(settings.uniqueId != _onstage.settings.uniqueId);
assert(_offstage.every((BoostContainer container) =>
container.settings.uniqueId != settings.uniqueId));
_offstage.add(_onstage);
// 根据settings获取到当前应该展示的容器
_onstage = BoostContainer.obtain(widget.initNavigator, settings);
setState(() {});
}
setState((){})之后会调用_refreshOverlayEntries。
void _refreshOverlayEntries() {
//获取到globalkey对应的overlayState
final OverlayState overlayState = _overlayKey.currentState;
if (overlayState == null) {
return;
}
//先移除所有的_ContainerOverlayEntry
if (_leastEntries != null && _leastEntries.isNotEmpty) {
for (_ContainerOverlayEntry entry in _leastEntries) {
entry.remove();
}
}
final List<BoostContainer> containers = <BoostContainer>[];
//先添加当前不显示的
containers.addAll(_offstage);
assert(_onstage != null, 'Should have a least one BoostContainer');
//再添加当前要显示的
containers.add(_onstage);
//转换成_ContainerOverlayEntry,_ContainerOverlayEntry的builder是BoostContainer
_leastEntries = containers
.map<_ContainerOverlayEntry>(
(BoostContainer container) => _ContainerOverlayEntry(container))
.toList(growable: false);
//栈结构 最后面添加的即是要显示的 也就是_onstage
overlayState.insertAll(_leastEntries);
}
目前我们就可以通过以下方法在iOS端展示我们想要的容器:
[FlutterBoostPlugin open:@"first" urlParams:@{kPageCallBackId:@"MycallbackId#1"} exts:@{@"animated":@(YES)} onPageFinished:^(NSDictionary *result) {
NSLog(@"call me when page finished, and your result is:%@", result);
} completion:^(BOOL f) {
NSLog(@"page is opened");
}];
最终都会走到实现FLBPlatform协议的方法内:
- (void)open:(NSString *)name
urlParams:(NSDictionary *)params
exts:(NSDictionary *)exts
completion:(void (^)(BOOL))completion;
- (void)present:(NSString *)name
urlParams:(NSDictionary *)params
exts:(NSDictionary *)exts
completion:(void (^)(BOOL))completion;
在iOS容器FLBFlutterViewContainer内我们之前分析了ViewWillAppear,我们再继续分析下dealloc,毕竟涉及到内存相关是我们比较关心的部分。
- (void)dealloc
{
[self notifyWillDealloc];
[NSNotificationCenter.defaultCenter removeObserver:self];
}
- (void)notifyWillDealloc
{
[BoostMessageChannel willDeallocPageContainer:^(NSNumber *r) {}
pageName:_name params:_params
uniqueId:[self uniqueIDString]];
[FLUTTER_APP removeViewController:self];
[self.class instanceCounterDecrease];
}
+ (void)willDeallocPageContainer:(void (^)(NSNumber *))result pageName:(NSString *)pageName params:(NSDictionary *)params uniqueId:(NSString *)uniqueId
{
[self.methodChannel invokeMethod:@"willDeallocPageContainer" arguments:tmp result:^(id tTesult) {
if (result) {
result(tTesult);
}
}];
}
flutter端监控到willDeallocPageContainer调用以下方法:
bool _nativeContainerWillDealloc(String name, Map params, String pageId) {
FlutterBoost.containerManager?.remove(pageId);
return true;
}
但是这个时候我们要注意在iOS端生命周期函数 当first_VC push到second_VC的时候再从second_VC返回的时候会按照以下顺序调用 1、second_VC的ViewWillDisappear 2、first_VC viewWillAppear 3、second_VC viewDidDisappear 4、first_VC viewDidAppear 5、second_VC dealloc
//所以我们会先调用first_VC对于的容器对应的showContainer。
void showContainer(BoostContainerSettings settings) {
//这个时候不想等 跳到下面
if (settings.uniqueId == _onstage.settings.uniqueId) {
_onShownContainerChanged(null, settings.uniqueId);
return;
}
//从_offstage内找到即将要显示的容器赋值给_onstage
final int index = _offstage.indexWhere((BoostContainer container) =>
container.settings.uniqueId == settings.uniqueId);
if (index > -1) {
_offstage.add(_onstage);
_onstage = _offstage.removeAt(index);
setState(() {});
} else {
pushContainer(settings);
}
}
然后监听dealloc调用到remove 在_offstage中remove之前的container。
void remove(String uniqueId) {
if (_onstage.settings.uniqueId == uniqueId) {
pop();
} else {
final BoostContainer container = _offstage.firstWhere(
(BoostContainer container) => container.settings.uniqueId == uniqueId,
orElse: () => null);
if (container != null) {
_offstage.remove(container);
setState(() {});
}
}
return null;
}
flutter_boost相关源码就暂且分析到这了。