SuperAPI AI 工具集

flutter-native-interop

编程与开发

查看项目

更新于 10-15 12:25

技能介绍

用途

本技能用于将 Flutter 应用与平台特定代码和原生功能集成,覆盖 Android、iOS 和 Web 环境。它会根据用户需求确定最优的互操作策略,并实现所需的 Dart 与原生代码绑定,同时遵循线程安全、WebAssembly(Wasm)兼容性以及现代构建钩子标准。

主要能力

技能通过决策逻辑选择集成路径,涵盖四类场景:

  • 场景 A:调用原生 C/C++ 代码。使用 dart:ffi 配合 package_ffi 模板和构建钩子。例外情况:若需访问 Flutter Plugin API 或需要在 iOS 上静态链接,则使用旧版 plugin_ffi 模板。
  • 场景 B:调用操作系统特定 API(Android 的 Java/Kotlin、iOS 的 Swift/Obj-C)。使用 Platform Channels(MethodChannel)或 pigeon 包进行类型安全代码生成。
  • 场景 C:将原生 UI 组件嵌入 Flutter widget 树。使用 Platform Views(Android 用 AndroidView / AndroidViewSurface,iOS 用 UiKitView)。
  • 场景 D:Web 集成与 JavaScript API。使用 package:web 和 dart:js_interop(Wasm 兼容),使用 HtmlElementView 嵌入 Web 内容。

在开始前,技能会要求用户明确目标平台以及需要集成的具体原生功能或 UI 组件。

使用方式

C/C++ 互操作(dart:ffi)

若选择场景 A,使用构建钩子(Flutter 3.38+)实现现代 FFI 架构。

生成包:

flutter create --template=package_ffi native_add

配置构建钩子(hook/build.dart)以编译原生代码:

import 'package:hooks/hooks.dart';
import 'package:native_toolchain_c/native_toolchain_c.dart';

void main(List<String> args) async {
  await build(args, (config, output) async {
    final builder = CBuilder.library(
      name: 'native_add',
      assetId: 'native_add/src/native_add.dart',
      sources: ['src/native_add.c'],
    );
    await builder.run(config: config, output: output);
  });
}

在 Dart 中绑定原生函数(lib/src/native_add.dart):

import 'dart:ffi';

@Native<Int32 Function(Int32, Int32)>()
external int sum(int a, int b);

Platform Channels(MethodChannel)

若选择场景 B,实现异步消息传递。

Dart 客户端实现:

import 'package:flutter/services.dart';

class NativeApi {
  static const platform = MethodChannel('com.example.app/channel');

  Future<String> getNativeData() async {
    try {
      final String result = await platform.invokeMethod('getData');
      return result;
    } on PlatformException catch (e) {
      return "Error: '${e.message}'.";
    }
  }
}

Android 宿主实现(Kotlin):

import androidx.annotation.NonNull
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity: FlutterActivity() {
  private val CHANNEL = "com.example.app/channel"

  override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
      if (call.method == "getData") {
        result.success("Data from Android")
      } else {
        result.notImplemented()
      }
    }
  }
}

iOS 宿主实现(Swift):

import Flutter
import UIKit

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
    let channel = FlutterMethodChannel(name: "com.example.app/channel", binaryMessenger: controller.binaryMessenger)
    
    channel.setMethodCallHandler({
      (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
      if call.method == "getData" {
        result("Data from iOS")
      } else {
        result(FlutterMethodNotImplemented)
      }
    })

    GeneratedPluginRegistrant.register(with: self)
    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

Platform Views

若选择场景 C,嵌入原生视图。

Dart 实现(iOS 示例):

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

Widget buildNativeView() {
  const String viewType = '<platform-view-type>';
  final Map<String, dynamic> creationParams = <String, dynamic>{};

  return UiKitView(
    viewType: viewType,
    layoutDirection: TextDirection.ltr,
    creationParams: creationParams,
    creationParamsCodec: const StandardMessageCodec(),
  );
}

iOS 工厂实现(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)
    }

    public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
          return FlutterStandardMessageCodec.sharedInstance()
    }
}

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

    init(frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?, binaryMessenger messenger: FlutterBinaryMessenger?) {
        _view = UIView()
        super.init()
        _view.backgroundColor = UIColor.blue
    }

    func view() -> UIView { return _view }
}

验证与修复:确保工厂已在 AppDelegate.swift 中通过 registrar.register(factory, withId: "<platform-view-type>") 注册。

Web 集成(Wasm 与 JS Interop)

若选择场景 D,实现 Wasm 兼容的 Web 集成。

JS Interop(Dart):

import 'dart:js_interop';
import 'package:web/web.dart' as web;

@JS('console.log')
external void log(JSAny? value);

void manipulateDOM() {
  final div = web.document.createElement('div') as web.HTMLDivElement;
  div.text = "Hello from Wasm-compatible Dart!";
  web.document.body?.append(div);
  log("DOM updated".toJS);
}

嵌入 HTML 元素:

import 'package:flutter/widgets.dart';
import 'package:web/web.dart' as web;

Widget buildVideoElement() {
  return HtmlElementView.fromTag('video', onElementCreated: (Object video) {
    final videoElement = video as web.HTMLVideoElement;
    videoElement.src = 'https://example.com/video.mp4';
    videoElement.style.width = '100%';
    videoElement.style.height = '100%';
  });
}

适用场景

  • 需要在 Flutter 应用中调用 C/C++ 原生库。
  • 需要调用 Android(Java/Kotlin)或 iOS(Swift/Obj-C)的操作系统特定 API。
  • 需要将原生 UI 组件嵌入 Flutter widget 树。
  • 需要进行 Web 集成或调用 JavaScript API,并希望保持 Wasm 兼容。

限制与约束

  • 线程安全:在平台侧调用面向 Flutter 的 channel 方法时,必须在平台的主/UI 线程上调用。若从后台线程跳转,Android 使用 Handler(Looper.getMainLooper()).post,iOS 使用 DispatchQueue.main.async。
  • WebAssembly 兼容性:不要使用 dart:html、dart:js 或 package:js。必须使用 package:web 和 dart:js_interop,以确保应用可编译为 Wasm。
  • Wasm iOS 限制:由于 WebKit 限制,编译为 Wasm 的 Flutter 目前无法在任何浏览器的 iOS 版本上运行。需确保保留回退到 JS 编译的能力。
  • FFI 命名:在为 Apple 平台实现 build.dart 钩子时,动态库在所有目标架构上必须使用一致的文件名(例如不要使用 lib_arm64.dylib)。
  • Platform View 性能:通过 Platform Views 处理 Android 上的 SurfaceView 存在问题,应尽可能避免。优先使用 TextureLayerHybridComposition 以获得更好的 Flutter 渲染性能。