从hybrid_stack_manager迁移到flutter_boost (iOS角度)

当前项目是flutter和iOS、安卓原生客户端混合使用的,有flutter和原生页面互相调用的需求,因此以前使用了hybrid_stack_manager来管理混合栈。ios

如上图所示,hybrid_stack_manager在原生端直接使用了UINavigationController,在不一样的FlutterWrapperViewController中共用同一个FlutterViewController。 当调用打开flutter的方法时,首先要获取当前的navigationController而后push。

//Push
    UINavigationController *currentNavigation = (UINavigationController*)[UIApplication sharedApplication].delegate.window.rootViewController;
    FlutterViewWrapperController *viewController = [[FlutterViewWrapperController alloc] initWithURL:[NSURL URLWithString:aUrl] query:query nativeParams:params];
    viewController.viewWillAppearBlock = ^(){
        //Process first & later message sending according distinguishly.
        if(sIsFirstPush){
            [HybridStackManager sharedInstance].mainEntryParams = arguments;
            sIsFirstPush = FALSE;
        }
        else{
            [methodChann invokeMethod:@"openURLFromFlutter" arguments:arguments result:^(id  _Nullable result) {
            }];
        }
    };
    [currentNavigation pushViewController:viewController animated:YES];//直接使用navigationController
复制代码

在flutter端缓存

pushPageWithOptionsFromFlutter({RouterOption routeOption, bool animated}) {
  Widget page =
      Router.sharedInstance().pageFromOption(routeOption: routeOption);
  if (page != null) {
    XMaterialPageRoute pageRoute = new XMaterialPageRoute(
        settings: new RouteSettings(name: routeOption.userInfo),
        animated: animated,
        builder: (BuildContext context) {
          return page;
        });

    Navigator.of(globalKeyForRouter.currentContext).push(pageRoute); //直接使用Navigator
    HybridStackManagerPlugin.hybridStackManagerPlugin
        .updateCurFlutterRoute(routeOption.userInfo);
  } else {
    HybridStackManagerPlugin.hybridStackManagerPlugin.openUrlFromNative(
        url: routeOption.url,
        query: routeOption.query,
        params: routeOption.params);
  }
  NavigatorState navState = Navigator.of(globalKeyForRouter.currentContext);
  List<Route<dynamic>> navHistory = navState.history;
}
复制代码

FlutterEngine消耗的资源很大,并且启用多个flutterEngine会形成不一样flutter页面通讯困难,因此在hybrid_stack_manager中不一样的flutterWrapperViewController中也是共用同一个FlutterViewController和flutterEngine的。这样基本解决了flutter和原生页面互相打开的问题。app

可是咱们很快就发现要打开flutter页面必须经过push的方法,存在很大的局限,在app使用tabbarViewController的时候,若是有一个tab中的页面是flutter页面,不能经过navigationController push的方式加载。 screenshot.png 框架

因此必须另外使用一个FlutterViewController和flutterEngine,这样会致使以下问题: DF39C9D2-61F0-4F72-925D-60E0BF2F5E7B.jpg
1.一个flutterEngine有四个线程,并且每一个engine会维护本身的图片缓存,两个flutterEngine会带来加倍的消耗,会影响到app性能。 2.不一样flutter engine没有共享内存,通讯必须使用port的方式。 3.调试带来不便,多个flutter engine存在的时候,ide找不到要调试的端口,咱们经过手动注释掉另一个flutterViewController的方式来临时解决。

Flutter Boost一样是闲鱼团队出品的混合栈框架,迁移到这个框架后,解决了上面提到的全部问题。 screenshot.png ide

这个图片看上去和hybrid_stack_manager的那张图差很少,但其实只有在中间FlutterViewController共用那边是类似的,在原生端和flutter端打开页面的方式都是彻底不同的。

FLBFlutterViewContainer *vc = FLBFlutterViewContainer.new;
[vc setName:name params:params];
[self.navigationController pushViewController:vc animated:animated];
复制代码

从上面代码能够看到,FLBFlutterViewContainer是flutter页面的原生容器,每个flutter页面都对应一个FLBFlutterViewContainer,经过调用setName:params方法到flutter端找到对应的widget,加载到这个原生容器中。 最关键的是[self.navigationController pushViewController:vc animated:animated];这句代码咱们业务代码本身调用的,并不像hybrid_stack_manager中强制有框架调用navigationController 的push方法。换句话说,flutter boost只完成从flutter widget到原生页面的转化,具体怎么用是留给业务代码控制的。 在flutter端也没有直接使用navigator push这样简单粗暴的方式,而是本身维护了一个导航栈。性能

BoostContainer _onstage;//当前显示的页面
final List<BoostContainer> _offstage = <BoostContainer>[];//以前的页面,用作缓存
复制代码

在hybrid_stack_manager中,flutter和原生都是使用navigationController,因此须要保持两端导航栈的一致。而在flutter boost中彻底放开了原生端页面的展现方式,在flutter端维护更加自由的导航栈,实现了更加自由的混合导航栈。 screenshot.png
iOS端:在AppDelegate中调用flutterBoost的start方法,在这里完成初始化flutterEngine,配置pageBuilder等工做

MMCommonNAVI *rootNav = [[MMCommonNAVI alloc]initWithRootViewController: [ZNUIManager sharedInstance].rootTab];
//flutter boost启动
ZNFlutterRouter *flutterRouter = [ZNFlutterRouter sharedRouter];
flutterRouter.navigationController = rootNav;
[FlutterBoostPlugin.sharedInstance startFlutterWithPlatform:flutterRouter
                                                    onStart:^(FlutterViewController *fvc) {
                                                            
                                                        }];
[rootNav setNavigationBarHidden:YES];
    
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
self.window.rootViewController = rootNav;
[self.window makeKeyAndVisible];
复制代码

须要实现一个本身的路由控制类,而且传入navigationController。ui

@interface ZNFlutterRouter : NSObject<FLBPlatform>
@property (nonatomic,strong) UINavigationController *navigationController;
+ (ZNFlutterRouter *)sharedRouter;
@end
复制代码
- (void)openPage:(NSString *)name
          params:(NSDictionary *)params
        animated:(BOOL)animated
      completion:(void (^)(BOOL))completion
{
    //打开原生页面,从原生或者flutter页面调用最终都会到这里
    if([name isEqualToString:@"hrd://product_detail"]){
        // 跳转至设备详情
        ZNProductDetailViewController *next = [ZNProductDetailViewController new];
        next.sn = [params objectForKey: @"pid"];
        [[[ZNUIManager sharedInstance] appRootNavi] pushViewController:next animated:YES];
    }
    //打开flutter页面,全部的flutter页面都会走到下面,设置不一样的name就会生成widget对应的原生页面
    else if([params[@"present"] boolValue]){
        FLBFlutterViewContainer *vc = FLBFlutterViewContainer.new;
        [vc setName:name params:params];
        [self.navigationController presentViewController:vc animated:animated completion:^{}];
    }else{
        FLBFlutterViewContainer *vc = FLBFlutterViewContainer.new;
        [vc setName:name params:params];
        [self.navigationController pushViewController:vc animated:animated];
    }
}
复制代码

打开flutter和原生页面最终都会走到openPage这个方法中,从原生打开页面是原生直接调用openPage方法,而从flutter侧打开页面是经过methodChannel调用openPage方法。atom

在flutter侧也须要注册pageName和widget的对应关系。url

static void registerPageBuilder(){
  //注册flutter页面
  FlutterBoost.singleton.registerPageBuilders({
    'hrd://product_list':(pageName, params, uniqueId) {
      RouterOption option = new RouterOption(url: pageName,params: params);
      return new ProductList(option);
    },
    //tabbar第三个tab,push进来页面和直接展现在tab中的页面采用一样的配置方式
    'hrd://board_main':(pageName, params, uniqueId) {
      return new BoardMain();
    },
  });
  FlutterBoost.handleOnStartPage();
}
复制代码

上面代码能够看到hrd://product_list 这是对应一个push进来的页面,而hrd://board_main是app启动后就初始化在tabbarViewController中的,两个页面都是相同的初始化和注册方式,具体怎么展现由原生端决定。spa

原生侧打开flutter页面调用方法以下,要传入pageName和params。

[ZNFlutterRouter.sharedRouter openPage:@"hrd://upload_contract" params:@{@"orderCode":self.orderCode,@"customerCode":self.orderDetailModel.customerCode,@"changeCode":@"",@"state":@"0"} animated:YES completion:^(BOOL finished) {
                    
                }];
复制代码

flutter打开原生页面调用方法以下:

FlutterBoost.singleton.openPage(url, params, animated: animated,resultHandler: resultHandler)
复制代码

须要注意的是,从页面效果上看原生打开flutter是push过来的,可是因为是直接把widget放入原生的viewController里面的,因此第一个flutter页面的左上角没有回退按钮的,咱们须要本身在Appbar中加入回退按钮并调用回退的方法。

return Scaffold(
  appBar: AppBar(
    title: Text(‘Flutter页面'),
    elevation: 0.0,
    leading: IconButton(icon: Icon(Icons.arrow_back_ios), onPressed: popCurrentPage),
  ),
  body: Container(),
);
复制代码

flutter关闭当前页面的调用方法以下:

FlutterBoost.singleton.closeCurPage(params);
复制代码

集成flutter_boost的过程是很顺利的,不过原理理解废了一番功夫,主要是因为源代码里面注释太少了,而代码量又特别大,我画了个类图帮助理解,能够从这个类图看到框架中各个类的关系。 Container.jpg

相关文章
相关标签/搜索