不得不看的Flutter與Android混合開發

大逗大人發表於2019-06-16

記得在flutter剛出來時,筆者就開始學習flutter。但由於當時嫌棄flutter複雜的層級組合且未推出穩定版,所以當時就放棄了深入學習,現如今隨著flutter的蓬勃發展及大佬們的力推,就又入坑flutter。

不得不看的Flutter與Android混合開發

雖說flutter能夠跨平臺,但由於現在幾乎都是現成的專案,所以不可能用flutter來重頭開發,所以目前幾乎都是採用native+flutter的混合開發方案。那麼該方案該如何實現尼?

1、flutter模組的匯入

首先,切換到native專案的根目錄的上一級目錄。以筆者專案為例,路徑為D:\FlutterHybrid\FlutterHybridAndroid,然後通過命令cd ../切換到上一級目錄。再執行下面命令來建立一個flutter模組。

    flutter create -t module flutter_module
複製程式碼

上面的flutter_module就是我們建立的flutter模組名稱。

當flutter模組建立成功後,我們就需要通過以下步驟來匯入該模組。

  1. 首先在在settings.gradle檔案中新增如下程式碼。
        setBinding(new Binding([gradle:this]))
        evaluate(new File(
                settingsDir.parentFile,'flutter_module/.android/include_flutter.groovy'
        ))
    複製程式碼
    新增完成後,就能夠在Android Studio中看到flutter模組,如下圖。
    不得不看的Flutter與Android混合開發
  2. 其次,在能夠正確顯示flutter模組後,我們就需要通過implementation project(':flutter')來匯入該模組。新增成功後就開始編譯專案,這時候就可能會遇到如下錯誤。
    不得不看的Flutter與Android混合開發
    這就是我們需要注意的一點,native專案的minSdkVersion不能小於Flutter模組的minSdkVersion。解決方案就是把native專案的minSdkVersion的值修改為大於flutter模組的minSdkVersion的值

經過上面兩步後,native專案就成功匯入了flutter模組,這時候就可以來執行native專案。但在執行native專案時卻又可能出現如下錯誤。

不得不看的Flutter與Android混合開發
該問題該怎麼解決尼?其實在上圖的最下面已經給出解決方案了,就是native專案必須使用Java 8,否則不讓執行。所以我們需要在app目錄下的build.gradle檔案中新增如下程式碼。

    android {
        compileOptions {
            sourceCompatibility 1.8
            targetCompatibility 1.8
        }
    }
複製程式碼

然後繼續執行native專案,這時候就能夠在裝置上跑起來了,但如何驗證flutter模組是否打包進apk里尼?這時候就可以藉助Android Studio的apk分析工具。通過該工具可以發現apk包由以下內容組成。

不得不看的Flutter與Android混合開發
其中flutter_assets存放的就是flutter程式碼,到這裡native專案就成功的匯入了flutter模組。

2、native專案載入flutter頁面

經過前面的一些操作,我們就在Native專案中成功依賴了flutter模組,那麼下面學習如何在Native專案中載入flutter頁面。通過檢視flutter模組程式碼可以發現,該模組提供了以下兩種方式來載入flutter頁面。

  1. 將flutter頁面構建成View,通過addView來顯示flutter頁面
  2. 將flutter頁面構建成Fragment,通過對fragment的操作來顯示flutter頁面

2.1、將flutter頁面構建成View

在flutter模組的Flutter類中給我們提供了一個方法——createView。通過該方法,我們可以將flutter頁面構建成一個View。而View的相關操作想必對於Android開發者來說都不陌生,所以就通過addView將flutter頁面新增到相應的地方。實現程式碼如下:

    public void onLoadFlutter(View view) {
        View flutterView = Flutter.createView(this, getLifecycle(), "route1");
        FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(600, 600);
        layoutParams.topMargin = 100;
        addContentView(flutterView, layoutParams);
    }
複製程式碼

2.2、將flutter頁面構建成flutter

同樣,flutter模組也提供了方法——createFragment,通過該方法就將flutter頁面構建成一個fragment,然後根據fragment的操作將flutter頁面新增到相應的地方。實現程式碼如下:

    public void onLoadFlutter(View view) {
       FragmentTransaction transaction= getSupportFragmentManager().beginTransaction();
       transaction.replace(R.id.someContainer,Flutter.createFragment("這裡是flutter頁面"));
       transaction.commit();
    }
複製程式碼

2.3、flutter頁面

在前面講述瞭如何在native專案中載入flutter頁面,下面就來看一下flutter頁面的程式碼。程式碼還使很簡單的,基本的都是建立module時自動生成的程式碼。

import 'package:flutter/material.dart';
import 'dart:ui';

void main() => runApp(MyApp(
      initParams: window.defaultRouteName,
    ));

class MyApp extends StatelessWidget {
  final String initParams;

  MyApp({Key key, @required this.initParams}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(
        title: 'Flutter Demo Home Page',
        initParams: initParams,
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String initParams;

  MyHomePage({Key key, this.title, this.initParams}) : super(key: key);
  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState(initParams);
}

class _MyHomePageState extends State<MyHomePage> {
  final String initParams;

  _MyHomePageState(this.initParams);

  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'initParams:$initParams',
              style: TextStyle(color: Colors.red),
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.display1,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}
複製程式碼

可以發現,在上面程式碼中,我們傳入了一個初始化屬性,它其實就是一個路由名稱,但其實我們也傳入一個json或者其他型別的資料,從而來做一些其他操作。其實這樣就可以看做native與flutter之間的一種通訊。

3、flutter模組的除錯

3.1、flutter模組的熱過載

flutter的優勢之一就是在開發過程中能夠通過熱過載功能來實現快速的除錯,但通過執行上面程式碼就會發現,flutter模組程式碼修改後無法立即生效,需要重新打包Native才能生效。這樣就讓flutter的一個重大優勢失效了,降低了除錯效率。那麼我們能不能在混合專案中做到flutter模組的熱過載尼?其實也是可以的,但需要經過一些步驟。

  1. 首先,關閉當前應用,注意:是要殺死當前應用所在程式,而不是退出應用

  2. 其次,在flutter模組中輸入命令flutter attach,就會顯示以下內容。

    不得不看的Flutter與Android混合開發

  3. 最後,再次開啟應用,就會出現如下內容。

    不得不看的Flutter與Android混合開發
    請注意圖中的這段話

    ? To hot reload changes while running, press "r". To hot restart (and rebuild state), press "R".

    它告訴我們如果要熱過載就按r鍵,想要熱重啟就按R鍵。當修改flutter程式碼後,按下r鍵,就會出現以下提示,代表修改成功。

    不得不看的Flutter與Android混合開發

經過上面的一些步驟,我們就可以在混合專案中使用flutter的熱過載功能,做到flutter修改後的立即生效。

3.2、flutter模組的除錯

其實混合專案的flutter模組除錯與flutter專案的的唯一卻別就是如何在Android Studio與裝置之間建立socket連線。在flutter專案中,我們可以直接點選debug按鈕來進行除錯,但在混合專案中,該按鈕就不起作用了,得通過其他方式來建立連線。Android Studio給我們提供了flutter attach按鈕,通過該按鈕,flutter模組就能跟裝置建立連線,就能對flutter模組進行除錯。

不得不看的Flutter與Android混合開發

4、總結

通過上面的一些講解,相信就能夠使用native+flutter的混合開發了。但細心一點就會發現,在前面的講解中,flutter模組並沒有與native專案進行通訊,那麼該如何通訊尼?在筆者的下一篇文章會進行詳細講解。

相關文章