写点什么

【Flutter 专题】78 图解 Android Native 集成 FlutterBoost 小尝试 (一)

发布于: 刚刚
【Flutter 专题】78 图解 Android Native 集成 FlutterBoost 小尝试 (一)

      小菜前几天刚将历史项目升级至 AndroidX 并接入 Flitter Module,接下来小菜准备采用 flutter_boost 进行 Native 与 Flutter 两端交互;小菜从未接触过 FlutterBoost,为了研究方便,小菜特意新建两个工程单独学习基本的映射和跳转;

Module 集成

1. 新建 AndroidX 工程

      小菜新建一个 AndroidX 工程,其中 minSdkVersion >= 16,等待接入 Flutter Module


compileSdkVersion 28defaultConfig {    applicationId "com.ace.flutter.ace_demo02"    minSdkVersion 16    targetSdkVersion 28    versionCode 1    versionName "1.0"    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}
复制代码

2. 新建 Flutter Module

      小菜新建一个 Flutter Module 集成到 Android Project 中;其中该 Module 也支持 AndroidX


compileSdkVersion 28defaultConfig {    applicationId "com.ace.flutter.flutter_module04.host"    minSdkVersion 16    targetSdkVersion 28    versionCode 1    versionName "1.0"    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}
复制代码

3. Flutter Module 中接入 FlutterBoost

      小菜按照官网尝试接入 'v0.1.61-androidx-hotfixes' 分支 FlutterBoost 发现并未完全适配 AndroidX,于是切换至较新的 'v1.12.13-hotfixes' 分支,Packages get 检验,可以正常运行;


flutter_boost:  git:    url: 'https://github.com/alibaba/flutter_boost.git'    ref: 'v1.12.13-hotfixes'
复制代码

4. AndroidX Project 接入 Flutter Module

      小菜将 Flutter Module 接入到 Android 工程中,方法不再赘述,注意 build.gradle 中需要加入 flutterflutter_boost 两个依赖;Sync 之后 Project 中会加入 Flutter 和 FlutterBoost 模块;


implementation project(':flutter')implementation project(':flutter_boost')    setBinding(new Binding([gradle: this]))evaluate(new File(        '/Users/user/Documents/ACE_FLUTTER/flutter_module04/.android/include_flutter.groovy'))
复制代码



Code 案例

      至此,Flutter 和 FlutterBoost 的集成已基本完成,接下来是两端映射与跳转方面的学习,小菜建议刚开始时可以将官网的代码复制拷贝到项目中,先跑通项目更直观的感受;小菜为了学习逐步渗透;

Android 端

      根据 FlutterBoost 官网用法,首先需要在 Application 中初始化 FlutterBoost;无论是 Flutter 之间路由跳转还是 FlutterNative 之间路由跳转均需要通过 INativeRouter 接口进行交互;


private void initFlutterBoost() {    INativeRouter router = new INativeRouter() {        @Override        public void openContainer(Context context, String url, Map<String, Object> urlParams, int requestCode, Map<String, Object> exts) {            String assembleUrl = Utils.assembleUrl(url, urlParams);            PageRouter.openPageByUrl(context, assembleUrl, urlParams);        }    };
FlutterBoost.BoostLifecycleListener boostLifecycleListener = new FlutterBoost.BoostLifecycleListener() { @Override public void onEngineCreated() { }
@Override public void onPluginsRegistered() { }
@Override public void onEngineDestroy() { } }; Platform platform = new FlutterBoost .ConfigBuilder(this, router) .isDebug(true) .whenEngineStart(FlutterBoost.ConfigBuilder.ANY_ACTIVITY_CREATED) .renderMode(FlutterView.RenderMode.texture) .lifecycleListener(boostLifecycleListener) .build(); FlutterBoost.instance().init(platform);}
复制代码


      其中路由管理是由公共的 PageRouter 文件管理;提供了通用的 openPageByUrl,根据用户提供的 url 与设置好的映射集合进行对比,确认一致之后通过 startActivity() 进行页面跳转;若需要传递 Bundle 参数的话,可以通过 Map 类型进行传递;


public static boolean openPageByUrl(Context context, String url, Map params, int requestCode) {    String path = url.split("\\?")[0];    try {        if (pageName.containsKey(path)) {            Intent intent = BoostFlutterActivity.withNewEngine().url(pageName.get(path)).params(params).backgroundMode(BoostFlutterActivity.BackgroundMode.opaque).build(context);            if(context instanceof Activity){                Activity activity=(Activity)context;                activity.startActivityForResult(intent,requestCode);            }else{                context.startActivity(intent);            }            return true;        }        return false;    } catch (Throwable t) {        return false;    }}
复制代码


      公共路由中需要 BoostFlutterActivity 作为容器进行处理,因此需要在 AndroidManifest.xml 中注册;其中 SplashScreenDrawable 作为路由跳转时背景效果,可以按照需要进行调整;


<activity    android:name="com.idlefish.flutterboost.containers.BoostFlutterActivity"    android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"    android:hardwareAccelerated="true"    android:theme="@style/Theme.AppCompat"    android:windowSoftInputMode="adjustResize">    <meta-data        android:name="io.flutter.embedding.android.SplashScreenDrawable"        android:resource="@drawable/ic_launcher" /></activity>
复制代码

Flutter 端

      无论是 Android 还是 Flutter 均需要初始化,在 main.dartbuild 方法中初始化;小菜新建了两个测试 Page,其中路由映射的 url 要与 Android Native 端一致;同时还提供了 NavigatorObserver 进行前后路由的监听;


@overridevoid initState() {  super.initState();
FlutterBoost.singleton.registerPageBuilders({ 'first_page': (pageName, params, _) => FirstPage(), 'second_page': (pageName, params, _) => SecondPage() }); FlutterBoost.singleton.addBoostNavigatorObserver(TestBoostNavigatorObserver());}
@overrideWidget build(BuildContext context) {return MaterialApp( title: 'Flutter Boost example', builder: FlutterBoost.init(postPush: _onRoutePushed), home: Container( color: Colors.white, child: Center(child: Text('${_result}', style: TextStyle(color: Colors.blueAccent, fontSize: 18.0)))));}
void _onRoutePushed(String pageName, String uniqueId, Map params, Route route, Future _) {}
复制代码

Route 跳转

      路由跳转主要是 NativeFlutter 两端之间双向的交互,小菜分为如下方式进行测试;

Android -> Android 跳转

      通过 openPageByUrl 中分析 Native 之间的跳转依旧是通过系统的 startActivity 来进行处理,小菜不做过多赘述;


startActivity(new Intent(A.this, B.class));
复制代码
Android -> Flutter 跳转

      AndroidFlutter 通过 BoostFlutterActivity 构建跳转,注意映射 url 一致;若需要获取返回值内容,可以通过 **** 固定的 KEY 获取,且获取的格式是 Object 格式;


Intent intent = BoostFlutterActivity.withNewEngine().url(pageName.get(path)).params(params).backgroundMode(BoostFlutterActivity.BackgroundMode.opaque).build(context);if (context instanceof Activity) {    Activity activity = (Activity) context;    activity.startActivityForResult(intent,requestCode);} else {    context.startActivity(intent);}
// Native 跳转 FirstPage (无参)PageRouter.openPageByUrl(this, "first_page", null);===============================================================================// Native 跳转 FirstPage (有参)Map map = new HashMap();map.put("params_name", "张三");map.put("params_age", 18);PageRouter.openPageByUrl(this, "first_page", map);===============================================================================// Native 跳转 SecondPage (有参 + 返回值)PageRouter.openPageByUrl(this, "second_page", map, 101);
@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (data != null && data.getExtras() != null) { Toast.makeText(this, data.getExtras().get(IFlutterViewContainer.RESULT_KEY).toString(), Toast.LENGTH_LONG).show(); }}
复制代码


Flutter -> Flutter 跳转

      Flutter 之间的跳转可以通过默认的 Navigator 方式,也可以通过 FlutterBoost.singleton.open 方式进行页面跳转;注意跳转的页面均需在 main.dart 中提前映射好;


// FirstPage 跳转 SecondPage (无参)FlutterBoost.singleton.open('second_page');
===============================================================================// FirstPage 跳转 SecondPage (有参)FlutterBoost.singleton.open('second_page', urlParams: {'params_name': '李四', 'params_age': 28});===============================================================================// FirstPage 跳转 SecondPage (有参 + 返回值)FlutterBoost.singleton.open('second_page', urlParams: { 'params_name': '李四', 'params_age': 28 }) .then((Map value) { print('Second Page 页面销毁时获取的返回结果 result = $value'); });
复制代码


Flutter -> Android 跳转

      FlutterNative 的跳转需要根据不同映射的 url 单独判断;其中接收参数通过 openPageByUrlparams 获取;若由 FlutterNative 需要返回值,注意页面跳转时使用 startActivityForResult 方式,且关闭 Native 时传参的 KEY 为固定的 IFlutterViewContainer.RESULT_KEY


if (path.startsWith("native://")) {    if (path.equals("native://main_activity")) {        Intent intent = new Intent(context, MainActivity.class);        intent.putExtra("params_from_flutter", params.toString());        // context.startActivity(intent);  // 无返回值        Activity activity = (Activity) context;        activity.startActivityForResult(intent, requestCode);  // 有返回值    }    return true;}
// SecondPage 跳转 MainActivity (无参)FlutterBoost.singleton.open('native://main_activity');===============================================================================// SecondPage 跳转 MainActivity (有参)FlutterBoost.singleton.open('native://main_activity', urlParams: {'params_name': '王五', 'params_age': 38});===============================================================================// SecondPage 跳转 MainActivity (有参 + 返回值)FlutterBoost.singleton.open('native://main_activity', urlParams: { 'params_name': '王五', 'params_age': 38 }) .then((Map value) { Toast.show('MainActivity 页面销毁时获取的返回结果 result = $value', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM); });// MainActivityMap map = new HashMap();map.put("params_name", "赵六");map.put("params_age", 48);Intent intent = new Intent();intent.putExtra(IFlutterViewContainer.RESULT_KEY, (Serializable) map);setResult(Activity.RESULT_OK, intent);finish();
复制代码


核心源码

class FirstPage extends StatelessWidget {  final Map params;  FirstPage({this.params});
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('First Page')), body: Column(mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: <Widget>[ Center(child: Text('params 为:${params ?? "null"}')), Center(child: RaisedButton(child: Text('打开 Second Flutter Page (无参)'), onPressed: () { FlutterBoost.singleton.open('second_page'); })), Center(child: RaisedButton(child: Text('打开 Second Flutter Page (有参)'), onPressed: () { FlutterBoost.singleton.open('second_page', urlParams: {'params_name': '李四', 'params_age': 28}); })), Center(child: RaisedButton( child: Text('打开 Second Flutter Page (有参 + 返回值)'), onPressed: () { FlutterBoost.singleton.open('second_page', urlParams: { 'params_name': '李四', 'params_age': 28 }).then((Map value) { print('Second Page 页面销毁时获取的返回结果 result = $value'); }); })) ])); }}
class SecondPage extends StatelessWidget { final Map params; SecondPage({this.params});
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Second Page')), body: Column(mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: <Widget>[ Center(child: Text('params 为:${params ?? 'null'}')), Center(child: RaisedButton(child: Text('关闭 Second Flutter Page 且带返回值'), onPressed: () { BoostContainerSettings settings = BoostContainer.of(context).settings; FlutterBoost.singleton.close(settings.uniqueId, result: {'result': '来自 SecondPage Result'}); })), Center(child: RaisedButton(child: Text('打开 Native MainActivity (无参)'), onPressed: () { FlutterBoost.singleton.open('native://main_activity'); })), Center(child: RaisedButton(child: Text('打开 Native MainActivity (有参)'), onPressed: () { FlutterBoost.singleton.open('native://main_activity', urlParams: {'params_name': '王五', 'params_age': 38}); })), Center(child: RaisedButton( child: Text('打开 Native MainActivity (有参 + 返回值)'), onPressed: () { FlutterBoost.singleton.open('native://main_activity', urlParams: { 'params_name': '王五', 'params_age': 38 }).then((Map value) { Toast.show('MainActivity 页面销毁时获取的返回结果 result = $value', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM); }); })) ])); }}
复制代码




      小菜对 FlutterBoost 的了解还知之甚少,且每个版本的方法注意点各有不同,如有错误请多多指导!


来源: 阿策小和尚

发布于: 刚刚阅读数: 2
用户头像

还未添加个人签名 2021.05.13 加入

Android / Flutter 小菜鸟~

评论

发布
暂无评论
【Flutter 专题】78 图解 Android Native 集成 FlutterBoost 小尝试 (一)