環(huán)境
FlutterBoost介紹
咸魚(yú)Flutter Boost介紹
混合開(kāi)發(fā)環(huán)境搭建
原生Android集成Flutter混合開(kāi)發(fā)
Flutter SDK版本:
v1.9.1
Flutter Boost 版本:
flutter_boost: ^0.1.63
FlutterBoost集成
FlutterBoost是以插件方式的引入到我們的native項(xiàng)目工程的辆它。
混合工程
混合開(kāi)發(fā)流程可以參考:
原生Android集成Flutter混合開(kāi)發(fā)
Flutter module項(xiàng)目集成FlutterBoost
在flutter_boost_module項(xiàng)目的pubspec.yaml文件中添加依賴(lài)插件配置
dependencies:
flutter:
sdk: flutter
flutter_boost: ^0.1.63
配置完成后下載依賴(lài)插件到本地
flutter packages get
在native原生項(xiàng)目中rebuild工程浙踢,即可引入flutter boost插件日矫,引入flutter boost后的代碼結(jié)構(gòu)。其中的amap_base_map是引入了地圖插件才顯示的救恨,如果沒(méi)有引入地圖插件則不會(huì)出現(xiàn)該文件。
在native的項(xiàng)目中,在app目錄下的build.gradle中添加flutter_boost項(xiàng)目依賴(lài)
implementation project(':flutter_boost')
FlutterBoost使用
Flutter工程開(kāi)發(fā)
在Flutter項(xiàng)目中創(chuàng)建兩個(gè)測(cè)試頁(yè)面Widget:FirstRouteWidget和SecondRouteWidget腿准。代碼參考了flutter_boost的測(cè)試代碼。
(1)關(guān)于onPressed里面的方法調(diào)用拾碌,其實(shí)就是調(diào)用flutter_boost的api進(jìn)行頁(yè)面的跳轉(zhuǎn)和傳遞參數(shù)吐葱。
class FirstRouteWidget extends StatelessWidget {
static const KEY_FLUTTER_BOOST_FIRST_ROUTE = "flutterbus://flutterWidget_FirstPage";
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First Route'),
),
body: Center(
child: RaisedButton(
child: Text('open amap widget'),
onPressed: () {
// print("open second page!");
FlutterBoost.singleton.open("flutterbus://flutternativePage", urlParams: {"test": "flutter to flutter "})
.then((Map value) {print(
"call me when page is finished. did recieve second route result $value");});
// BoostContainerSettings settings = BoostContainer.of(context).settings;
// FlutterBoost.singleton.close(settings.uniqueId, result: {"result": "data from second"});
},
),
),
);
}
}
class SecondRouteWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: Center(
child: RaisedButton(
onPressed: () {
// Navigate back to first route when tapped.
BoostContainerSettings settings =
BoostContainer.of(context).settings;
FlutterBoost.singleton.close(settings.uniqueId,
result: {"result": "data from second"});
},
child: Text('Go back with result!'),
),
),
);
}
}
在main.dart中注冊(cè)注冊(cè)這兩個(gè)頁(yè)面route。
(1)在build方法中初始化Flutter Boost
(2)在initState中注冊(cè)需要native啟動(dòng)的route校翔。這里的pageName一定要和native啟動(dòng)時(shí)配置的一致弟跑,否則會(huì)找不到對(duì)應(yīng)route。
class _MyAppState extends State<MyApp> {
@override
void initState() {
super.initState();
FlutterBoost.singleton.registerPageBuilders({
//fisrt widget
FirstRouteWidget.KEY_FLUTTER_BOOST_FIRST_ROUTE: (pageName, params, _) {
debugPrint("params :${params.toString()}");
return FirstRouteWidget();
},
//second widget
'flutterbus://flutterSecondPage': (pageName, params, _) {
debugPrint("params :${params.toString()}");
return SecondRouteWidget();
},
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Boost example',
//初始化FlutterBoost
builder: FlutterBoost.init(postPush: _onRoutePushed),
home: Container());
}
void _onRoutePushed(
String pageName, String uniqueId, Map params, Route route, Future _) {
debugPrint("pageName :${pageName}" + "params :${params.toString()}");
}
}
native項(xiàng)目中使用FlutterBoost
初始化flutter引擎防症。
(1)Flutter引擎初始化孟辑,代碼參考Flutter_boost demo.
(2)INativeRouter接口需要注意,不僅通過(guò)flutter啟動(dòng)native頁(yè)面會(huì)調(diào)用該方法蔫敲,而且通過(guò)flutter會(huì)用flutter頁(yè)面饲嗽,也會(huì)執(zhí)行該方法。
(3)在Application中調(diào)用FlutterBootManager.init()方法完成初始化奈嘿。
(4)NativePageRouter在啟動(dòng)route會(huì)用到貌虾。可以自行封裝裙犹。
object FlutterBootManager {
fun init(app: Application) {
/**
* 不僅打開(kāi)Native頁(yè)面會(huì)執(zhí)行尽狠,在Flutter頁(yè)面打開(kāi)Flutter widget也會(huì)執(zhí)行該方法
**/
val router = INativeRouter { context, url, urlParams, requestCode, exts ->
val assembleUrl = Utils.assembleUrl(url, urlParams)
Timber.e("native open :${url} ${urlParams}")
NativePageRouter.openPageByUrl(context, assembleUrl, requestCode)
}
val pluginsRegister = object : FlutterBoost.BoostPluginsRegister {
override fun registerPlugins(mRegistry: PluginRegistry?) {
GeneratedPluginRegistrant.registerWith(mRegistry)
TextPlatformViewPlugin.register(mRegistry?.registrarFor("TextPlatformViewPlugin"))
}
}
val platform = FlutterBoost.ConfigBuilder(app, router)
.isDebug(BuildConfig.DEBUG)
.whenEngineStart(FlutterBoost.ConfigBuilder.ANY_ACTIVITY_CREATED)
.renderMode(FlutterView.RenderMode.texture)
.pluginsRegister(pluginsRegister)
.build()
FlutterBoost.instance().init(platform)
}
}
配置Flutter頁(yè)面對(duì)應(yīng)的Native容器。
<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">
頁(yè)面跳轉(zhuǎn)route
頁(yè)面路由跳轉(zhuǎn)我把它分成了三種情況巫财,下面說(shuō)說(shuō)每中情況的代碼實(shí)現(xiàn)哩陕。
一平项、native啟動(dòng)flutter
(1)不攜帶參數(shù):
在native測(cè)可以通過(guò)BoostFlutterActivity通過(guò)構(gòu)建者模式構(gòu)建一個(gè)intent。BoostFlutterActivity要注冊(cè)在manifest文件中悍及。
companion object {
const val FLUTTER_FIRST_PAGE_URL = "flutterbus://flutterWidget_FirstPage"
}
override fun openFlutterWidget(context: Context, params: HashMap<String,Any>?): Boolean {
try {
val intent = BoostFlutterActivity.withNewEngine().url(FLUTTER_FIRST_PAGE_URL)
.backgroundMode(BoostFlutterActivity.BackgroundMode.opaque).build(context)
context.startActivity(intent)
return true
} catch (e: Exception) {
return false
}
}
在flutter module中打印出請(qǐng)求的url心赶,和params扣讼。
FirstRouteWidget.KEY_FLUTTER_BOOST_FIRST_ROUTE: (pageName, params, _) {
debugPrint("params :${params.toString()}");
return FirstRouteWidget();
}
(2)攜帶請(qǐng)求參數(shù)
flutter module代碼不變。
companion object {
const val FLUTTER_FIRST_PAGE_URL = "flutterbus://flutterWidget_FirstPage"
}
override fun openFlutterWidget(context: Context, params: HashMap<String,Any>?): Boolean {
try {
var params = mutableMapOf<String,Any>()
params["str"] = "flutter boost"
params["in"] = 1
val intent = BoostFlutterActivity.withNewEngine().url(FLUTTER_FIRST_PAGE_URL).params(params)
.backgroundMode(BoostFlutterActivity.BackgroundMode.opaque).build(context)
context.startActivity(intent)
return true
} catch (e: Exception) {
return false
}
}
(3)獲取flutter的處理結(jié)果缨叫。
在native測(cè)通過(guò)startActivityForResult啟動(dòng)flutter container。
override fun openFlutterWidgetForResult(activity: BaseActivity, params: HashMap<String,Any>?, requestCode: Int): Boolean {
try {
var params = mutableMapOf<String,Any>()
params["str"] = "flutter boost"
params["in"] = 1
val intent = BoostFlutterActivity.withNewEngine().url(FLUTTER_FIRST_PAGE_URL).params(params)
.backgroundMode(BoostFlutterActivity.BackgroundMode.opaque).build(activity)
activity.startActivityForResult(intent, requestCode)
return true
} catch (e: Exception) {
return false
}
}
}
在當(dāng)前activity的onActivityResult獲取請(qǐng)求結(jié)果
注意這里的KEY值销钝,這個(gè)值是定值琐簇,不可修改。并且在flutter獲取native的返回值是也需要用到該值婉商。
REQUEST_CODE -> {
Timber.e("flutter data :"+data?.extras?.get(IFlutterViewContainer.RESULT_KEY))
}
在flutter module通過(guò)FlutterBoost的close方法丈秩,把處理結(jié)果返回給上一個(gè)頁(yè)面。
BoostContainerSettings settings = BoostContainer.of(context).settings;
FlutterBoost.singleton.close(settings.uniqueId, result: {"result": "data from second"});
最終native獲取的返回值格式:
flutter data :{result=data from second}
二挽唉、flutter啟動(dòng)flutter
(1)直接啟動(dòng),不攜帶參數(shù)
注意筷狼;這里還需要在native層進(jìn)行實(shí)現(xiàn),才能最終啟動(dòng)flutter route埂材,因?yàn)閒lutter route需要一個(gè)native容器。
FlutterBoost.singleton.open("flutterbus://flutterSecondPage");
在native的FlutterBootManager中有個(gè)INativeRouter接口的實(shí)現(xiàn)類(lèi)严拒,在給INativeRouter的openContainer方法中裤唠,能獲取到啟動(dòng)的url,請(qǐng)求參數(shù)urlParams种蘸、和requestCode和當(dāng)前的activity的上下問(wèn)context航瞭。
在openContainer方法中,在調(diào)用啟動(dòng)“native啟動(dòng)flutter”的相關(guān)方法刊侯,啟動(dòng)flutter route。
例如:
val intent = BoostFlutterActivity.withNewEngine().url(NATIVE_FIRST_PAGE_URL)
.backgroundMode(BoostFlutterActivity.BackgroundMode.opaque).build(context)
if(context is Activity){
(context as Activity).startActivityForResult(intent,0x13)
}
(2)攜帶參數(shù)啟動(dòng)
FlutterBoost.singleton.open("flutterbus://flutterSecondPage", urlParams: {"test": "flutter to flutter "})
(3)獲取返回值
在first route通過(guò)如下代碼啟動(dòng)second route
FlutterBoost.singleton.open("flutterbus://flutternativePage", urlParams: {"test": "flutter to flutter "})
.then((Map value) {print(
"call me when page is finished. did recieve second route result $value");});
在second通過(guò)如下代碼關(guān)閉當(dāng)前頁(yè)面藕届,并返回處理的數(shù)據(jù)
BoostContainerSettings settings =
BoostContainer.of(context).settings;
FlutterBoost.singleton.close(settings.uniqueId,
result: {"result": "data from second"});
三亭饵、flutter 啟動(dòng)native頁(yè)面
(1)直接啟動(dòng),不帶參數(shù)
FlutterBoost.singleton.open("flutterbus://flutternativePage");
在native的FlutterBootManager中有個(gè)INativeRouter接口的實(shí)現(xiàn)類(lèi)冬骚,在給INativeRouter的openContainer方法中只冻,能獲取到啟動(dòng)的url计技,請(qǐng)求參數(shù)urlParams、和requestCode和當(dāng)前的activity的上下問(wèn)context舍悯。
在openContainer方法中睡雇,在調(diào)用啟動(dòng)startActivity的相關(guān)方法,啟動(dòng)native route秕豫。
例如
var intent = Intent(context,FlutterOpenNativeTestActivity::class.java)
if(context is Activity){
(context as Activity).startActivityForResult(intent,requestCode)
}
(2)攜帶請(qǐng)求參數(shù)
在INativeRouter的openContainer方法,可以獲取到請(qǐng)求的參數(shù)祠墅。
這里的請(qǐng)求最終會(huì)執(zhí)行到native的INativeRouter 實(shí)現(xiàn)類(lèi)歌径。
FlutterBoost.singleton.open("flutterbus://flutternativePage", urlParams: {"test": "flutter to flutter "})
(3)獲取返回值
flutter 的代碼實(shí)現(xiàn)
FlutterBoost.singleton.open("flutterbus://flutternativePage", urlParams: {"test": "flutter to flutter "})
.then((Map value) {print(
"call me when page is finished. did recieve second route result $value");});
native的代碼實(shí)現(xiàn)
注意點(diǎn):putExtra的key是定值回铛,并且值只能是map。
close_btn.setOnClickListener{
var map = mutableMapOf<String,Any>()
map.put("native","test native")
intent.putExtra(IFlutterViewContainer.RESULT_KEY,(map as Serializable))
setResult(Activity.RESULT_OK,intent)
finish()
}
四勺届、native啟動(dòng)native
和原生開(kāi)發(fā)沒(méi)有區(qū)別免姿。
PS:主要點(diǎn)不是flutter_boost的API使用,而是獲取返回值的閱讀官方demo遇到的坑故俐,demo寫(xiě)的不夠全面紊婉,一些常見(jiàn)的場(chǎng)景都沒(méi)有覆蓋。