常用三方類庫
flutter_lints - Flutter團(tuán)隊(duì)推薦的Flutter相關(guān)規(guī)則集
cupertino_icons - 加載蘋果風(fēng)格的圖標(biāo)
ios_platform_images - share images between Flutter and iOS
flutter_boost - 新一代Flutter-Native混合解決方案
flustars - Flutter常用工具類庫,包括日期、時(shí)間、正則、log日志、json轉(zhuǎn)換、屏幕相關(guān)
shared_preferences - 本地?cái)?shù)據(jù)存取插件 ,在Android上它是基于 SharePreferences的,在iOS上它是基于 NSUserDefaults
flutter_boost
隨著Flutter的發(fā)展,國(guó)內(nèi)越來越多的App開始使用Flutter。為了降低風(fēng)險(xiǎn),大部分App采用漸進(jìn)式方式引入Flutter,在App里選幾個(gè)頁面用Flutter來編寫,但都碰到了相同的問題,在原生頁面和Flutter頁面共存的情況下,如何管理路由? 官方?jīng)]有提供這樣的解決方案,而FlutterBoost就是為了解決這個(gè)問題而生。
FlutterBoost的使命是讓開發(fā)者非常簡(jiǎn)單的在原生App中開發(fā)Flutter頁面。
很多Flutter開發(fā)者只會(huì)一端,只會(huì)Android 或者只會(huì)IOS,但他需要接入雙端,所以雙端統(tǒng)一能降低他的 學(xué)習(xí)成本和接入成本。FlutterBoost3.0,在設(shè)計(jì)上 Android和IOS都做了對(duì)齊,特別接口上做到了參數(shù)級(jí)的對(duì)齊。
新一代Flutter-Native混合解決方案。 FlutterBoost是一個(gè)Flutter插件,它可以輕松地為現(xiàn)有原生應(yīng)用程序提供Flutter混合集成方案。FlutterBoost的理念是將Flutter像Webview那樣來使用。在現(xiàn)有應(yīng)用程序中同時(shí)管理Native頁面和Flutter頁面并非易事。 FlutterBoost幫你處理頁面的映射和跳轉(zhuǎn),你只需關(guān)心頁面的名字和參數(shù)即可(通常可以是URL)。
將FlutterBoost添加到你的Flutter工程依賴中
flutter_boost:
git:
url: 'https://github.com/alibaba/flutter_boost.git'
ref: 'v3.0-release.2'
之后在flutter工程下運(yùn)行flutter pub get dart端就集成完畢了
將FlutterBoost集成到Android部分和iOS部分
將FlutterBoost添加到你的iOS工程依賴中
首先到自己的iOS目錄下,執(zhí)行一次pod install
yushuhuideMacBook-Pro:wargame_cloudgame_ios yushuhui$ pod install
Analyzing dependencies
Downloading dependencies
Installing FlutterPluginRegistrant 0.0.1
Installing flutter_boost (0.0.2)
Installing ios_platform_images (0.0.1)
Installing path_provider_ios (0.0.1)
Installing shared_preferences_ios (0.0.1)
Generating Pods project
Integrating client project
Pod installation complete! There are 31 dependencies from the Podfile and 45 total pods installed.
進(jìn)行準(zhǔn)備工作創(chuàng)建FlutterBoostDelegate。 這里面的內(nèi)容是完全可以自定義的,在您了解各個(gè)API的含義時(shí),你可以完全自定義這里面每個(gè)方法的代碼,下面只是給出大多數(shù)場(chǎng)景的默認(rèn)解法
import flutter_boost
class BoostDelegate: NSObject,FlutterBoostDelegate {
static let shared = BoostDelegate()
///您用來push的導(dǎo)航欄
var navigationController:UINavigationController?
var flutterVC:FBFlutterViewContainer?
///用來存返回flutter側(cè)返回結(jié)果的表
var resultTable:Dictionary<String,([AnyHashable:Any]?)->Void> = [:];
func pushNativeRoute(_ pageName: String!, arguments: [AnyHashable : Any]!) {
//可以用參數(shù)來控制是push還是pop
let isPresent = arguments["isPresent"] as? Bool ?? false
let isAnimated = arguments["isAnimated"] as? Bool ?? true
//這里根據(jù)pageName來判斷生成哪個(gè)vc,這里給個(gè)默認(rèn)的了
var targetViewController = UIViewController()
if(isPresent){
self.navigationController?.present(targetViewController, animated: isAnimated, completion: nil)
}else{
self.navigationController?.pushViewController(targetViewController, animated: isAnimated)
}
}
func pushFlutterRoute(_ options: FlutterBoostRouteOptions!) {
let vc:FBFlutterViewContainer = FBFlutterViewContainer()
vc.setName(options.pageName, uniqueId: options.uniqueId, params: options.arguments,opaque: options.opaque)
self.flutterVC = vc
//用參數(shù)來控制是push還是pop
let isPresent = (options.arguments?["isPresent"] as? Bool) ?? false
let isAnimated = (options.arguments?["isAnimated"] as? Bool) ?? true
//對(duì)這個(gè)頁面設(shè)置結(jié)果
resultTable[options.pageName] = options.onPageFinished;
//如果是present模式 ,或者要不透明模式,那么就需要以present模式打開頁面
if(isPresent || !options.opaque){
self.navigationController?.present(vc, animated: isAnimated, completion: nil)
}else{
self.navigationController?.pushViewController(vc, animated: isAnimated)
}
}
func popRoute(_ options: FlutterBoostRouteOptions!) {
//如果當(dāng)前被present的vc是container,那么就執(zhí)行dismiss邏輯
if let vc = self.navigationController?.presentedViewController as? FBFlutterViewContainer,vc.uniqueIDString() == options.uniqueId{
//這里分為兩種情況,由于UIModalPresentationOverFullScreen下,生命周期顯示會(huì)有問題
//所以需要手動(dòng)調(diào)用的場(chǎng)景,從而使下面底部的vc調(diào)用viewAppear相關(guān)邏輯
if vc.modalPresentationStyle == .overFullScreen {
//這里手動(dòng)beginAppearanceTransition觸發(fā)頁面生命周期
self.navigationController?.topViewController?.beginAppearanceTransition(true, animated: false)
vc.dismiss(animated: true) {
self.navigationController?.topViewController?.endAppearanceTransition()
}
}else{
//正常場(chǎng)景,直接dismiss
vc.dismiss(animated: true, completion: nil)
}
}else{
self.navigationController?.popViewController(animated: true)
}
//否則直接執(zhí)行pop邏輯
//這里在pop的時(shí)候?qū)?shù)帶出,并且從結(jié)果表中移除
if let onPageFinshed = resultTable[options.pageName] {
onPageFinshed(options.arguments)
resultTable.removeValue(forKey: options.pageName)
}
}
}
在AppDelegate的didFinishLaunchingWithOptions方法中進(jìn)行初始化
//創(chuàng)建代理,做初始化操作
import flutter_boost
let flutterBoostDelegate = BoostDelegate()
FlutterBoost.instance().setup(application, delegate: flutterBoostDelegate, callback: { engine in
})
iOS原生跳轉(zhuǎn)到flutter模塊的settingPage頁面
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let flutterBtn = UIButton()
flutterBtn.backgroundColor = ColorConst.color_e8f4
flutterBtn.layer.cornerRadius = 5
flutterBtn.layer.masksToBounds = true
flutterBtn.setTitle("toFlutterVC", for: .normal)
flutterBtn.setTitleColor(ColorConst.color_28, for: .normal)
flutterBtn.addTarget(self, action: #selector(clickToSettingVC), for: .touchUpInside)
flutterBtn.frame = CGRect(x: 100, y: 100, width: 100, height: 100)
view.addSubview(flutterBtn)
}
@objc func clickToSettingVC() {
// 創(chuàng)建options,進(jìn)行open操作的構(gòu)建
let options = FlutterBoostRouteOptions()
options.pageName = "settingPage"
options.arguments = ["data": "settingPage", "isPresent": false, "isAnimated": true]
// 這個(gè)是push操作完成的回調(diào),而不是頁面關(guān)閉的回調(diào)!?。。? options.completion = { _ in
print("open operation is completed")
}
// 這個(gè)是頁面關(guān)閉并且返回?cái)?shù)據(jù)的回調(diào),回調(diào)實(shí)際需要根據(jù)您的Delegate中的popRoute來調(diào)用
options.onPageFinished = { [weak self] dic in
if let data = dic?["data"] as? String {
print("settingPage return data is: \(data)")
}
}
BoostDelegate.shared.navigationController = UIViewController.current.navigationController
BoostDelegate.shared.pushFlutterRoute(options)
}
}
flutter run 配置iOS證書
vim ~/.flutter_settings
{
"ios-signing-cert": "Apple Development: XXXX@gmail.com (XXXXXXXX)",
"enable-macos-desktop": true
}
單獨(dú)調(diào)試某個(gè)flutter頁面示例,無需經(jīng)過集成到原生工程
Widget appBuilder(Widget home) {
return const MaterialApp(
home: MySettingPage(), // 這里是需要調(diào)試的頁面的名稱
// home: home,
debugShowCheckedModeBanner: false,
///必須加上builder參數(shù),否則showDialog等會(huì)出問題
// builder: (_, __) {
// return home;
// },
);
將FlutterBoost添加到你的android工程依賴中,原生和flutter通信的各種情況如下:
-
Android 原生攜帶參數(shù)打開flutter頁面
HashMap<String, Object> map = new HashMap<>();//傳遞給flutter的參數(shù)map map.put("data","1"); map.put("UserName","2"); map.put("HeaderUrl","3"); map.put("CurrUserId","4"); FlutterBoostRouteOptions options = new FlutterBoostRouteOptions.Builder() .pageName("settingPage") //settingPage打開的flutter頁面的名稱 .arguments(map) .requestCode(1111) .build(); FlutterBoost.instance().open(options);flutter接收傳遞的數(shù)據(jù):
Map<String, FlutterBoostRouteFactory> routerMap = { 'settingPage': (settings, uniqueId) { return CupertinoPageRoute( settings: settings, builder: (_) { Map<String, Object> map = settings.arguments as Map<String, Object>; print("收到原生傳遞的消息:"+ map.toString()); String data = map['data'] as String; print("收到原生傳遞的消息 data:"+ data.toString()); return MySettingPage( data: data, ); }); }, }; flutter攜帶參數(shù)打開Android原生頁面
var user = {
'data' : 'number',
'age': 'turtledoves'
};
MainActivity:打開Android原生的目標(biāo)頁面。user傳遞的參數(shù)
Android原生接收:
先發(fā)送消息到原生,原生收到消息后,進(jìn)行分發(fā)打開原生界面:
EventListener listener = (key, args) -> {
Log.d("EventListener", "onCreate: args ----key是:" + key+ "--------args是: "+ args);
if (key.equals("event")){
// Map<Object, Object> args
if (args.get("data").equals("Logout")){
startActivity(new Intent(this, MainActivity.class));
}
}
}
};
remover = FlutterBoost.instance().addEventListener("event", listener);
-
flutter向Android原生發(fā)送消息
發(fā)送的key是data,value是一個(gè)map集合 BoostChannel.instance.sendEventToNative("event", {'data': "UserInfo"});Android原生接收消息:
EventListener listener = (key, args) -> {Log.d("EventListener", "onCreate: args ----key是:" + key+ "--------args是: "+ args); };ListenerRemover remover = FlutterBoost.instance().addEventListener("event", listener);
注意://最后在清理的時(shí)候移除監(jiān)聽(比如onDestroy中) remover.remove();
-
Android向Flutter發(fā)送消息
Map<Object,Object> map = new HashMap<>(); map.put("key","value"); FlutterBoost.instance().sendEventToFlutter("eventToFlutter",map);flutter端接收:
```
///聲明一個(gè)用來存回調(diào)的對(duì)象
VoidCallback removeListener;
///添加事件響應(yīng)者,監(jiān)聽native發(fā)往flutter端的事件
removeListener = BoostChannel.instance.addEventListener("yourEventKey", (key, arguments) {
///deal with your event here
return;
});
///然后在退出的時(shí)候(比如dispose中)移除監(jiān)聽者
removeListener?.call();
```