iOS 采用 flutter_boost接入flutter 模
Demo下载亲测可用. 不坑请点赞
在阅读此篇文章,默认已经有相应的 flutter基础
场景:
如果我们在原生项目上想要接入 flutter. 这个时候就不能采用创建一个 flutter项目的模式.因为原生项目已经开发很多了. 那么这个时候就要采取原生接入 flutter 模块的形式. 然后把模块接入 iOS/安卓端.
接入的方案是采用 flutter_boost(亿级用户). 就可以实现原生到 flutter 页面的相互跳转,相互传递数据. 是不是很香?
步骤:
1.创建 ios 项目(我这里的项目叫做 flutter_swift).
2:创建 flutter 模块
//打开终端,执行命令
cd xxxx(你的目录)
flutter create -t module flutter_demo
注意iOS 项目和 flutter 模块的目录要在同一层级,如下图

3:集成flutter_boost
flutter_boost github地址
里面有详细的集成文档. 按照步骤一步一步来.
我的步骤:
修改配置:
注意: 这里一定要对 environment 进行修改. 不然会因为 null safety 的问题报错. 我在这里折腾了好久. 改了这里就一切正常了.

修改之后 flutter packages get
我的 demo 很简单. 主要代码在main.dart. 简单实现通信. 其他的请按需扩展
flutter main.dart
import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'mine.dart';
import 'homePage.dart';
//这里要特别注意,如果你的工程里已经有一个继承自WidgetsFlutterBinding的自定义Binding,则只需要将其with上BoostFlutterBinding
//如果你的工程没有自定义的Binding,则可以参考这个CustomFlutterBinding的做法
//BoostFlutterBinding用于接管Flutter App的生命周期,必须得接入的
class CustomFlutterBinding extends WidgetsFlutterBinding
with BoostFlutterBinding {}
void main() {
CustomFlutterBinding();
runApp(MyApp());
}
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
///路由表
static Map<String, FlutterBoostRouteFactory> routerMap = {
'homePage': (settings, uniqueId) {
return PageRouteBuilder<dynamic>(
settings: settings,
pageBuilder: (_, __, ___) {
return homePage();
});
},
'minePage': (settings, uniqueId) {
return PageRouteBuilder<dynamic>(
settings: settings, pageBuilder: (_, __, ___) {
Map<String,Object> map = settings.arguments;
String data = uniqueId;
return minePage(
data: data,
);
});
},
};
Route<dynamic> routeFactory(RouteSettings settings, String uniqueId) {
FlutterBoostRouteFactory func = routerMap[settings.name];
if (func == null) {
return null;
}
return func(settings, uniqueId);
}
Widget appBuilder(Widget home) {
return MaterialApp(home: home, debugShowCheckedModeBanner: false);
}
@override
Widget build(BuildContext context) {
return FlutterBoostApp(
routeFactory,
appBuilder: appBuilder,
);
}
}
4:iOS 端的配置
修改 pod
注意点:
flutter_application_path = '../flutter_demo' flutter_demo 就是同级目录的 flutter模块的名字. 也是它的路径
一共需要添加
flutter_application_path = '../flutter_demo'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb') install_flutter_engine_pod
install_all_flutter_pods(flutter_application_path)
# Uncomment the next line to define a global platform for your project
platform :ios, '9.0'
flutter_application_path = '../flutter_demo'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
target 'flutter_swift' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
install_flutter_engine_pod
install_all_flutter_pods(flutter_application_path)
# Pods for flutter_swift
end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
end
end
install! 'cocoapods', :disable_input_output_paths => true
然后pod install
接下来:
在AppDelegate 导入import flutter_boost
创建类BoostDelegate. 其作用是实现FlutterBoostDelegate,用它来实现跳转.用这个类统一管理页面跳转,数据传输
import flutter_boost
class BoostDelegate: NSObject,FlutterBoostDelegate {
static let shared = BoostDelegate()
///您用来push的导航栏
var navigationController:UINavigationController?
///用来存返回flutter侧返回结果的表
var resultTable:Dictionary<String,([AnyHashable:Any]?)->Void> = [:];
func pushNativeRoute(_ pageName: String!, arguments: [AnyHashable : Any]!) {
//可以用参数来控制是push还是pop
let isPresent = arguments["isPresent"] as? Bool ?? false
let isAnimated = arguments["isAnimated"] as? Bool ?? true
//这里根据pageName来判断生成哪个vc,这里给个默认的了
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)
//用参数来控制是push还是pop
let isPresent = (options.arguments?["isPresent"] as? Bool) ?? false
let isAnimated = (options.arguments?["isAnimated"] as? Bool) ?? true
//对这个页面设置结果
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!) {
//如果当前被present的vc是container,那么就执行dismiss逻辑
if let vc = self.navigationController?.presentedViewController as? FBFlutterViewContainer,vc.uniqueIDString() == options.uniqueId{
//这里分为两种情况,由于UIModalPresentationOverFullScreen下,生命周期显示会有问题
//所以需要手动调用的场景,从而使下面底部的vc调用viewAppear相关逻辑
if vc.modalPresentationStyle == .overFullScreen {
//这里手动beginAppearanceTransition触发页面生命周期
self.navigationController?.topViewController?.beginAppearanceTransition(true, animated: false)
vc.dismiss(animated: true) {
self.navigationController?.topViewController?.endAppearanceTransition()
}
}else{
//正常场景,直接dismiss
vc.dismiss(animated: true, completion: nil)
}
}else{
self.navigationController?.popViewController(animated: true)
}
//否则直接执行pop逻辑
//这里在pop的时候将参数带出,并且从结果表中移除
if let onPageFinshed = resultTable[options.pageName] {
onPageFinshed(options.arguments)
resultTable.removeValue(forKey: options.pageName)
}
}
}
需要在 appdelegate 里面进行初始化操作
//
// AppDelegate.swift
// flutter_swift
//
// Created by liuyaozong on 2021/8/10.
//
import UIKit
import flutter_boost
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
//创建代理,做初始化操作
let delegate = BoostDelegate()
FlutterBoost.instance().setup(application, delegate: delegate) { engine in
}
window = UIWindow(frame: UIScreen.main.bounds)
let navc = UINavigationController(rootViewController: ViewController())
window?.rootViewController = navc
window?.makeKeyAndVisible()
return true
}
}
如何使用呢:
直接上代码
//
// ViewController.swift
// flutter_swift
//
// Created by liuyaozong on 2021/8/10.
//
import UIKit
import flutter_boost
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "我的原生页面"
// Do any additional setup after loading the view.
view.backgroundColor = .white
BoostDelegate.shared.navigationController = self.navigationController
let btn1 = creatBtn(title: "跳转到首页")
btn1.addTarget(self, action: #selector(clickHome), for: .touchUpInside)
let btn2 = creatBtn(title: "跳转到我的")
btn2.addTarget(self, action: #selector(clickMine), for: .touchUpInside)
view.addSubview(btn1)
view.addSubview(btn2)
btn1.frame = .init(x: 100, y: 100, width: 100, height: 50)
btn2.frame = .init(x: 100, y: 300, width: 100, height: 50)
}
//跳转到首页
@objc func clickHome() {
let options = FlutterBoostRouteOptions()
options.pageName = "homePage"
BoostDelegate.shared.pushFlutterRoute(options)
}
//跳转到个人中心
@objc func clickMine() {
let options = FlutterBoostRouteOptions()
options.pageName = "minePage"
options.arguments = ["data": "床前明月光,疑是地上霜"]
options.uniqueId = "举头望明月,低头思故乡"
BoostDelegate.shared.pushFlutterRoute(options)
}
func creatBtn(title: String) -> UIButton {
let btn = UIButton()
btn.setTitle(title, for: .normal)
btn.setTitleColor(.black, for: .normal)
return btn
}
}
效果:

跳转到flutter我的页面
