最近學了下Flutter,確實挺好的。android
單獨的Flutter項目跑起來沒有多大問題。bash
目前也有一些混合開發的需求,因此找了一些文章來看,收穫頗豐。app
這一步要注意,建立項目的目錄,不是,不是,不是Android項目的根目錄ide
↑不是它!佈局
應該是下圖的目錄結構。flutter_hybrid目錄即爲咱們要建立的flutter項目,FlutterrHybridDemo是咱們現有的Android項目,叫什麼都好。能夠像我這樣,創建一個空文件夾,把現有Android項目拖進去,這樣方便查找和管理,否則flutter_hybrid會和FlutterHybridDemo平級,在一大堆項目中找這兩個也怪麻煩的。gradle
在這個目錄下打開cmd,執行命令:ui
flutter create -t module xxxx(想要建立的flutter項目名)執行完複製代碼
執行完後,一個和Android項目平級的flutter項目就已經建立好了。this
在Android項目根目錄下的settings.gradle文件中,添加以下代碼:lua
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
"flutter_hybrid/.android/include_flutter.groovy"
))
複製代碼
而後,在項目的app目錄下,build.gradle文件中,添加以下dependency:spa
implementation project(':flutter')複製代碼
如上。添加完以後,準備工做就完成了。
目前有兩種方式能夠添加
首先經過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的方式能夠達到這樣的效果。
不過這也足夠咱們開發中使用了。
未完待續...