为了把 Flutter 引入到原生工程,我们需要把 Flutter 工程改造为原生工程的一个组件依赖,并以组件化的方式管理不同平台的 Flutter 构建产物,即 Android 平台使用 aar、iOS 平台使用 pod 进行依赖管理。这样,我们就可以在 Android 工程中通过 FlutterView,iOS 工程中通过 FlutterViewController,为 Flutter 搭建应用入口,实现 Flutter 与原生的混合开发方式。

对于混合开发的应用而言,通常我们只会将应用的部分模块修改成 Flutter 开发,其他模块继续保留原生开发,因此应用内除了 Flutter 的页面之外,还会有原生 Android、iOS 的页面。在这种情况下,Flutter 页面有可能会需要跳转到原生页面,而原生页面也可能会需要跳转到 Flutter 页面。这就涉及到了一个新的问题:如何统一管理原生页面和 Flutter 页面跳转交互的混合导航栈。

混合导航栈

混合导航栈,指的是在混合开发中原生页面和Flutter页面相互掺杂,存在于用户视角的页面导航栈视图,如图11-12所示。在混合开发的应用中,原生Android、iOS与Flutter各自实现了一套互不相同的页面映射机制,原生平台采用的是单容器单页面,即一个ViewController或Activity对应一个原生页面;而Flutter采用单容器多页面的机制,即一个ViewController或Activity对应多个Flutter页面。Flutter在原生的导航栈之上又自建了一套Flutter导航栈,这使得原生页面与Flutter页面与之间进行页面切换时,需要处理跨引擎的页面切换问题。

接下来,我们就分别从原生页面跳转至 Flutter 页面,以及从 Flutter 页面跳转至原生页面来看看混合开发的路由管理。

原生页面跳转Flutter页面

从原生页面跳转至 Flutter 页面,实现起来比较简单。因为 Flutter 本身依托于原生提供的容器,即iOS 使用的是FlutterViewController,Android 使用的是Activity 中的 FlutterView。所以我们通过初始化 Flutter 容器,为其设置初始路由页面之后,就可以以原生的方式跳转至 Flutter 页面了。

对于iOS混合工程来说,可以先初始化一个FlutterViewController实例,然后设置初始化页面路由,将其加入原生的视图导航栈中即可完成跳转,如下所示。

//iOS 跳转至Flutter页面
FlutterViewController *vc = [[FlutterViewController alloc] init];
//设置Flutter初始化路由页面
[vc setInitialRoute:@"defaultPage"];
//完成页面跳转
[self.navigationController pushViewController:vc animated:YES];

对于Android混合工程而言,则需要多加一步。因为Flutter页面的入口并不是原生视图导航栈的最小单位Activity,而是一个FlutterView,所以我们需要把这个View包装到Activity的contentView中,然后才能实现跳转。在Activity内部设置页面初始化路由之后,在外部就可以采用打开一个普通的原生视图的方式来打开Flutter页面了,如下所示。

//Android 跳转至Flutter页面
//创建一个作为Flutter页面容器的Activity
public class FlutterHomeActivity extends AppCompatActivity {protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//设置Flutter初始化路由页面,传入路由标识符View FlutterView = Flutter.createView(this, getLifecycle(), "defaultRoute"); //用FlutterView替代Activity的ContentViewsetContentView(FlutterView);}
}
//用FlutterPageActivity完成页面跳转
Intent intent = new Intent(MainActivity.this, FlutterHomeActivity.class);
startActivity(intent);

运行项目代码,最终的效果下图所示。

对于Android混合工程来说,Flutter的原生容器就是一个Activity,只需要创建一个FlutterView,然后利用addContentView()方法将当前页面的layout页面布局添加进去即可。如果Flutter的原生容器是一个Fragment,那么只需要创建一个FlutterFragment,然后在指定的容器中添加Flutter页面即可。同样,对于iOS混合工程来说,Flutter的原生容器是一个FlutterViewController。

Flutter 页面跳转至原生页面

相比原生页面跳转Flutter页面,从Flutter页面跳转至原生页面则会相对麻烦些。因为我们需要考虑以下两种场景,即从Flutter页面打开新的原生页面和从Flutter页面回退到旧的原生页面。 由于Flutter并没有提供对原生页面的操作方法,所以不能通过直接调用原生平台的方法来实现页面跳转,不过可以使用Flutter提供的方法通道来间接实现,即打开原生页面使用的是openNativePage()方法,需要关闭Flutter页面时则调用closeFlutterPage()方法。 具体来说,在Flutter和原生两端各自初始化方法通道,并提供Flutter操作原生页面的方法,并在原生代码中注册方法通道,当原生端收到Flutter的方法调用时就可以打开新的原生页面。 在混合开发的应用中,FlutterView与FlutterViewController是Flutter模块的入口,也是Flutter模块初始化的地方。可以看到,在混合开发的应用中接入Flutter与开发一个纯Flutter应用在运行机制上并无任何区别,因为对于混合工程来说,原生工程只不过是为Flutter提供了一个容器而已,即Android使用的是FlutterView,iOS使用的是FlutterViewController。接下来,Flutter模块就可以使用自己的导航栈来管理Flutter页面,并且可以实现多个复杂页面的渲染和切换。 因为Flutter容器本身属于原生导航栈的一部分,所以当Flutter容器内的根页面需要返回时,开发者需要处理Flutter容器的关闭问题,从而实现Flutter根页面的关闭。由于Flutter并没有提供操作Flutter容器的方法,因此我们依然需要通过方法通道,在原生代码宿主为Flutter提供操作Flutter容器的方法,在页面返回时关闭Flutter页面。如图下图所示,是Flutter跳转原生页面的两种场景的示意图。

使用方法通道实现Flutter页面至原生页面的跳转,注册方法通道最合适的地方是Flutter应用的入口,即在iOS端的FlutterViewController和Android端的是FlutterView初始化Flutter页面之前。因此,在混合开发的应用中,需要分别继承iOS的FlutterViewController和Android的AppCompatActivity,然后在iOS的viewDidLoad和Android的onCreate生命周期函数中初始化Flutter容器时,注册openNativePage和closeFlutterPage两个方法。 下面是使用方法通道实现Flutter跳转原生页面的原生Android端的代码,如下所示。

public class FlutterModuleActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);
//初始化Flutter容器FlutterView fv = Flutter.createView(this, getLifecycle(), "defaultPage");  //注册方法通道  new MethodChannel(fv, "com.xzh/navigation").setMethodCallHandler(new MethodChannel.MethodCallHandler() {@Overridepublic void onMethodCall(MethodCall call, Result result) {if (call.method.equals("openNativePage")) {Intent intent = new Intent(this, AndroidNativeActivity.class);tartActivity(intent);result.success(0);} else if (call.method.equals("closeFlutterPage")) {finish();result.success(0);} else {result.notImplemented();}}});setContentView(fv);}
}

可以发现,在上面的代码中,首先使用FlutterView初始化一个Flutter容器,然后在原生代码中注册openNativePage和closeFlutterPage两个方法,当Flutter页面通过方法通道调用原生方法时即可打开原生页面。 与原生Android端的实现原理类似,使用方法通道实现页面的跳转页需要在原生iOS端中注册openNativePage和closeFlutterPage两个方法,代码如下。

@interface FlutterHomeViewController : FlutterViewController
@end@implementation FlutterHomeViewController
- (void)viewDidLoad {
[super viewDidLoad];
//声明方法通道FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:@"com.xzh/navigation" binaryMessenger:self];[channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {if([call.method isEqualToString:@"openNativePage"]) {
//打开一个新的原生页面iOSNativeViewController *vc = [[iOSNativeViewController alloc] init];[self.navigationController pushViewController:vc animated:YES];result(@0);}else if([call.method isEqualToString:@"closeFlutterPage"]) {//关闭Flutter页面[self.navigationController popViewControllerAnimated:YES];result(@0);}else {result(FlutterMethodNotImplemented);}}];
}
@end

经过上面的方法注册后,接下来就可以在Flutter中使用openNativePage()方法来打开原生页面了,如下所示。

void main() => runApp(_widgetForRoute(window.defaultRouteName));
//获取方法通道
const platform = MethodChannel('com.xzh/navigation'); //根据路由标识符返回应用入口视图
Widget _widgetForRoute(String route) {switch (route) {default://返回默认视图return MaterialApp(home:DefaultPage());}
}class PageA extends StatelessWidget {...@overrideWidget build(BuildContext context) {return Scaffold(body: RaisedButton(child: Text("Go PageB"),onPressed: ()=>platform.invokeMethod('openNativePage')//打开原生页面));}
}class DefaultPage extends StatelessWidget {...@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: Text("DefaultPage Page"),leading: IconButton(icon:Icon(Icons.arrow_back), onPressed:() => platform.invokeMethod('closeFlutterPage')//关闭Flutter页面)),body: RaisedButton(child: Text("Go PageA"),onPressed: ()=>Navigator.push(context, MaterialPageRoute(builder: (context) => PageA())),//打开Flutter页面 PageA));}
}

在上面的例子中,Flutter 容器的根视图 DefaultPage 包含有两个按钮。点击左上角的按钮后,可以通过 closeFlutterPage 返回原生页面;点击中间的按钮后,会打开一个新的 Flutter 页面 PageA。PageA 中也有一个按钮,点击这个按钮之后会调用 openNativePage 来打开一个新的原生页面。 整个混合导航栈示例的代码流程,如下图所示。通过这张图,你就可以把这个示例的整个代码流程串起来了。

在混合应用工程中,RootViewController 与 MainActivity 分别是 iOS 和 Android 应用的原生页面入口,可以初始化为 Flutter 容器的 FlutterHomeViewController(iOS 端)与 FlutterHomeActivity(Android 端)。

在为其设置初始路由页面 DefaultPage 之后,就可以以原生的方式跳转至 Flutter 页面。但是,Flutter 并未提供接口,来支持从 Flutter 的 DefaultPage 页面返回到原生页面,因此我们需要利用方法通道来注册关闭 Flutter 容器的方法,即 closeFlutterPage,让 Flutter 容器接收到这个方法调用时关闭自身。

在 Flutter 容器内部,我们可以使用 Flutter 内部的页面路由机制,通过 Navigator.push 方法,完成从 DefaultPage 到 PageA 的页面跳转;而当我们想从 Flutter 的 PageA 页面跳转到原生页面时,因为涉及到跨引擎的页面路由,所以我们仍然需要利用方法通道来注册打开原生页面的方法,即 openNativePage,让 Flutter 容器接收到这个方法调用时,在原生代码宿主完成原生页面 SomeOtherNativeViewController(iOS 端)与 SomeNativePageActivity(Android 端)的初始化,并最终完成页面跳转。

总结

对于原生 Android、iOS 工程混编 Flutter 开发,由于应用中会同时存在 Android、iOS 和 Flutter 页面,所以我们需要妥善处理跨渲染引擎的页面跳转,解决原生页面如何切换 Flutter 页面,以及 Flutter 页面如何切换到原生页面的问题。

在原生页面切换到 Flutter 页面时,我们通常会将 Flutter 容器封装成一个独立的 ViewController(iOS 端)或 Activity(Android 端),在为其设置好 Flutter 容器的页面初始化路由(即根视图)后,原生的代码就可以按照打开一个普通的原生页面的方式来打开 Flutter 页面了。

而如果我们想在 Flutter 页面跳转到原生页面,则需要同时处理好打开新的原生页面,以及关闭自身回退到老的原生页面两种场景。在这两种场景下,我们都需要利用方法通道来注册相应的处理方法,从而在原生代码宿主实现新页面的打开和 Flutter 容器的关闭。

需要注意的是,与纯 Flutter 应用不同,原生应用混编 Flutter 由于涉及到原生页面与 Flutter 页面之间切换,因此导航栈内可能会出现多个 Flutter 容器的情况,即多个 Flutter 实例。Flutter 实例的初始化成本非常高昂,每启动一个 Flutter 实例,就会创建一套新的渲染机制,即 Flutter Engine,以及底层的 Isolate。而这些实例之间的内存是不互相共享的,会带来较大的系统资源消耗。

为了解决混编工程中 Flutter 多实例的问题,业界有两种解决方案: - 以今日头条为代表的修改 Flutter Engine 源码,使多 FlutterView 实例对应的多 Flutter Engine 能够在底层共享 Isolate; - 以闲鱼为代表的共享 FlutterView,即由原生层驱动 Flutter 层渲染内容的方案。

不过,目前这两种解决方案都不够完美。所以,在 Flutter 官方支持多实例单引擎之前,应该尽量使用Flutter去开发一些闭环业务,减少原生页面与Flutter页面之间的交互,尽量避免Flutter页面跳转到原生页面,原生页面又启动一个新的Flutter实例的情况,并且保证应用内不要出现多个 Flutter 容器实例的情况。

flutter 应用场景_Flutter混合开发的路由栈管理相关推荐

  1. Flutter 核心原理与混合开发模式

    作者:airingdeng,腾讯QQ前端开发工程师 本文将从 Flutter 原理出发,详细介绍 Flutter 的绘制原理,借由此来对比三种跨端方案:之后再进入第三篇章 Flutter 混合开发模式 ...

  2. android 实现表格横向混动_Flutter混合开发和Android动态更新实践

    Flutter混合开发和Android动态更新实践 感谢闲鱼和csdn的文章给的思路: 本篇是实践性文章包含两部分 将Flutter工程编译后的文件集成到Android项目 将Flutter代码热更新 ...

  3. 教你如何使用Flutter和原生App混合开发

    点击上方 "程序员小乐"关注, 星标或置顶一起成长 每天凌晨00点00分, 第一时间与你相约 每日英文 Things in this world are temporary. If ...

  4. Flutter和原生App混合开发

    可以查看官方文档 https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps 混合开发有两种集成方式 Flutter源码集 ...

  5. flutter 九宫格菜单_Flutter页面开发体验

    背景 Flutter作为新晋 网红 ,虽然还没有在我们项目中商用,但是热度已经有赶超ReactNative的趋势. 为了体验其开发效率和能力验证,我们将项目主界面中的个人页面进行Flutter重构. ...

  6. flutter 应用场景_flutter有哪些实际应用?

    一.社交网络 1.KlasterMe –用于内容发现和创建的应用程序 用于创建,共享和发现从图像到文章的不同形式内容的应用程序.用户可以创建一个页面来展示他们的内容并在社区中得到认可.该应用程序的试用 ...

  7. qt框架的开发模式_Flutter 混合开发框架模式探索

    Flutter 混合开发框架模式探索 由于 Google 官方提供的 Flutter 混合式开发方案过于简单,仅支持打开一个 Flutter View 的能力,而不支持路由间传参.统一的生命周期.路由 ...

  8. Flutter混合开发、安卓,ios

    date:12.29,for flutter >v1.12.x 更新说明: 适配Flutter >=v1.12.x 在Flutter的应用场景中,有时候一个APP只有部分页面是由Flutt ...

  9. Flutter混合开发:Android中如何启动Flutter

    目录 现有项目中引入Flutter 启动flutter页面 加速启动 启动传参 flutter可以独立完成项目,但是在现有项目情况下最好的方式就是混合开发,逐步过渡.这样就会共存native和flut ...

最新文章

  1. 宏基因组蚂蚁森林公益合种树项目,支持祖国绿化事业,让世界更美好
  2. 苹果首席设计官离职!这个被称为“强纳肾”的男人,设计了iMac、iPhone等无数产品...
  3. 利用gulp搭建less编译环境
  4. 记录10本关于开关电源的书
  5. php写幻灯片,原生JS写的幻灯片
  6. 校园一卡通管理信息系统的设计与实现(asp.net)
  7. winword.exe应用程序错误
  8. 自定义容器实现类似Windows屏保功能
  9. Git git update-index --assume-unchanged
  10. Jquery如何去掉复选框的勾
  11. 手把手教你刷github提交记录
  12. 大脑小胶质细胞“隐藏技能”被发现
  13. 微信小程序直播二三事
  14. AutoConfiguration排除指定和过滤自动配置组件
  15. 地火明夷 (易經大意 韓長庚)
  16. [Kafka] Kafka基本架构
  17. Shell编程(week4_day5)--技术流ken
  18. 2. C++ Visual Studio中同一个项目包含多个有main函数的源文件怎么分别运行?
  19. 深入理解java虚拟机之java内存区域
  20. Android 解析包时候出现错误

热门文章

  1. 详解tomcat的连接数与线程池
  2. 《大数据分析原理与实践》——小结
  3. ovs router
  4. linux之SQL语句简明教程---IN
  5. 闭关第1天——儿童节快乐,永远年轻快乐
  6. ASP.NET的include的用法
  7. python3统一为什么类型_Python3 基本数据类型
  8. 打印机通讯 tspl指令_西门子工控设备通讯
  9. 山东计算机考研909,山东大学考研大纲909数据结构.pdf
  10. python怎么理解函数的参数_理解Python中函数的参数