简述Flutter集成到Android原生项目

开发集成环境
[✓] Flutter (Channel stable, v1.12.13+hotfix.9, on Mac OS X 10.14.6 18G103, locale zh-Hans-CN)

接触使用Flutter也有段时间了,利用假期来梳理下在Flutter在使用层面的一些混编知识点,其实整理起来会发现原生与Flutter的混编原生与RN的混编相似度很大,其实所有的跨平台框架谈到和原生混编核心也就是方法调用和消息通信,自然很相似了,这里简单列以下几点(前几点方便你前期技术调研方案可行性研究,便于你快速去创建demo落实到项目中进行试手,后期再逐步梳理Dart语法笔记以及Flutter开发中的笔记)(这里就不讲述Flutter的环境搭建了):
一、简述Flutter集成到Android原生项目
二、Android原生以AAR形式集成Flutter项目
三、Flutter与原生(Android/IOS)的消息通信
四、Flutter中如何使用原生控件/组件
五、Flutter状态管理Provider与Redux
六、Flutter升级及开发中遇到的问题汇总

image.png

在现有Android项目中集成Flutter项目,可参考官网方法Add Flutter to existing app
当然也可按照下列步骤操作即可。

1、 新建Flutter项目,选择Flutter Module类型

  • 通过Android Studio 选择 File -> New -> New Flutter Project -> Flutter Module 创建。
  • 通过命令行 $ flutter create -t module my_flutter 创建。
    创建完成后,Flutter Module可正常运行到设备上。

2、 在Android项目中集成Flutter项目

在宿主项目app下的的 build.gradle 里面,android {} 下修改:

android {
  //...
  compileOptions {
    sourceCompatibility 1.8
    targetCompatibility 1.8
  }
}

如何在原生项目中引入Flutter模块

方式一:主module通过模块依赖方式来依赖flutter

将flutter作为module,然后native主工程引入进来。这种方式适合参与人数比较少的项目,如果有多人协作开发的大型项目就不合适了,因为其他人首先要配置Flutter环境,而且团队里面其他人还要配置module的依赖,都要熟悉flutter,成本是很高的。

  • 在工程的settings.gradle增加以下配置:
    // 加入下面配置
    setBinding(new Binding([gradle: this]))
    evaluate(new File(
            settingsDir.parentFile,
            'my_flutter/.android/include_flutter.groovy' //更改成自己的项目目录
    ))
    
  • 在app 的gradle里添加依赖:
    implementation project(':my_flutter')
    
    弊端:
    这种方式适合参与人数比较少的项目,如果有多人协作开发的大型项目就不合适了,因为其他人首先要配置Flutter环境,而且团队里面其他人还要配置module的依赖,都要熟悉flutter,成本是很高的。所以还需要以依赖jar/aar的方式来集成。
方式二:通过aar包引入
  • 将Flutter module打包成aar文件:
    进入根目录下的.android目录下执行./gradlew assembleRelease 编译成功后会在.android/Flutter/build/outputs/aar/flutter-release.aar 生成aar文件。 (此种方式生成的aar包之前还能用,当前版本会报错,稍后会提供通过使用fat-aar使用脚本打包方式)
    注意:暂时以第一种方式集成,稍后在【二、Android原生以AAR形式集成Flutter项目】 会详细讲解方式二的使用。

3、在Android项目中加载Flutter页面:

  • 继承FlutterActivity加载Flutter页面
    如果初期只是为了加载出对应的Flutter页面,简单点可不继承FlutterActivity创建自己的Activity,可直接使用FlutterActivity
    public class MyFlutterActivity extends FlutterActivity {
    
      // 定义Channel名称
      private static final String CHANNEL_NATIVE = "com.cc.flutter/native";
    
     public static void openFlutter(Activity activity, String routerUrl){
          Intent intent = MyFlutterActivity.withNewEngine()
                .initialRoute(routerUrl)
                .build(activity);
          //设置Activity透明
          //intent.putExtra("background_mode","transparent");
          activity.startActivity(intent);
      }
    
      public static CCEngineIntentBuilder withNewEngine() {
          return new CCEngineIntentBuilder(MyFlutterActivity.class);
      }
    
      public static CCEngineIntentBuilder withNewEngine(Class<? extends FlutterActivity> activityClass) {
          return new CCEngineIntentBuilder(activityClass);
      }
    
      public static class CCEngineIntentBuilder extends NewEngineIntentBuilder {
    
        protected CCEngineIntentBuilder(Class<? extends FlutterActivity> activityClass) {
              super(activityClass);
          }
      }
    
      @Override
      public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
          GeneratedPluginRegistrant.registerWith(flutterEngine);
          //此处是Flutter与原生通信方法,暂时可不关注
          MethodChannel methodChannel = new MethodChannel(flutterEngine.getDartExecutor(), CHANNEL_NATIVE);
          methodChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
            @Override
            public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
                System.out.println("MethodChannel call.method:"+call.method+ "  call arguments:"+call.arguments.toString());
                switch (call.method){
                    case "envType":
                        result.success(2);
                        break;
                    default:
                        result.error("404", "未匹配到对应的方法"+call.method, null);
                }
            }
        });
    }
    
    此处的FlutterActivity使用的是import io.flutter.embedding.android.FlutterActivity; 而不是io.flutter.app.FlutterActivity , 貌似是说为了更好地支持将 Flutter 添加到现有项目的执行环境,托管 Flutter 运行时的旧版 Android 平台端包装器位于 io.flutter.app.FlutterActivity 及其关联的类现在已弃用。新的包装器 io.flutter.embedding.android.FlutterActivity 及相关类替代了他们。
    其实Flutter 1.2升级了很多东西,具体可参考 Upgrading pre 1.12 Android projects 一步步操作。(稍后我也会梳理在开发中需要操作的升级点及碰到的问题)
  • AndroidManifest注册Activity:
    <!--flutter相关 start-->
          <activity android:name=".activity.flutter.MyFlutterActivity"
              android:launchMode="singleTop"
              android:screenOrientation="portrait"
              android:windowSoftInputMode="adjustPan" />
    
          <activity
              android:name="io.flutter.embedding.android.FlutterActivity"
              android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
              android:hardwareAccelerated="true"
              android:theme="@style/AppTheme"
              android:windowSoftInputMode="adjustResize" >
          </activity>
          <meta-data
              android:name="flutterEmbedding"
              android:value="2" />
          <!--flutter相关 end-->
    
  • Flutter中添加如下代码:
    import 'dart:ui';
    import 'package:flutter/material.dart';
    
    void main() => runApp(_widgetForRoute(window.defaultRouteName));
    
    Widget _widgetForRoute(String route) {
      switch (route) {
        case 'route1':
          return MaterialApp(
            debugShowCheckedModeBanner: false,
            home: Scaffold(
              appBar: AppBar(
                title: Text('MyFlutter页面'),
                centerTitle: true,
              ),
              body: Center(
                child: Column(
                  children: <Widget>[
                    Text('Flutter页面,route=$route, params=$paramsJson'),
                    RaisedButton(
                      textColor: Colors.blue,
                      child: Text("跳转原生页面"),
                      onPressed:(){
                        // 跳转原生页面
                        Map<String, dynamic> result = {'name': '你好,${params["name"]}'};
                        nativeChannel.invokeMethod('jumpToNative', result);
                      },
                    ),
                  ],
                ),
              ),
            ),
          );
        default:
          return Center(
            child: Text('Unknown route: $route', textDirection: TextDirection.ltr),
          );
      }
    }
    

以上是Android原生项目中集成Flutter步骤。


这样就实现了 Android 原生跳转到 Flutter 页面进行渲染, 可以边开发,边编译看效果了。

注意:文中暂时以第一种方式集成,稍后在【二、Android原生以AAR形式集成Flutter项目】 会详细讲解方式二的使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,513评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,312评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,124评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,529评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,937评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,913评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,084评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,816评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,593评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,788评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,267评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,601评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,265评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,158评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,953评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,066评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,852评论 2 277

推荐阅读更多精彩内容