iOS+Flutter--FlutterBoost(一)
上一章介绍了iOS项目如何集成FlutterBoost,这一章介绍使用FlutterBoost管理页面。
一、修改AppDelegate
import UIKit
// 导入Flutter
import Flutter
import flutter_boost
@UIApplicationMain
class AppDelegate: FlutterAppDelegate { //将UIResponder, UIApplicationDelegate替换为FlutterAppDelegate
// var window: UIWindow? //去除window声明,因为FlutterAppDelegate中已经声明
override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
self.window = UIWindow(frame: UIScreen.main.bounds)
let rootViewController = UINavigationController(rootViewController: FirstViewController())
self.window?.rootViewController = rootViewController
self.window?.backgroundColor = .white
self.window?.makeKeyAndVisible()
let router: FLBPlatform = PlatformRouter()
FlutterBoostPlugin.sharedInstance().startFlutter(with: router) { (flutterEngine) in
print("####### flutterEngine #########")
}
return true
}
}
二、添加PlatformRouter,代码如下:
import Foundation
import flutter_boost
class PlatformRouter: NSObject, FLBPlatform {
func openNative(_ url: String, urlParams: [AnyHashable : Any],
exts: [AnyHashable : Any], animated: Bool,
completion: @escaping (Bool) -> Void) {
let prefix = "nativebus://"
guard url.hasPrefix(prefix) else {
completion(false)
return
}
let namespace = Bundle.main.infoDictionary!["CFBundleExecutable"] as! String
let viewControllerName = String(url.dropFirst(prefix.count))
guard let cls: AnyClass = NSClassFromString(namespace + "." + viewControllerName),
let clsType = cls as? UIViewController.Type else {
completion(false)
return
}
let targetViewController = clsType.init()
targetViewController.urlParams = urlParams
self.navigationController().pushViewController(targetViewController, animated: animated)
}
func open(_ url: String, urlParams: [AnyHashable : Any],
exts: [AnyHashable : Any], completion: @escaping (Bool) -> Void) {
var animated = false
if exts["animated"] != nil {
animated = exts["animated"] as! Bool
}
guard url.hasPrefix("flutterbus://") else {
//处理 nativebus: 页面
openNative(url, urlParams: urlParams, exts: exts, animated: animated, completion: completion)
return
}
let vc = FLBFlutterViewContainer()
vc.setName(url, params: urlParams)
navigationController().pushViewController(vc, animated: animated)
completion(true)
}
func present(_ url: String, urlParams: [AnyHashable : Any], exts: [AnyHashable : Any],
completion: @escaping (Bool) -> Void) {
var animated = false
if exts["animated"] != nil {
animated = exts["animated"] as! Bool
}
let vc = FLBFlutterViewContainer()
vc.setName(url, params: urlParams)
navigationController().present(vc, animated: animated) {
completion(true)
};
}
func close(_ uid: String, result: [AnyHashable : Any], exts: [AnyHashable : Any],
completion: @escaping (Bool) -> Void) {
var animated = false
if exts["animated"] != nil {
animated = exts["animated"] as! Bool
}
let presentedVC = navigationController().presentedViewController
let vc = presentedVC as? FLBFlutterViewContainer
if vc?.uniqueIDString() == uid {
vc?.dismiss(animated: animated, completion: {
completion(true)
})
} else {
navigationController().popViewController(animated: animated)
}
}
func navigationController() -> UINavigationController {
let delegate = UIApplication.shared.delegate as! AppDelegate
let navigationController = delegate.window?.rootViewController as! UINavigationController
return navigationController;
}
}
Flutter_Boost官方给出了FLBPlatform协议默认实现,具体可查看PlatformRouterImp,PlatformRouter在此基础上加了openNative代码
三、在FlutterBoostDemo module中配置路由页面
import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'FirstPage.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State {
@override
void initState() {
super.initState();
// 注册Native可以调用的页面
FlutterBoost.singleton.registerPageBuilders({
'flutterbus://FirstPage': (pageName, params, _) =>
FirstPage(params: params),
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter',
builder: FlutterBoost.init(),
home: Container(),
);
}
}
四、Native 打开Flutter页面
通过FlutterBoostPlugin的open方法打开Flutter页面,代码如下
import UIKit
import Foundation
import flutter_boost
class FirstViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
title = "First Page"
let button = UIButton(frame: CGRect(x: 80, y: 100, width: 200, height: 40))
button.backgroundColor = .red
button.setTitle("Native打开Fluter页面", for: UIControl.State.normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 13)
button.setTitleColor(.white, for: UIControl.State.normal)
button.addTarget(self, action: #selector(openFlutterPage), for: UIControl.Event.touchUpInside)
view.addSubview(button)
}
@objc func openFlutterPage() {
let urlParams = ["startCount": 5] // 传入参数,Flutter可接收
FlutterBoostPlugin.open("flutterbus://FirstPage", urlParams: urlParams, exts: [:], onPageFinished: { (map) in
print(map)
}) { (finished) in
}
}
}
五、Flutter打开Native页面
Flutter代码如下:
import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
class FirstPage extends StatefulWidget {
FirstPage({
Key key,
this.params,
}) : super(key: key);
final Map params; //Native传入
@override
State createState() {
_FirstPageState homeState = _FirstPageState();
homeState.counter = params['startCount'];
return homeState;
}
}
class _FirstPageState extends State {
int counter;
void _incrementCounter() {
setState(() {
counter++;
});
}
void _stopCounter() {
FlutterBoost.singleton.open("nativebus://SecondViewController",
urlParams: {"message": "Flutter打开Native页面", "finalCount": counter});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'当前计数为:',
),
Text(
'$counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
Padding(
padding: EdgeInsets.all(12),
),
Text(
'点击下面按钮计数加1',
),
FlatButton(onPressed: _incrementCounter, child: Icon(Icons.add)),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _stopCounter,
tooltip: 'Stop',
child: Icon(Icons.stop),
),
);
}
}
Native页面代码如下:
import UIKit
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
title = "Second Page"
var count = 0
if let params = urlParams as? [String: Any], let counter = params["finalCount"] as? Int {
count = counter
}
let label = UILabel(frame: CGRect(x: 80, y: 100, width: 200, height: 40))
label.text = "最终计数为\(count)"
label.backgroundColor = .blue
label.textColor = .white
view.addSubview(label)
}
}
其中urlParams是用UIViewController扩展添加的,代码如下:
import UIKit
private struct AssociatedKey {
static var urlParams = "urlParams"
}
extension UIViewController {
var urlParams: [AnyHashable : Any]? {
set {
objc_setAssociatedObject(self,
&AssociatedKey.urlParams,
newValue,
.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
}
get {
var result: [AnyHashable : Any]?
if let params = objc_getAssociatedObject(self, &AssociatedKey.urlParams) as? [AnyHashable : Any] {
result = params
}
return result
}
}
}