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

Flutter与Swift平台通道:如何用Swift的View替代UIView显示?

问题描述

我通过Flutter Platform Channel和Swift实现原生视图显示,但遇到一个核心问题:Flutter要求视图必须是UIView类型,而我的Swift代码返回的是SwiftUI的View类型。目前我用UIHostingController做转换,虽然能运行,但视图周围出现大黑框,而且这种实现方式感觉不够规范,想咨询有没有其他替代方案。

当前实现代码

FluffViewFactory

// factory that returns FluffView
public class FluffViewFactory : NSObject, FlutterPlatformViewFactory {
    public func create(
        withFrame frame: CGRect,
        viewIdentifier viewId: Int64,
        arguments args: Any?
    ) -> FlutterPlatformView {
        return FluffView(frame, viewId: viewId, args: args)
    }
}

FluffView

// the view
public class FluffView: NSObject, FlutterPlatformView {

    let frame: CGRect
    let viewId: Int64

    init(_ frame: CGRect, viewId: Int64, args: Any?) {
        self.frame = frame
        self.viewId = viewId
    }

    let context: DeviceActivityReport.Context = .totalActivity
    let filter = DeviceActivityFilter(
        segment:.daily(
            during: DateInterval(
                start: Date().addingTimeInterval(-24 * 3600),
                end: Date()
            )
        )
    )

    // converts View into UIView for Flutter
    public func view() -> UIView {
        var child = UIHostingController(rootView: ContentView())
        var parent = UIViewController()
        child.view.translatesAutoresizingMaskIntoConstraints = false
        child.view.frame = parent.view.bounds
        NSLayoutConstraint.activate([
            child.view.topAnchor.constraint(equalTo: parent.view.topAnchor),
            child.view.leadingAnchor.constraint(equalTo: parent.view.leadingAnchor),
            child.view.bottomAnchor.constraint(equalTo: parent.view.bottomAnchor),
            child.view.trailingAnchor.constraint(equalTo: parent.view.trailingAnchor)
        ])
        // First, add the view of the child to the view of the parent
        parent.view.addSubview(child.view)
        // TODO: set background color here
        // parent.view.backgroundColor = BackgroundStyle()
        // Then, add the child to the parent
        parent.addChild(child)
        
        return parent.view
    }
}

AppDelegate

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
    
        @State var context: DeviceActivityReport.Context = .totalActivity
        @State var filter = DeviceActivityFilter(segment: .daily(during: thisWeek))

        // calls and registers the fluffview that will handle the conversion
        let viewFactory = FluffViewFactory()
        registrar(forPlugin: "Kitty")?.register(viewFactory, withId: "FluffView")

        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}
解决方案

1. 简化UIHostingController使用,移除冗余父ViewController

不需要额外创建父ViewController,直接返回UIHostingController的view即可,同时设置背景色透明解决大黑框问题:

public func view() -> UIView {
    let hostingController = UIHostingController(rootView: ContentView())
    // 设置背景透明,消除大黑框
    hostingController.view.backgroundColor = .clear
    // 应用Flutter传递的frame
    hostingController.view.frame = self.frame
    // 禁用自动调整mask,避免布局冲突
    hostingController.view.translatesAutoresizingMaskIntoConstraints = false
    return hostingController.view
}

2. 修正AppDelegate中的错误

@State是SwiftUI专属属性,不能在UIKit的AppDelegate中使用,需要移除这部分代码:

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let controller : FlutterViewController = window?.rootViewController as! FlutterViewController

        // 移除错误的@State变量
        let viewFactory = FluffViewFactory()
        registrar(forPlugin: "Kitty")?.register(viewFactory, withId: "FluffView")

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

3. 更规范的布局适配

如果需要更灵活的布局,可以让UIHostingController的view自动适配Flutter的布局约束,无需手动设置frame:

public func view() -> UIView {
    let hostingController = UIHostingController(rootView: ContentView())
    hostingController.view.backgroundColor = .clear
    hostingController.view.translatesAutoresizingMaskIntoConstraints = false
    
    // 让view填充Flutter提供的frame范围
    hostingController.view.widthAnchor.constraint(equalToConstant: frame.width).isActive = true
    hostingController.view.heightAnchor.constraint(equalToConstant: frame.height).isActive = true
    
    return hostingController.view
}

核心总结

  • 大黑框的根源是UIHostingController或父ViewController的默认背景色为黑色,设置为透明即可解决
  • 无需额外创建父ViewController,直接返回UIHostingController的view更简洁规范
  • 禁止在UIKit类中使用SwiftUI的@State属性,属于错误用法

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 22:15:08