1、原生app接入flutter模塊:目前已經(jīng)有了原生的app,新的功能模塊想要flutter 實現(xiàn)雙端邏輯統(tǒng)一,
2、原生app接入flutter模塊:高效開發(fā)
3、原生app接入flutter模塊:減少開發(fā)成本
綜合已經(jīng)有足夠的理由使用flutter模塊進行app的開發(fā)
淺談如何在原生項目中使用flutter 模塊呢
1、官方的方法,傳入一個頁面,通過channel去進行通信,實現(xiàn)頁面交互(路由有原生和flutter不好管理)
2、使用閑魚的框架flutter_boost(更新還算及時)
這里只針對iOS項目通過使用flutter_boost 進行講解,他的詳細使用
1、原生項目
2、通過創(chuàng)建flutter_module
3、iOS原生項目podfile中
platform :ios, '13.0'
#source 'https://github.com/CocoaPods/Specs.git'
source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git'
flutter_application_path = '../xxx_module/'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
inhibit_all_warnings!
target 'XXX' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
install_all_flutter_pods(flutter_application_path)
post_install do |installer|
flutter_post_install(installer) if defined?(flutter_post_install)
end
end
4、cd到iOS目錄 pod install 就完成了flutter 模塊集成到iOS項目中
5、開始集成使用flutter_boost 再flutter 項目的yaml文件中添加三方依賴
flutter_boost:
git:
url: 'https://github.com/alibaba/flutter_boost.git'
ref: '4.2.3'
6、此時可以去github下載flutter_boost 查看exmple代碼了
7、這里貼出我的iOS代碼和flutter 模塊中的代碼
iOS項目中的代碼創(chuàng)建delegate 統(tǒng)一管理pushNativeRoute pushFlutterRoute popRoute
import UIKit
import Flutter
import flutter_boost
import FlutterPluginRegistrant
class MyBoostAppDelegate: NSObject,FlutterBoostDelegate {
static let shared = MyBoostAppDelegate()
///您用來push的導(dǎo)航欄
var navigationController:UINavigationController?
///用來存返回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來判斷生成哪個vc,這里給個默認的了
let targetViewController = DDflutterViewController()
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)
//用參數(shù)來控制是push還是pop
let isPresent = (options.arguments?["isPresent"] as? Bool) ?? false
let isAnimated = (options.arguments?["isAnimated"] as? Bool) ?? true
//對這個頁面設(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!) {
print("flutter 中 popRoute\(String(describing: options.arguments))");
//如果當前被present的vc是container,那么就執(zhí)行dismiss邏輯
if let vc = self.navigationController?.presentedViewController as? FBFlutterViewContainer,vc.uniqueIDString() == options.uniqueId{
//這里分為兩種情況,由于UIModalPresentationOverFullScreen下,生命周期顯示會有問題
//所以需要手動調(diào)用的場景,從而使下面底部的vc調(diào)用viewAppear相關(guān)邏輯
if vc.modalPresentationStyle == .overFullScreen {
//這里手動beginAppearanceTransition觸發(fā)頁面生命周期
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)
}
//否則直接執(zhí)行pop邏輯
//這里在pop的時候?qū)?shù)帶出,并且從結(jié)果表中移除
if let onPageFinshed = resultTable[options.pageName] {
onPageFinshed(options.arguments)
resultTable.removeValue(forKey: options.pageName)
}
}
}
8、iOS原生項目首先要跳轉(zhuǎn)到flutter 頁面中 ----調(diào)整代碼是
import UIKit
import Flutter
import flutter_boost
class DDflutterViewController: BaseViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.addNavigationBar(title: "flutter-native",.white)
self.view.backgroundColor = .white
// Do any additional setup after loading the view.
}
@IBAction func jumpToFlutter(_ sender: Any) {
MyBoostAppDelegate.shared.navigationController = self.navigationController
let options = FlutterBoostRouteOptions()
options.pageName = "flutterPage" /// flutterPage 等下需要在flutter 中進行定義的key 也可以講是url,路由的路徑
options.arguments = ["type":"argumenaaats"] /// 需要傳遞給flutter 的參數(shù)
FlutterBoost.instance().open(options)
}
}
以上已完成了iOS項目里面的操作,接下來flutter 模塊項目代碼中進行操作
9、話不多說,直接貼出 main里面的代碼
import 'package:dido_module/secondpage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:oktoast/oktoast.dart';
import 'flutter_page.dart';
void main() {
PageVisibilityBinding.instance
.addGlobalObserver(AppGlobalPageVisibilityObserver());
CustomFlutterBinding();
runApp(MyApp());
}
class AppGlobalPageVisibilityObserver extends GlobalPageVisibilityObserver {
@override
void onPagePush(Route<dynamic> route) {
Logger.log(
'boost_lifecycle: AppGlobalPageVisibilityObserver.onPageCreate route:${route.settings.name}');
}
@override
void onPageShow(Route<dynamic> route) {
Logger.log(
'boost_lifecycle: AppGlobalPageVisibilityObserver.onPageShow route:${route.settings.name}');
}
@override
void onPageHide(Route<dynamic> route) {
Logger.log(
'boost_lifecycle: AppGlobalPageVisibilityObserver.onPageHide route:${route.settings.name}');
}
@override
void onPagePop(Route<dynamic> route) {
Logger.log(
'boost_lifecycle: AppGlobalPageVisibilityObserver.onPageDestroy route:${route.settings.name}');
}
@override
void onForeground(Route route) {
Logger.log(
'boost_lifecycle: AppGlobalPageVisibilityObserver.onForeground route:${route.settings.name}');
}
@override
void onBackground(Route<dynamic> route) {
Logger.log(
'boost_lifecycle: AppGlobalPageVisibilityObserver.onBackground route:${route.settings.name}');
}
}
class CustomFlutterBinding extends WidgetsFlutterBinding
with BoostFlutterBinding {}
class CustomInterceptor1 extends BoostInterceptor {
@override
void onPrePush(
BoostInterceptorOption option, PushInterceptorHandler handler) {
Logger.log('CustomInterceptor#onPrePush1~~~, $option');
// Add extra arguments
option.arguments!['CustomInterceptor1'] = "1";
super.onPrePush(option, handler);
}
@override
void onPostPush(
BoostInterceptorOption option, PushInterceptorHandler handler) {
Logger.log('CustomInterceptor#onPostPush1~~~, $option');
handler.next(option);
}
}
class CustomInterceptor2 extends BoostInterceptor {
@override
void onPrePush(
BoostInterceptorOption option, PushInterceptorHandler handler) {
Logger.log('CustomInterceptor#onPrePush2~~~, $option');
// Add extra arguments
option.arguments!['CustomInterceptor2'] = "2";
if (!option.isFromHost! && option.name == "interceptor") {
handler.resolve(<String, dynamic>{'result': 'xxxx'});
} else {
handler.next(option);
}
}
@override
void onPostPush(
BoostInterceptorOption option, PushInterceptorHandler handler) {
Logger.log('CustomInterceptor#onPostPush2~~~, $option');
handler.next(option);
}
}
class CustomInterceptor3 extends BoostInterceptor {
@override
void onPrePush(
BoostInterceptorOption option, PushInterceptorHandler handler) {
Logger.log('CustomInterceptor#onPrePush3~~~, $option');
// Replace arguments
// option.arguments = <String, dynamic>{'CustomInterceptor3': '3'};
handler.next(option);
}
@override
void onPostPush(
BoostInterceptorOption option, PushInterceptorHandler handler) {
Logger.log('CustomInterceptor#onPostPush3~~~, $option');
handler.next(option);
}
}
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
static Map<String, FlutterBoostRouteFactory> routerMap = {
///可以在native層通過 getContainerParams 來傳遞參數(shù)
'flutterPage': (settings, uniqueId) {
/// 包含了傳遞過來的參數(shù)
debugPrint('flutterPage settings:$settings, uniqueId:$uniqueId');
return PageRouteBuilder<dynamic>(
settings: settings,
pageBuilder: (_, __, ___) => FlutterPage(
params: settings.arguments as Map<dynamic, dynamic>?,
uniqueId: uniqueId,
),
);
},
'SecoundPage': (settings, uniqueId) {
return PageRouteBuilder<dynamic>(
settings: settings,
pageBuilder: (_, __, ___) => SecoundPage(
params: settings.arguments as Map<dynamic, dynamic>?,
));
},
};
Route<dynamic>? routeFactory(RouteSettings settings, String? uniqueId) {
FlutterBoostRouteFactory? func = routerMap[settings.name!];
if (func == null) {
return null;
}
return func(settings, uniqueId);
}
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return ScreenUtilInit(
designSize: const Size(375, 812),
minTextAdapt: true,
splitScreenMode: true,
builder: (context,child) {
return OKToast(
dismissOtherOnShow: true,
backgroundColor: Colors.blue,
radius: 5.0,
child: FlutterBoostApp(routeFactory,
// 如果自定了appBuilder,需要將傳入的參數(shù)添加到widget層次結(jié)構(gòu)中去,
// 否則會導(dǎo)致FluttBoost初始化失敗。
appBuilder: (child) => MaterialApp(
home: child,
),
interceptors: [
CustomInterceptor1(),
CustomInterceptor2(),
CustomInterceptor3(),
]),
);
},
);
}
}
class BoostNavigatorObserver extends NavigatorObserver {
@override
void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
debugPrint('boost-didPush${route.settings.name}');
}
@override
void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
debugPrint('boost-didPop${route.settings.name}');
}
@override
void didRemove(Route<dynamic> route, Route<dynamic>? previousRoute) {
debugPrint('boost-didRemove${route.settings.name}');
}
@override
void didStartUserGesture(
Route<dynamic> route, Route<dynamic>? previousRoute) {
debugPrint('boost-didStartUserGesture${route.settings.name}');
}
}
10、9點總降到的flutterPage 再main里面進行了定義,并且關(guān)聯(lián)上了對應(yīng)的頁面FlutterPage
11、再flutter 頁面中進行路由跳轉(zhuǎn)操作flutterpage中的代碼
import 'package:dido_module/customwidget/comment_widget_global.dart';
import 'package:dido_module/util/comment_util_global.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_boost/flutter_boost.dart';
class FlutterPage extends StatefulWidget {
const FlutterPage({this.params, this.message, this.uniqueId});
final Map? params;
final String? message;
final String? uniqueId;
@override
State<FlutterPage> createState() => _FlutterPageState();
}
class _FlutterPageState extends State<FlutterPage>
with PageVisibilityObserver {
static const String _kTag = 'page_visibility';
bool withContainer = true;
@override
void initState() {
super.initState();
Logger.log('$_kTag#initState, ${widget.uniqueId}, $this');
}
@override
void didChangeDependencies() {
Logger.log('$_kTag#didChangeDependencies, ${widget.uniqueId}, $this');
PageVisibilityBinding.instance.addObserver(this, ModalRoute.of(context)!);
super.didChangeDependencies();
}
@override
void dispose() {
PageVisibilityBinding.instance.removeObserver(this);
Logger.log('$_kTag#dispose~, ${widget.uniqueId}, $this');
super.dispose();
}
@override
void onPageShow() {
Logger.log('$_kTag#onPageShow, ${widget.uniqueId}, $this');
}
@override
void onPageHide() {
Logger.log('$_kTag#onPageHide, ${widget.uniqueId}, $this');
}
@override
void onForeground() {
Logger.log('$_kTag#onForeground, ${widget.uniqueId}, $this');
}
@override
void onBackground() {
Logger.log('$_kTag#onBackground, ${widget.uniqueId}, $this');
}
@override
Widget build(BuildContext context) {
Logger.log(
'${MediaQuery.of(context).padding.top} uniqueId=${widget.uniqueId}');
Logger.log(
'${MediaQuery.of(context).padding.bottom} uniqueId=${widget.uniqueId}');
Logger.log(
'${MediaQuery.of(context).size.width} uniqueId=${widget.uniqueId}');
Logger.log(
'${MediaQuery.of(context).size.height} uniqueId=${widget.uniqueId}');
return Scaffold(
appBar: AppTitleBar(titleWidget: Text("flutter 頁面"),leftCallback: (){
Navigator.of(context).pop({'msg': 'I am from dart ...'});
},leftWidget: Image.asset(getImgPath("icon_arrow_left")),),
body: SingleChildScrollView(
child: Container(
margin: const EdgeInsets.all(24.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
InkWell(
child: Container(
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.all(8.0),
color: Colors.yellow,
child: const Text(
'Pop with parameter',
style: TextStyle(fontSize: 22.0, color: Colors.blue),
)),
onTap: () =>
Navigator.of(context).pop({'msg': 'I am from dart ...'}),
),
InkWell(
child: Container(
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.all(8.0),
color: Colors.yellow,
child: const Text(
'Open native page',
style: TextStyle(fontSize: 22.0, color: Colors.blue),
)),
onTap: () => BoostNavigator.instance.push("native",arguments: {"type":"to nativepagearguments"}).then(
(value) =>
debugPrint("Return from Native: ${value.toString()}")),
),
InkWell(
child: Container(
padding: const EdgeInsets.all(8.0),
margin: const EdgeInsets.all(8.0),
color: Colors.yellow,
child: const Text(
'SecoundPage demo',
style: TextStyle(fontSize: 22.0, color: Colors.black),
)),
onTap: () => BoostNavigator.instance
.push("SecoundPage",arguments: {"type":"SecoundPage"}, withContainer: withContainer),
),
]
)
)
)
);
}
}
class PushWidget extends StatefulWidget {
@override
State<PushWidget> createState() => _PushWidgetState();
}
class _PushWidgetState extends State<PushWidget> {
late VoidCallback _backPressedListenerUnsub;
@override
void dispose() {
super.dispose();
_backPressedListenerUnsub.call();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: Builder(builder: (BuildContext context) {
return IconButton(
icon: const Icon(Icons.arrow_back),
// 如果有抽屜的話的就打開
onPressed: () {
// BoostNavigator.instance.pop('Hello, I am from PushWidget.');
Navigator.of(context).pop('Hello, I am from PushWidget.');
},
// 顯示描述信息
tooltip: MaterialLocalizations.of(context).openAppDrawerTooltip,
);
}),
title: const Text('flutter_boost_example'),
),
body: Container(
color: Colors.red,
width: 300,
height: 300,
));
}
}