「這是我參與8月更文挑戰的第10天,活動詳情查看: 8月更文挑戰」 juejin.cn/post/698796…android
小菜前幾天剛將歷史項目升級至 AndroidX 並接入 Flitter Module,接下來小菜準備採用 flutter_boost 進行 Native 與 Flutter 兩端交互;小菜從未接觸過 FlutterBoost,爲了研究方便,小菜特地新建兩個工程單獨學習基本的映射和跳轉;git
小菜新建一個 AndroidX 工程,其中 minSdkVersion >= 16,等待接入 Flutter Module;github
compileSdkVersion 28
defaultConfig {
applicationId "com.ace.flutter.ace_demo02"
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
複製代碼
小菜新建一個 Flutter Module 集成到 Android Project 中;其中該 Module 也支持 AndroidX;markdown
compileSdkVersion 28
defaultConfig {
applicationId "com.ace.flutter.flutter_module04.host"
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
複製代碼
小菜按照官網嘗試接入 'v0.1.61-androidx-hotfixes' 分支 FlutterBoost 發現並未徹底適配 AndroidX,因而切換至較新的 'v1.12.13-hotfixes' 分支,Packages get 檢驗,能夠正常運行;app
flutter_boost:
git:
url: 'https://github.com/alibaba/flutter_boost.git'
ref: 'v1.12.13-hotfixes'
複製代碼
小菜將 Flutter Module 接入到 Android 工程中,方法再也不贅述,注意 build.gradle 中須要加入 flutter 和 flutter_boost 兩個依賴;Sync 以後 Project 中會加入 Flutter 和 FlutterBoost 模塊;less
implementation project(':flutter')
implementation project(':flutter_boost')
setBinding(new Binding([gradle: this]))
evaluate(new File(
'/Users/user/Documents/ACE_FLUTTER/flutter_module04/.android/include_flutter.groovy'
))
複製代碼
至此,Flutter 和 FlutterBoost 的集成已基本完成,接下來是兩端映射與跳轉方面的學習,小菜建議剛開始時能夠將官網的代碼複製拷貝到項目中,先跑通項目更直觀的感覺;小菜爲了學習逐步滲透;ide
根據 FlutterBoost 官網用法,首先須要在 Application 中初始化 FlutterBoost;不管是 Flutter 之間路由跳轉仍是 Flutter 與 Native 之間路由跳轉均須要經過 INativeRouter 接口進行交互;oop
private void initFlutterBoost() {
INativeRouter router = new INativeRouter() {
@Override
public void openContainer(Context context, String url, Map<String, Object> urlParams, int requestCode, Map<String, Object> exts) {
String assembleUrl = Utils.assembleUrl(url, urlParams);
PageRouter.openPageByUrl(context, assembleUrl, urlParams);
}
};
FlutterBoost.BoostLifecycleListener boostLifecycleListener = new FlutterBoost.BoostLifecycleListener() {
@Override
public void onEngineCreated() { }
@Override
public void onPluginsRegistered() { }
@Override
public void onEngineDestroy() { }
};
Platform platform = new FlutterBoost
.ConfigBuilder(this, router)
.isDebug(true)
.whenEngineStart(FlutterBoost.ConfigBuilder.ANY_ACTIVITY_CREATED)
.renderMode(FlutterView.RenderMode.texture)
.lifecycleListener(boostLifecycleListener)
.build();
FlutterBoost.instance().init(platform);
}
複製代碼
其中路由管理是由公共的 PageRouter 文件管理;提供了通用的 openPageByUrl,根據用戶提供的 url 與設置好的映射集合進行對比,確認一致以後經過 startActivity() 進行頁面跳轉;若須要傳遞 Bundle 參數的話,能夠經過 Map 類型進行傳遞;post
public static boolean openPageByUrl(Context context, String url, Map params, int requestCode) {
String path = url.split("\\?")[0];
try {
if (pageName.containsKey(path)) {
Intent intent = BoostFlutterActivity.withNewEngine().url(pageName.get(path)).params(params).backgroundMode(BoostFlutterActivity.BackgroundMode.opaque).build(context);
if(context instanceof Activity){
Activity activity=(Activity)context;
activity.startActivityForResult(intent,requestCode);
}else{
context.startActivity(intent);
}
return true;
}
return false;
} catch (Throwable t) {
return false;
}
}
複製代碼
公共路由中須要 BoostFlutterActivity 做爲容器進行處理,所以須要在 AndroidManifest.xml 中註冊;其中 SplashScreenDrawable 做爲路由跳轉時背景效果,能夠按照須要進行調整;學習
<activity
android:name="com.idlefish.flutterboost.containers.BoostFlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density"
android:hardwareAccelerated="true"
android:theme="@style/Theme.AppCompat"
android:windowSoftInputMode="adjustResize">
<meta-data
android:name="io.flutter.embedding.android.SplashScreenDrawable"
android:resource="@drawable/ic_launcher" />
</activity>
複製代碼
不管是 Android 仍是 Flutter 均須要初始化,在 main.dart 的 build 方法中初始化;小菜新建了兩個測試 Page,其中路由映射的 url 要與 Android Native 端一致;同時還提供了 NavigatorObserver 進行先後路由的監聽;
@override
void initState() {
super.initState();
FlutterBoost.singleton.registerPageBuilders({
'first_page': (pageName, params, _) => FirstPage(),
'second_page': (pageName, params, _) => SecondPage()
});
FlutterBoost.singleton.addBoostNavigatorObserver(TestBoostNavigatorObserver());
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Boost example',
builder: FlutterBoost.init(postPush: _onRoutePushed),
home: Container( color: Colors.white,
child: Center(child: Text('${_result}', style: TextStyle(color: Colors.blueAccent, fontSize: 18.0)))));
}
void _onRoutePushed(String pageName, String uniqueId, Map params, Route route, Future _) {}
複製代碼
路由跳轉主要是 Native 與 Flutter 兩端之間雙向的交互,小菜分爲以下方式進行測試;
經過 openPageByUrl 中分析 Native 之間的跳轉依舊是經過系統的 startActivity 來進行處理,小菜不作過多贅述;
startActivity(new Intent(A.this, B.class));
複製代碼
Android 到 Flutter 經過 BoostFlutterActivity 構建跳轉,注意映射 url 一致;若須要獲取返回值內容,能夠經過 **** 固定的 KEY 獲取,且獲取的格式是 Object 格式;
Intent intent = BoostFlutterActivity.withNewEngine().url(pageName.get(path)).params(params).backgroundMode(BoostFlutterActivity.BackgroundMode.opaque).build(context);
if (context instanceof Activity) {
Activity activity = (Activity) context;
activity.startActivityForResult(intent,requestCode);
} else {
context.startActivity(intent);
}
// Native 跳轉 FirstPage (無參)
PageRouter.openPageByUrl(this, "first_page", null);
===============================================================================
// Native 跳轉 FirstPage (有參)
Map map = new HashMap();
map.put("params_name", "張三");
map.put("params_age", 18);
PageRouter.openPageByUrl(this, "first_page", map);
===============================================================================
// Native 跳轉 SecondPage (有參 + 返回值)
PageRouter.openPageByUrl(this, "second_page", map, 101);
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (data != null && data.getExtras() != null) {
Toast.makeText(this, data.getExtras().get(IFlutterViewContainer.RESULT_KEY).toString(), Toast.LENGTH_LONG).show();
}
}
複製代碼
Flutter 之間的跳轉能夠經過默認的 Navigator 方式,也能夠經過 FlutterBoost.singleton.open 方式進行頁面跳轉;注意跳轉的頁面均需在 main.dart 中提早映射好;
// FirstPage 跳轉 SecondPage (無參)
FlutterBoost.singleton.open('second_page');
===============================================================================
// FirstPage 跳轉 SecondPage (有參)
FlutterBoost.singleton.open('second_page', urlParams: {'params_name': '李四', 'params_age': 28});
===============================================================================
// FirstPage 跳轉 SecondPage (有參 + 返回值)
FlutterBoost.singleton.open('second_page', urlParams: { 'params_name': '李四', 'params_age': 28 })
.then((Map value) { print('Second Page 頁面銷燬時獲取的返回結果 result = $value'); });
複製代碼
Flutter 到 Native 的跳轉須要根據不一樣映射的 url 單獨判斷;其中接收參數經過 openPageByUrl 的 params 獲取;若由 Flutter 到 Native 須要返回值,注意頁面跳轉時使用 startActivityForResult 方式,且關閉 Native 時傳參的 KEY 爲固定的 IFlutterViewContainer.RESULT_KEY;
if (path.startsWith("native://")) {
if (path.equals("native://main_activity")) {
Intent intent = new Intent(context, MainActivity.class);
intent.putExtra("params_from_flutter", params.toString());
// context.startActivity(intent); // 無返回值
Activity activity = (Activity) context;
activity.startActivityForResult(intent, requestCode); // 有返回值
}
return true;
}
// SecondPage 跳轉 MainActivity (無參)
FlutterBoost.singleton.open('native://main_activity');
===============================================================================
// SecondPage 跳轉 MainActivity (有參)
FlutterBoost.singleton.open('native://main_activity', urlParams: {'params_name': '王五', 'params_age': 38});
===============================================================================
// SecondPage 跳轉 MainActivity (有參 + 返回值)
FlutterBoost.singleton.open('native://main_activity', urlParams: { 'params_name': '王五', 'params_age': 38 })
.then((Map value) { Toast.show('MainActivity 頁面銷燬時獲取的返回結果 result = $value', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM); });
// MainActivity
Map map = new HashMap();
map.put("params_name", "趙六");
map.put("params_age", 48);
Intent intent = new Intent();
intent.putExtra(IFlutterViewContainer.RESULT_KEY, (Serializable) map);
setResult(Activity.RESULT_OK, intent);
finish();
複製代碼
class FirstPage extends StatelessWidget {
final Map params;
FirstPage({this.params});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('First Page')),
body: Column(mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max,
children: <Widget>[
Center(child: Text('params 爲:${params ?? "null"}')),
Center(child: RaisedButton(child: Text('打開 Second Flutter Page (無參)'),
onPressed: () { FlutterBoost.singleton.open('second_page'); })),
Center(child: RaisedButton(child: Text('打開 Second Flutter Page (有參)'),
onPressed: () { FlutterBoost.singleton.open('second_page',
urlParams: {'params_name': '李四', 'params_age': 28}); })),
Center(child: RaisedButton(
child: Text('打開 Second Flutter Page (有參 + 返回值)'),
onPressed: () {
FlutterBoost.singleton.open('second_page', urlParams: {
'params_name': '李四', 'params_age': 28
}).then((Map value) {
print('Second Page 頁面銷燬時獲取的返回結果 result = $value');
});
}))
]));
}
}
class SecondPage extends StatelessWidget {
final Map params;
SecondPage({this.params});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Second Page')),
body: Column(mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max,
children: <Widget>[
Center(child: Text('params 爲:${params ?? 'null'}')),
Center(child: RaisedButton(child: Text('關閉 Second Flutter Page 且帶返回值'),
onPressed: () {
BoostContainerSettings settings = BoostContainer.of(context).settings;
FlutterBoost.singleton.close(settings.uniqueId, result: {'result': '來自 SecondPage Result'});
})),
Center(child: RaisedButton(child: Text('打開 Native MainActivity (無參)'),
onPressed: () { FlutterBoost.singleton.open('native://main_activity'); })),
Center(child: RaisedButton(child: Text('打開 Native MainActivity (有參)'),
onPressed: () { FlutterBoost.singleton.open('native://main_activity',
urlParams: {'params_name': '王五', 'params_age': 38}); })),
Center(child: RaisedButton(
child: Text('打開 Native MainActivity (有參 + 返回值)'),
onPressed: () {
FlutterBoost.singleton.open('native://main_activity',
urlParams: { 'params_name': '王五', 'params_age': 38
}).then((Map value) {
Toast.show('MainActivity 頁面銷燬時獲取的返回結果 result = $value', context, duration: Toast.LENGTH_SHORT, gravity: Toast.BOTTOM);
});
}))
]));
}
}
複製代碼
小菜對 FlutterBoost 的瞭解還知之甚少,且每一個版本的方法注意點各有不一樣,若有錯誤請多多指導!
來源: 阿策小和尚