最近學了下Flutter,確實挺好的。
單獨的Flutter專案跑起來沒有多大問題。
目前也有一些混合開發的需求,所以找了一些文章來看,收穫頗豐。
一.在現有專案上整合Flutter步驟詳解
1.建立專案
這一步要注意,建立專案的目錄,不是,不是,不是Android專案的根目錄
↑不是它!
應該是下圖的目錄結構。flutter_hybrid目錄即為我們要建立的flutter專案,FlutterrHybridDemo是我們現有的Android專案,叫什麼都好。可以像我這樣,建立一個空資料夾,把現有Android專案拖進去,這樣方便查詢和管理,不然flutter_hybrid會和FlutterHybridDemo平級,在一大堆專案中找這兩個也怪麻煩的。
在這個目錄下開啟cmd,執行命令:
flutter create -t module xxxx(想要建立的flutter專案名)執行完複製程式碼
執行完後,一個和Android專案平級的flutter專案就已經建立好了。
2.新增flutter到當前Android專案
在Android專案根目錄下的settings.gradle檔案中,新增如下程式碼:
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
"flutter_hybrid/.android/include_flutter.groovy"
))
複製程式碼
然後,在專案的app目錄下,build.gradle檔案中,新增如下dependency:
implementation project(':flutter')複製程式碼
如上。新增完之後,準備工作就完成了。
二.整合Flutter
目前有兩種方式可以新增
1.通過FlutterView方式新增到佈局中
首先通過Flutter.createFlutterView方法來建立一個FlutterView型別的View。它有三個構造引數。
public static FlutterView createView(Activity activity, Lifecycle lifecycle, String initialRoute)複製程式碼
三個引數分別是:
1.Activity,即當前附著的Activity
2.LifeCycle,且是不能為空(NonNull)的,這也就要求我們必須用AppCompatActivity來承載FlutterView了,因為我試過了,如果我們的Activity繼承自android.app.Activity是沒有getLifeCycle()這個方法的。
public class FlutterViewActivity extends AppCompatActivity 複製程式碼
3.initialRoute
這是一個String型別的變數,可以理解為一個需要在Flutter中作為身份標記的變數,根據不同的標識返回不同的Flutter頁面。
那麼在Flutter檔案中我們要這樣寫:
在 Flutter檔案頁面中的build方法中,我們可以拿到window物件,通過window.defaultRouteName(就是我們建立的時候傳遞的第三個引數)來區分返回不同介面
@override Widget build(BuildContext context) { switch(window.defaultRouteName){ case "flutter_view": return _flutterHomeView(context); default: return Center( child: Text('Unknown route: route', textDirection: TextDirection.ltr), );
}
}
Widget _flutterHomeView(context){ return Scaffold( appBar: AppBar(title: Text('FlutterView的AppBar'),), body: Center( child: Text('現在是Flutter'), ), ); }
複製程式碼
在當前Activity的OnCreate方法中,執行
setContentView(R.layout.activity_flutter);
FlutterView flutterView = Flutter.createView(this, getLifecycle(), "flutter_view");
layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
addContentView(flutterView, layoutParams);
複製程式碼
由於addContentView這個方法必須要傳遞一個LayoutParams引數,我們就要用ViewGroup的LayoutParams。
這樣執行的結果是:
可以看到Flutter頁面已經作為一個View被新增到了Android原生介面中。這樣我們就邁出了成功的第一步。
但是問題在於,我們看到FlutterView仍然不是全屏的,Android的ActionBar和Flutter的AppBar疊了兩層額頭,很不舒服。
這種情況,我們可以去掉兩者任意一個。
比如在Android中,將當前Activity在AndroidManifest.xml檔案中的theme屬性改一下
<activity android:name=".FlutterViewActivity" android:theme="@style/AppTheme.NoActionBar"></activity>複製程式碼
那麼Activity的ActionBar就去掉了
不過,現在狀態列不是沉浸式。關於這點,可以用Android程式碼在當前Activity中設定沉浸式狀態列,網上有很多成熟的解決方式,在此筆者就不提供了。
不過僅僅這樣,我還是不滿足的。因為我們的佈局中還有一個TextView控制元件,這時候怎麼沒了...
<RelativeLayotu
android:id="@+id/rl_container"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_native"
android:textSize="50sp"
android:text="原生文字控制元件"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>複製程式碼
所以我要的效果是這樣的:
我們都知道,如果使用了RelativeLayout或者FrameLayout的話,View預設addView是可以重疊在一起的。所以我們的根佈局如果是RelativeLayotu的話,要設定LayoutParams,新增Rule規定FlutterView要處於控制元件的下方。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flutter);
rlContainer = findViewById(R.id.rl_container);
rlContainer.addView(flutterView)
RelativeLayout.LayoutParams rlLayoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
rlLayoutParams.addRule(BELOW,R.id.tv_native);
llContainer.addView(flutterView,rlLayoutParams);
複製程式碼
不過我發現,addContentView()來新增的話, 即使是有LayoutParams也是會重疊在一起的。所以我的建議是,使用最外層容器(或者是你要新增進的那個容器).addView來新增。
當然,通過LinearLayout來作為容器,直接addView也是可以的。
通過嘗試,發現只有addView的方式可以達到這樣的效果。
不過這也足夠我們開發中使用了。
未完待續...