Flutterからネイティブ(Swift)の画面を呼び出して表示する方法

4 min read読了の目安(約3900字

今回はFlutterでデフォルトで生成されるカウントアップアプリを使っていきます
挙動としては、floatingButtonをタップするとiOSの画面へ遷移するアプリを作りました

Flutter | Project作成

iOS | 表示したいiOS側の画面を作成

作成するディレクトリ

作成するViewController

//
//  RunnerViewController.swift
//  Runner

import UIKit

class RunnerViewController: UIViewController {
    weak var delegate: RunnerViewControllerDelegate? = nil
    let someText: String

        init(someText: String) {
            self.someText = someText
            super.init(nibName: nil, bundle: nil)
        }

        required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") }

        private lazy var container: UIView = {
            let container = UIView.init(frame: CGRect(x: 150, y: 150, width: 200, height: 200))
            container.backgroundColor = .yellow

            let label = UILabel.init(frame: CGRect(x: 100, y: 100, width: 100, height: 100))
            label.text = someText

            container.addSubview(label)
            return container
        }()

        override func viewDidLoad() {
            super.viewDidLoad()
            view.backgroundColor = .red
            self.view.addSubview(container)
        }

        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
}

protocol RunnerViewControllerDelegate: AnyObject {
    func runnerViewControllerSendMessage(_ viewController: RunnerViewController, message: String)
}

iOS | AppDelegateを更新

import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {

  private let methodChannelName = "com.example.show"
  private var result: FlutterResult?

  private var flutterViewController: FlutterViewController {
      return self.window.rootViewController as! FlutterViewController
  }

  override func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {

    let methodChannel = FlutterMethodChannel(name: methodChannelName, binaryMessenger: flutterViewController.binaryMessenger)
    methodChannel.setMethodCallHandler { [weak self] methodCall, result  in
        if methodCall.method == "test" {
            let parameters = methodCall.arguments as? String
            self?.launchNativeScreen(parameters)
        } else {
            result(FlutterError(code: "ErrorCode", message: "ErrorMessage",details: nil))
        }
    }

    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }

  func launchNativeScreen(_ parameters: String?) {
    let viewController: RunnerViewController = RunnerViewController(someText: "oimo")
    viewController.delegate = self
    flutterViewController.present(viewController, animated: true, completion: nil)
  }
}

extension AppDelegate: RunnerViewControllerDelegate {
    func runnerViewControllerSendMessage(_ viewController: RunnerViewController, message: String) {
        result?(message)
    }
}

Flutter | main.dartを更新

floatingButtonをタップした時にネイティブ画面へ遷移する

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      //_counter++;
      _launchNativeScreen();
    });
  }

  Future<Null> _launchNativeScreen() async {
    MethodChannel _methodChannel = MethodChannel('com.example.show');
    try {
      final String result = await _methodChannel.invokeMethod('test', "test_param");
      print(result);
    } on PlatformException catch (e) {
      print(e);
    }
  }

  ...
}

結果

終わり