1、原生app接入flutter模块:目前已经有了原生的app,新的功能模块想要flutter 实现双端逻辑统一,
2、原生app接入flutter模块:高效开发
3、原生app接入flutter模块:减少开发成本
综合已经有足够的理由使用flutter模块进行app的开发
浅谈如何在原生项目中使用flutter 模块呢
1、官方的方法,传入一个页面,通过channel去进行通信,实现页面交互(路由有原生和flutter不好管理)
2、使用闲鱼的框架flutter_boost(更新还算及时)
这里只针对iOS项目通过使用flutter_boost 进行讲解,他的详细使用
1、原生项目
2、通过创建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项目中的代码创建delegate 统一管理pushNativeRoute pushFlutterRoute popRoute
import UIKit
import Flutter
import flutter_boost
import FlutterPluginRegistrant
class MyBoostAppDelegate: NSObject,FlutterBoostDelegate {
static let shared = MyBoostAppDelegate()
///您用来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,这里给个默认的了
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)
//用参数来控制是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!) {
print("flutter 中 popRoute\(String(describing: options.arguments))");
//如果当前被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)
}
}
}
8、iOS原生项目首先要跳转到flutter 页面中 ----调整代码是
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 的参数
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 来传递参数
'flutterPage': (settings, uniqueId) {
/// 包含了传递过来的参数
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,需要将传入的参数添加到widget层次结构中去,
// 否则会导致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里面进行了定义,并且关联上了对应的页面FlutterPage
11、再flutter 页面中进行路由跳转操作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,
));
}
}
网友评论