Posted at

Flutter on iOS で rootViewController を変更する

※この記事は Flutter v0.7.3 をもとにして書かれています

Flutter の iOS 向けの実装で、標準では FlutterViewController が rootViewController となっています。Flutter だけでアプリのすべてを実装できる場合は問題ありませんが、ネイティブの ViewController と連携する場合に rootViewController を違うものにしたい場合があります。例えば CARTUNE では root には UINavigationController を設定し、その最初の画面として FlutterViewController を使っています。

まず ios/Runner/Info.plistUIMainStoryboardFile の項目を削除し、使わなくなった ios/Runner/Base.lproj/Main.storyboard も削除しておきます。

続いて AppDelegate の起動時の処理 override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool でコードから rootViewController を設定するようにします。

@objc class AppDelegate: FlutterAppDelegate {

var flutterViewController: FlutterViewController?

...

self.flutterViewController = FlutterViewController()

let nav = UINavigationController.init(rootViewController: self.flutterViewController!)
self.window.rootViewController = nav
self.window.makeKeyAndVisible()

あとは、いくつかのメソッドをオーバーライドします。Flutter の標準の実装では rootViewController が FlutterViewController である前提となっていますので、オーバーライドしてメソッド呼び出しが Flutter へ伝わるようにします。

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

super.touchesBegan(touches, with: event)

// rootがFlutterViewControllerの場合はsuper.touchesBeganだけでよいのでreturn
if let vc = UIApplication.shared.keyWindow?.rootViewController as? FlutterViewController {
return
}

// そうでない場合はFlutterViewControllerに対してhandleStatusBarTouchesを呼ぶ
self.flutterViewController?.handleStatusBarTouches(event)
}

override func registrar(forPlugin: String) -> FlutterPluginRegistrar {
return (self.flutterViewController?.registrar(forPlugin: forPlugin))!
}

override func hasPlugin(_ pluginKey: String) -> Bool {
return (self.flutterViewController?.hasPlugin(pluginKey))!
}

override func valuePublished(byPlugin pluginKey: String) -> NSObject {
return (self.flutterViewController?.valuePublished(byPlugin: pluginKey))!
}