跳转至正文

在 Flutter 应用中通过平台视图嵌入原生 macOS 视图

了解如何在 Flutter 应用中通过平台视图嵌入原生 macOS 视图。

平台视图让你能在 Flutter 应用中嵌入原生视图,从而从 Dart 对原生视图应用变换、裁剪和透明度。

例如,你可以直接在 Flutter 应用中使用原生 Web 视图。

macOS 使用混合合成(Hybrid composition),即原生 NSView 会追加到视图层级中。

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

Dart 端

#

在 Dart 端,创建一个 widget 并添加 build 实现,如下列步骤所示:

在 Dart widget 文件中,进行与 native_view_example.dart 中类似的修改:

  1. Add the following imports:

    1. 添加以下 import:
    dart
    import 'package:flutter/foundation.dart';
    import 'package:flutter/services.dart';
    
  2. Implement a build() method:

    1. 实现 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 检测平台并决定使用哪个 widget:

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 在专用光栅线程上合成,该线程很少阻塞,因此应用能保持较快响应。

使用混合合成渲染平台视图时,Flutter UI 仍由专用光栅线程合成,但平台视图在平台线程上执行图形操作。为光栅化合并内容,Flutter 会在光栅线程与平台线程之间同步;因此平台线程上的缓慢或阻塞操作会影响 Flutter 图形性能。