上一篇有讲到项目混合开发配置问题。
上一篇是跟网上的各大网游搜罗的混合过程中的问题。这篇根据官方的介绍做了尝试配置比较简单。这里可以分享一下使用:
首先也是一样,在我们的项目MyApp的根目录同级文件夹下执行flutter方法
flutter create -t module my_flutter
来生成一个flutter的module来供我们配置。
如果你有的话就更好了,就不用执行这一段代码,直接将flutter项目拖过来就好了(这里其实只是一个目录,这里不过采用官方的介绍,这里主要是针对pod依赖里面的一个路径问题。你也可以随便放置你的flutter项目,只要pod依赖里面的路径匹配正确能够找到即可。但是为了多层次文件夹可能会导致的一个文件夹命名修改导致你的pod文件路径要更改,建议还是放在一个文件夹下来避免这样的不确定性因素导致的问题。)
生成的结果路径文件如下:
然后我们可以尝试一下在my_flutter文件夹下执行一下项目的正确性,
flutter put get
来安装项目所依赖的三方包
flutter run -d all
在我们的模拟器或者真机上面试运行一下先匹配代码没有问题。
这里需要注意的是:
如果你的三方依赖里面有系统版本号限制,你这里下载也是要改你的podfile文件的
我的在iOS项目里面的依赖Flutter的三方组件需要platform 10.0以及以上才可以。
就需要我们在运行的时候的临时.ios文件下的podfile文件中的8.0改成10.0依赖平台。(如果看不到隐藏文件:键盘command+shift+. 进行隐藏文件的显示和隐藏)
终端的运行设备的时候一般我们会先清理一下,
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静态包形式,我们下一章介绍。喜欢的可以关注我!