はじめに
SwiftUIの勉強をしようと思ったのですが、SwiftUIのプロジェクトを作成して勉強進めるより、馴染みのあるFlutterからSwiftUIを呼び出して利用する方が勉強しやすいかなと思い、FlutterからSwiftUIを呼び出す方法を調査しました。
呼び出し方
Flutter側
ネイティブ側の処理を呼び出すコードです。
ボタンのイベントなどから呼び出してください。
import 'package:flutter/services.dart';
Future<void> _showSwiftUIView() async {
try {
// ChatGPTに聞いたところ、Channel名には、
// パッケージ名+用途を表す名前を指定するのが推奨されるようです
const MethodChannel channel = MethodChannel("com.example.app/view");
await channel.invokeMethod('showSwiftUIView');
} on PlatformException catch (e) {
print("Failed to invoke method: '${e.message}'.");
}
}
iOS側
ネイティブ側のメソッドを作成
Flutter側から呼び出すiOS側の処理を実装します。
ios/Runner/AppDelegate.swift
import Flutter
import UIKit
+import SwiftUI
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
+ let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
+ // nameはFlutter側と合わせる必要がある
+ let methodChannel = FlutterMethodChannel(name: "com.example.app/view", binaryMessenger: controller as! FlutterBinaryMessenger)
+ methodChannel.setMethodCallHandler({
+ (call: FlutterMethodCall, result: FlutterResult) -> Void in
+ switch call.method {
+ case "showSwiftUIView" :
+ // SwiftUIで実装した画面を呼び出す
+ let swiftUIView = SampleView()
+ let hostingController = UIHostingController(rootView: swiftUIView)
+ controller.present(hostingController, animated: true, completion: nil)
+ default :
+ result(nil)
+ }
+ })
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
SwiftUIでUIを作成
Flutter側から呼び出し表示するUIを作成します。
ios/Runner/SampleView.swift
import SwiftUI
struct SampleView: View {
var body: some View {
Text("Hello, World!")
}
}
#Preview {
SampleView()
}
表示
その他の表示
モーダル表示以外にも画面遷移が可能です
画面遷移
ios/Runner/AppDelegate.swift
import Flutter
import UIKit
+import SwiftUI
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
+ // ナビゲーションようにコントローラーを作成
+ let navigationController = UINavigationController(rootViewController: controller)
+ // 透明にしておかないと、Flutter側の画面のAppBar上部が白色となる
+ navigationController.navigationBar.isTranslucent = true
+ navigationController.navigationBar.setBackgroundImage(UIImage(), for: .default)
+ navigationController.navigationBar.shadowImage = UIImage()
+ window = UIWindow(frame: UIScreen.main.bounds)
+ window?.rootViewController = navigationController
+ window?.makeKeyAndVisible()
// nameはFlutter側と合わせる必要がある
let methodChannel = FlutterMethodChannel(name: "com.example.app/view", binaryMessenger: controller as! FlutterBinaryMessenger)
methodChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: FlutterResult) -> Void in
switch call.method {
case "showSwiftUIView" :
// SwiftUIで実装した画面を呼び出す
let swiftUIView = SampleView()
let hostingController = UIHostingController(rootView: swiftUIView)
- controller.present(hostingController, animated: true, completion: nil)
+ controller.navigationController?.pushViewController(hostingController, animated: true)
default :
result(nil)
}
})
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}