美文网首页
第十五章:混合开发

第十五章:混合开发

作者: Mr姜饼 | 来源:发表于2021-05-25 11:26 被阅读0次

    本节知识点为大家介绍Flutter与iOS双向通信

    • Flutter引用iOS原生

    我们将沿用之前的wechatDemo来完成此次的课题研究
    我们来实现,点击用户头像,调用iOS原生的选择照片框架,来显示我们新选择的照片

    16.png

    "#####MethodChannel"

    step1:为头像盒子包一个gestor,实现我们的点击事件

    GestureDetector(
                    onTapDown: (e){
                      _selectHeadIcon();
                    },
                    child: Container(//装饰盒子(头像)设置圆角
                      width: 60,
                      height: 60,
                      decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(5),
                          image: DecorationImage(image:_filePath == null ? AssetImage('images/ice.png') : FileImage(File(_filePath.substring(7))))
                      ),
                    ),
                  ),//头部
    
      //选择头像
      _selectHeadIcon(){
        print('选择头像');
        //调用原生的selectHead方法
        _methodChannel.invokeMethod('selectHead');
      }
    

    step2:Flutter端定义方法通道,并实行监听

    //定义方法通道minePage、 进行传输
      final MethodChannel _methodChannel = MethodChannel('minePage');
      //选择图片的文件路径
      String _filePath;
     @override
      void initState() {
        // TODO: implement initState
        super.initState();
        //注册回调监听
        _methodChannel.setMethodCallHandler((call){
          print('收到回调');
          if(call.method == 'selectHeadFinish'){//由原生端发送消息至flutter端
              String filePath =  call.arguments.toString();
              print('收到回调$filePath');
              setState(() {
                _filePath = filePath;
              });
          }
          //return null;
        });
      }
    

    step3:在原生端注册

    #import "AppDelegate.h"
    #import "GeneratedPluginRegistrant.h"
    
    @interface AppDelegate()
    @property (nonatomic ,strong)FlutterMethodChannel* methodChannel;
    @end
    
    @implementation AppDelegate
    - (BOOL)application:(UIApplication *)application
        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        UIImagePickerController* pickVC = [[UIImagePickerController alloc] init];
        pickVC.delegate = self;
        //获取Flutter页面
        FlutterViewController* flutterVC = (FlutterViewController*)self.window.rootViewController;
        //创建并获取Flutter的方法通道
        self.methodChannel = [FlutterMethodChannel methodChannelWithName:@"minePage" binaryMessenger:flutterVC];
        //实行监听回调
        [self.methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
            //监听flutter发起的消息回调,调起原生的相机
            if([call.method isEqual:@"selectHead"]){
                [flutterVC presentViewController:pickVC animated:YES completion:nil];
            }
        }];
      [GeneratedPluginRegistrant registerWithRegistry:self];
      return [super application:application didFinishLaunchingWithOptions:launchOptions];
    }
    - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *)info{
        
        [picker dismissViewControllerAnimated:YES completion:^{
            NSURL* url = info[@"UIImagePickerControllerImageURL"];
            //向FLutter发送消息selectHeadFinish
            [self.methodChannel invokeMethod:@"selectHeadFinish" arguments:url.absoluteString];
        }];
    }
    @end
    
    

    思路详解: Flutter注册methodChannel并监听回调 -> iOS获取flutter中注册的methodChannel,并且监听回调方法'selectHead' ->当flutter发送‘ selectHead’消息时,iOS监听到回调,会调起原生相册 -> iOS选择完照片之后,将照片的filePath回调给flutter -> flutter监听到iOS回传的回调,获取照片filePath刷新页面

    注意:flutter与原生两端,注册的方法和回调都必须统一,一收一发!!!!!

    • iOS原生引用Flutter

    可能我们在一个成型的iOS项目中需要引用flutter的指定页面,这时候我们就需要在xcode做如下操作

    step1:首先我们在本地的iOS工程通目录下创建一个flutter的module工程

    //cd到Xcode工程同级目录下,执行
    flutter create -t module flutter_modlue
    
    51.png

    step2:在iOS的podfile文件中添加如下引用

    
    flutter_application_path = '../flutter_module'
    
    load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb’)
    
    platform :ios, '9.0'
    
    target 'iOS_flutter' do
      # Comment the next line if you don't want to use dynamic frameworks
    
      # 引入flutter
    
      install_flutter_engine_pod
    
      install_all_flutter_pods(flutter_application_path)
    
      use_frameworks!
    
      # Pods for iOS_flutter
    
      target 'iOS_flutterTests' do
        inherit! :search_paths
        # Pods for testing
      end
    
      target 'iOS_flutterUITests' do
        # Pods for testing
      end
    
    end
    

    pod install

    mac@icedeMacBook-Pro 原生嵌入Flutter % pod install
    Analyzing dependencies
    Downloading dependencies
    Installing Flutter (1.0.0)
    Installing FlutterPluginRegistrant (0.0.1)
    Installing flutter_modlue (0.0.1)
    Generating Pods project
    Integrating client project
    Pod installation complete! There are 3 dependencies from the Podfile and 3 total pods installed.
    
    
    

    打开.xcworkspace文件

    #import "ViewController.h"
    #import <Flutter/Flutter.h>
    @interface ViewController ()
    @end
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        UIButton* btn = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 50, 50)];
        btn.backgroundColor = [UIColor redColor];
        [self.view addSubview:btn];
        [btn addTarget:self action:@selector(pushFlutter) forControlEvents:UIControlEventTouchUpInside];
        
        // Do any additional setup after loading the view.
    }
    - (void)pushFlutter{
        FlutterViewController* VC = [[FlutterViewController alloc] init];
        [self presentViewController:VC animated:YES completion:nil];
    
    }
    @end
    

    run

    52.png

    !!!!!特别提醒:千万不要以中文名字命名路径文件夹,否则pod install的时候会找不到flutter_pod的库,楼主被这个蠢问题困扰了半天!!!!!!!

    高级进阶,iOS引用指定的flutter页面

    熟练掌握FlutterEngine : Flutter引擎

    修改flutter项目

    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    void main() => runApp(MyApp());
    
    
    class MyApp extends StatefulWidget {
      @override
      _State createState() => _State();
    }
    
    class _State extends State<MyApp> {
    
      String _pageName;//page的名字
      //定义两个页面管道
      MethodChannel _oneMethodChannel = MethodChannel('onePage');
      MethodChannel _twoMethodChannel = MethodChannel('twoPage');
      @override
      void initState() {
        super.initState();
        print('super.initState');
        //注册回调
        _oneMethodChannel.setMethodCallHandler((call){
          print('收到onePage的创建回调');
          if(call.method == 'init'){//实例化
            setState(() {
              _pageName =  'onePage';
            });
          }
        });
        _twoMethodChannel.setMethodCallHandler((call){
          print('收到twoPage的创建回调');
          if(call.method == 'init'){//实例化
            setState(() {
              _pageName =  'twoPage';
            });
          }
        });
    
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
          ),
          home: buildPage(_pageName),
        );
      }
    
      Widget buildPage(String pageName){
        switch(pageName){
          case 'onePage':{
            return Scaffold(
              appBar: AppBar(
                title: Text('onePage'),
              ),
              body: Center(
                child: IconButton(icon: Icon(Icons.add),onPressed: (){
                  //回退页面事件
                  _oneMethodChannel.invokeMethod('exit');
                },),
              ),
            );
          }
          break;
          case 'towPage' :{
            return Scaffold(
              appBar: AppBar(
                title: Text('TwoPage'),
              ),
              body: Center(
                child: IconButton(icon: Icon(Icons.add),onPressed: (){
                  //回退页面事件
                  _oneMethodChannel.invokeMethod('exit');
                },),
              ),
            );
          }
          break;
          default :{
            return Scaffold(
              appBar: AppBar(
                title: Text('defalutPage'),
              ),
              body: Center(
                child: Text('defalutPage'),
              ),
            );
          }
          break;
        }
      }
    }
    
    

    原生修改

    //
    //  ViewController.m
    //  iOS_flutter
    //
    //  Created by jiangbin on 2021/5/25.
    //
    
    #import "ViewController.h"
    #import <Flutter/Flutter.h>
    
    @interface ViewController ()
    @property (nonatomic , strong)FlutterEngine* flutterEngine;
    @property (nonatomic , strong)FlutterViewController* flutterVC;
    @end
    
    @implementation ViewController
    
    //懒加载
    - (FlutterEngine*)flutterEngine{
        FlutterEngine* tmpFlutterEngine = [[FlutterEngine alloc] initWithName:@"ice"];
        if(tmpFlutterEngine.run){//提前启动引擎
            _flutterEngine = tmpFlutterEngine;
        }
        return _flutterEngine;
    }
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        UIButton* btn = [[UIButton alloc] initWithFrame:CGRectMake(100, 100, 200, 50)];
        btn.backgroundColor = [UIColor redColor];
        [btn setTitle:@"进入flutterPageOne页面" forState:UIControlStateNormal];
        [self.view addSubview:btn];
        [btn addTarget:self action:@selector(pushOneFlutter) forControlEvents:UIControlEventTouchUpInside];
        
        
        UIButton* btn2 = [[UIButton alloc] initWithFrame:CGRectMake(100, 300, 200, 50)];
        btn2.backgroundColor = [UIColor redColor];
        [btn2 setTitle:@"进入flutterPageTwo页面" forState:UIControlStateNormal];
        [self.view addSubview:btn2];
        [btn2 addTarget:self action:@selector(pushTwoFlutter) forControlEvents:UIControlEventTouchUpInside];
    
        
        UIButton* btn3 = [[UIButton alloc] initWithFrame:CGRectMake(100, 500, 200, 50)];
        btn3.backgroundColor = [UIColor redColor];
        [btn3 setTitle:@"进入flutter默认页面" forState:UIControlStateNormal];
        [self.view addSubview:btn3];
        [btn3 addTarget:self action:@selector(pushDefaultFlutter) forControlEvents:UIControlEventTouchUpInside];
    
        self.flutterVC = [[FlutterViewController alloc] initWithEngine:self.flutterEngine nibName:nil bundle:nil];
        self.flutterVC.modalPresentationStyle = 0;
        
        // Do any additional setup after loading the view.
    }
    - (void)pushOneFlutter{
        FlutterMethodChannel* methodChannel = [FlutterMethodChannel methodChannelWithName:@"onePage" binaryMessenger:self.flutterVC];
        [methodChannel invokeMethod:@"init" arguments:nil];
        [methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
            if([call.method isEqualToString:@"exit"]){
                [self.flutterVC dismissViewControllerAnimated:YES completion:nil];
            }
        }];
        [self presentViewController:self.flutterVC animated:YES completion:nil];
    }
    - (void)pushTwoFlutter{
        FlutterMethodChannel* methodChannel = [FlutterMethodChannel methodChannelWithName:@"twoPage" binaryMessenger:self.flutterVC];
        [methodChannel invokeMethod:@"init" arguments:nil];
        [methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
            if([call.method isEqualToString:@"exit"]){
                [self.flutterVC dismissViewControllerAnimated:YES completion:nil];
            }
        }];
        [self presentViewController:self.flutterVC animated:YES completion:nil];
    }
    - (void)pushDefaultFlutter{
        FlutterViewController* VC = [[FlutterViewController alloc] initWithEngine:self.flutterEngine nibName:nil bundle:nil];
        VC.modalPresentationStyle = 0;
        [self presentViewController:VC animated:YES completion:nil];
    }
    
    @end
    

    run

    53.png

    点击进入对应的flutter页面

    小结:

    项目中不建议把引擎和flutterViewController当做属性,而应该当做一个单例来创建获取,一旦iOS工程引用了flutter文件后,会一直占用着内存,不要试图回收再重新创建,而是应该一直保留在项目内存中,避免出现原生页面切换之间的卡顿!!!!!

    因此我们把它们抽出来:

    #import "KFlutterEngineManager.h"
    
    static FlutterEngine* g_flutterEndine = nil;
    
    static FlutterViewController* g_flutterVC = nil;
    
    @implementation KFlutterEngineManager
    
    + (void)start{
        [self shareEngine];
    }
    
    //项目启动 会执行一次此方法
    + (void)load{
        NSLog(@"KFlutterEngineManager");
        [self start];
    }
    
    + (FlutterEngine*)shareEngine{
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            FlutterEngine* tmpFlutterEngine = [[FlutterEngine alloc] initWithName:@"ice"];
            if(tmpFlutterEngine.run){//提前启动引擎
                g_flutterEndine = tmpFlutterEngine;
            }
        });
        return g_flutterEndine;
    }
    
    
    + (FlutterViewController*)shareFlutterVC{
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            g_flutterVC = [[FlutterViewController alloc] initWithEngine:[self shareEngine] nibName:nil bundle:nil];
            g_flutterVC.modalPresentationStyle = 0;
    
        });
        return g_flutterVC;
    }
    @end
    
    
    #import <Foundation/Foundation.h>
    #import <Flutter/Flutter.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface KFlutterEngineManager : NSObject
    + (FlutterViewController*)shareFlutterVC;
    @end
    

    相关文章

      网友评论

          本文标题:第十五章:混合开发

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