美文网首页
将Flutter Module集成到iOS工程中

将Flutter Module集成到iOS工程中

作者: handsome丶亮 | 来源:发表于2022-07-18 15:57 被阅读0次

    1. Flutter环境搭建

    1.1. 环境

    • 时间:2022.7.13
    • 电脑:MacBook Pro (13-inch, M1, 2020)
    • 系统:macOS Monterey 12.0.1 (21A559)
    • Xcode版本:13.4.1 (13F100)
    • Android Studio版本:Chipmunk | 2021.2.1 Patch 1
      • Flutter插件版本:69.0.2
      • Dart插件版本:212.5744
    • Flutter SDK版本:3.0.4
    • CocoaPods版本:1.11.3
    • flutter_boost官方文档
    • 默认已安装HomebrewCocoaPods,如果未安装可以参考:

    1.2. 下载Flutter库文件

    • 下载链接
      下载最新的SDK,目前最新版本为3.0.4

    • 将下载好的压缩包解压,并将解压后的flutter文件夹移动到想要安装的目录中


      当前安装目录路径为:~/Documents/flutter

    1.3. 下载安装Android Studio

    • 下载链接

      直接点击Download Android Studio,弹出协议勾选同意,当前环境为M1,所以点击下载Mac with Apple chip,如果不是M系列处理器,则下载Mac with Intel chip。
    • 安装好后,打开Android Studio,选择标准模式。
    • 选择同意所有协议,点击Finish
    • 开始下载相关组件
    • 出现错误,经查阅应该是网络问题,点击Retry后成功
    • 添加Flutter和Dart插件,Android Studio打开Preferences下载Dart和Flutter插件,如下如所示打开设置:

    1.4. 配置环境变量:

    • 打开终端,当前终端默认为zsh模式
    • 在终端输入命令:vim ~/.zshrc(如果当前终端模式bash,则为vim ~/.bash_profile,后续操作也是以~/.bash_profile为准)
    • 按【i】进入编辑模式,将以下内容保存添加进去:
    export PATH=~/Documents/flutter/bin:$PATH
    export PUB_HOSTED_URL=https://pub.flutter-io.cn
    export FLUTTER_STORAGE_BASE_URL=https://storage.flutter-io.cn
    

    其中export PATH = 此处为flutter库文件安装路径/bin:$PATH。

    • 添加完以后按esc退出编辑模式,再输入:wq进行保存。
    • 然后再运行命令使.zshrc文件生效:source ~/.zshrc

    1.5. 测试Flutter环境

    • 在终端运行命令:flutter doctor
    • 如果此时报错:env: bash: No such file or directory。则说明export PATH的路径设置不对,检查配置正确后即可。
    • 如果配置正确,运行结果如下:

    1.6. 解决flutter doctor(flutter运行环境检测)的问题

    • 按提示的执行命令flutter doctor --android-licenses,出现报错 解决方法:在Android Studio的设置中勾选下图中的Android SDK Command-line Tools,并应用
    • 再次执行命令成功

      一直选y即可

    • 再次执行flutter doctor,全部正常。至此Flutter环境安装成功。

    2. 创建Flutter模块项目

    2.1. Flutter模块项目创建

    • cd到指定目录,用命令创建项目:flutter create -t module cmcc_test_module 注意:flutter项目中的所有文件命名包括工程名,默认不使用驼峰命名法(有些地方如果用驼峰会直接被禁止创建),一般全部小写,以下划线分割。
    • 将项目文件夹拖动到Android Studio上,即可打开项目。打开项目后,引入相关依赖后,点击Pub get,其中ref一定要使用空安全版本。具体版本可以到:https://github.com/alibaba/flutter_boost进行查看
    flutter_boost:
      git:
       url: 'https://github.com/alibaba/flutter_boost.git'
       ref: 'v3.0-null-safety-release.2.1'
    
    • 在工程的插件中,已经可以看到flutter boost了。

    2.2. Flutter模块项目代码

    • 新建home.dart,用于纯跳转
    import 'package:flutter/material.dart';
    
    /// flutter首页
    class CMHomePage extends StatelessWidget {
      const CMHomePage({Key? key}) : super(key: key);
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: TextButton(
                onPressed: () => _pushNativeViewController(),
                child: const Text("首页跳转原生界面")
            ),
          ),
        );
      }
    
      // 跳转原生界面
      _pushNativeViewController() {
        BoostNavigator.instance.push("TestViewController");
      }
    
    }
    
    • 新建mine.dart,用于传参跳转
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    /// flutter我的
    class CMMinePage extends StatefulWidget {
      final String data;
      const CMMinePage({Key? key, required this.data}) : super(key: key);
    
      @override
      State<CMMinePage> createState() => _CMMinePageState();
    }
    
    class _CMMinePageState extends State<CMMinePage> {
      MethodChannel eventChannel = const MethodChannel('com.flutterToNative.test');
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Text("flutter我的,传输数据为:${widget.data}")
          ),
        );
      }
      
    }
    
    • 在main.dart中配置路由表
    import 'package:cmcc_test_module/home.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter_boost/flutter_boost.dart';
    import 'mine.dart';
    import 'home.dart';
    
    // BoostFlutterBinding用于接管Flutter App的生命周期,必须得接入的
    class CustomFlutterBinding extends WidgetsFlutterBinding with BoostFlutterBinding {
    }
    
    void main() {
      CustomFlutterBinding();
      runApp(const MyApp());
    }
    
    class MyApp extends StatefulWidget {
      const MyApp({Key? key}) : super(key: key);
    
      @override
      _MyAppState createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      ///路由表
      static Map<String, FlutterBoostRouteFactory> routerMap = {
        'homePage': (settings, uniqueId) {
          return PageRouteBuilder<dynamic>(
              settings: settings,
              pageBuilder: (_, __, ___) {
                return const CMHomePage();
              });
        },
        'minePage': (settings, uniqueId) {
          return PageRouteBuilder<dynamic>(
              settings: settings,
              pageBuilder: (_, __, ___) {
                Map<String, dynamic>? map = settings.arguments as Map<String, dynamic>?;
                String data = map?['data'] as String ?? "";
                return CMMinePage(
                  data: data,
                );
          });
        },
      };
    
      Route<dynamic>? routeFactory(RouteSettings settings, String? uniqueId) {
        FlutterBoostRouteFactory? func = routerMap[settings.name];
        if (func == null) {
          return null;
        }
        return func(settings, uniqueId);
      }
    
      Widget appBuilder(Widget home) {
        return MaterialApp(home: home, debugShowCheckedModeBanner: false);
      }
    
      @override
      Widget build(BuildContext context) {
        return FlutterBoostApp(
          routeFactory,
          appBuilder: appBuilder,
        );
      }
    
    }
    
    • 至此Flutter工程代码编写完毕。

    3. 集成Flutter工程到iOS工程中

    • 为了方便调试,先用新建的iOSDemo工程进行测试,这样方便排查问题,减少不必要的影响因素。把Flutter Module项目文件夹拖动到新建的iOSDemo工程的根目录中
    • 配置Podfile文件,并在终端执行pod install
    platform:ios,'9.0'
    
    flutter_application_path = './FlutterModule/cmcc_test_module'
    load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
    
    target 'FlutterTest' do
    install_all_flutter_pods(flutter_application_path)
    end
    
    • pod install执行成功后,打开工程目录,在pod中发现flutter_boost以及module已导入
    • 在iOSDemo工程中新建:BoostDelegate
      BoostDelegate.h
    //
    //  BoostDelegate.h
    //  FlutterTest
    //
    //  Created by mac on 2022/7/13.
    //
    //  原生界面跳转Flutter界面代理
    
    #import <Foundation/Foundation.h>
    #import <flutter_boost/FlutterBoost.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface BoostDelegate : NSObject<FlutterBoostDelegate>
    
    // 设置导航控制器(外部必须设置,否则会导致无法跳转)
    @property (nonatomic, strong) UINavigationController *navigationController;
    
    // 创建单例
    + (instancetype)sharedInstance;
    
    @end
    
    NS_ASSUME_NONNULL_END
    

    BoostDelegate.m

    //
    //  BoostDelegate.m
    //  FlutterTest
    //
    //  Created by mac on 2022/7/13.
    //
    
    #import "BoostDelegate.h"
    
    @implementation BoostDelegate
    
    // 创建单例
    + (instancetype)sharedInstance{
        static BoostDelegate *myInstance = nil;
        if(myInstance == nil){
            myInstance = [[BoostDelegate alloc] init];
        }
        return myInstance;
    }
    
    // 跳转原生界面api,Flutter界面跳转原生界面,会自动调用此方法
    -(void)pushNativeRoute:(NSString *)pageName arguments:(NSDictionary *)arguments {
        // 是否有动画
        BOOL animated = [arguments[@"animated"] boolValue];
        // 弹出方式
        BOOL present = [arguments[@"present"] boolValue];
        // 这里根据pageName来判断生成哪个vc
        UIViewController *vc;
        if ([pageName isEqualToString:@"TestViewController"]) {
            vc = [TestViewController new];
        }else{
            // 没有找到,则创建一个控制器作为容错
            vc = [UIViewController new];
        }
        if (present) {
            [self.navigationController presentViewController:vc animated:animated completion:^{
            }];
        } else {
            [self.navigationController pushViewController:vc animated:YES];
        }
    }
    
    // 当框架的withContainer为true的时候,会调用此方法来做原生的push
    -(void)pushFlutterRoute:(FlutterBoostRouteOptions *)options {
        // 创建Flutter控制器
        FBFlutterViewContainer *vc = FBFlutterViewContainer.new;
        // 设置页面名称,参数等信息
        [vc setName:options.pageName uniqueId:options.uniqueId params:options.arguments opaque:options.opaque];
        // 是否有动画
        BOOL animated = [options.arguments[@"animated"] boolValue];
        // 弹出方式
        BOOL present = [options.arguments[@"present"] boolValue] || !options.opaque;
        if (present) {
            [self.navigationController presentViewController:vc animated:animated completion:^{
                options.completion(YES);
            }];
        } else {
            [self.navigationController pushViewController:vc animated:animated];
            options.completion(YES);
        }
    }
    
    // 当pop调用涉及到原生容器的时候,此方法将会被调用
    -(void)popRoute:(FlutterBoostRouteOptions *)options {
        // 取出控制器
        FBFlutterViewContainer *vc = (id)self.navigationController.presentedViewController;
        // 判断控制器是否为Flutter控制器
        if ([vc isKindOfClass:FBFlutterViewContainer.class] && [vc.uniqueIDString isEqual:options.uniqueId]) {
            if (vc.modalPresentationStyle == UIModalPresentationFullScreen) {
                [self.navigationController.topViewController beginAppearanceTransition:YES animated:NO];
                [vc dismissViewControllerAnimated:YES completion:^{
                    [self.navigationController.topViewController endAppearanceTransition];
                }];
            } else {
                [vc dismissViewControllerAnimated:YES completion:^{
                }];
            }
        } else {
            [self.navigationController popViewControllerAnimated:YES];
        }
    }
    @end
    
    • appDelegate中注册FlutterBoost
      AppDelegate.m
    //
    //  AppDelegate.m
    //  FlutterTest
    //
    //  Created by mac on 2022/7/13.
    //
    
    #import "AppDelegate.h"
    #import <FlutterPluginRegistrant/FlutterPluginRegistrant-umbrella.h>
    #import "BoostDelegate.h"
    #import <flutter_boost/FlutterBoost.h>
    #import "ViewController.h"
    
    @interface AppDelegate ()
    
    @end
    
    @implementation AppDelegate
    
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // 初始化window
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        ViewController *vc = [ViewController new];
        UINavigationController *navigation = [[UINavigationController alloc] initWithRootViewController:vc];
        self.window.rootViewController = navigation;
        [self.window makeKeyAndVisible];
        self.window.backgroundColor = [UIColor whiteColor];
        
        // 初始化FlutterBoost
        BoostDelegate *delegate = [BoostDelegate sharedInstance];
        delegate.navigationController = navigation;
        [[FlutterBoost instance] setup:application delegate:delegate callback:^(FlutterEngine *engine) {
        }];
        return YES;
    }
    
    @end
    
    • 在根控制器ViewController中进行跳转
      ViewController.m
    //
    //  ViewController.m
    //  FlutterTest
    //
    //  Created by mac on 2022/7/13.
    //
    
    #import "ViewController.h"
    #import <flutter_boost/FlutterBoost.h>
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor whiteColor];
        
        UIButton *pushHomePageButton = [UIButton buttonWithType:UIButtonTypeSystem];
        pushHomePageButton.frame = CGRectMake(100, 200, 200, 100);
        [pushHomePageButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
        [pushHomePageButton setTitle:@"跳转到Flutter首页" forState:UIControlStateNormal];
        [pushHomePageButton addTarget:self action:@selector(pushFlutterHomePage) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:pushHomePageButton];
        
        UIButton *pushMinePageButton = [UIButton buttonWithType:UIButtonTypeSystem];
        pushMinePageButton.frame = CGRectMake(100, 100, 200, 100);
        [pushMinePageButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
        [pushMinePageButton setTitle:@"跳转到Flutter我的" forState:UIControlStateNormal];
        [pushMinePageButton addTarget:self action:@selector(pushFlutterMinePage) forControlEvents:UIControlEventTouchUpInside];
        [self.view addSubview:pushMinePageButton];
    }
    
    // 跳转Flutter首页
    - (void)pushFlutterHomePage {
        FlutterBoostRouteOptions *options = [FlutterBoostRouteOptions new];
        // 此处填写的页面名称,需要在Flutter Module项目中main的路由表中有对应的路由名称,否则会导致匹配不上跳转失败
        options.pageName = @"homePage";
        options.arguments = @{@"animated": @(YES)};
        options.completion = ^(BOOL completion) {
        };
        [[FlutterBoost instance] open:options];
        options.onPageFinished = ^(NSDictionary *dic) {
            NSLog(@"%@", dic);
        };
    }
    
    // 跳转Flutter我的
    - (void)pushFlutterMinePage {
        FlutterBoostRouteOptions *options = [FlutterBoostRouteOptions new];
        options.pageName = @"minePage";
        options.arguments = @{@"animated": @(YES), @"data": @"原生界面参数传递"};
        options.completion = ^(BOOL completion) {
        };
        [[FlutterBoost instance] open:options];
        options.onPageFinished = ^(NSDictionary *dic) {
            NSLog(@"%@", dic);
        };
    }
    
    @end
    
    • 新建测试页面,用于Flutter界面跳转原生界面。
      TestViewController.m
    //
    //  TestViewController.m
    //  FlutterTest
    //
    //  Created by mac on 2022/7/18.
    //
    
    #import "TestViewController.h"
    
    @interface TestViewController ()
    
    @end
    
    @implementation TestViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.view.backgroundColor = [UIColor redColor];
    }
    
    @end
    
    • 运行项目,跳转成功。 Flutter界面跳转到原生界面

    附:Demo下载

    相关文章

      网友评论

          本文标题:将Flutter Module集成到iOS工程中

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