記得在flutter剛出來時,筆者就開始學習flutter。但由於當時嫌棄flutter複雜的層級組合且未推出穩定版,所以當時就放棄了深入學習,現如今隨着flutter的蓬勃發展及大佬們的力推,就又入坑flutter。
雖說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模塊,如下圖。
2.其次,在能夠正確顯示flutter模塊後,我們就需要通過implementation project(':flutter')來導入該模塊。添加成功後就開始編譯項目,這時候就可能會遇到如下錯誤。
這就是我們需要注意的一點,native項目的minSdkVersion不能小於Flutter模塊的minSdkVersion。解決方案就是把native項目的minSdkVersion的值修改爲大於flutter模塊的minSdkVersion的值。
經過上面兩步後,native項目就成功導入了flutter模塊,這時候就可以來運行native項目。但在運行native項目時卻又可能出現如下錯誤。
該問題該怎麼解決尼?其實在上圖的最下面已經給出解決方案了,就是native項目必須使用Java 8,否則不讓運行。所以我們需要在app目錄下的build.gradle文件中添加如下代碼。
android {
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}
然後繼續運行native項目,這時候就能夠在設備上跑起來了,但如何驗證flutter模塊是否打包進apk里尼?這時候就可以藉助Android Studio的apk分析工具。通過該工具可以發現apk包由以下內容組成。
其中flutter_assets存放的就是flutter代碼,到這裏native項目就成功的導入了flutter模塊。
注意:如在果項目中使用AndroidX,就會導致很嚴重的兼容性問題。所以如果項目中使用了AndroidX,則要慎重導入flutter模塊。如果一定要導入,則可以去閱讀flutter官方提供的解決方案——AndroidX compatibility。
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頁面構建成fragment
同樣,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,就會顯示以下內容
3.最後,再次打開應用,就會出現如下內容。
請注意圖中的這段話