flutter_boost 实现原理,源码解读

2020-08-11  本文已影响0人  564a

[TOC]

序言

在学习flutter之后,大多数的情况都是混合编程,在原生中使用flutter以页面或模块为单位介入(最小介入单元界面iOS中称为viewcontroller)。

在以界面为单位介入flutter是遇到一个问题,就是无法替换flutter module中的root界面,哪怕设置了initialRoute属性也没有用(
不管是在原生中设置:

    flutterViewController.setInitialRoute("/test_page/aa"),

还是在flutter中设置

    initialRoute: "/test_page",

)都不生效。

让人很头疼,单也不是没办法解决,使用
flutterViewController.pushRoute("/test_page/sss")
直接显示自己的界面,跟页面就不管他就行了,如果要用到flutter的导航控制器的话需要手动管理一下,不然会退到跟页面显示很不友好

flutter boost 使用

之后看到flutter boost 很好的解决了跟页面的问题,就下载下来使用

// flutter_boost 依赖配置
flutter_boost:
  git:
    url: 'https://github.com/alibaba/flutter_boost.git'
    ref: 'v1.17.1-hotfixes'

配置好后开始使用,如下:

@override
Widget build(BuildContext context) {
  return StoreProvider<AppState>(
    store: store,
    child: MaterialApp(
      theme: ThemeData(highlightColor: Color.fromRGBO(0, 0, 0, 0), splashColor: Color.fromRGBO(0, 0, 0, 0)),
      title: 'CHERY',
      builder: FlutterBoost.init(postPush: _onRoutePushed),
      home: Home(),
    ),
  );
}

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    ScreenUtil.init(context, width: 750, height: 1334);
    return Container(
      color: Color.fromARGB(255, 255, 0, 253),
      child: Center(
        child: Text("root page"),
      ),
    );
  }
}

Home效果图,后面有用
Screen Shot 2020-08-11 at 9.42.13 AM.png

想测试一下flutter boost 实现是不是也是用的push的方法,并没有真正的改变跟页面,做如下测试:
在页面展示后加定时器pop出这个页面,代码如下:

Timer timer = new Timer(new Duration(seconds: 5), () {
  print("time up pop -------");
  Navigator.pop(context);
});

运行后页面并没有推出到跟页面。

flutter源码阅读

很好奇他是怎么做到改变跟页面的,于是去读flutter boost 的源码

结合原生代码,viewcontroller的生命周期发现如下代码

flutter boost 内 iOS原生的生命周期消息发送

- (void)viewWillDisappear:(BOOL)animated
{
    [BoostMessageChannel willDisappearPageContainer:^(NSNumber *result) {}
                                                 pageName:_name
                                                   params:_params
                                                 uniqueId:self.uniqueIDString];
    [[[UIApplication sharedApplication] keyWindow] endEditing:YES];
    [super viewWillDisappear:animated];
}

//BoostMessageChannel 文件代码
 + (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);
         }
     }];
 }

是以channel通信的方式将原生的生命周期嫁接到flutter端来处理

flutter boost flutter 内接受iOS原生生命周期的方法

//ios view controller 生命周期
  Future<dynamic> _onMethodCall(MethodCall call) {
    Logger.log('onMetohdCall ${call.method}');

    final String pageName = call.arguments['pageName'] as String;
    final Map<String, dynamic> params =
        (call.arguments['params'] as Map<dynamic, dynamic>)
            ?.cast<String, dynamic>();
    final String uniqueId = call.arguments['uniqueId'] as String;

    switch (call.method) {
      case 'didInitPageContainer':
        _nativeContainerDidInit(pageName, params, uniqueId);
        break;
      case 'willShowPageContainer':
        _nativeContainerWillShow(pageName, params, uniqueId);
        break;
      case 'didShowPageContainer':
        nativeContainerDidShow(pageName, params, uniqueId);
        break;
      case 'willDisappearPageContainer':
        _nativeContainerWillDisappear(pageName, params, uniqueId);
        break;
      case 'didDisappearPageContainer':
        _nativeContainerDidDisappear(pageName, params, uniqueId);
        break;
      case 'willDeallocPageContainer':
        _nativeContainerWillDealloc(pageName, params, uniqueId);
        break;
      case 'onNativePageResult':
        break;
    }

    return Future<dynamic>(() {});
  }

flutter 内 iOS 生命周期中的viewwillappear的方法实现

bool _nativeContainerWillShow(
    String name,
    Map<String, dynamic> params,
    String pageId,
) {
    print("_nativeContainerWillShow--------");
    if (FlutterBoost.containerManager?.containsContainer(pageId) != true) {
      print("_nativeContainerWillShow--------");
      FlutterBoost.containerManager?.pushContainer(
        _createContainerSettings(name, params, pageId),
      );
    }

    // TODO(unknown): 需要验证android代码是否也可以移到这里
    if (Platform.isIOS) {
      try {
        final SemanticsOwner owner =
            WidgetsBinding.instance.pipelineOwner?.semanticsOwner;
        final SemanticsNode root = owner?.rootSemanticsNode;
        root?.detach();
        root?.attach(owner);
      } catch (e) {
        assert(false, e.toString());
      }
    }
    return true;
}

看到了push的字眼,我又回到了,我以前的想法(跟页面还在,push了新的页面?),抱着这个想法想继续看代码

发现重写了导航控制器,如下:

//重写的navigatir 文件名boost_container.dart
class ContainerNavigatorObserver extends NavigatorObserver {
  
  @override
  void didPush(Route<dynamic> route, Route<dynamic> previousRoute) {
    print("didPush--------");
    for (final NavigatorObserver observer in boostObservers) {
      observer.didPush(route, previousRoute);
    }
  }

  @override
  void didPop(Route<dynamic> route, Route<dynamic> previousRoute) {
    print("didPop--------");
    for (final NavigatorObserver observer in boostObservers) {
      observer.didPop(route, previousRoute);
    }
  }

  @override
  void didRemove(Route<dynamic> route, Route<dynamic> previousRoute) {
    print("didRemove--------");
    for (final NavigatorObserver observer in boostObservers) {
      observer.didRemove(route, previousRoute);
    }
  }

  @override
  void didReplace({Route<dynamic> newRoute, Route<dynamic> oldRoute}) {
    print("didReplace--------");
    for (final NavigatorObserver observer in boostObservers) {
      observer.didReplace(newRoute: newRoute, oldRoute: oldRoute);
    }
  }
}

为了验证到底是不是push的方法改变页面的,我添加了一些日志(后面带“--------”)运行后发现

确实走了push方法

又添加了一些代码来验证,把上面的系统的导航控制器换成boost的导航控制器方法,试了一下

Timer timer = new Timer(new Duration(seconds: 5), () {
  print("time up pop -------");
  FlutterBoost.containerManager?.pop();
});

出现了我在上面的设置的跟页面的效果,

运行日志如下:

//log
2020-08-11 09:11:14.681256+0800 Exeed[1657:2435378] flutter: FlutterBoost#onMetohdCall didShowPageContainer
2020-08-11 09:11:14.684337+0800 Exeed[1657:2435378] flutter: pushContainer--------
2020-08-11 09:11:14.685383+0800 Exeed[1657:2435378] flutter: FlutterBoost#_refreshOverlayEntries in setState
2020-08-11 09:11:14.686330+0800 Exeed[1657:2435378] flutter: FlutterBoost#ContainerObserver#2 didPush
2020-08-11 09:11:14.686927+0800 Exeed[1657:2435378] flutter: FlutterBoost#BoostContainerLifeCycleObservercontainer:flutter://lion.com?pageName=homeContainerlifeCycle:ContainerLifeCycle.Appear
2020-08-11 09:11:14.688237+0800 Exeed[1657:2435378] flutter: FlutterBoost#native containner did show-flutter://lion.com?pageName=homeContainer,
  {uniqueId=0,name=flutter://lion.com?pageName=homeContainer}
2020-08-11 09:11:14.758341+0800 Exeed[1657:2435378] flutter: didPush--------
...
2020-08-11 09:17:23.008669+0800 Exeed[1665:2437360] flutter: time up pop -------
2020-08-11 09:17:23.010918+0800 Exeed[1665:2437360] flutter: FlutterBoost#_refreshOverlayEntries in setState
2020-08-11 09:17:23.012686+0800 Exeed[1665:2437360] flutter: FlutterBoost#ContainerObserver#2 didPop
2020-08-11 09:17:23.074269+0800 Exeed[1665:2437360] flutter: FlutterBoost#onShownContainerChanged old:0 now:default

总结

到此可以判定flutter boost 也是使用的push的方法,而且是重写的后的导航控制器的方法,flutter原来的方法无效

根据flutter 不难发现他是在iOS的生命周期viewwillappear中发送信息给 flutter 来push想要的界面

以上内容,如有不全面,或是错误的地方欢迎指正

上一篇下一篇

猜你喜欢

热点阅读