Flutter简介(摘自Flutter github)
新一代Flutter-Native混合解决方案。 FlutterBoost是一个Flutter插件,它可以轻松地为现有原生应用程序提供Flutter混合集成方案。FlutterBoost的理念是将Flutter像Webview那样来使用。在现有应用程序中同时管理Native页面和Flutter页面并非易事。 FlutterBoost帮你处理页面的映射和跳转,你只需关心页面的名字和参数即可(通常可以是URL)。
接入
在配置文件pubspec.yaml中加入依赖:
flutter_boost: ^0.0.420
或
flutter_boost:
git:
url: 'https://github.com/alibaba/flutter_boost.git'
ref: '0.0.420'
iOS端的简单实用(swift工程)
Flutter端示例代码:
- 1、工程中app首页(一般为main.dart)添加如下代码:
import 'package:flutter_boost/flutter_boost.dart';
import './pages/TestDemoPage.dart';
// 此处的platform以及messageChannel在具体的page页面会用到, 他们是全局的实例对象
const MethodChannel platform = const MethodChannel("infosearch.flutter.io/flutterCallNative");
const BasicMessageChannel messageChannel = const BasicMessageChannel("infosearch.flutter.io/basic", StandardMessageCodec());
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp>{
@override
void initState() {
super.initState();
FlutterBoost.singleton.registerPageBuilders({
'searchinfo_demo': (pageName, params, _) => TestDemoPage(params: params),
'searchinfo_news_favorites': (pageName, params, _) => NewsList(params: params, pageName: pageName),
'searchinfo_news_collection': (pageName, params, _) => NewsList(params: params, pageName: pageName),
'searchinfo_news_history': (pageName, params, _) => NewsList(params: params, pageName: pageName),
});
FlutterBoost.handleOnStartPage();
}
@override
Widget build(BuildContext context) {
return new CupertinoApp(
title: "Flutter Boost demo",
debugShowCheckedModeBanner: false,
builder: FlutterBoost.init(postPush: _onRoutePushed),
home: Container(),
);
}
void _onRoutePushed(String pageName, String uniqueId, Map params, Route route, Future _) {
print('flutter main page name is $pageName');
}
}
- 2、接main中注册的TestDemoPage.dart页面代码:
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:flutter_module/main.dart';
class TestDemoPage extends StatefulWidget {
TestDemoPage({Key key, this.params}) : super(key: key);
@required
final Map params;
_TestDemoPageState createState() => _TestDemoPageState();
}
class _TestDemoPageState extends State<TestDemoPage> {
void _receiveMessage() {
messageChannel.setMessageHandler((message) async {
print('message: $message, current page is ${widget.pageName}');
return '返回Native端的数据';
});
}
@override
void initState() {
super.initState();
// 初始化后开始监听通过BasicMessageChannel从Native传递过来的数据
_receiveMessage();
}
// 通过调用方法关闭当前页面,back按钮可调用此函数
void _closeCurrentPage() {
FlutterBoost.singleton.closePageForContext(context);
}
// 以下三个函数是通过MethodChannel实现Flutter调用Native的方法
void _showLoading() async {
await platform.invokeMethod('showLoading');
}
void _dismissLoading() async {
await platform.invokeMethod('dismissLoading');
}
void _showTips() async {
await platform.invokeMethod('showTips', {'tips': '请求失败,请稍后重试'});
}
// 此方法需要绑定到具体的事件上去
void _openNativePage(dynamic obj) async {
// 如果参数obj是model,需要调用model对应的类名.toJson()方法转为json对象
Map<String, dynamic> jsonObj;
// jsonObj = (obj as News.Result).toJson();
// 因为我要跳转native的页面故加一个type字段
// 每个Flutter的页面都是从native调用进行实例化FlutterViewContainer的
// native通过解析此处的type字段决定跳转Native还是Flutter页面
jsonObj["type"] = "App";
FlutterBoost.singleton.openPage("news_detail", jsonObj);
}
@override
Widget build(BuildContext context) {
return new CupertinoPageScaffold(
navigationBar: new CupertinoNavigationBar(
backgroundColor: Color(0xffDB2C30),
middle: Text(
widget.params['title'],
style: new TextStyle(color: CupertinoColors.white),
),
leading: CupertinoButton(
padding: EdgeInsets.only(top: 0, bottom: 0, left: 0),
child: Icon(
Icons.arrow_back,
color: CupertinoColors.white,
),
onPressed: () {
_closeCurrentPage();
}),
),
child: new Container(),
);
}
}
iOS端代码(swift):
主要实现一个类FlutterRouter.swift
import Foundation
import UIKit
import flutter_boost
import EasyNavigationbar
class FlutterRouter: NSObject, FLBPlatform {
lazy var navigationController: UINavigationController? = {
let nav = UIViewController.easy_currentViewController()?.navigationController
return nav
}()
var _fvc: FlutterViewController?
var fvc: FlutterViewController? {
didSet {
_fvc = fvc
setupFlutterCallNative()
}
}
private static var share: FlutterRouter?
class func sharedRouter() -> FlutterRouter {
guard let instance = share else {
share = FlutterRouter.init()
FlutterBoostPlugin.sharedInstance()?.startFlutter(with: share!, onStart: { (fvc) in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.001, execute: {
FlutterRouter.sharedRouter().fvc = fvc
})
})
return share!
}
return instance
}
// Flutter调用此函数的方式:
// FlutterBoost.singleton.openPage("news_detail", jsonObj);
// Native可直接调用
func openPage(_ name: String, params: [AnyHashable : Any], animated: Bool, completion: @escaping (Bool) -> Void) {
print("打开页面传递的参数 = \(params)")
// ["type": "Flutter", "session": session, "title": "title"]
guard let value = params["type"], let type = value as? String else {
return
}
// type为调转目标为App or Flutter
// params的所有键值对可以自己定义,我的格式是["type": "Flutter", "session": session, "title": "title"]
switch type {
case "App":
// 跳转原生以及传递参数
openNativeController(with: name, params: params, animated: animated)
default:
// 跳转Flutter页面以及传参
if let isPresent = params["present"], (isPresent as? Bool) == true{
let vc = CFLBFlutterViewContainer.init();
vc.setName(name, params: params)
navigationController?.present(vc, animated: animated, completion: {})
} else {
let vc = CFLBFlutterViewContainer.init();
vc.setName(name, params: params)
vc.hidesBottomBarWhenPushed = true
navigationController?.easy_pushViewController(vc, animated: animated)
}
}
}
// Flutter调用此函数的方式:
// FlutterBoost.singleton.closePageForContext(context);
func closePage(_ uid: String, animated: Bool, params: [AnyHashable : Any], completion: @escaping (Bool) -> Void) {
print("关闭页面传递的参数 = \(params)")
if let vc = navigationController?.presentedViewController,
vc.isKind(of: CFLBFlutterViewContainer.self),
(vc as! CFLBFlutterViewContainer).uniqueIDString == uid{
vc.dismiss(animated: animated, completion: {})
} else {
navigationController?.popViewController(animated: animated)
}
}
func accessibilityEnable() -> Bool {
return true
}
// 判断跳转原生各Native页面处理
private func openNativeController(with pageName: String, params: [AnyHashable : Any], animated: Bool) {
switch pageName {
case "news_detail":
let vc = UIViewController.init()
navigationController?.easy_pushViewController(vc, animated: true)
default:
let vc = NativeViewController.init()
vc.navBar.title = params["title"] as? String
vc.hidesBottomBarWhenPushed = true
navigationController?.easy_pushViewController(vc, animated: true)
}
}
// MARK: FlutterCallNative Flutter调用Native在此处回调,处理完成还可以通过result(params)回调params数据给flutter使用
private func setupFlutterCallNative() {
// 用于Flutter 调用 Native
// 这个channelname必须与Native里接收的一致
guard let vc = _fvc else {
return
}
let flutterCallNativeChannel = FlutterMethodChannel(name: "infosearch.flutter.io/flutterCallNative", binaryMessenger: vc)
//FlutterMethodCall call, FlutterResult result
flutterCallNativeChannel.setMethodCallHandler { (call, result) in
print("method: \(call.method)")
print("arguments: \(String(describing: call.arguments))")
switch call.method {
case "showLoading":
YZTProgressHUD.show()
case "dismissLoading":
YZTProgressHUD.dismiss()
case "showTips":
if let arg = call.arguments as? [String: String], let tips = arg["tips"] {
YZTProgressHUD.showText(tips)
}
default:
result(FlutterMethodNotImplemented)
}
}
}
// BasicMessageChannel在native的实现代码,需要发送消息给Flutter就调用此处函数:
// FlutterRouter.sharedRouter().setupBasicMessageChannel(pageName: "collect")
// 入参可以自定义,在Flutter端各页面可用此参数来区分是从哪里传递过来的数据
func setupBasicMessageChannel(pageName: String) {
if let vc = _fvc {
let messageChannel = FlutterBasicMessageChannel.init(name: "infosearch.flutter.io/basic", binaryMessenger: vc)
messageChannel.sendMessage(pageName)
}
}
}