一、环境搭建
1. 首先确定下环境,如果环境不对,很可能出现问题,FlutterBoost 3.0用的Flutter SDK大于2.0
zfz:flutter_module zhangfengzhou$ flutter doctor -v
[✓] Flutter (Channel unknown, 2.0.5, on macOS 12.0.1 21A559 darwin-x64, locale zh-Hans-CN)
• Flutter version 2.0.5 at /Users/zhangfengzhou/Develop/Flutter/flutter
• Framework revision adc687823a (8 months ago), 2021-04-16 09:40:20 -0700
• Engine revision b09f014e96
• Dart version 2.12.3
• Pub download mirror https://pub.flutter-io.cn
• Flutter download mirror https://storage.flutter-io.cn
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
• Android SDK at /Users/zhangfengzhou/Library/Android/sdk
• Platform android-30, build-tools 30.0.2
• ANDROID_HOME = /Users/zhangfengzhou/Library/Android/sdk
• Java binary at: /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/bin/java
• Java version Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS
• Xcode at /Applications/Xcode.app/Contents/Developer
• Xcode 13.1, Build version 13A1030d
• CocoaPods version 1.11.2
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[!] Android Studio (version 2020.3)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
✗ Unable to find bundled Java version.
• Try updating or re-installing Android Studio.
[✓] IntelliJ IDEA Ultimate Edition (version 2019.3.1)
• IntelliJ at /Applications/IntelliJ IDEA.app
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
[✓] VS Code (version 1.55.2)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.22.0
[✓] Connected device (2 available)
• Android SDK built for x86 (mobile) • emulator-5554 • android-x86 • Android 10 (API 29) (emulator)
• Chrome (web) • chrome • web-javascript • Google Chrome 96.0.4664.93
! Doctor found issues in 1 category.
2. 在FlutterBoostDemo 目录下创建名为 flutter_module 的 Flutter Module,命令如下:
zfz:flutter_module zhangfengzhou$ flutter create -t module flutter_module
Flutter 的Module 项目是没有 android 和 ios 目录的,这点要注意。
3. 在FlutterBoostDemo 目录下创建名为 Android_demo 的 Android项目,最终的FlutterBoostDemo 目录结构如下:
4. 在flutter_module 项目的pubspec.yaml 中引入 flutter_boost :
environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
#闲鱼 flutter_boost
flutter_boost:
git:
url: 'https://github.com/alibaba/flutter_boost.git'
#ref: '1.17.1'
ref: 'v3.0-preview.8'
5. 更新下依赖,看是否能够将 flutter_boost 下载下来,如果成功,则执行下一步
6. 创建Dart部分代码:
void main() {
//添加全局生命周期监听类
PageVisibilityBinding.instance.addGlobalObserver(AppLifecycleObserver());
//CustomFlutterBinding 调用务必不可缺少,用于控制Boost状态的resume和pause
CustomFlutterBinding();
runApp(MyApp());
}
class AppLifecycleObserver extends GlobalPageVisibilityObserver{
@override
void onPagePush(Route<dynamic> route) {
}
@override
void onBackground(Route<dynamic> route) {
}
@override
void onForeground(Route<dynamic> route) {
}
@override
void onPagePop(Route<dynamic> route) {
}
@override
void onPageHide(Route<dynamic> route) {
}
@override
void onPageShow(Route<dynamic> route) {
}
}
//创建一个自定义的Binding, 继承和 With的关系如下,里面什么都不用写
class CustomFlutterBinding extends WidgetsFlutterBinding with BoostFlutterBinding {
}
7. Fluttter mainApp
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
@override
_MyAppPageState createState() => _MyAppPageState();
}
class _MyAppPageState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return FlutterBoostApp( //必修以此为根Widget
routeFactory,
appBuilder: appBuilder,
);
}
}
Widget appBuilder(Widget home) {
return MaterialApp(
home: home,
debugShowCheckedModeBanner: true,
//必须加上builder参数,否则showDialog等会出现问题
builder: (_,__){
return home;
},
);
}
//routeFactory 用于构建路由
Route<dynamic> routeFactory(RouteSettings settings, String uniqueId){
FlutterBoostRouteFactory func = routerMap[settings.name];
if (func == null){
return null;
}
return func(settings, uniqueId);
}
//RouterMap key是String, value是函数的实现 最后通过key获取函数的实现
//然后再调用这个函数,就可以获取到这个函数的返回值了 返回值就是具体的Route页面
static Map<String, FlutterBoostRouteFactory> routerMap = {
'mainPage': (settings, uniqueid){ //函数的实现
return MaterialPageRoute (
settings: settings,
builder: (_){
Map<String, Object> map = settings.arguments;
String data = map['data'];
return SimplePage(data: data,);
});
},
'simplePage': (settings, uniqueid){ //函数的实现
Map<String, Object> map = settings.arguments ?? {};
String data = map['data'];
print("从原生过来的数据:$data");
return MaterialPageRoute (
settings: settings,
builder: (_){
return SimplePage(data: data,);//简单页面
});
},
'otherPage':(settings, uniqueId){
return MaterialPageRoute(
settings: settings,
builder: (_){
return OtherPage(); //其他Flutter页面
});
},
'dialogPage': (settings, uniqueId){
return PageRouteBuilder<dynamic> (
opaque: false, //不开启新容器的flutter内部弹窗(推荐)
barrierColor: Colors.black45,
settings: settings,
pageBuilder: (_,__,___)=>AlertDialog(title: Text('哈哈'),));
},
'popPage': (settings, uniqueId){
return PageRouteBuilder<dynamic> (
opaque: false, //不开启新容器的flutter内部弹窗(推荐)
barrierColor: Colors.black45,
settings: settings,
pageBuilder: (_,__,___){
return PopPage();
});
},
'revalPage': (settings, uniqueId){
return PageRouteBuilder<dynamic> (
opaque: false, //不开启新容器的flutter内部弹窗(推荐)
barrierColor: Colors.black45,
settings: settings,
pageBuilder: (_,__,___){
return RevalPage();
});
}
};
以上就是主页面的路由的注册和配置过程,后面就可以根据路由的名称进行Native页面跳转Flutter页面, Flutter页面 跳转 Flutter页面 操作。
8. Android Native 部分
前面已经初步完成Flutter 部分的代码,接下来实现Native与Flutter之间互相跳转。在前面的创建好的Android_demo 项目中首先引入 flutter_module 这个项目,具体在 settings.gradle 文件中添加如下代码:
include ':app'
rootProject.name = "Android_demo"
setBinding(new Binding([gradle: this]))
evaluate(new File(
'../flutter_module/.android/include_flutter.groovy'
))
include ':flutter_module'
project(':flutter_module').projectDir = new File('../flutter_module')
注意:这里的引入的路径和名称按照自己的具体情况来确定。
9. 在 app 目录下的 gradle 文件中添加如下代码,然后点击同步按钮,可以看到引入的Flutter项目
//FlutterBoost 依赖
implementation project(":flutter")
implementation project(":flutter_boost")
10. 在清单文件中添加如下代码和初始化Application
<activity
android:name="com.idlefish.flutterboost.containers.FlutterBoostActivity"
android:theme="@style/Theme.AppCompat"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
>
</activity>
<meta-data android:name="flutterEmbedding"
android:value="2">
</meta-data>
public class App extends Application {
private static final String TAG = "App";
@Override
public void onCreate() {
super.onCreate();
//经过实验得出如下结论: pushNativeRoute 不是通过StartActivity过来的,而是从Flutter页面过来的
//在Activity中通过 FlutterBoost.instance().open(options); 打开的页面是Flutter页面,不是原生界面
FlutterBoost.instance().setup(this, new FlutterBoostDelegate() {
@Override
public void pushNativeRoute(FlutterBoostRouteOptions options) {
Log.e(TAG, "pushNativeRoute "+options.pageName());
//从 Flutter 打开Native 页面,具体打开哪个页面 是根据 options.pageName 来决定的
//这里根据options.pageName来判断你想跳转哪个页面,这里简单给一个
Intent intent = new Intent(FlutterBoost.instance().currentActivity(), SimpleNativeActivity.class);
FlutterBoost.instance().currentActivity().startActivityForResult(intent, options.requestCode());
}
@Override
public void pushFlutterRoute(FlutterBoostRouteOptions options) {
System.out.println("pushFlutterRoute "+options.pageName());
//打开Flutter 页面时调用到此处 1.Native 打开 Flutter 2. Flutter 打开 Flutter
Intent intent = new FlutterBoostActivity.CachedEngineIntentBuilder(FlutterBoostActivity.class)
.backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent)
.destroyEngineWithActivity(false)
.uniqueId(options.uniqueId())
.url(options.pageName())
.urlParams(options.arguments())
.build(FlutterBoost.instance().currentActivity());
FlutterBoost.instance().currentActivity().startActivity(intent);
}
}, engine -> {});
}
}
如此以上,就基本上完成了Flutter混合开发的基本配置工作,后面需要进行的是Natvie与Flutter之间的互相跳转yiji。
11. Flutter 跳转 Flutter
1. 普通带参数跳转
BoostNavigator.instance.push('otherPage', //页面在路由表中的名字
withContainer: false, //是否伴随原生容器弹出
arguments: {"name": "wangwu"}, //携带到下一页面的参数
opaque: true //页面是否透明
);
我们可以在路由配置代码那块获取传递的参数
'otherPage':(settings, uniqueId){
Map<String, Object> map = settings.arguments ?? {};
String name = map['name'];
print("从Flutter页面带过来的数据是 $name");
return MaterialPageRoute(
settings: settings,
builder: (_){
return OtherPage(); //其他Flutter页面
});
},
2. Flutter 跳转 Flutter 页面,然后带参数返回上一页
//如果要接收返回参数 注意在方法签名处加上 async 进行标记
final result = await BoostNavigator.instance.push("popPage");
print("返回的数据是 $result");
//返回到上一页并且返回数据【任意类型】,如果新开的页面是带容器的,则返回数据是Map类型的
BoostNavigator.instance.pop("data from popPage");
3. Flutter 跳转 Native 页面
//如果要接收返回参数
BoostNavigator.instance.push("nativeSimplePage").then((value) {
//从Native 返回 Flutter 时返回的数据
print('retval data is $value');
});
//在前面的Application中的初始化Engine和plugin处,我们进行可以在 pushNativeRoute 方法中通过options.pageName 获取
//路由名称,然后根据路由名称来选择要跳转哪个Activity, 下面给出一个简单的例子
@Override
public void pushNativeRoute(FlutterBoostRouteOptions options) {
Log.e(TAG, "pushNativeRoute "+options.pageName());
//从 Flutter 打开Native 页面,具体打开哪个页面 是根据 options.pageName 来决定的
//这里根据options.pageName来判断你想跳转哪个页面,这里简单给一个
Intent intent = new Intent(FlutterBoost.instance().currentActivity(), SimpleNativeActivity.class);
FlutterBoost.instance().currentActivity().startActivityForResult(intent, options.requestCode());
}
public class SimpleNativeActivity extends AppCompatActivity {
private static final String TAG = "SimpleNativeActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_simple);
}
@Override
public void finish() {
Log.e(TAG, "finish: ");
Intent intent = new Intent();
intent.putExtra("msg","This message is from Native!!!");
//intent.putExtra("bool", true);
//intent.putExtra("int", 666);
setResult(Activity.RESULT_OK, intent); // 返回结果给dart
super.finish();
}
}
在intent设置好在该Activity结束的时候回传给Flutter页面的数据,我们就可以在 then 方法中获取传递过来的数据。
12. Native 跳转 Flutter
1. Native 正常跳转 Flutter 页面,不需要从 Flutter 回传数据
//第一种情况 统一打开打Flutter 页面方法 开Flutter页面 pageName就是路由名称
val options:FlutterBoostRouteOptions = FlutterBoostRouteOptions.Builder()
.pageName("simplePage")
.arguments(mapOf(Pair("data","我是来自Native的数据")))
.build();
FlutterBoost.instance().open(options)
simplePage 是在Flutter注册的路由名称
'simplePage': (settings, uniqueid){ //函数的实现
Map<String, Object> map = settings.arguments ?? {};
String data = map['data'];
print("从原生过来的数据:$data");
return MaterialPageRoute (
settings: settings,
builder: (_){
return SimplePage(data: data,);//简单页面
});
},
2. Native 跳转 Flutter,并从Flutter 返回数据给Native
//第二种情况,打开Flutter 页面,然后等待 Flutter 页面返回数据
val map = mapOf("name" to "Jack")
val intent = FlutterBoostActivity.CachedEngineIntentBuilder(FlutterBoostActivity::class.java)
.backgroundMode(io.flutter.embedding.android.FlutterActivityLaunchConfigs.BackgroundMode.opaque)
.destroyEngineWithActivity(false)
.url("revalPage")
.urlParams(map)
.build(this)
startActivityForResult(intent, 1001)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 1001){
//E/MainActivity: onActivityResult: I am from dart
//onActivityResult: {reval=I am from dart}
val msg = data?.getSerializableExtra(ACTIVITY_RESULT_KEY) as Map<*, *> // 将数据转换成 Map 然后再取出来
Log.e(TAG, "onActivityResult: ${msg.get("reval")}")
Log.e(TAG, "onActivityResult: ${data.getSerializableExtra(ACTIVITY_RESULT_KEY)}")
}
}
revalPage 是在Flutter 中注册的路由名称
'revalPage': (settings, uniqueId){
Map<String, Object> map = settings.arguments ?? {};
String name = map['name'];
print("从Native页面带过来的数据是 $name"); //I/flutter: 从Native页面带过来的数据是 Jack
return PageRouteBuilder<dynamic> (
opaque: false, //不开启新容器的flutter内部弹窗(推荐)
barrierColor: Colors.black45,
settings: settings,
pageBuilder: (_,__,___){
return RevalPage();
});
}
InkWell(
onTap: (){
BoostNavigator.instance.pop({'reval': "I am from dart"}); //返回给Native的数据
},
child: Text('点击返回到native页面'),
),
二、总结
FlutterBoost 是可以进行混合开发的框架,可以在原有的原生项目上接入Flutter项目的代码,使得原生与Flutter页面可以互相切换,提高了代码的复用性,后面还会针对Flutter Boost的其他特性进行研究,比如Flutter Boost的生命周期API,Flutter Boost消息发布与订阅,拦截器,PlatformView 等等。