上文介绍了原生跳转Flutter页面的基本方法,即

 startActivity(
   FlutterActivity.createDefaultIntent(currentActivity)
   );

但是这样跳转只会跳转至Flutter的main入口处,现在介绍如何跳转至其他路由

首先得在flutter模组中创建一个dart文件用来存储路由表,命名为routes.dart,文件代码如下

String homePage = "/";
String secondPage = "/secondPage";
​
final routes = {
 homePage: (context) => const MyApp(),
 secondPage: (context) => const DemoPage()
};
​
//下面这段代码是将一个匿名方法赋值给一个变量
//匿名方法做的事情:处理路由传参,生成 MaterialPageRoute 路由对象
var onGenerateRoute = (settings) {
 Function? pageContentBuilder = routes[settings.name];
 if (pageContentBuilder != null) {
  if (settings.arguments != null) {
   var route = MaterialPageRoute(
     builder: (context) =>
       pageContentBuilder(context, arguments: settings.arguments));
   return route;
   } else {
   var route =
   MaterialPageRoute(builder: (context) => pageContentBuilder(context));
   return route;
   }
  }
};

然后修改main()函数如下

void main() => runApp(MaterialApp(  //app 启动路由页面
 initialRoute: homePage,
 //路由生成
 onGenerateRoute: onGenerateRoute,));

这里的初始入口仍然是flutter的初始页面,但是通过onGenerateRoute方法已经配置好了其他页面的路由,所以我们只需要在原生代码中传入指定页面的路由就能跳转了,以下是原生端的代码

startActivity(
    FlutterActivity
         .withNewEngine()
         .initialRoute("/secondPage")
         .build(currentActivity)//currentActivity替换为当前Activity
);

但是这样子有一个致命缺陷,从按下跳转键到完全跳转过去花费了大概五六秒的时间,这在实际开发中自然是不被允许的,因此有了FlutterEngine,先声明引擎

public FlutterEngine flutterEngine;
public void preWarm(){
    flutterEngine = new FlutterEngine(this);
    //设置初始化路由
    flutterEngine.getNavigationChannel().setInitialRoute("/secondPage");
    // 开始执行Dart模块预热引擎
    flutterEngine.getDartExecutor().executeDartEntrypoint(
        DartExecutor.DartEntrypoint.createDefault()
     );
​
    // 缓存引擎
    FlutterEngineCache
         .getInstance()
         .put("my_engine_id", flutterEngine);
   }

然后在onCreate()方法中调用,就能达到提前预热的效果,这样在执行跳转时耗时会大大缩短。下面是跳转代码

startActivity(
    FlutterActivity
         .withCachedEngine("my_engine_id")
         .build(SecondPage.this)
);

要注意的是,预热也是需要时间的,如果刚执行预热代码就马上跳转flutter页面,花费的时间与之前无异,但是缓存机制另一个好处就是第二次开始的跳转时间都会大幅缩短。而这样做的代价就是性能的消耗了,以下是官方文档原话:

当使用一个缓存的 FlutterEngine 时, FlutterEngine 会比展示它的 FlutterActivityFlutterFragment 存活得更久。切记,Dart 代码会在你预热 FlutterEngine 时就开始执行,并且在你的 FlutterActivityFlutterFragment 销毁后继续运行。要停止代码运行和清理相关资源,可以从 FlutterEngineCache 中获取你的 FlutterEngine,然后使用 FlutterEngine.destroy() 来销毁 FlutterEngine

运行时的性能考量并不是你会预热和缓存一个 FlutterEngine 的唯一原因。一个预热的 FlutterEngine 会独立于 FlutterActivity 执行 Dart 代码,即一个 FlutterEngine 可以在任意时刻用于执行任意代码。非 UI 的应用逻辑可以在 FlutterEngine 中执行,例如网络请求和数据缓存,以及在 Service 中或其他地方的后台行为。当使用 FlutterEngine 在后台执行任务时,确保满足 Android 对于后台执行的所有限制。

并且,在原生和flutter之间进行跳转时,路由的变化是一个非常值得注意的点,举个例子,按照文中flutter路由表的定义方法来,原生界面是A,flutter界面有两个,分别是初始页面面B、第二个页面C,当我从A跳转至C时,B因为是main函数的初始页面,它其实也是被添加进了路由表的,这就导致了一个问题,当我在C页面调用flutter的Navigator.pop时,回到的页面不是A而是B,而在B调用pop方法则会直接黑屏,因为pop是flutter的路由方法,不能用它回到原生页面中,除非是使用手机自带的返回功能。并且,用这种方法回到了A界面后,再次调用同一个引擎跳转flutter界面时,即便你之前配置的初始化路由是C页面,此时跳转的界面仍然是B,因为缓存的引擎并没有被释放,它仍然停留在最后一次展示的flutter界面即B,故而再次调用时初始页面是B而不是C。原因是因为”/”,这里附上国外网友的回答:

Here is extract from the documentation:

If the route name starts with a slash, then it is treated as a “deep link”, and before this route is pushed, the routes leading to this one are pushed also. Even if the route was just /a, the app would start with / and /a loaded. api.flutter.dev/flutter/mat…

You could try re-naming your ‘/settings’ route to ‘settings’.

简单翻译一下就是,当你的路由表里使用了”/”时,flutter是把它当作深链接来处理的,即指向这个页面的页面也会被添加进路由表,这就是为什么我们只添加了C页面路由里却有B的原因,解决办法也很简单,在路由表里把”/secondPage”改成”secondPage”就行了。

但是还有一个问题没解决,即便flutter的路由表里只有一个页面了,调用Navigator.pop仍然无法回到原生页面,这时候就需要用到系统层级的返回方法了,即SystemNavigator.pop()。

这篇文章就记录到这里了,下一篇文章就记录原生和flutter的通讯交互了。