Android與Flutter混合開發-UI交互

前言

  • 本人是作android的,這邊只介紹下Android和Flutter的混合開發。
  • 關於原生和Flutter的混合開發,網上有不少相關的文章了,基本上都是使用FlutterView和FlutterFragment的方式去作的,可是在新版Flutter SDK 1.12版本上,Flutter團隊把io.flutter.facade.Flutter這個包給刪了,上面倆種方式直接涼了,根本沒法在Android項目裏拿到Flutter對象。因此只能去看官方文檔,跑下新的集成方法,這邊作個備忘,也同時說下會踩的坑。
  • Flutter官方文檔:flutter.cn/docs/develo…
  • 本文章適用於Flutter SDK 1.12版本

注:說明下,這個連接是中文文檔,可是還有不少章節仍是沒翻譯,例如將Flutter module集成到Android項目的章節。ios的已被翻譯,大佬們都是喜歡玩ios,因此首先翻譯ios?java

準備工做

新建一個Android項目

  • 這個隨意,新建一個Android項目或者使用已有的Android項目均可以。

配置Android項目

  • Flutter 目前只支持 armeabi-v7a 和 arm64-v8a 架構。
android {
  //...
  defaultConfig {
    ndk {
      // 限制下架構,Flutter僅支持:armeabi-v7a 和 arm64-v8a 架構(目前1.12版本)
      abiFilters 'armeabi-v7a', 'arm64-v8a'
    }
  }
}
複製代碼
  • Flutter使用了 Java 8的特性,設置支持Java 8
android {
  //...
  compileOptions {
    sourceCompatibility 1.8
    targetCompatibility 1.8
  }
}
複製代碼

新建一個Flutter Module

  • 新建Flutter Module

新建Module

  • 設置Flutter Module

導入Flutter Module

自動導入

注:Android Studio支持自動導入,前提條件是:Android項目和Flutter Module在同一文件夾下。android

  • 選擇:「New Module」
  • 選擇 「Import Flutter Module」
  • 選擇下你的Flutter Module項目文件夾就好了

手動導入

注:只須要設置下就好了,上面自動導入,就是默認生成代碼的。ios

  • MyApp/app/build.gradle,添加下依賴
    dependencies {
      implementation project(':flutter')
    }
    複製代碼
  • MyApp/settings.gradle,settings.gradle加上下述代碼,「flutter_module」是項目名稱,寫上你本身的項目名稱(官網上的代碼)
    include ':app'                                   
    setBinding(new Binding([gradle: this]))                            
    evaluate(new File(                                                      
      settingsDir.parentFile,                                              
      'flutter_module/.android/include_flutter.groovy'                         
    ))                                                                    
    複製代碼
    通常默認生成的是下述代碼形式
    include ':app'
    setBinding(new Binding([gradle: this]))
    evaluate(new File(
      settingsDir,
      '../flutter_module/.android/include_flutter.groovy'
    ))
    複製代碼
  • 說明下路徑符號相關區別
    • / :根目錄
    • ./ :當前目錄
    • ../ : 父級目錄(上一級目錄)
    • settingsDir:獲取的是當前項目的全路徑,包括項目文件夾
    • settingsDir.parentFile:獲取項目文件夾上一級路徑

Flutter和Android UI交互

添加單個Flutter頁面

使用FlutterActivity的形式,呈現Flutter界面十分方便緩存

  • 前置條件:FlutterActivity 必須在 AndroidManifest.xml 中註冊bash

    <activity
      android:name="io.flutter.embedding.android.FlutterActivity"
      android:theme="@style/AppTheme"
      android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
      android:hardwareAccelerated="true"
      android:windowSoftInputMode="adjustResize"
      />
    複製代碼
  • 啓動Flutter Module默認路由(最簡單形式)架構

    Intent intent = FlutterActivity.createDefaultIntent(this);
    startActivity(intent);
    複製代碼

    這樣能夠直接跳轉到Flutter界面,實際是啓動FlutterActivity,在FlutterActivity裏面加載Flutter的Ui界面。app

  • 選擇性跳轉某個路由界面ide

    Intent intent = FlutterActivity.withNewEngine()
                    .initialRoute("/you/route/") //設置你的路由地址
                    .build(this);
    startActivity(intent);
    複製代碼

    實際上createDefaultIntent()方法裏面就是封裝了withNewEngine().build(launchContext)方法,有興趣的,能夠點進代碼裏面看看。佈局

  • 使用緩存的 FlutterEnginegradle

    //使用緩存的FlutterEngine(最大程度地減小啓動標準的延遲)
    FlutterEngine flutterEngine = new FlutterEngine(this);  //初始路由
    //flutterEngine.getNavigationChannel().setInitialRoute("your/route/here"); //自定義路由
    //開始執行Dart代碼以預熱FlutterEngine。
    flutterEngine.getDartExecutor().executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault());
    //緩存FlutterActivity要使用的FlutterEngine。
    FlutterEngineCache.getInstance().put("my_engine_id", flutterEngine);
    Intent intent = FlutterActivity
            .withCachedEngine("my_engine_id")
            .build(this);
    
    //某個按鈕的點擊事件
    findViewById(R.id.flutter_button).setOnClickListener(v -> {
            startActivity(intent);
        });
    
    複製代碼

    說明下:實際上FlutterActivity.withNewEngine()...方式跳轉,都是新生成一個FlutterEngine對象,每一個FlutterEngine都有一個很是重要的預熱時間。這意味着啓動一個標準的FlutterActivity 會在Flutter體驗變得可見以前有一個短暫的延遲。爲了最小化這個延遲,咱們能夠在到達FlutterActivity以前預熱FlutterEngine,而後可使用緩存的FlutterEngine。這種狀況在debug安裝的狀況尤爲顯著,會有一段時間黑屏,提早緩存好FlutterEngine,能夠避免這種狀況,提高交互體驗,推薦。

添加FlutterFragment

  • 說明:FlutterFragment可用的地方仍是蠻多的,可用於顯示一個滑動的抽屜、標籤式內容、 ViewPager 中的一個頁面等等,固然,若是FlutterActivity能解決,建議使用FlutterActivity,由於FlutterActivity更容易使用。 官網原話:If an Activity is equally applicable for your application needs, consider using a FlutterActivity instead of a FlutterFragment, which is quicker and easier to use.

  • 使用FlutterFragment

    public class MyActivity extends FragmentActivity {
        private FlutterFragment flutterFragment;
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.my_activity_layout);
    
              //直接啓動FlutterFragment
            FragmentManager fragmentManager = getSupportFragmentManager();
            //默認路由,至關於:initialRoute("/")
            //FlutterFragment flutterFragment = FlutterFragment.createDefault();
            FlutterFragment flutterFragment = FlutterFragment.withNewEngine()
                    .initialRoute("/")   //設置路由
                    .build();
            fragmentManager
                    .beginTransaction()
                    .add(R.id.flutter_ui, flutterFragment, "flutter_fragment")
                    .commit();
        }
    }
    複製代碼
    • R.id.flutter_ui:是佈局文件裏面一個Fragment的id

    • 比較坑比的地方

      • FlutterFragment沒法強轉成Fragment類型,涉及到強轉的部分會爆紅,可是不影響運行。

      • 緣由:看了下FlutterFragment,繼承Fragment的,子類強轉父類是沒問題的,可是很坑的是,FlutterFragment裏面用的是Support包,我用的是Androidx,致使倆個對象無法強轉。 就是:support.v4.app.Fragment的子類無法強轉成androidx.fragment.app.Fragment ? 這個報錯真是血坑。

      • 解決辦法:在下只找到了可能的緣由,實在找不到解決之法。可能的解決辦法

        • 報錯就報錯吧,留在那,不影響程序運行,佛系點
        • 等待Flutter團隊,把這個FlutterFragment裏面fuck的support包給刪掉,換成androidx
        • 有其餘辦法解決,但願評論告知下
    • 使用上面的代碼,足以把你的Flutter頁面展現出來了

  • 使FlutterFragment週期和Activity同步

    public class MyActivity extends FragmentActivity {
        @Override
        public void onPostResume() {
            super.onPostResume();
            flutterFragment.onPostResume();
        }
    
        @Override
        protected void onNewIntent(@NonNull Intent intent) {
            flutterFragment.onNewIntent(intent);
        }
    
        @Override
        public void onBackPressed() {
            flutterFragment.onBackPressed();
        }
    
        @Override
        public void onRequestPermissionsResult(
            int requestCode,
            @NonNull String[] permissions,
            @NonNull int[] grantResults
        ) {
            flutterFragment.onRequestPermissionsResult(
                requestCode,
                permissions,
                grantResults
            );
        }
    
        @Override
        public void onUserLeaveHint() {
            flutterFragment.onUserLeaveHint();
        }
    
        @Override
        public void onTrimMemory(int level) {
            super.onTrimMemory(level);
            flutterFragment.onTrimMemory(level);
        }
    }
    複製代碼
  • 使用緩存的 FlutterEngine

    //使用緩存的FlutterEngine(最大程度地減小啓動標準的延遲)
    FragmentManager fragmentManager = getSupportFragmentManager();
    FlutterEngine flutterEngine = new FlutterEngine(this);  //初始路由
    // flutterEngine.getNavigationChannel().setInitialRoute("/"); //自定義路由
    //開始執行Dart代碼以預熱FlutterEngine。
    flutterEngine.getDartExecutor().executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault());
    FlutterEngineCache.getInstance().put("my_engine_id", flutterEngine);//緩存要使用的FlutterEngine。
    
    //點擊事件
    findViewById(R.id.flutter_button).setOnClickListener(v -> {
            FlutterFragment flutterFragment = FlutterFragment.withCachedEngine("my_engine_id").build();
            fragmentManager
                    .beginTransaction()
                    .add(R.id.flutter_ui, flutterFragment, "flutter_fragment")
                    .commit();
        });
    複製代碼

    基本上和上面使用FlutterActivity緩存機制同樣

    • Flutter官網上寫完緩存代碼,單獨來了這麼段代碼:
      flutterFragment.withCachedEngine("my_engine_id").build();
      複製代碼
      真是嗶嗶了狗,我還覺得withCachedEngine("my_engine_id").build()直接把緩存的FlutterEngine寫入到flutterFragment對象裏面,實際上,build() 返回的是FlutterFragment對象,上面的代碼只是取設置好的FlutterFragment。
      • 猜想下官網上代碼意圖,難道是這樣:
      fragmentManager
                .beginTransaction()
                .add(R.id.flutter_ui, flutterFragment.withCachedEngine("my_engine_id").build(), "flutter_fragment")
                .commit();
      複製代碼
相關文章
相關標籤/搜索