Flutter混合开发:Android中如何启动Flutter
Posted 一叶飘舟
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter混合开发:Android中如何启动Flutter相关的知识,希望对你有一定的参考价值。
简介: flutter可以独立完成项目,但是在现有项目情况下最好的方式就是混合开发,逐步过渡。这样就会共存native和flutter代码,而其中最关键的就是native如何启动flutter页面,及flutter与native如何交互。 本文以android为例,展示如何在一个现有项目中引入flutter、启动flutter,如何加速启动以及如何传参。
现有项目中引入Flutter
在现有的Android项目中,新建一个flutter module。创建完module后会发现自动在主module中依赖了。当然我们如果其他项目使用该flutter模块,并不会自动进行这一步,所以要先在setting.gradle中注册,如下:
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir,
'flutter_module/.android/include_flutter.groovy'
))
include ':flutter_module'
然后在主module中依赖:
implementation project(path: ':flutter')
这样就可以进行混合开发了。
启动flutter页面
新建flutter module后会自动创建一个main页面,那么native如何打开这个页面?
首先在主module的manifest中添加:
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
/>
然后用一下代码即可打开flutter主页面
startActivity(FlutterActivity.createDefaultIntent(this))
那么如何打开其他页面?
比如我们创建一个新的flutter页面second:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class SecondPage extends StatefulWidget
@override
State<StatefulWidget> createState()
return _SecondPage();
class _SecondPage extends State<SecondPage>
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text("test"),
),
body:Text("test")
);
然后在main.dart的App下注册这个页面:
class MyApp extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
routes:
"second" : (BuildContext context) => SecondPage(), //也可以用其他方式注册
,
);
这样在flutter中可以用以下代码打开这个页面:
Navigator.of(context).pushNamed("second");
而在Android中就可以用以下代码即可打开该页面:
startActivity(FlutterActivity.withNewEngine().initialRoute("second").build(this))
加速启动
通过上面代码打开flutter页面时会出现黑屏现象,时间并不短,很影响体验。因为每次都重新new一个flutter engine( createDefaultIntent函数内部其实也是withNewEngine().build(launchContext) )。
官方给出的解决方案是使用engine cache,比如在Appliation中添加cache:
var flutterEngine = FlutterEngine(this)
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
FlutterEngineCache.getInstance().put("main", flutterEngine)
然后将启动改成:
startActivity(FlutterActivity.withCachedEngine("main").build(this))
但是上面仅仅是启动main页面,如果想启动其他页面,比如second,就需要继续添加cache:
var flutterEngine2 = FlutterEngine(this)
flutterEngine2.navigationChannel.setInitialRoute("second")
flutterEngine2.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
FlutterEngineCache.getInstance().put("second", flutterEngine2)
注意这里通过setInitialRoute设置了route。然后启动即可:
startActivity(FlutterActivity.withCachedEngine("second").build(this))
通过缓存engine,启动时黑屏时间缩短了很多,几乎不可察觉(注意第一次可能还会稍微黑屏一下)。
启动传参
上面我们打开main和second页面没有传参,那么如果想传入一些初始化必要的参数,如何处理?
目前flutter框架并没有封装携带参数的api,也就是说native跳转flutter官方是没有参数。但是我们实际场景又有这样的需求,怎么处理?
官方没有给出相应的api,那么只能从route上想办法。首先改变app中注册route的方式,上面直接使用routes这种map的形式,我们换成onGenerateRoute这种RouteFactory形式,如下:
onGenerateRoute: (RouteSettings settings)
if(settings.name.startsWith("second"))
return MaterialPageRoute(builder: (BuildContext context)
return SecondPage(settings.name);
);
else
return MaterialPageRoute(builder: (BuildContext context)
return Scaffold(
body: Center(
child: Text("page not found"),
),
);
);
,
这里的settings.name就是route,因为我们想在route后面添加参数,所以通过开头来判断是那个页面。
注意:示例中直接将route url传给页面,其实应该在这里统一解析出来,以map的形式传给页面。
然后修改Second页面:
class SecondPage extends StatefulWidget
String url;
SecondPage(String url)
this.url = url;
@override
State<StatefulWidget> createState()
return _SecondPage();
class _SecondPage extends State<SecondPage>
@override
Widget build(BuildContext context)
return Scaffold(
appBar: AppBar(
title: Text("test"),
),
body:Text("test:$widget.url")
);
这里没有解析,直接将url展示出来了,目的是参数传到位即可。
最后在native使用如下代码:
startActivity(FlutterActivity.withNewEngine().initialRoute("second?text=second test").build(this))
就可以传递参数了。
但是这样就引出了另外一个问题,因为上面这种启动方式并没有使用engine cache,如果使用engine cache那么route就必须提前定好以便在Appllication中放入cache中。但是我们既然要传参,那么说明route是动态改变的,所以这两个是冲突的,这样在传参的情况下就无法加速启动了么?
因为我们传参本身不是官方api的行为,所以官方的engine cache没有相应的支持。但是这个问题并不是无法解决,比如闲鱼开放的flutter混合框架 —— flutter-boost,就可以很轻松的实现native携参打开flutter页面。不过这里面涉及的东西比较多,后面我单独用一篇文章来解读一下flutter-boost是如何实现传参+快速启动的。
以上是关于Flutter混合开发:Android中如何启动Flutter的主要内容,如果未能解决你的问题,请参考以下文章
FlutterFlutter 混合开发 ( 关联 Android 工程与 Flutter 工程 | 安卓页面中嵌入 Flutter 页面 | 安卓中启动 Flutter 页面 )
Flutter(六)Android与Flutter混合开发(Hybird)
FlutterFlutter 混合开发 ( 安卓端向 Flutter 传递数据 | FlutterFragment 数据传递 | FlutterActivity 数据传递 )