跳到主內容

使用平臺檢視(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 的圖形效能產生負面影響。