Skip to main content

使用平台视图在 Flutter 应用中托管原生 macOS 视图

平台视图允许您在Flutter应用中嵌入原生视图,以便您可以从Dart应用变换、裁剪和不透明度到原生视图。

例如,这允许您直接在Flutter应用中使用原生网页视图。

macOS使用混合合成,这意味着原生NSView被附加到视图层次结构中。

要在macOS上创建平台视图,请使用以下说明:

Dart端

#

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

在Dart widget文件中,进行类似于native_view_example.dart中所示的更改:

  1. 添加以下导入:

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

    dart
    Widget build(BuildContext context) {
      // 这在平台端用于注册视图。
      const String viewType = '<platform-view-type>';
      // 将参数传递给平台端。
      final Map<String, dynamic> creationParams = <String, dynamic>{};
    
      return AppKitView(
        viewType: viewType,
        layoutDirection: TextDirection.ltr,
        creationParams: creationParams,
        creationParamsCodec: const StandardMessageCodec(),
      );
    }

更多信息,请参见以下API文档:AppKitView

平台端

#

实现工厂和平台视图。NativeViewFactory创建平台视图,平台视图提供对NSView的引用。例如,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 视图可以在这里创建
    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

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) {
  // 这在平台端用于注册视图。
  const String viewType = '<platform-view-type>';
  // 将参数传递给平台端。
  final Map<String, dynamic> creationParams = <String, dynamic>{};

  switch (defaultTargetPlatform) {
    case TargetPlatform.android:
    // 在Android上返回widget。
    case TargetPlatform.iOS:
    // 在iOS上返回widget。
    case TargetPlatform.macOS:
    // 在macOS上返回widget。
    default:
      throw UnsupportedError('不支持的平台视图');
  }
}

性能

#

Flutter中的平台视图存在性能权衡。

例如,在一个典型的Flutter应用程序中,Flutter UI是在一个专用的光栅线程上组成的。这使得Flutter应用程序运行速度很快,因为此线程很少被阻塞。

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