基于现有的项目接入flutter,官方提供的一个比较完整的接入方案,但是存在一个问题,由于FlutterEngine
很重,而多个FlutterViewController
共享一个Engine,并且同一时间下,一个engine只能与一个viewcontroller绑定,在管理起来,特别是对于多层跳转(native->flutter->native->flutter)非常麻烦,而如果创建多个FlutterEngine
,就会带来很大的开销,导致内存暴涨,而flutter_boost
提供了像WebView的方式操作FlutterViewController,可以存在多个FlutterViewController,这里基于一个现有的demo继承flutter_boost并解决侧滑手势冲突的问题
环境配置
官方的流程的教程很详细,这里简单说明
创建flutter模块(这里取为
my_flutter
)1
2cd some/path/
flutter create --template module my_flutter从原来的项目的
Podfile
添加引用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20source 'https://github.com/CocoaPods/Specs.git'
platform:ios,'10.0'
# 不用动态库,动态库多了影响启动速度
# use_frameworks!
use_modular_headers!
inhibit_all_warnings!
# 加上flutter脚本
flutter_application_path = '../my_flutter'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
target 'Demo' do
# 其他native用到的第三方库
pod 'SnapKit'
# flutter pod
install_all_flutter_pods(flutter_application_path)
end先运行
flutter pub get
,再运行pod(最好先运行一下my_flutter
)1
2
3
4
5cd /path/to/flutter_module
flutter pub get
cd /path/to/iosproject
pod install接下来就可以直接在Xcode运行了
当然还有其他的接入方式,笔者认为这个方式比较简单,具体可以参考官方教程
开始接入
为了更方便的管理FlutterEngine
和FlutterViewController
,这里使用flutter_boost
来管理
为了方便管理,我们对
FLBFlutterViewContainer
进行封装1
2
3
4
5
6
7class FlutterVC: FLBFlutterViewContainer {
override func loadView() {
super.loadView()
// 隐藏navigationbar
self.navigationController?.navigationBar.isHidden = true
}
}实现flutter到本地的路由(
NavigationHelper
为帮助类,具体见后面源码链接)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42class PlatformRouterImp: NSObject, FLBPlatform {
// 从flutter传过来的push页面需求
// await FlutterBoost.singleton.open("nativePage", urlParams: {"a": 1}, exts: {"b": 2});
func open(_ url: String, urlParams: [AnyHashable : Any], exts: [AnyHashable : Any], completion: @escaping (Bool) -> Void) {
switch url {
case "nativePage":
// 跳转到本地页面
let vc = HomeVC()
NavigationHelper.nav2VC(vc)
completion(true);
default:
completion(false);
}
}
func present(_ url: String, urlParams: [AnyHashable : Any], exts: [AnyHashable : Any], completion: @escaping (Bool) -> Void) {
switch url {
case "nativeFlutterPage":
// 创建新的FlutterVC
let vc = FlutterVC();
vc.setName("home", params: urlParams);
let navVC = UINavigationController(rootViewController: vc)
NavigationHelper.presentVC(navVC)
completion(true);
default:
completion(false);
}
}
func close(_ uid: String, result: [AnyHashable : Any], exts: [AnyHashable : Any], completion: @escaping (Bool) -> Void) {
let topVC = NavigationHelper.getTopVC()
let presentVC = topVC.presentingViewController
// 判断是否时present
if let _ = presentVC {
topVC.dismiss(animated: true, completion: { [unowned topVC] in
topVC.removeFromParent()
})
} else {
topVC.navigationController?.popViewController(animated: true)
}
}
}初始化,可以放在
application:didFinishLaunchingWithOptions:
,定义一个名为myflutter_method_channel
的MethodChannel,用于通信1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// 用于处理flutter发送的页面跳转消息
let router = PlatformRouterImp.init();
FlutterBoostPlugin.sharedInstance().startFlutter(with: router, onStart: { (engine) in
// 配置channel,用于通信
let batteryChannel = FlutterMethodChannel(name: "myflutter_method_channel",
binaryMessenger: engine.binaryMessenger)
batteryChannel.setMethodCallHandler({ (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
switch call.method {
case "alert":
// native弹窗
let vc = UIAlertController(title: "测试弹窗", message: "弹窗内容", preferredStyle: .alert)
vc.addAction(UIAlertAction(title: "取消", style: .cancel, handler: nil))
NavigationHelper.getTopVC().present(vc, animated: true, completion: nil)
result(true)
default:
result(false)
}
})
})在Dart初始化
flutter_boost
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
void initState() {
// 注册Page,用于native跳转
FlutterBoost.singleton.registerPageBuilders(<String, PageBuilder>{
'home': (String pageName, Map params, String uniqueId) {
return HomePage();
},
"me": (String pageName, Map params, String uniqueId) {
return MePage();
}
});
super.initState();
}
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
// 使用FlutterBoost作为builder
builder: FlutterBoost.init(),
home: HomePage(),
);
}
}flutter给native发消息
1
2
3
4
5
6
7try {
// 给native发消息
final bool result = await method_channel.invokeMethod("alert", <String, dynamic>{ "a": 1 });
print(result);
} on Exception catch (e) {
print(e);
}native给flutter发消息
1
FlutterBoostPlugin.sharedInstance().sendEvent("showToast", arguments: ["message": "native消息"]);
flutter 监听消息
1
2
3
4
5
6
7
8
9
10
11// 监听消息,返回值是个匿名方法,可以取消监听
_listenCancelable = FlutterBoost.singleton.channel.addEventListener('showToast', (name, arguments) async {
var msg = arguments["message"];
if (msg != null) {
await _hudKey.currentState.showAndDismiss(ProgressHudType.success, msg);
}
return null;
});
// 取消监听
_listenCancelable.call()flutter给native发消息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19private var cancelable: FLBVoidCallback?
...
// 监听flutter发来的消息
cancelable = FlutterBoostPlugin.sharedInstance().addEventListener({ [weak self] (name, arguments) in
guard let self = self else { return }
if let arguments = arguments, let msg = arguments["message"] as? String {
let vc = UIAlertController(title: "tip", message: msg, preferredStyle: .alert)
vc.addAction(UIAlertAction(title: "cancel", style: .default, handler: nil))
self.present(vc, animated: true, completion: nil)
}
}, forName: "alert");
...
// 取消监听
cancelable?()flutter发送消息
1
FlutterBoost.singleton.channel.sendEvent("alert", {"message": "flutter消息"});
flutter给native发消息交互也可以使用
FlutterMethodChannel
兼容侧滑返回
iOS的UINavigationController
默认支持侧滑返回,flutter的MaterialApp
在iOS上也支持侧滑返回,由于flutter运行在FlutterViewController上,所以默认情况下侧滑走的是UINavigationController,为了让侧滑可以衔接起来,我们需要
- 当
MaterialApp
的只有一个页面的时候,使用native侧滑 - 当
MaterialApp
有不止一个页面的时候,禁用native的侧滑操作
我们在MaterialApp
添加一个observer
1 | import 'package:flutter/material.dart'; |
由于flutter_boost
会接管MeterialApp
自带的observer,并且提供了对应的方法(设置在MaterialApp会无效)
1 | // 添加navigationObserver,用于控制返回操作 |
在FlutterVC
添加popGestureRecognizerEnabled
用于设置native侧滑功能
1 | class FlutterVC: FLBFlutterViewContainer { |
由于UINavigationController
是多个viewController
共用的,所以在本地页面跳转的时候,也需要更新popGestureRecognizerEnabled
1 | protocol PopGestureEnable { |
添加消息handle
1 | // 配置channel |