Flutter 78: 图解 Android Native 集成 FlutterBoost 小尝试 (一)

简介: 0 基础学习 Flutter,第七十八步:学习基本的 FlutterBoost 的用法!

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

Module 集成

1. 新建 AndroidX 工程

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

compileSdkVersion 28
defaultConfig {
    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 28
defaultConfig {
    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 进行前后路由的监听;

@override
void initState() {
  super.initState();

  FlutterBoost.singleton.registerPageBuilders({
    'first_page': (pageName, params, _) => FirstPage(),
    'second_page': (pageName, params, _) => SecondPage()
  });
  FlutterBoost.singleton.addBoostNavigatorObserver(TestBoostNavigatorObserver());
}

@override
Widget 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);

@Override
protected 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); });
// MainActivity
Map 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 的了解还知之甚少,且每个版本的方法注意点各有不同,如有错误请多多指导!

来源: 阿策小和尚

目录
相关文章
|
26天前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
105 4
|
27天前
|
前端开发 数据处理 Android开发
Flutter前端开发中的调试技巧与工具使用方法,涵盖调试的重要性、基本技巧如打印日志与断点调试、常用调试工具如Android Studio/VS Code调试器和Flutter Inspector的介绍
本文深入探讨了Flutter前端开发中的调试技巧与工具使用方法,涵盖调试的重要性、基本技巧如打印日志与断点调试、常用调试工具如Android Studio/VS Code调试器和Flutter Inspector的介绍,以及具体操作步骤、常见问题解决、高级调试技巧、团队协作中的调试应用和未来发展趋势,旨在帮助开发者提高调试效率,提升应用质量。
45 8
|
26天前
|
传感器 前端开发 Android开发
在 Flutter 开发中,插件开发与集成至关重要,它能扩展应用功能,满足复杂业务需求
在 Flutter 开发中,插件开发与集成至关重要,它能扩展应用功能,满足复杂业务需求。本文深入探讨了插件开发的基本概念、流程、集成方法、常见类型及开发实例,如相机插件的开发步骤,同时强调了版本兼容性、性能优化等注意事项,并展望了插件开发的未来趋势。
39 2
|
2月前
|
前端开发 JavaScript Android开发
Flutter 与 React Native - 详细深入对比分析(2024 年)
Flutter和React Native是两大跨平台框架,各有优缺点。Flutter性能优越,UI灵活,使用Dart;React Native生态广泛,适合JavaScript开发。
674 5
Flutter 与 React Native - 详细深入对比分析(2024 年)
|
25天前
|
开发框架 Dart Android开发
安卓与iOS的跨平台开发:Flutter框架深度解析
在移动应用开发的海洋中,Flutter作为一艘灵活的帆船,正引领着开发者们驶向跨平台开发的新纪元。本文将揭开Flutter神秘的面纱,从其架构到核心特性,再到实际应用案例,我们将一同探索这个由谷歌打造的开源UI工具包如何让安卓与iOS应用开发变得更加高效而统一。你将看到,借助Flutter,打造精美、高性能的应用不再是难题,而是变成了一场创造性的旅程。
|
2月前
|
移动开发 Dart 搜索推荐
打造个性化安卓应用:从零开始的Flutter之旅
【10月更文挑战第20天】本文将引导你开启Flutter开发之旅,通过简单易懂的语言和步骤,让你了解如何从零开始构建一个安卓应用。我们将一起探索Flutter的魅力,实现快速开发,并见证代码示例如何生动地转化为用户界面。无论你是编程新手还是希望扩展技能的开发者,这篇文章都将为你提供价值。
|
2月前
|
开发框架 移动开发 Android开发
安卓与iOS开发中的跨平台解决方案:Flutter入门
【9月更文挑战第30天】在移动应用开发的广阔舞台上,安卓和iOS两大操作系统各自占据半壁江山。开发者们常常面临着选择:是专注于单一平台深耕细作,还是寻找一种能够横跨两大系统的开发方案?Flutter,作为一种新兴的跨平台UI工具包,正以其现代、响应式的特点赢得开发者的青睐。本文将带你一探究竟,从Flutter的基础概念到实战应用,深入浅出地介绍这一技术的魅力所在。
91 7
|
3月前
|
开发框架 搜索推荐 开发工具
打造个性化安卓应用:从零开始的Flutter之旅
【8月更文挑战第51天】本文是一篇面向初学者的Flutter入门教程,旨在通过简单易懂的语言和实际代码示例,引导读者步入跨平台移动应用开发的世界。文章首先介绍了Flutter的基本概念和优势,然后逐步展示了如何搭建开发环境、创建第一个Flutter应用,并实现了一个简单的待办事项列表。最后,文章探讨了Flutter在实现高性能和美观界面方面的潜力,鼓励读者发挥创意,探索更多可能。
90 15
|
2月前
|
Dart Android开发
鸿蒙Flutter实战:03-鸿蒙Flutter开发中集成Webview
本文介绍了在OpenHarmony平台上集成WebView的两种方法:一是使用第三方库`flutter_inappwebview`,通过配置pubspec.lock文件实现;二是编写原生ArkTS代码,自定义PlatformView,涉及创建入口能力、注册视图工厂、处理方法调用及页面构建等步骤。
68 0
|
3月前
|
Dart 开发工具 Android开发
在 Android 系统上搭建 Flutter 环境的具体步骤是什么?
在 Android 系统上搭建 Flutter 环境的具体步骤是什么?