zoukankan      html  css  js  c++  java
  • flutter 与iOS混合开发

    在Flutter项目开发中有时候有些常用的框架没有Flutter版本,这样的场景就需要接入原生sdk并完成与原生sdk通讯 这里主要讲解如何实现与iOS的混合开发
    大致思路就是创建Flutter_module项目,并将Flutter项目以及引用的第三方库编译成静态Framework并在iOS中通过pod的方式引入

    第一步:创建一个原生的iOS工程
    1.创建一个空文件夹 名字叫 flutter_iOS_Mixture
    2.在flutter_iOS_Mixture文件夹中创建XCode工程,并在工程中执行

    pod init
    pod install
    

    第二步:创建Flutter_Module
    1.定位到flutter_iOS_Mixture文件夹目录,并在终端执行命令,创建flutter module

    flutter create -t module flutter_project
    


    执行完毕后,工程中的目录结构
    2.查看目录结构查看隐藏文件请使用快捷键打开隐藏文件

    command + shift + .
    

    目录结构

    .
    ├── flutter_project
    │   ├── README.md
    │   ├── flutter_project.iml
    │   ├── flutter_project_android.iml
    │   ├── lib
    │   ├── pubspec.lock
    │   ├── pubspec.yaml
    │   └── test
    └── iOS_App
        ├── iOS_App
        ├── iOS_App.xcodeproj
        ├── iOS_AppTests
        └── iOS_AppUITests
    

    3.打开Flutter工程,并在pubspec.yaml文件中添加两个第三方框架 执行 pub get

      cupertino_icons: ^0.1.2
      webview_flutter: ^0.3.19+9
      url_launcher: ^5.1.2
    

    第三步:将Flutter编译成静态Framework并引用到iOS工程中
    这里就有个分支了两种解决方案 1种是直接在iOS中添加依赖,就可以实现Flutter与iOS的混合,操作简单,但是有个缺点就是如果是多人开发项目的话,直接引入,需要每个开发者都需要有Flutter环境才可以正常编译通过,否则会报错,这样侵入性太强,但是如果开发人数少,使用这种方式确实可以提升开发效率(不能每次修改Flutter内容后都需要重新将Flutter打包成Framework,节约了不少时间),这也是苹果官方推荐使用的解决方案
    直接在Podfile文件中加入如果内容,Flutter与iOS的桥接就算完成了

    flutter_application_path = '../flutter_project/'
    load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
    install_all_flutter_pods(flutter_application_path)
    

    全部文件如下:

    # Uncomment the next line to define a global platform for your project
    # platform :ios, '9.0'
    flutter_application_path = '../flutter_project/'
    
    load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')
    
    target 'iOS_App' do
      # Comment the next line if you don't want to use dynamic frameworks
      use_frameworks!
      
      install_all_flutter_pods(flutter_application_path)
    
      # Pods for iOS_App
    
      target 'iOS_AppTests' do
        inherit! :search_paths
        # Pods for testing
      end
    
      target 'iOS_AppUITests' do
        # Pods for testing
      end
    
    end
    
    

    执行pod install将Flutter引入到iOS项目中

    chenhaodeMac:iOS_App chenhao$ pod install
    Analyzing dependencies
    Downloading dependencies
    Installing FlutterPluginRegistrant 0.0.1
    Installing url_launcher (0.0.1)
    Installing url_launcher_macos (0.0.1)
    Installing url_launcher_web (0.0.1)
    Installing webview_flutter (0.0.1)
    Generating Pods project
    Integrating client project
    Pod installation complete! There are 7 dependencies from the Podfile and 7 total pods installed.
    
    

    查看XCode工程发现导入的Flutter库也被加入到了iOS中

    以上是Flutter与iOS桥接的第一种方式

    2.将Flutter作为一个组件加入到iOS工程中,这样需要使用的时候,直接通过pod导入就行了,这样的好处是任何人都可以导入该插件,不需要电脑中装有Flutter环境,但是这种方式桥接操作相对繁杂,Flutter项目中内容有修改,需要重新打包并提交iOS工程中才可以生效,下面主要介绍这种方式如何实现与iOS的桥接
    1> 创建一个Pod库在flutter_iOS_Mixture根目录执行命令创建pod lib

    pod lib create flutter_lib
    
    chenhaodeMac:flutter_iOS_ Mixture chenhao$ pod lib create flutter_lib
    Cloning `https://github.com/CocoaPods/pod-template.git` into `flutter_lib`.
    Configuring flutter_lib template.
    
    To get you started we need to ask a few questions, this should only take a minute.
    
    If this is your first time we recommend running through with the guide: 
     - https://guides.cocoapods.org/making/using-pod-lib-create.html
     ( hold cmd and double click links to open in a browser. )
    
    
    What platform do you want to use?? [ iOS / macOS ]
     > iOS
    
    What language do you want to use?? [ Swift / ObjC ]
     > ObjC
    
    Would you like to include a demo application with your library? [ Yes / No ]
     > NO
    
    Which testing frameworks will you use? [ Specta / Kiwi / None ]
     > None
    
    Would you like to do view based testing? [ Yes / No ]
     > No 
    
    What is your class prefix?
     > ASS 
    
    Running pod install on your new library.
    
    Analyzing dependencies
    Downloading dependencies
    Installing flutter_lib (0.1.0)
    Generating Pods project
    Integrating client project
    
    [!] Please close any current Xcode sessions and use `flutter_lib.xcworkspace` for this project from now on.
    Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.
    Ace! you're ready to go!
    

    此时的目录结构如下

    .
    ├── flutter_lib
    │   ├── Example
    │   ├── LICENSE
    │   ├── README.md
    │   ├── _Pods.xcodeproj -> Example/Pods/Pods.xcodeproj
    │   ├── flutter_lib
    │   └── flutter_lib.podspec
    ├── flutter_project
    │   ├── README.md
    │   ├── flutter_project.iml
    │   ├── flutter_project_android.iml
    │   ├── lib
    │   ├── pubspec.lock
    │   ├── pubspec.yaml
    │   └── test
    └── iOS_App
        ├── Podfile
        ├── Podfile.lock
        ├── Pods
        ├── iOS_App
        ├── iOS_App.xcodeproj
        ├── iOS_App.xcworkspace
        ├── iOS_AppTests
        └── iOS_AppUITests
    

    在flutter_lib中创建ios_frameworks文件夹用来存放Flutter编译后的静态文件
    找到flutter_lib中flutter_lib.podspec找打并修改引用 在文件最后添加如下一段代码

      s.ios.deployment_target = '8.0'
    
      s.static_framework = true
      p = Dir::open("ios_frameworks")
      arr = Array.new
      arr.push('ios_frameworks/*.framework')
      s.ios.vendored_frameworks = arr
    
      #s.source_files = 'flutter_lib/Classes/**/*'
      
      # s.resource_bundles = {
      #   'flutter_lib' => ['flutter_lib/Assets/*.png']
      # }
    
      # s.public_header_files = 'Pod/Classes/**/*.h'
      # s.frameworks = 'UIKit', 'MapKit'
      # s.dependency 'AFNetworking', '~> 2.3'
    

    下面开始执行一段脚本 将Flutter编译并打包,将生成的frameworks自动移入到flutter_lib中的ios_frameworks中,这个ios_frameworks也正好是刚刚修改的flutter_lib.podspec引入的路径,将脚本放在flutter项目根目录中,脚本内容

    if [ -z $out ]; then
        out='ios_frameworks'
    fi
    
    echo "准备输出所有文件到目录: $out"
    
    echo "清除所有已编译文件"
    find . -d -name build | xargs rm -rf
    flutter clean
    rm -rf $out
    rm -rf build
    
    flutter packages get
    
    addFlag(){
        cat .ios/Podfile > tmp1.txt
        echo "use_frameworks!" >> tmp2.txt
        cat tmp1.txt >> tmp2.txt
        cat tmp2.txt > .ios/Podfile
        rm tmp1.txt tmp2.txt
    }
    
    echo "检查 .ios/Podfile文件状态"
    a=$(cat .ios/Podfile)
    if [[ $a == use* ]]; then
        echo '已经添加use_frameworks, 不再添加'
    else
        echo '未添加use_frameworks,准备添加'
        addFlag
        echo "添加use_frameworks 完成"
    fi
    
    echo "编译flutter"
    flutter build ios --debug --no-codesign
    #flutter build ios --release --no-codesign
    
    echo "编译flutter完成"
    mkdir $out
    
    cp -r build/ios/Debug-iphoneos/*/*.framework $out
    #cp -r build/ios/Release-iphoneos/*/*.framework $out
    cp -r .ios/Flutter/App.framework $out
    cp -r .ios/Flutter/engine/Flutter.framework $out
    
    echo "复制framework库到临时文件夹: $out"
    
    libpath='../flutter_lib/'
    
    rm -rf "$libpath/ios_frameworks"
    mkdir $libpath
    cp -r $out $libpath
    
    echo "复制库文件到: $libpath"
    

    执行脚本后发现flutter_lib中的ios_frameworks中多了一些flutter的使用的库文件

    sh build_ios.sh
    

    在podfile文件中引入组件化的flutter库

      pod 'flutter_lib', :path => '../flutter_lib'
    
    

    执行pod install

    chenhaodeMac:iOS_App chenhao$ pod install
    Analyzing dependencies
    Downloading dependencies
    Installing flutter_lib (0.1.0)
    Generating Pods project
    Integrating client project
    Pod installation complete! There is 1 dependency from the Podfile and 1 total pod installed.
    

    此时Flutter与iOS的第二种桥接方式算是操作完了,此时flutter_lib已经通过pod引入到了项目中

    第四步:iOS与Flutter互相通讯

    1.iOS中调用Flutter工程

    //初始化FlutterViewController
    self.flutterViewController = [[FlutterViewController alloc] init];
    //这里可以传递参数用来控制flutter做一些操作
    [self.flutterViewController setInitialRoute:@"{"msg":"我是iOS传入的指令"}"];
    self.flutterViewController.modalPresentationStyle = UIModalPresentationFullScreen;
    [self presentViewController:self.flutterViewController animated:YES completion:nil];
    

    2.iOS与flutter通讯
    通讯方式共有三种
    - BasicMessageChannel通用数据传输,全双工,实时传递
    - MethodChannel方法传递通道,传递只执行一次 全双工
    - EventChannel事件监听通道持续监听如果电池电量的监听

    这里只写MethodChannel写几个方法实现flutter与iOS的方法互调
    在iOS中首先要创建消息通道并初始化通道名,这样后面所有消息都通过这个通道名对应的通道传递

    //初始化通道
    FlutterMethodChannel *methodChannel = [FlutterMethodChannel methodChannelWithName:@"MSGChannel" binaryMessenger:self.flutterViewController.binaryMessenger];
    self.methodChannel = methodChannel;
    
    //通过block回调监听通道中来自flutter的消息体 这里做一个dismiss方法,由于iOS中将flutter页面push出来,次数实现dismiss方法,给flutter发送dismss消息,就知道是让iOS将当前页面关闭的动作,iOS收到后,执行关闭操作
    __weak typeof(self) weakself = self;
    [methodChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
        __strong typeof(weakself) strongself = weakself;
        //dissmiss当前页面
        if([call.method isEqualToString:@"dismiss"]){
            [strongself dismissViewControllerAnimated:YES completion:nil];
        }
        if (result) {
            result(@"成功关闭页面");
        }
    }];
    
    //iOS中也可以主动给Flutter发消息通过invokeMethod 只需要注意消息通道名要跟初始化保持一致
    [self.methodChannel invokeMethod:@"MSGChannel" arguments:@"我是iOS发送过来的消息"];
    
    

    在flutter中,首先要在main方法中通过window.defaultRouteName的方式获取iOS中传入的Route参数
    flutter中同样需要创建消息通道

    //创建消息通道并初始化消息名 这个名字要与iOS对应
     static const MethodChannel methodChannel = MethodChannel('MSGChannel');
    
    //设置消息监听
    methodChannel.setMethodCallHandler((MethodCall call){
      //接收到消息
      print(call.method);
      print(call.arguments);
      return Future.value(1);
    });
    
    //发送消息通过invokeMethod方法
    methodChannel.invokeMethod('dismiss');
    

    flutter中完整代码如下

    import 'dart:ui';
    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    
    void main() => runApp(MyApp(route: window.defaultRouteName));
    
    class MyApp extends StatefulWidget {
      String route;
      MyApp({@required this.route});
      @override
      _MyAppState createState() => _MyAppState();
    }
    
    class _MyAppState extends State<MyApp> {
      @override
      void initState() {
        super.initState();
        //收到iOS中传入指令
        print(widget.route);
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          theme: ThemeData(
            primarySwatch: Colors.blue,
          ),
          home: HomePage(),
        );
      }
    }
    
    class HomePage extends StatefulWidget {
      @override
      _HomePageState createState() => _HomePageState();
    }
    
    class _HomePageState extends State<HomePage> {
    
    //创建消息通道并初始化消息名 这个名字要与iOS对应
     static const MethodChannel methodChannel = MethodChannel('MSGChannel');
     
      @override
      void initState() {
        super.initState();
    
        //设置消息监听
        methodChannel.setMethodCallHandler((MethodCall call){
          //接收到消息
          print(call.method);
          print(call.arguments);
          return Future.value(true);
        });
    
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('iOS与Flutter通讯'),
          ),
          body: Center(
            child: GestureDetector(
              onTap: () {
                //发送消息通过invokeMethod方法
                 methodChannel.invokeMethod('dismiss');
              },
              child: Container(
                alignment: Alignment.center,
                color: Colors.red,
                 100,
                height: 40,
                child: Text(
                  '点击返回iOS',
                  style: TextStyle(
                    color: Colors.white,
                  ),
                ),
              ),
            ),
          ),
        );
      }
    }
    
    

    最终效果图如下:

    需要特别注意一点:当flutter中内容修改后,需要重新执行sh脚本,将flutter重新打包成framework,在iOS中操作才会有效,要不然改动后,iOS中还是使用的之前的老版本

    demo放在github中,如果需要请自取:https://github.com/qqcc1388/flutter_iOS_Mixture

    参考来源:

    https://juejin.im/post/5e228d21518825265c248e7b
    https://blog.csdn.net/qq_28478281/article/details/92416686
    https://www.jianshu.com/p/c1034513be13

    转载请标注出处https://www.cnblogs.com/qqcc1388/p/12693991.html和参考源 谢谢!

  • 相关阅读:
    asp.net六大对象
    python学习之类和实例的属性;装饰器@property
    第一次写博客,不知道写什么,就随便写一点咯
    Bash脚本编写初体验
    python学习之参数传递
    2016.9.30词法分析程序 108
    实验三 108
    10.28实验二 108
    词法分析实验报告 108
    组合数据类型练习,综合练习 108
  • 原文地址:https://www.cnblogs.com/qqcc1388/p/12693991.html
Copyright © 2011-2022 走看看