在 Flutter 应用中使用集成平台视图托管您的原生 iOS 视图

集成平台视图 (后称为平台视图) 允许将原生视图嵌入到 Flutter 应用中,所以您可以通过 Dart 将变换、裁剪和不透明度等效果应用到原生视图。

例如,这使您可以通过使用平台视图直接在 Flutter 应用内部使用 Android 和 iOS SDK 中的 Google Maps。

iOS 只支持混合集成模式,这意味着原生的 UIView 会被加入视图层级中。

要在 iOS 中创建平台视图,需要如下步骤:

在 Dart 端

在 Dart 端,创建一个 Widget 并添加如下的实现,具体如下:

在 Dart 文件中,例如 native_view_example.dart,请执行下列操作:

  1. 添加如下导入代码:

    import 'package:flutter/widgets.dart';
    
    class IOSCompositionWidget extends StatelessWidget {
      const IOSCompositionWidget({super.key});
    
      @override
      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 UiKitView(
          viewType: viewType,
          layoutDirection: TextDirection.ltr,
          creationParams: creationParams,
          creationParamsCodec: const StandardMessageCodec(),
        );
      }
    }
    
    class TogetherWidget extends StatelessWidget {
      const TogetherWidget({super.key});
    
      @override
      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.
          default:
            throw UnsupportedError('Unsupported platform view');
        }
      }
    }
  2. 实现 build() 方法:

    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 UiKitView(
        viewType: viewType,
        layoutDirection: TextDirection.ltr,
        creationParams: creationParams,
        creationParamsCodec: const StandardMessageCodec(),
      );
    }

更多信息,请查看 API 文档:UIKitView

在平台端

在平台端,您可以使用 Swift 或是 Objective-C:

实现工厂和平台视图。 FLNativeViewFactory 创建一个关联了 UIView 的平台视图。举个例子,FLNativeView.swift

import Flutter
import UIKit

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

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

    func create(
        withFrame frame: CGRect,
        viewIdentifier viewId: Int64,
        arguments args: Any?
    ) -> FlutterPlatformView {
        return FLNativeView(
            frame: frame,
            viewIdentifier: viewId,
            arguments: args,
            binaryMessenger: messenger)
    }
}

class FLNativeView: NSObject, FlutterPlatformView {
    private var _view: UIView

    init(
        frame: CGRect,
        viewIdentifier viewId: Int64,
        arguments args: Any?,
        binaryMessenger messenger: FlutterBinaryMessenger?
    ) {
        _view = UIView()
        super.init()
        // iOS views can be created here
        createNativeView(view: _view)
    }

    func view() -> UIView {
        return _view
    }

    func createNativeView(view _view: UIView){
        _view.backgroundColor = UIColor.blue
        let nativeLabel = UILabel()
        nativeLabel.text = "Native text from iOS"
        nativeLabel.textColor = UIColor.white
        nativeLabel.textAlignment = .center
        nativeLabel.frame = CGRect(x: 0, y: 0, width: 180, height: 48.0)
        _view.addSubview(nativeLabel)
    }
}

最后,注册这个平台视图。这一步可以在应用中,也可以在插件中。

要在应用中进行注册,修改应用中的 AppDelegate.swift:

import Flutter
import UIKit

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]?
    ) -> Bool {
        GeneratedPluginRegistrant.register(with: self)

        weak var registrar = self.registrar(forPlugin: "plugin-name")

        let factory = FLNativeViewFactory(messenger: registrar!.messenger())
        self.registrar(forPlugin: "<plugin-name>")!.register(
            factory,
            withId: "<platform-view-type>")
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}

要在插件中进行注册,修改插件的主类 (例如 FLPlugin.swift):

import Flutter
import UIKit

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

在工厂类和平台视图的文件头部添加以下内容。用 FLNativeView.h 举例:

#import <Flutter/Flutter.h>

@interface FLNativeViewFactory : NSObject <FlutterPlatformViewFactory>
- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
@end

@interface FLNativeView : NSObject <FlutterPlatformView>

- (instancetype)initWithFrame:(CGRect)frame
               viewIdentifier:(int64_t)viewId
                    arguments:(id _Nullable)args
              binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;

- (UIView*)view;
@end

实现工厂类和平台视图。 FLNativeViewFactory 创建一个关联了 UIView 的平台视图。用 FLNativeView.m 举例:

#import "FLNativeView.h"

@implementation FLNativeViewFactory {
  NSObject<FlutterBinaryMessenger>* _messenger;
}

- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
  self = [super init];
  if (self) {
    _messenger = messenger;
  }
  return self;
}

- (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
                                   viewIdentifier:(int64_t)viewId
                                        arguments:(id _Nullable)args {
  return [[FLNativeView alloc] initWithFrame:frame
                              viewIdentifier:viewId
                                   arguments:args
                             binaryMessenger:_messenger];
}

@end

@implementation FLNativeView {
   UIView *_view;
}

- (instancetype)initWithFrame:(CGRect)frame
               viewIdentifier:(int64_t)viewId
                    arguments:(id _Nullable)args
              binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
  if (self = [super init]) {
    _view = [[UIView alloc] init];
  }
  return self;
}

- (UIView*)view {
  return _view;
}

@end

最后,注册这个平台视图。这一步可以在应用中,也可以在插件中。

要在应用中进行注册,修改应用中的 AppDelegate.m

#import "AppDelegate.h"
#import "FLNativeView.h"
#import "GeneratedPluginRegistrant.h"

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];

   NSObject<FlutterPluginRegistrar>* registrar =
      [self registrarForPlugin:@"plugin-name"];

  FLNativeViewFactory* factory =
      [[FLNativeViewFactory alloc] initWithMessenger:registrar.messenger];

  [[self registrarForPlugin:@"<plugin-name>"] registerViewFactory:factory
                                                          withId:@"<platform-view-type>"];
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

@end

要在插件中进行注册,修改插件主文件 (例如 FLPlugin.m):

#import <Flutter/Flutter.h>
#import "FLNativeView.h"

@interface FLPlugin : NSObject<FlutterPlugin>
@end

@implementation FLPlugin

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
  FLNativeViewFactory* factory =
      [[FLNativeViewFactory alloc] initWithMessenger:registrar.messenger];
  [registrar registerViewFactory:factory withId:@"<platform-view-type>"];
}

@end

更多信息,请查看 API 文档:

整合起来

在 Dart 中实现 build() 方法时,您可以使用 defaultTargetPlatform 来检测当前的平台,并且决定如何使用这个 widget:

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.
    default:
      throw UnsupportedError('Unsupported platform view');
  }
}

性能

在 Flutter 中使用平台视图时,性能会有所取舍。

例如,在典型的 Flutter 应用中,Flutter 的 UI 是专门在 raster 线程上合成的。由于平台的主线程很少被阻塞,因此 Flutter 应用程序可以快速运行。

使用混合集成模式渲染平台视图时, Flutter UI 由平台线程完成,与其他线程一起竞争,例如:处理系统或插件消息等任务。

在 Android 10 之前,混合集成模式将每个 Flutter 帧从显存中复制到主内存中,然后再将其复制回 GPU 纹理中。在 Android 10 或更高版本中,显存会被复制两次。由于每帧都会进行一次复制,因此可能会影响整个 Flutter UI 的性能。

另一方面,虚拟显示模式使平台视图的每个像素流经附加的中间图形缓冲区,这会浪费显存和绘图性能。

对于复杂的情况,可以使用一些技巧来缓解这些问题。

例如,当 Dart 中发生动画时,您可以使用占位符纹理。换句话说,如果在渲染平台视图时动画很慢,请考虑对原生视图进行截图,并将其渲染为纹理。

更多信息,请查看下面链接: