美文网首页
iOS或安卓项目如何接入flutter模块

iOS或安卓项目如何接入flutter模块

作者: 骑着雅迪小毛驴上班的老瞿 | 来源:发表于2023-06-12 15:41 被阅读0次

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,
        ));
  }
}


相关文章

网友评论

      本文标题:iOS或安卓项目如何接入flutter模块

      本文链接:https://www.haomeiwen.com/subject/fslqydtx.html