美文网首页
Flutter_iOS混合开发

Flutter_iOS混合开发

作者: yahibo | 来源:发表于2019-07-19 15:55 被阅读0次

    iOS项目最终是要打包上线,上线后的代码我们动都不敢动,可能是动不了吧,尴尬……。然而Flutter应用也是不可以的😂,带有Flutter工程的iOS项目,模拟器测试的时候才可以对Flutter业务做热更新(其实也不是热更新)。下面就完成一个简单的iOS-Flutter交互项目。

    参考:《Flutter环境配置》《Xcode配置》

    1、创建一个flutter_module工程

    module.png

    2、创建混合开发的iOS工程
    3、引入pod,创建Podfile文件添加内容

    platform :ios, '9.0'
    use_frameworks!
    flutter_application_path = '../flutter_module/'
    load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
    
    target 'navDemo' do
      install_all_flutter_pods(flutter_application_path)
    end
    target 'navDemoTests' do
      install_all_flutter_pods(flutter_application_path)
    end
    

    执行安装命令即可:

    pod install
    
    pod.png

    重新打开工程。

    4、禁用掉工程的bitcode
    targets -> Build Settings -> Enable Bitcode 设置为No
    要运行Flutter页面,需要禁用掉BitcodeFlutter页面不支持Bitcode

    Enable Bitcode:在应用商店上,设置了Bitcode的应用可以被转换为任意CPU上的可执行程序。

    5、设计Flutter脚本
    找到本地安装Flutter目录下的flutter_tools->bin->xcode_backend.sh脚本。这个脚本用来在Xcode编译的时候对Flutter代码也进行编译。
    TARGETS -> Build Phases中新建脚本,并添加路径:

    "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build
    "$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed
    
    xcode_backend.png

    Xcode中编译是按照Build Phases下的列表顺序编译的,Flutter官方提出脚本需要放在Target Dependences下。编译因此需要将Run Script拖动到第二位:

    path.png

    编译一下,编译通过。

    6、在iOS工程中设置Flutter页面
    引入Flutter头文件,并创建FlutterViewController。设置初始化路由,即要显示的Flutter页面。

    @implementation ViewController
    @property (nonatomic,strong)FlutterViewController *vc;
    - (void)viewDidLoad {
        [super viewDidLoad];
    }
    -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
        vc = [[FlutterViewController alloc] init];
        [vc setInitialRoute:@"one"];
        [self presentViewController:vc animated:NO completion:nil];
    }
    @end
    

    运行点击屏幕即可显示Flutter页面,setInitalRoute是设置要显示的页面,向Flutter发送路由,在Flutter中接收消息后加载相应的界面。当前会打印flutter: null,说明Flutter工程中没有配置路由。

    7、配置Flutter路由
    Fluttermain.dart文件中引入dart:ui头文件
    runAppMyApp中添加method属性,在widget中定义method属性,用来传入路由信息。
    代码如下:

    import 'package:flutter/material.dart';
    import 'dart:ui';
    
    void main() => runApp(MyApp(method: window.defaultRouteName));
    
    class MyApp extends StatelessWidget {
      final String method;
    
      const MyApp({Key key, this.method}) : super(key: key);
    
      // This widget is the root of your application.
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            backgroundColor: Colors.white
          ),
          color: Colors.yellow,
          home: rootPage(method),
        );
      }
    }
    
    Widget rootPage(String method){
      print(method);
      if(method=='one'){
        return Scaffold(
          backgroundColor: Colors.white,
          body: Center(
            child: Container(
              child: Text("混合开发one",style: TextStyle(fontSize: 30),),
            ),
          )
        );
      }else {
        return Scaffold(
          backgroundColor: Colors.white,
          body: Center(
            child: Container(
              child: Text("混合开发other",style: TextStyle(fontSize: 30),),
            ),
          )
        );
      }
    }
    

    此时在运行xcode就会打印flutter: one

    8、Xcode上通过设置不同的路由进入不同的Flutter页面
    代码如下:

    #import "ViewController.h"
    #import <Flutter/Flutter.h>
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.title = @"混合开发";
        self.view.backgroundColor = [UIColor whiteColor];
        CGFloat width = [UIScreen mainScreen].bounds.size.width;
        UIButton *button = [[UIButton alloc] initWithFrame:CGRectMake((width-200)/2, 200, 200, 40)];
        [button setTitle:@"进入第一个页面" forState:UIControlStateNormal];
        button.backgroundColor = [UIColor grayColor];
        [self.view addSubview:button];
        [button addTarget:self action:@selector(btn:) forControlEvents:UIControlEventTouchUpInside];
        
        button = [[UIButton alloc] initWithFrame:CGRectMake((width-200)/2, 280, 200, 40)];
        [button setTitle:@"进入第二个页面" forState:UIControlStateNormal];
        button.backgroundColor = [UIColor grayColor];
        [self.view addSubview:button];
        [button addTarget:self action:@selector(btn:) forControlEvents:UIControlEventTouchUpInside];
    }
    -(void)btn:(UIButton *)button{
        FlutterViewController *vc = [[FlutterViewController alloc] init];
        if ([button.titleLabel.text isEqualToString:@"进入第一个页面"]) {
            [vc setInitialRoute:@"one"];
        }else{
            [vc setInitialRoute:@"other"];
        }
        [self.navigationController pushViewController:vc animated:YES];
    }
    @end
    

    通过setInitialRoute告诉Flutter需要进入的页面。运行结果:

    ios_flutter.gif

    9、Flutter调用OC代码
    导入services.dart头文件,添加方法调用代码:

    MethodChannel('one_page').invokeMapMethod('popview','');
    

    触发该方法,即可想OC发送消息。
    在Xcode代码中添加方法通道,监听Flutter消息:

    //Flutter通讯,调用OC方法
    FlutterMethodChannel *methodChannel = [FlutterMethodChannel methodChannelWithName:@"one_page" binaryMessenger:vc];
    __weak typeof(self) weakSelf = self;
    [methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
        NSLog(@"method:%@",call.method);
        NSLog(@"arguments:%@",call.arguments);
        [weakSelf.navigationController popViewControllerAnimated:YES];
    }];
    

    Flutter触发MethodChannel方法,将会调用OC中的方法监听block,从而达到Flutter->OC通讯的目的。

    10、OC通过MethodChannel调用Flutter方法
    在开发中我们只使用一个vc即可,通过FlutterMethodChannel来调用Flutter方法来重新加载页面。

    Flutter中方法监听:

    //设置页面通道
    final MethodChannel _pageChannel = MethodChannel("page_method");
    String _method = 'one';
    @override
    void initState() {
      // TODO: implement initState
      super.initState();
      //页面监听OC调用方法
      _pageChannel.setMethodCallHandler((MethodCall call){
        _method = call.method;
        setState(() {});
      });
    }
    

    OC中配置:

    -(void)btn:(UIButton *)button{
        self.vc.title = button.titleLabel.text;
        FlutterMethodChannel *methodChannel = [FlutterMethodChannel methodChannelWithName:@"page_method" binaryMessenger:self.vc];
        //Flutter通讯,调用OC方法
        if ([button.titleLabel.text isEqualToString:@"进入第一个页面"]) {
            //监听page方法
            [methodChannel invokeMethod:@"one" arguments:@"arg"];//方法名+参数
        }else{
            [methodChannel invokeMethod:@"two" arguments:@"arg"];//方法名+参数
        }
        __weak typeof(self) weakSelf = self;
        [methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
            NSLog(@"method:%@",call.method);
            NSLog(@"arguments:%@",call.arguments);
            [weakSelf.navigationController popViewControllerAnimated:YES];
        }];
        [self.navigationController pushViewController:self.vc animated:YES];
    }
    

    除了setInitialRouteFlutterMethodChannel可以进行交互还有FlutterBasicMessageChannel也可以进行交互。

    通过以上几种方法,OCFlutter之间的通讯就可以实现了。Flutter还不支持热更新,只有在本机模拟器上才有热更新的效果-修改Flutter代码(设置颜色)模拟器重新进入页面,页面颜色发生改变。

    常用方法使用及说明:

    1、初始化Flutter控制器,加载Flutter页面

    _vc = [[FlutterViewController alloc] init];
    

    2、FlutterMethodChannel的OC、Flutter交互
    OC:

    //初始化方法通道
    _methodChannel = [FlutterMethodChannel methodChannelWithName:@"page_method" binaryMessenger:self.vc];
    //向Flutter发送消息,方法名+参数
    [self.methodChannel invokeMethod:@"one" arguments:@"arg”];
    //接收Flutter发送的消息
    [self.methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
        NSLog(@"method:%@",call.method);
        NSLog(@"arguments:%@",call.arguments);
    }];
    

    Flutter:

    //初始化方法通道参数name与OC保持一致
    final MethodChannel _pageChannel = MethodChannel("page_method”);
    //监听OC发送的消息
    _pageChannel.setMethodCallHandler((MethodCall call){
      _method = call.method;
      setState(() {});
    });
    //像OC发送消息,方法名+参数
    _pageChannel.invokeMapMethod('page','arg1');
    

    3、MessageChannel的OC、Flutter交互
    OC:

    _msgChannel = [FlutterBasicMessageChannel messageChannelWithName:@"messageChannel" binaryMessenger:self.vc];
    //向flutter发送消息
    [self.msgChannel sendMessage:@"I am pitt"];
    //接收Flutter消息
    [_msgChannel setMessageHandler:^(id  _Nullable message, FlutterReply  _Nonnull callback) {
        NSLog(@"接收flutter发送过来的消息:%@",message);
    }];
    

    Flutter:

    //持续消息发送与接收,OC对应的name参数需要和当前name保持一致
    final BasicMessageChannel _messageChannel = BasicMessageChannel("messageChannel", StandardMessageCodec());
    //向OC发送一条消息
    _messageChannel.send("I am hibo");
    //接收OC发送的消息
    _messageChannel.setMessageHandler((message){
      print(message);
    }
    
    完整的测试代码:https://gitee.com/yahibo/flutter_ios.git
    仿微信Flutter工程:https://gitee.com/yahibo/we_chat_demo.git
    weChat1.gif weChat2.gif weChat3.gif

    相关文章

      网友评论

          本文标题:Flutter_iOS混合开发

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