swift项目嵌入flutter的module混合开发(官方推荐模式)

上一篇有讲到项目混合开发配置问题。

 

上一篇是跟网上的各大网游搜罗的混合过程中的问题。这篇根据官方的介绍做了尝试配置比较简单。这里可以分享一下使用:

 

首先也是一样,在我们的项目MyApp的根目录同级文件夹下执行flutter方法

flutter create -t module my_flutter

来生成一个flutter的module来供我们配置。

如果你有的话就更好了,就不用执行这一段代码,直接将flutter项目拖过来就好了(这里其实只是一个目录,这里不过采用官方的介绍,这里主要是针对pod依赖里面的一个路径问题。你也可以随便放置你的flutter项目,只要pod依赖里面的路径匹配正确能够找到即可。但是为了多层次文件夹可能会导致的一个文件夹命名修改导致你的pod文件路径要更改,建议还是放在一个文件夹下来避免这样的不确定性因素导致的问题。)

生成的结果路径文件如下:

swift项目嵌入flutter的module混合开发(官方推荐模式)_第1张图片

然后我们可以尝试一下在my_flutter文件夹下执行一下项目的正确性,

flutter put get

来安装项目所依赖的三方包

flutter run -d all

在我们的模拟器或者真机上面试运行一下先匹配代码没有问题。

这里需要注意的是:

如果你的三方依赖里面有系统版本号限制,你这里下载也是要改你的podfile文件的

swift项目嵌入flutter的module混合开发(官方推荐模式)_第2张图片

swift项目嵌入flutter的module混合开发(官方推荐模式)_第3张图片

我的在iOS项目里面的依赖Flutter的三方组件需要platform 10.0以及以上才可以。

就需要我们在运行的时候的临时.ios文件下的podfile文件中的8.0改成10.0依赖平台。(如果看不到隐藏文件:键盘command+shift+. 进行隐藏文件的显示和隐藏)

swift项目嵌入flutter的module混合开发(官方推荐模式)_第4张图片

终端的运行设备的时候一般我们会先清理一下,

flutter clean

这个时候是没有.ios或者.android的文件夹的。在清理之后重新创建的时候才会生成,

生成之后即把上面的依赖改掉即可。(终端这个时候是一直在按照步骤运行的,这个平台号要在终端执行pod install之前改掉,这个做iOS的就不用多说为什么要在这个之前改掉了吧)。

这里也抛出一个问题,希望各位大神们能够帮忙看看,这个podfile文件生成的平台最低系统号能不能在flutter哪里设置掉,就不用每次运行都需要把它给设置好就不用改了。

 

运行成功的话,我们接下来处理我们的iOS项目MyApp(这里做了优化,我们不用在AppDelegate里面做操作,这样flutter的module就能做到对原项目的侵入最低)(项目中的Enable Bitcode要设置为NO,这个就不再截图演示了)

1.首先在我们需要用的ViewController里面添加一个按钮,当按钮点击的时候跳往我们设置的继承于FlutterViewController的控制器SecondViewController里面

//
//  ViewController.swift
//  MyApp
//
//  Created by 曹世鑫 on 2020/3/4.
//  Copyright © 2020 曹世鑫. All rights reserved.
//

import UIKit
import Flutter
import FlutterPluginRegistrant

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        self.title = "原生页面"
               
        let btn: UIButton = UIButton()
        btn.backgroundColor = .cyan
        btn.frame = CGRect(x: 50, y: 100, width: 100, height: 50)
        btn.addTarget(self, action: #selector(btnChoose), for: .touchUpInside)
        self.view.addSubview(btn);
        
    }
    
    @objc func btnChoose() {
        let flutterEngine = FlutterEngine(name: "")
        flutterEngine.run()
        GeneratedPluginRegistrant.register(with: flutterEngine);
//        let flutterEngine = (UIApplication.shared.delegate as! AppDelegate).flutterEngine
        let flutterController =
            SecondViewController.init(engine: flutterEngine, nibName: nil, bundle: nil)
        flutterController.channelMethodArguments = ["navigate":["path":"ROCKET_GAME","data":["duration":30]]]
        flutterController.channelName = "tech.brainco.focusgame/router"
//        flutterController.pushRoute("ROCKET_GAME")
        self.navigationController?.pushViewController(flutterController, animated: true)
    }

}

然后在SecondViewController里面我们做一些与flutter的交互操作

//
//  SecondViewController.swift
//  MyApp
//
//  Created by 曹世鑫 on 2020/3/4.
//  Copyright © 2020 曹世鑫. All rights reserved.
//

import UIKit
import Flutter

class SecondViewController: FlutterViewController {
    
    var channelMethodArguments: [String: Any] = [:]
    // 要与main.dart中一致
    var channelName = "tech.brainco.focusgame/router"
    private var eventSink:FlutterEventSink?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        self.navigationItem.title = "Flutter页面"
        
/*---------FlutterMethodChannel是flutter主动与IOS交互--它的messageChannel.invokeMethod好像没有用(后续再研究一下吧,如果有用就不用下面的eventchannel了)---------*/
        let messageChannel = FlutterMethodChannel(name: channelName, binaryMessenger: self.binaryMessenger)
        messageChannel.invokeMethod("navigate", arguments: ["path":"ROCKET_GAME","data":["duration":30]])
        messageChannel.setMethodCallHandler {[weak self] (call, result) in
            guard let strongSelf = self else { return }
// call.method 获取 flutter 给回到的方法名,要匹配到 channelName 对应的多个 发送方法名,一般需要判断区分
// call.arguments 获取到 flutter 给到的参数,(比如跳转到另一个页面所需要参数)
// result 是给flutter的回调, 该回调只能使用一次
            print("flutter 给到我 method:\(call.method) arguments:\(String(describing: call.arguments))")
            strongSelf.navigationController?.popViewController(animated: true)
        }
        

 /*---------FlutterEventChannel是IOS主动与flutter交互-----------*/
        let messageEventChannel: FlutterEventChannel = FlutterEventChannel(name: channelName, binaryMessenger: self.binaryMessenger)
        messageEventChannel.setStreamHandler(self)
    }

}


/*
使用FlutterEventChannel,一定要先在flutter里面先注册一下:
// 注册一个通知,监听原生传给自己的值
  static EventChannel _eventChannel = EventChannel(RouterConstants.channelName);
然后在我们实例化需要使用的时候(由于是iOS主响应flutter的,那么建议在页面加载初期就初始监听事件,并启动监听唤起代理方法,让ios的self.eventSink有效赋值)
static init() {
    print("实例化channel:" + _channel.name);
     // 监听事件,同时发送参数:启动监听
    _eventChannel.receiveBroadcastStream('启动监听').listen(_onEvent,onError: _onError);
  }

  // 回调事件
  static void _onEvent(Object event){
    print('eventChannel回掉:' + event);
    Map map = convert.jsonDecode(event) as Map ;
    // print('channel回掉添加参数:' + map.toString());
    _routerController.add(RouterInfo.fromMap(map[RouterConstants.method]));
  }

  // 错误返回
  static void _onError(Object error) {

  }
*/

extension SecondViewController: FlutterStreamHandler {
    
    func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
        self.eventSink = events;
        if (self.eventSink != nil) {
            let data = try? JSONSerialization.data(withJSONObject: self.channelMethodArguments, options: [])
            let str = String(data: data!, encoding: String.Encoding.utf8)
            self.eventSink!(str);
        }
        return nil
    }
    
    func onCancel(withArguments arguments: Any?) -> FlutterError? {
        self.eventSink = nil;
        return nil
    }
}
     

我们在项目pod里面也可以看到所依赖的flutter三方库的依赖:

 

项目代码地址:https://github.com/KirstenDunst/FlutterAddIOSOptionA

 

swift和 flutter混合demo

 

 

还有另一种混合开发模式:framework静态包形式,我们下一章介绍。喜欢的可以关注我!

你可能感兴趣的:(swift,Flutter)