因为笔者本身主要从事是Android开发,所以很多角度都是作为一个Android开发者学习Flutter的角度出发,IOS或者H5的开发同学可以选择性阅读
目录
前言
如果我们目前的项目是Android的,但是接下来我们希望部分页面可以使用Flutter进行开发,甚至我们希望在Native页面中嵌入FlutterUI组件,那么我们该如何实现呢?
创建Flutter Module
假设你现在Android项目的目录的结构是这样的
xxx/flutter_hybrid/FlutterHybridAndroid
这时候如果你想创建一个Flutter模块,使得Android模块和Flutter模块之间可以进行交互,我们可以通过Android Studio新建一个Flutter Module,具体过程是:File —> New —> New Module ,之后选择Flutter Module,指定Project Location的路径为
xxx/flutter_hybrid
也就是说,最终你的项目结构会是这样的
- flutter_hybrid
- flutter_module
- FlutterHybridAndroid
接下来在Android Module的build.gradle
文件中添加flutter依赖
//FlutterHybridAndroid/app/build.gradle
...
dependencies {
implementation project(':flutter')
...
}
Android启动Flutter页面的两种方式
先创建一个Flutter页面
这里比较重要的是window.defaultRouteName
这个字段,这个字段可以接收从Native传递过来的参数(下文我们会介绍原生传递参数的方法),也就是说通过这个字段我们就可以进行Flutter页面的路由的分发
import 'dart:ui';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FlutterHybird',
theme: ThemeData(
primarySwatch: Colors.blue,
),
//window.defaultRouteName可以接收从Native传过来的参数
home: _widgetForRoute(window.defaultRouteName),
);
}
}
Widget _widgetForRoute(String route) {
switch (route) {
case 'route1':
return Center(
child: Text("route1"),
);
case 'route2':
return Center(
child: Text("route2"),
);
default:
return Center(
child: Text('Unknown route: $route', textDirection: TextDirection.ltr),
);
}
}
- 直接启动一个FlutterActivity
我们可以直接在Android的MainActivity
中启动一个FlutterActivity
,这里的initialRoute
方法中传递的参数就对应Flutter层的window.defaultRouteName
findViewById(R.id.jump).setOnClickListener(v -> {
startActivity(FlutterActivity.withNewEngine()
.initialRoute("route2")
.build(MainActivity.this));
});
注意:需要在AndroidManifest.xml
注册FlutterActivity
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize" />
- 启动复写后的FlutterActivity(推荐)
自己创建一个FlutterAppActivity
继承自FlutterActivity
public class FlutterAppActivity extends FlutterActivity {
public final static String INIT_PARAMS = "initParams";
private String initParams;
public static void start(Context context, String initParams) {
Intent intent = new Intent(context, FlutterAppActivity.class);
intent.putExtra(INIT_PARAMS, initParams);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initParams = getIntent().getStringExtra(INIT_PARAMS);
}
/**
* 重载该方法来传递初始化参数
*
* @return
*/
@NonNull
@Override
public String getInitialRoute() {
return initParams == null ? super.getInitialRoute() : initParams;
}
}
在MainActivity
中启动FlutterAppActivity
(另外别忘了在AndroidManifest.xml
中注册FlutterAppActivity
)
findViewById(R.id.jump).setOnClickListener(v -> {
FlutterAppActivity.start(MainActivity.this,"route2");
});
两种启动方式的区别
如果单纯只是想打开一个Flutter页面,两种方式实际上基本没有太大区别,第一种方式也许还会更简单一点。但是,在Flutter开发中,我们往往还需要开发一些Native插件供Flutter调用,如果使用复写FlutterActivity
的方式更有利于我们在FlutterActivity
中注册我们的Native插件,所以实际开发中一般推荐使用第二种方式
扩展思考
initialRoute
从名称上看起来是Flutter提供给我们进行Native与Flutter交互的路由跳转的,但是实际上他就是一个字符串,我们不仅仅可以传递一个路由名称,有时候我们也可以通过这个参数传递一串JSON数据,然后在Flutter端进行解析,这样我们就可以通过这个参数做更多的事情
在Android原生页面中嵌入FlutterUI组件
activity_main.xml
FrameLayout
用于承载Flutter组件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@color/colorAccent"
android:gravity="center"
android:text="Hello World!" />
<FrameLayout
android:id="@+id/container"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1">
</FrameLayout>
</LinearLayout>
MainActivity.java
使用FragmentManager
将FlutterFragment
添加到FrameLayout
容器中
FlutterFragment fragment = FlutterFragment.withNewEngine().initialRoute("route1").build();
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, fragment)
.commit();
运行结果
上半部分是原生的TextView,下半部分是Flutter的Text组件
总结
本节主要介绍了Native和Flutter之间的页面跳转,以及同一个页面中Native与Flutter组件的组合。接下来会介绍如何编写Android插件与Flutter进行数据交互