簡述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升級及開發中遇到的問題彙總

在現有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項目】 會詳細講解方式二的使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章