跳到主内容

使用平台视图(Platform Views)在 Flutter 应用中托管原生 macOS 视图

了解如何使用平台视图(Platform Views)在 Flutter 应用中托管原生 macOS 视图。

平台视图允许你在 Flutter 应用中嵌入原生视图,这样你就可以从 Dart 应用转换、裁剪和调整原生视图的不透明度。

这使你能够直接在 Flutter 应用中使用原生 web 视图等组件。

macOS 使用混合组合(Hybrid composition),这意味着原生 NSView 被添加到视图层级结构中。

若要在 macOS 上创建平台视图,请按照以下说明操作:

在 Dart 侧

#

在 Dart 端,创建一个 Widget 并添加构建实现,如下所示:

在 Dart 小部件文件中,进行类似于 native_view_example.dart 中的更改:

  1. 添加以下导入:

    dart
    import 'package:flutter/foundation.dart';
    import 'package:flutter/services.dart';
    
  2. 实现 build() 方法

    dart
    Widget build(BuildContext context) {
      // This is used in the platform side to register the view.
      const String viewType = '<platform-view-type>';
      // Pass parameters to the platform side.
      final Map<String, dynamic> creationParams = <String, dynamic>{};
    
      return AppKitView(
        viewType: viewType,
        layoutDirection: TextDirection.ltr,
        creationParams: creationParams,
        creationParamsCodec: const StandardMessageCodec(),
      );
    }
    

欲了解更多信息,请查阅 AppKitView API 文档。

在平台侧

#

实现工厂和平台视图。NativeViewFactory 用于创建平台视图,而平台视图提供对 NSView 的引用。例如:NativeView.swift

NativeView.swift
swift
import Cocoa
import FlutterMacOS

class NativeViewFactory: NSObject, FlutterPlatformViewFactory {
  private var messenger: FlutterBinaryMessenger

  init(messenger: FlutterBinaryMessenger) {
    self.messenger = messenger
    super.init()
  }

  func create(
    withViewIdentifier viewId: Int64,
    arguments args: Any?
  ) -> NSView {
    return NativeView(
      viewIdentifier: viewId,
      arguments: args,
      binaryMessenger: messenger)
  }

  /// Implementing this method is only necessary when
  /// the `arguments` in `createWithFrame` is not `nil`.
  public func createArgsCodec() -> (FlutterMessageCodec & NSObjectProtocol)? {
    return FlutterStandardMessageCodec.sharedInstance()
  }
}

class NativeView: NSView {

  init(
    viewIdentifier viewId: Int64,
    arguments args: Any?,
    binaryMessenger messenger: FlutterBinaryMessenger?
  ) {
    super.init(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
    wantsLayer = true
    layer?.backgroundColor = NSColor.systemBlue.cgColor
    // macOS views can be created here
    createNativeView(view: self)
  }

    required init?(coder nsCoder: NSCoder) {
        super.init(coder: nsCoder)
    }

  func createNativeView(view _view: NSView) {
    let nativeLabel = NSTextField()
    nativeLabel.frame = CGRect(x: 0, y: 0, width: 180, height: 48.0)
    nativeLabel.stringValue = "Native text from macOS"
    nativeLabel.textColor = NSColor.black
    nativeLabel.font = NSFont.systemFont(ofSize: 14)
    nativeLabel.isBezeled = false
    nativeLabel.focusRingType = .none
    nativeLabel.isEditable = true
    nativeLabel.sizeToFit()
    _view.addSubview(nativeLabel)
  }
}

最后,注册平台视图。这可以在应用或插件中完成。

对于应用注册,请修改应用的 MainFlutterWindow.swift

MainFlutterWindow.swift
swift
import Cocoa
import FlutterMacOS

class MainFlutterWindow: NSWindow {
  override func awakeFromNib() {
    // ...

    let registrar = flutterViewController.registrar(forPlugin: "plugin-name")
    let factory = NativeViewFactory(messenger: registrar.messenger)
    registrar.register(
      factory,
      withId: "<platform-view-type>")
  }
}

对于插件注册,请修改插件的主文件

Plugin.swift
swift
import Cocoa
import FlutterMacOS

public class Plugin: NSObject, FlutterPlugin {
  public static func register(with registrar: FlutterPluginRegistrar) {
    let factory = NativeViewFactory(messenger: registrar.messenger)
    registrar.register(factory, withId: "<platform-view-type>")
  }
}

欲了解更多信息,请查看以下 API 文档:

整合起来

#

在 Dart 中实现 build() 方法时,你可以使用 defaultTargetPlatform 来检测平台,并决定使用哪个小部件。

dart
Widget build(BuildContext context) {
  // This is used in the platform side to register the view.
  const String viewType = '<platform-view-type>';
  // Pass parameters to the platform side.
  final Map<String, dynamic> creationParams = <String, dynamic>{};

  switch (defaultTargetPlatform) {
    case TargetPlatform.android:
    // return widget on Android.
    case TargetPlatform.iOS:
    // return widget on iOS.
    case TargetPlatform.macOS:
    // return widget on macOS.
    default:
      throw UnsupportedError('Unsupported platform view');
  }
}

性能

#

Flutter 中的平台视图会带来性能上的权衡。

例如,在典型的 Flutter 应用中,Flutter UI 是在专门的栅格线程(raster thread)上合成的。这使得 Flutter 应用运行迅速,因为该线程很少被阻塞。

当使用混合组合渲染平台视图时,Flutter UI 仍会从专门的栅格线程进行合成,但平台视图会在平台线程上执行图形操作。为了对组合后的内容进行栅格化,Flutter 会在其栅格线程和平台线程之间进行同步。因此,平台线程上的任何缓慢或阻塞操作都可能对 Flutter 的图形性能产生负面影响。