You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过平台通道在iOS原生代码中销毁Flutter页面?

问题:Flutter集成iOS原生应用中关闭Flutter页面失败

我正在学习将Flutter集成到iOS原生应用中,目前已实现从iOS View跳转到Flutter页面,但在使用官方平台通道的方法销毁Flutter页面时遇到问题。

官方示例基于Flutter app与原生平台通信的场景,在AppDelegate.swift中通过FlutterMethodChannel和setMethodCallHandler接收方法调用并执行原生逻辑。但我是按照Flutter Module集成iOS文档进行集成的,这种场景下默认用OC创建AppDelegate.h和AppDelegate.m,没有AppDelegate.swift来处理通道和方法调用。

我尝试手动创建AppDelegate.swift,但出现import Flutter提示“无此模块”的错误。后来在SwiftUI测试应用中创建该文件,导入Flutter不再报错,但解包window?.rootViewController时触发Thread 1: Swift runtime failure: force unwrapped a nil value异常。

我的目标是调用controller.dismiss(animated:true)关闭Flutter页面,Swift不太熟练,希望找出问题所在。


Flutter侧代码

通知Swift的工具类

import 'package:flutter/services.dart';

class Utilities {
  static void closeFlutterView() {
    const MethodChannel channel =
        MethodChannel('com.example/close_flutter_view');
    channel.invokeMethod('close');
  }
}

Flutter页面代码

import 'dart:io';

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_module/utilities.dart';

@pragma('vm:entry-point')
void screenAEntrypoint() {
  runApp(const MaterialApp(
    home: ScreenA(),
  ));
}

class ScreenA extends StatefulWidget {
  const ScreenA({super.key});

  @override
  State<ScreenA> createState() => _ScreenAState();
}

class _ScreenAState extends State<ScreenA> {
  dynamic backButton = Platform.isIOS ? CupertinoIcons.back : Icons.arrow_back;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Screen A"),
        leading: IconButton(
            icon: Icon(backButton),
            color: Colors.redAccent,
            onPressed: () {
              Utilities.closeFlutterView();
            }),
      ),
      body: const Text("Screen A entry point"),
    );
  }
}

iOS侧代码

Swift主文件

import SwiftUI
import Flutter
import FlutterPluginRegistrant

class FlutterDependencies: ObservableObject {
    let flutterEngine = FlutterEngine(name: "my flutter engine");
    let flutterEngine2 = FlutterEngine(name: "my flutter engine 2");
  init(){
      flutterEngine.run(withEntrypoint: "screenAEntrypoint", libraryURI: "package:flutter_module/screen_a.dart");
      flutterEngine2.run(withEntrypoint: "screenBEntrypoint", libraryURI: "package:flutter_module/screen_b.dart");

      GeneratedPluginRegistrant.register(with: self.flutterEngine);
      GeneratedPluginRegistrant.register(with: self.flutterEngine2);
  }
}

@main
struct flutter_integration_testApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    @StateObject var flutterDependencies = FlutterDependencies()
    
      var body: some Scene {
          WindowGroup {
              ContentView().environmentObject(flutterDependencies)
          }
      }
}

Swift主视图

import SwiftUI
import CoreData
import Flutter


struct ContentView: View {
    @EnvironmentObject var flutterDependencies: FlutterDependencies
    var body: some View {
      Text("iOS View")

      Button("Show Flutter!") {
        showFlutter()
      }
        Button("Show Flutter2!") {
          showFlutter2()
        }

    }

  func showFlutter() {
      guard
        let windowScene = UIApplication.shared.connectedScenes
          .first(where: { $0.activationState == .foregroundActive && $0 is UIWindowScene }) as? UIWindowScene,
        let window = windowScene.windows.first(where: \.isKeyWindow),
        let rootViewController = window.rootViewController
      else { return }

      let flutterViewController = FlutterViewController(
        engine: flutterDependencies.flutterEngine,
        nibName: nil,
        bundle: nil)
      flutterViewController.modalPresentationStyle = .overCurrentContext
      flutterViewController.isViewOpaque = false

      rootViewController.present(flutterViewController, animated: true)
    }

    func showFlutter2() {
        guard
          let windowScene = UIApplication.shared.connectedScenes
            .first(where: { $0.activationState == .foregroundActive && $0 is UIWindowScene }) as? UIWindowScene,
          let window = windowScene.windows.first(where: \.isKeyWindow),
          let rootViewController = window.rootViewController
        else { return }

        let flutterViewController = FlutterViewController(
          engine: flutterDependencies.flutterEngine2,
          nibName: nil,
          bundle: nil)
      flutterViewController.modalPresentationStyle = .overCurrentContext
      flutterViewController.isViewOpaque = false
        rootViewController.present(flutterViewController, animated: true)
      }
}

AppDelegate.swift

import Foundation
import UIKit
import Flutter

class AppDelegate: NSObject, UIApplicationDelegate {
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        print("Your code here")
         
        let windowScene = UIApplication.shared.connectedScenes
            .first(where: { $0.activationState == .foregroundActive && $0 is UIWindowScene }) as? UIWindowScene;
        let window = windowScene?.windows.first(where: \.isKeyWindow);

        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController;
                
        let flutterChannel = FlutterMethodChannel(name: "com.example/close_flutter_view",
                                                          binaryMessenger: controller.binaryMessenger)
                
        flutterChannel.setMethodCallHandler({
                    
            (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
            
            guard call.method == "close" else {
                result(FlutterMethodNotImplemented)
                return
            }
            controller.dismiss(animated: true);
        })

        return true
    }
}

问题分析与解决步骤

1. 崩溃原因:AppDelegate中强制解包错误

在application(_:didFinishLaunchingWithOptions:)执行时,SwiftUI的根视图是ContentView而非FlutterViewController,直接强制解包window?.rootViewController as! FlutterViewController必然触发崩溃。

2. 正确实现:创建FlutterViewController时绑定通道

不要在AppDelegate中处理通道,而是在showFlutter()和showFlutter2()方法创建控制器时,直接为当前实例设置MethodChannel处理器。修改后的ContentView代码:

import SwiftUI
import CoreData
import Flutter


struct ContentView: View {
    @EnvironmentObject var flutterDependencies: FlutterDependencies
    var body: some View {
      Text("iOS View")

      Button("Show Flutter!") {
        showFlutter()
      }
        Button("Show Flutter2!") {
          showFlutter2()
        }

    }

  func showFlutter() {
      guard
        let windowScene = UIApplication.shared.connectedScenes
          .first(where: { $0.activationState == .foregroundActive && $0 is UIWindowScene }) as? UIWindowScene,
        let window = windowScene.windows.first(where: \.isKeyWindow),
        let rootViewController = window.rootViewController
      else { return }

      let flutterViewController = FlutterViewController(
        engine: flutterDependencies.flutterEngine,
        nibName: nil,
        bundle: nil)
      flutterViewController.modalPresentationStyle = .overCurrentContext
      flutterViewController.isViewOpaque = false

      // 绑定MethodChannel到当前控制器
      let channel = FlutterMethodChannel(name: "com.example/close_flutter_view", binaryMessenger: flutterViewController.binaryMessenger)
      channel.setMethodCallHandler { call, result in
          if call.method == "close" {
              flutterViewController.dismiss(animated: true)
              result(nil)
          } else {
              result(FlutterMethodNotImplemented)
          }
      }

      rootViewController.present(flutterViewController, animated: true)
    }

    func showFlutter2() {
        guard
          let windowScene = UIApplication.shared.connectedScenes
            .first(where: { $0.activationState == .foregroundActive && $0 is UIWindowScene }) as? UIWindowScene,
          let window = windowScene.windows.first(where: \.isKeyWindow),
          let rootViewController = window.rootViewController
        else { return }

        let flutterViewController = FlutterViewController(
          engine: flutterDependencies.flutterEngine2,
          nibName: nil,
          bundle: nil)
      flutterViewController.modalPresentationStyle = .overCurrentContext
      flutterViewController.isViewOpaque = false

      // 绑定MethodChannel到当前控制器
      let channel = FlutterMethodChannel(name: "com.example/close_flutter_view", binaryMessenger: flutterViewController.binaryMessenger)
      channel.setMethodCallHandler { call, result in
          if call.method == "close" {
              flutterViewController.dismiss(animated: true)
              result(nil)
          } else {
              result(FlutterMethodNotImplemented)
          }
      }

        rootViewController.present(flutterViewController, animated: true)
      }
}

3. 简化AppDelegate

移除无效的通道代码,修改后的AppDelegate.swift:

import Foundation
import UIKit
import Flutter

class AppDelegate: NSObject, UIApplicationDelegate {
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        print("App started")
        return true
    }
}

4. 解决import Flutter无模块问题

  • 确保Podfile已正确集成Flutter模块,执行过pod install
  • 在Xcode中打开.xcworkspace文件而非.xcodeproj
  • 清理Xcode缓存(Cmd+Shift+K)并重新构建

内容的提问来源于stack exchange,提问作者Vincenzo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 12:05:53