最近学了下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的方式能够达到这样的效果。
不过这也足够咱们开发中使用了。
未完待续...