最近公司想分別兩頁均顯示同一個Flutter module 記錄一下
首先參考了官方的add_to_app sample
commit id: 6e9824d
首先先跑起官方的sample, 今次我只研究了iOS
#!/bin/bash
set -e
cd flutter_module_using_plugin
flutter pub get
cd ../flutter_module_books
flutter pub get
cd ../flutter_module
flutter pub get
# For Android builds:
flutter build aar
# For iOS builds:
flutter build ios-framework --xcframework --output=../ios_using_prebuilt_module/Flutter
cd ../ios_fullscreen
pod install
cd ../ios_using_plugin
pod install
我是使用 ios_using_prebuilt_module
的例子
官方sample流程如下
AppDelegate
先預熱(prewarm) FlutterEngineAppDelegate
取回 FlutterEngine
並建立methodChannel
AppDelegate
取回 FlutterEngine
生 FlutterViewController
並present
出來為了分別兩頁均顯示同一個Flutter, methodChannel
需要放到AppDelegate
因為兩頁需要共用同一個methodChannel
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var flutterEngine : FlutterEngine?
var methodChannel : FlutterMethodChannel?
var count = 0
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Instantiate Flutter engine
self.flutterEngine = FlutterEngine(name: "io.flutter", project: nil)
self.flutterEngine?.run(withEntrypoint: nil)
GeneratedPluginRegistrant.register(with: self.flutterEngine!)
if let flutterEngine = (UIApplication.shared.delegate as? AppDelegate)?.flutterEngine {
methodChannel = FlutterMethodChannel(name: "dev.flutter.example/counter",
binaryMessenger: flutterEngine.binaryMessenger)
methodChannel?.setMethodCallHandler({ [weak self]
(call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
if let strongSelf = self {
switch(call.method) {
case "incrementCounter":
strongSelf.count += 1
strongSelf.reportCounter()
case "requestCounter":
strongSelf.reportCounter()
default:
// Unrecognized method name
print("Unrecognized method name: \\(call.method)")
}
}
})
}
return true
}
func reportCounter() {
methodChannel?.invokeMethod("reportCounter", arguments: count)
}
}
為了更貼近真實場景,我使用了TabbarController
第一個(紅色背景)中間白色的部份顯示Flutter View
第二個NavigationController 的rootViewController 顯示Flutter View
import UIKit
import Flutter
class ViewController: UIViewController {
@IBOutlet weak var flutterView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let flutterEngine = (UIApplication.shared.delegate as? AppDelegate)?.flutterEngine,
let methodChannel = (UIApplication.shared.delegate as? AppDelegate)?.methodChannel{
flutterEngine.viewController = nil
let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
self.embed(flutterViewController, inView: flutterView)
//methodChannel.invokeMethod("reportCounter", arguments: 100)
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.removeChild()
}
}
extension UIViewController {
func embed(_ viewController:UIViewController, inView view:UIView){
viewController.willMove(toParent: self)
viewController.view.frame = view.bounds
view.addSubview(viewController.view)
self.addChild(viewController)
viewController.didMove(toParent: self)
}
func removeChild() {
self.children.forEach {
$0.willMove(toParent: nil)
$0.view.removeFromSuperview()
$0.removeFromParent()
}
}
}
import UIKit
import Flutter
class NavigationController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if let flutterEngine = (UIApplication.shared.delegate as? AppDelegate)?.flutterEngine,
let methodChannel = (UIApplication.shared.delegate as? AppDelegate)?.methodChannel{
flutterEngine.viewController = nil
let flutterViewController = FlutterViewController(engine: flutterEngine, nibName: nil, bundle: nil)
self.setViewControllers([flutterViewController], animated: false)
}
}
}
必須在 viewWillAppear
是放入flutterViewController, 否則可能會沒有反應
在顯示flutterViewController 前必須先將flutterEngine.viewController 清空, 否則會有錯誤
FlutterEngine <FlutterEngine: 0x7fa004c11c00> is already used with FlutterViewController instance <FlutterViewController: 0x7fa00882ec00>. One instance of the FlutterEngine can only be attached to one FlutterViewController at a time. Set FlutterEngine.viewController to nil before attaching it to another FlutterViewController.
flutterEngine.viewController = nil