《Flutter全栈开发实战指南:从零到高级》- 22 -插件开发与原生交互

引言

很多人把Flutter插件开发想得太复杂,其实它的本质就是把Dart语言转换成Java/Swift能识别的语言,再把原生平台的回应翻译回Dart。今天,我们将Flutter与原生平台交互的工作机制彻底搞明白。

为什么需要原生交互?

在正式开始之前,我们先思考一个问题:Flutter作为一个跨平台框架,它的跨平台能力边界在哪里?

实际上,Flutter的跨平台能力主要体现在UI渲染层。Dart代码编译后可以在Android和iOS上运行,Flutter引擎负责将Widget树渲染成对应的平台视图。但当我们想要访问平台某些特定功能时,比如:调用系统相机/相册、使用蓝牙或NFC等功能,这些情况下,我们就需要桥接原生代码。

一、Platform Channel:Flutter与原生交互的桥梁

1.1 核心架构

很多人以为Platform Channel就是一个简单的管道,其实它是三层协议栈Dart层引擎层平台层;让我们先通过一张架构图来加深理解Platform Channel的整体结构:

graph TD A[Flutter Dart代码] -->|MethodChannel| B[Flutter Framework层] B -->|C++编解码| C[Flutter Engine引擎] C -->|平台消息路由| D[Android平台] C -->|平台消息路由| E[iOS平台] D -->|JNI/Java调用| F[Native Android代码] E -->|Objective-C/Swift调用| G[Native iOS代码] subgraph "Platform Channel通信流" direction LR H[Dart发起调用] --> I[消息序列化] --> J[通过BinaryMessenger传递] --> K[平台侧反序列化] --> L[执行原生代码] --> M[返回结果序列化] --> N[Dart侧接收结果] end

备注

  • MethodChannel:最常用的通道,用于方法调用
  • EventChannel:用于从原生到Dart的事件流
  • BasicMessageChannel:用于简单的消息传递,较少使用
  • BinaryMessenger:底层消息传递机制,所有Channel都基于它

1.2 交互原理

具体步骤

  1. Dart代码:你把需求用Dart语言写好,MethodChannel会将其转换成二进制数据格式
  2. Engine:通过Flutter引擎传递到对应平台
  3. Platform:Android/iOS接收到数据后进行拆解并理解其内容
  4. Native:Android/iOS端完成需求
  5. 返回结果:反向再来一遍上述流程

我们简单画个流程图,方便理解:

sequenceDiagram participant D as Dart代码 participant C as MethodChannel participant B as BinaryMessenger participant E as Flutter引擎 participant P as 平台层(Android/iOS) participant N as Native代码 D->>C: invokeMethod("拍照", 参数) Note over C: 步骤1:打包
把方法名+参数编码成二进制 C->>B: 发送二进制消息 B->>E: 跨边界传输 Note over E: 步骤2:数据传递
通过JNI/FFI传递 E->>P: 分发到对应平台 P->>N: 调用原生方法 Note over N: 步骤3:本地执行
Java/Swift实际处理 N-->>P: 返回结果 P-->>E: 编码结果 E-->>B: 返回二进制 B-->>C: 接收响应 Note over C: 步骤4:解析
解码二进制为Dart对象 C-->>D: Future完成,返回结果

代码层面的流程

dart 复制代码
// Dart侧发起调用
Future<String> result = channel.invokeMethod('getBatteryLevel');

// 这行代码背后发生了什么?
// 1. invokeMethod将方法名和参数序列化为二进制消息
// 2. 通过BinaryMessenger.send()发送
// 3. 引擎收到消息,路由到对应平台
// 4. 平台侧处理消息,执行原生代码
// 5. 结果序列化后返回
// 6. Dart侧反序列化得到结果

1.3 数据编解码

如何跨语言传递复杂数据?不同编程语言有不同的数据类型系统,如何让Dart的List<Map<String, dynamic>>在Java和Swift中也能被理解?

Flutter使用标准化的消息编解码器:

  • StandardMessageCodec:默认编解码器,支持基础类型和列表、字典
  • JSONMessageCodec:使用JSON格式编码
  • StringCodec:只处理字符串
  • BinaryCodec:原始二进制数据

编码规则表

Dart类型 Java/Kotlin类型 Swift/OC类型
null null nil
bool java.lang.Boolean NSNumber(numberWithBool:)
int java.lang.Integer NSNumber(numberWithInt:)
double java.lang.Double NSNumber(numberWithDouble:)
String java.lang.String NSString
Uint8List byte[] FlutterStandardTypedData typedDataWithBytes:
Int32List int[] FlutterStandardTypedData typedDataWithInt32:
Int64List long[] FlutterStandardTypedData typedDataWithInt64:
Float64List double[] FlutterStandardTypedData typedDataWithFloat64:
List java.util.ArrayList NSArray
Map java.util.HashMap NSDictionary

主要限制

  • 不支持自定义类的直接传递
  • 复杂对象需要先转换为Map/List等基本类型组合
  • 大数据量传输需要考虑性能问题

二、与Android原生交互

2.1 以获取电池电量为例

让我们从一个最简单的例子开始,了解完整的通信流程。

Dart侧代码
dart 复制代码
import 'package:flutter/services.dart';

class BatteryPlugin {
  // 1. 创建MethodChannel实例
  // 约定好通道名称(必须与原生侧完全一致)
  static const MethodChannel _channel = 
      MethodChannel('com.xxxx/battery');
  
  // 2. 公开的Dart方法
  static Future<int> getBatteryLevel() async {
    try {
      // 3. 调用原生方法
      // invokeMethod的第一个参数是方法名(必须与原生侧对应)
      final int level = await _channel.invokeMethod('getBatteryLevel');
      return level;
    } on PlatformException catch (e) {
      // 4. 异常处理
      print("获取电量失败: ${e.message}");
      return -1;
    }
  }
}

// 使用
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: ElevatedButton(
            child: Text('获取电量'),
            onPressed: () async {
              // 调用插件方法
              int level = await BatteryPlugin.getBatteryLevel();
              if (level != -1) {
                print('当前电量: $level%');
              }
            },
          ),
        ),
      ),
    );
  }
}
Android侧代码(Kotlin)
kotlin 复制代码
package com.example.myapp

import android.content.Context
import android.content.ContextWrapper
import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity : FlutterActivity() {
    // 1. 定义通道名称(必须与Dart侧一致)
    companion object {
        private const val CHANNEL = "com.xxxx/battery"
    }
    
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        
        // 2. 创建MethodChannel
        MethodChannel(
            flutterEngine.dartExecutor.binaryMessenger,
            CHANNEL
        ).setMethodCallHandler { call, result ->
            // 3. 处理方法调用
            when (call.method) {
                "getBatteryLevel" -> {
                    // 4. 执行实际的原生代码
                    val batteryLevel = getBatteryLevel()
                    
                    if (batteryLevel != -1) {
                        // 5. 返回成功结果
                        result.success(batteryLevel)
                    } else {
                        // 6. 返回错误结果
                        result.error(
                            "UNAVAILABLE",
                            "无法获取电量信息",
                            null
                        )
                    }
                }
                else -> {
                    // 7. 处理未实现的方法
                    result.notImplemented()
                }
            }
        }
    }
    
    // 获取电量的实际实现
    private fun getBatteryLevel(): Int {
        return if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
            val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
            batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
        } else {
            // 旧版本Android的兼容处理
            val intent = ContextWrapper(applicationContext)
                .registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))
            val level = intent?.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) ?: -1
            val scale = intent?.getIntExtra(BatteryManager.EXTRA_SCALE, -1) ?: -1
            
            if (level == -1 || scale == -1) {
                -1
            } else {
                (level * 100 / scale.toFloat()).toInt()
            }
        }
    }
}

2.2 复杂参数传递:从Dart传递数据到Android

实际开发中,我们经常需要传递复杂参数。看看如何传递一个用户对象:

dart 复制代码
// Dart侧
Future<void> saveUser(User user) async {
  try {
    // 将User对象转换为Map
    Map<String, dynamic> userMap = {
      'name': user.name,
      'age': user.age,
      'email': user.email,
      'isVip': user.isVip,
    };
    
    await _channel.invokeMethod('saveUser', userMap);
  } on PlatformException catch (e) {
    print('保存用户失败: ${e.message}');
  }
}
kotlin 复制代码
// Android侧(Kotlin)
.setMethodCallHandler { call, result ->
    when (call.method) {
        "saveUser" -> {
            // 获取参数
            val arguments = call.arguments as? Map<*, *>
            
            if (arguments != null) {
                val name = arguments["name"] as? String
                val age = arguments["age"] as? Int
                val email = arguments["email"] as? String
                val isVip = arguments["isVip"] as? Boolean
                
                // 保存
                val success = saveUserToStorage(name, age, email, isVip)
                
                if (success) {
                    result.success(null) 
                } else {
                    result.error("SAVE_FAILED", "保存用户失败", null)
                }
            } else {
                result.error("INVALID_ARGUMENTS", "参数格式错误", null)
            }
        }
    }
}

2.3 异步操作处理:长时间运行的任务

有些原生操作可能耗时较长,比如下载文件、处理图像等,我们需要正确处理异步:

kotlin 复制代码
// Android侧:使用协程处理异步任务
.setMethodCallHandler { call, result ->
    when (call.method) {
        "processImage" -> {
            val imagePath = call.arguments as? String
            
            // 启动协程处理耗时操作
            CoroutineScope(Dispatchers.IO).launch {
                try {
                    val processedPath = processImageHeavy(imagePath)
                    
                    // 切回主线程返回结果
                    withContext(Dispatchers.Main) {
                        result.success(processedPath)
                    }
                } catch (e: Exception) {
                    withContext(Dispatchers.Main) {
                        result.error("PROCESS_FAILED", e.message, null)
                    }
                }
            }
            
            // 注意:这里没有立即调用result方法,异步操作完成后才调用
        }
    }
}
dart 复制代码
// Dart侧:添加超时处理
Future<String> processImage(String imagePath) async {
  try {
    // 设置超时时间
    final result = await _channel.invokeMethod('processImage', imagePath)
        .timeout(Duration(seconds: 10));
    
    return result as String;
  } on TimeoutException {
    print('图片处理超时');
    throw Exception('处理超时');
  } on PlatformException catch (e) {
    print('图片处理失败: ${e.message}');
    throw Exception(e.message);
  }
}

2.4 以Toast消息插件为例

让我们创建一个完整的Android Toast插件:

dart 复制代码
// flutter_toast.dart
import 'package:flutter/services.dart';

class FlutterToast {
  static const MethodChannel _channel = 
      MethodChannel('com.xxxx/toast');
  
  // 显示短时Toast
  static Future<void> showShort(String message) async {
    try {
      await _channel.invokeMethod('showShortToast', message);
    } on PlatformException catch (e) {
      print('显示Toast失败: ${e.message}');
    }
  }
  
  // 显示长时Toast
  static Future<void> showLong(String message) async {
    try {
      await _channel.invokeMethod('showLongToast', message);
    } on PlatformException catch (e) {
      print('显示Toast失败: ${e.message}');
    }
  }
  
  // 显示自定义时长Toast
  static Future<void> showCustom(String message, int duration) async {
    try {
      await _channel.invokeMethod('showCustomToast', {
        'message': message,
        'duration': duration,
      });
    } on PlatformException catch (e) {
      print('显示Toast失败: ${e.message}');
    }
  }
}
kotlin 复制代码
// Android侧 - MainActivity.kt
package com.example.myapp

import android.widget.Toast
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel

class MainActivity : FlutterActivity() {
    companion object {
        private const val CHANNEL = "com.xxxx/toast"
    }
    
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)
        
        MethodChannel(
            flutterEngine.dartExecutor.binaryMessenger,
            CHANNEL
        ).setMethodCallHandler { call, result ->
            // 主线程执行UI操作
            runOnUiThread {
                when (call.method) {
                    "showShortToast" -> {
                        val message = call.arguments as? String
                        Toast.makeText(
                            this,
                            message ?: "",
                            Toast.LENGTH_SHORT
                        ).show()
                        result.success(null)
                    }
                    
                    "showLongToast" -> {
                        val message = call.arguments as? String
                        Toast.makeText(
                            this,
                            message ?: "",
                            Toast.LENGTH_LONG
                        ).show()
                        result.success(null)
                    }
                    
                    "showCustomToast" -> {
                        val arguments = call.arguments as? Map<*, *>
                        val message = arguments?.get("message") as? String
                        val duration = arguments?.get("duration") as? Int ?: 2000
                        
                        // 自定义Toast
                        val toast = Toast.makeText(this, message ?: "", Toast.LENGTH_SHORT)
                        
                        // Android的Toast不支持直接设置毫秒数,这是一个简单的案例明白如何用即可
                        // 实际项目中需要自定义View
                        toast.duration = if (duration > 2000) Toast.LENGTH_LONG else Toast.LENGTH_SHORT
                        toast.show()
                        
                        result.success(null)
                    }
                    
                    else -> result.notImplemented()
                }
            }
        }
    }
}

三、与iOS原生交互

3.1 iOS与Android的差异

在开始iOS开发前,需要了解一些iOS与Android两端的差异:

维度 Android (Kotlin/Java) iOS (Swift/OC)
项目结构 MainActivity.kt AppDelegate.swift
管道注册 configureFlutterEngine application didFinishLaunchingWithOptions
内存管理 JVM自动垃圾回收 ARC自动引用计数
平台特性 Context对象 UIViewController

3.2 以获取iOS设备信息为例

iOS侧代码(Swift)
swift 复制代码
// AppDelegate.swift
import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        // 1. 获取FlutterViewController
        let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
        
        // 2. 创建MethodChannel
        let batteryChannel = FlutterMethodChannel(
            name: "com.xxxx/battery",
            binaryMessenger: controller.binaryMessenger
        )
        
        // 3. 设置方法处理器
        batteryChannel.setMethodCallHandler { [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) in
            // 4. 根据方法名分派处理
            if call.method == "getBatteryLevel" {
                self?.receiveBatteryLevel(result: result)
            } else if call.method == "getDeviceInfo" {
                self?.getDeviceInfo(result: result)
            } else {
                // 5. 未实现的方法
                result(FlutterMethodNotImplemented)
            }
        }
        
        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
    
    // 获取电池电量
    private func receiveBatteryLevel(result: FlutterResult) {
        // 6. 获取设备对象
        let device = UIDevice.current
        
        // 7. 开启电池监控
        device.isBatteryMonitoringEnabled = true
        
        // 8. 获取电池状态
        if device.batteryState == .unknown {
            // 9. 无法获取电量
            result(FlutterError(
                code: "UNAVAILABLE",
                message: "电池信息不可用",
                details: nil
            ))
        } else {
            // 10. 计算电量百分比
            let batteryLevel = Int(device.batteryLevel * 100)
            result(batteryLevel)
        }
    }
    
    // 获取设备信息
    private func getDeviceInfo(result: FlutterResult) {
        let device = UIDevice.current
        
        // 11. 设备信息字典
        let deviceInfo: [String: Any] = [
            "name": device.name,
            "model": device.model,
            "systemName": device.systemName,
            "systemVersion": device.systemVersion,
            "identifierForVendor": device.identifierForVendor?.uuidString ?? "",
            "batteryLevel": device.batteryLevel,
            "batteryState": "\(device.batteryState)",
            "isBatteryMonitoringEnabled": device.isBatteryMonitoringEnabled,
            "proximityState": device.proximityState,
            "isProximityMonitoringEnabled": device.isProximityMonitoringEnabled,
            "orientation": "\(device.orientation)",
            "isMultitaskingSupported": device.isMultitaskingSupported,
            "userInterfaceIdiom": "\(device.userInterfaceIdiom)"
        ]
        
        // 12. 返回结果
        result(deviceInfo)
    }
}
Dart侧代码
dart 复制代码
// ios_device_info.dart
import 'package:flutter/services.dart';

class IOSDeviceInfo {
  static const MethodChannel _channel = 
      MethodChannel('com.xxxx/battery');
  
  // 获取iOS设备信息
  static Future<Map<String, dynamic>> getDeviceInfo() async {
    try {
      final Map<dynamic, dynamic> info = 
          await _channel.invokeMethod('getDeviceInfo');
      
      // 转换为明确的类型
      return Map<String, dynamic>.from(info);
    } on PlatformException catch (e) {
      print('获取设备信息失败: ${e.message}');
      return {};
    }
  }
  
  // 获取电池电量
  static Future<int> getBatteryLevel() async {
    try {
      final int level = await _channel.invokeMethod('getBatteryLevel');
      return level;
    } on PlatformException catch (e) {
      print('获取电量失败: ${e.message}');
      return -1;
    }
  }
}

3.3 iOS特有功能:使用Face ID/Touch ID

iOS的生物识别是一个很好的平台特定功能示例:

swift 复制代码
// AppDelegate.swift 中添加
import LocalAuthentication

extension AppDelegate {
    // 生物识别验证
    private func authenticateWithBiometrics(result: @escaping FlutterResult) {
        let context = LAContext()
        var error: NSError?
        
        // 1. 检查设备是否支持生物识别
        if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
            let reason = "请进行生物识别以继续"
            
            // 2. 执行验证
            context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, 
                                  localizedReason: reason) { success, authenticationError in
                // 3. 主线程返回结果
                DispatchQueue.main.async {
                    if success {
                        // 4. 验证成功
                        result(["success": true, "message": "验证成功"])
                    } else {
                        // 5. 验证失败
                        let errorMessage: String
                        
                        if let laError = authenticationError as? LAError {
                            switch laError.code {
                            case .userCancel:
                                errorMessage = "用户取消"
                            case .userFallback:
                                errorMessage = "用户选择使用密码"
                            case .authenticationFailed:
                                errorMessage = "验证失败"
                            case .passcodeNotSet:
                                errorMessage = "未设置密码"
                            case .systemCancel:
                                errorMessage = "系统取消"
                            case .biometryNotAvailable:
                                errorMessage = "生物识别不可用"
                            case .biometryNotEnrolled:
                                errorMessage = "未录入生物信息"
                            case .biometryLockout:
                                errorMessage = "生物识别被锁定"
                            default:
                                errorMessage = "未知错误"
                            }
                        } else {
                            errorMessage = "未知错误"
                        }
                        
                        result(FlutterError(
                            code: "AUTH_FAILED",
                            message: errorMessage,
                            details: nil
                        ))
                    }
                }
            }
        } else {
            // 6. 设备不支持生物识别
            let errorMessage = error?.localizedDescription ?? "设备不支持生物识别"
            result(FlutterError(
                code: "NOT_SUPPORTED",
                message: errorMessage,
                details: nil
            ))
        }
    }
    
    // 在setMethodCallHandler中添加
    // batteryChannel.setMethodCallHandler { [weak self] (call, result) in
    //     if call.method == "authenticateWithBiometrics" {
    //         self?.authenticateWithBiometrics(result: result)
    //     }
    // }
}
dart 复制代码
// dart侧
class BiometricAuth {
  static const MethodChannel _channel = 
      MethodChannel('com.xxxx/battery');
  
  static Future<Map<String, dynamic>> authenticate() async {
    try {
      final result = await _channel.invokeMethod('authenticateWithBiometrics');
      return Map<String, dynamic>.from(result);
    } on PlatformException catch (e) {
      return {
        'success': false,
        'message': e.message ?? '验证失败',
        'errorCode': e.code,
      };
    }
  }
}

3.4 iOS与Android的兼容性处理

在实际插件开发中,我们经常需要处理平台之间差异:

dart 复制代码
// 工具类
class PlatformUtils {
  static bool get isAndroid => Platform.isAndroid;
  static bool get isIOS => Platform.isIOS;
  static bool get isMobile => Platform.isAndroid || Platform.isIOS;
  
  // 平台特定的提示
  static Future<void> showPlatformToast(String message) async {
    if (isAndroid) {
      await AndroidToast.showShort(message);
    } else if (isIOS) {
      // iOS没有Toast,使用其他方式
      await _showIOSToastLike(message);
    } else {
      // Web或桌面端
      print('Toast: $message');
    }
  }
  
  static Future<void> _showIOSToastLike(String message) async {
    // iOS上可以使用其他方式模拟Toast
    // 比如使用SnackBar或自定义View
  }
}

四、EventChannel

如何实现从原生到Dart的持续数据流?

graph LR subgraph "Android/iOS原生侧" A[数据源
传感器/位置/GPS] --> B[EventChannel.EventSink] B --> C[BinaryMessenger发送] end subgraph "Dart侧" C --> D[StreamController] D --> E[Stream] E --> F[StreamBuilder/widget] end style B fill:#9f9 style E fill:#f99

关键组件

  1. EventSink:原生侧的数据发射器
  2. Stream:Dart侧的响应式数据流
  3. StreamHandler:连接两者的桥梁

4.1 EventChannel与MethodChannel的区别

维度 MethodChannel EventChannel
通信方向 双向 主要是原生→Dart
通信模式 请求-响应 事件流/数据流
使用场景 方法调用 实时数据
连接状态 短连接 长连接
性能影响 每次调用都建立连接 一次建立,多次传输

4.2 以监听传感器数据为例

Android侧(Kotlin)
kotlin 复制代码
// SensorEventPlugin.kt
class SensorEventPlugin(private val context: Context) : 
    StreamHandler {
    
    private var sensorManager: SensorManager? = null
    private var accelerometerSensor: Sensor? = null
    private var eventSink: EventChannel.EventSink? = null
    
    companion object {
        fun registerWith(registrar: Registrar) {
            val channel = EventChannel(
                registrar.messenger(), 
                "com.xxxx/sensor"
            )
            val plugin = SensorEventPlugin(registrar.context())
            channel.setStreamHandler(plugin)
        }
    }
    
    // 1. 监听
    override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
        eventSink = events
        
        // 2. 初始化传感器
        sensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
        accelerometerSensor = sensorManager?.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
        
        // 3. 注册传感器监听器
        sensorManager?.registerListener(
            sensorListener,
            accelerometerSensor,
            SensorManager.SENSOR_DELAY_NORMAL
        )
    }
    
    // 4. 监听停止
    override fun onCancel(arguments: Any?) {
        // 5. 取消传感器监听
        sensorManager?.unregisterListener(sensorListener)
        sensorManager = null
        accelerometerSensor = null
        eventSink = null
    }
    
    // 6. 传感器监听器
    private val sensorListener = object : SensorEventListener {
        override fun onSensorChanged(event: SensorEvent?) {
            event?.let {
                // 7. 获取加速度数据
                val x = it.values[0]
                val y = it.values[1]
                val z = it.values[2]
                
                // 8. 发送到Dart
                eventSink?.success(mapOf(
                    "x" to x,
                    "y" to y,
                    "z" to z,
                    "timestamp" to System.currentTimeMillis()
                ))
            }
        }
        
        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
            // .....
        }
    }
}
Dart侧
dart 复制代码
// sensor_stream.dart
import 'package:flutter/services.dart';

class SensorStream {
  final EventChannel _channel = EventChannel('com.xxxx/sensor');
  Stream<Map<String, dynamic>>? _stream;
  
  // 获取传感器数据流
  Stream<Map<String, dynamic>> get sensorStream {
    _stream ??= _channel
        .receiveBroadcastStream()
        .map((data) => Map<String, dynamic>.from(data));
    return _stream!;
  }
  
  // 使用
  void startListening() {
    sensorStream.listen((data) {
      print('加速度: X=${data['x']}, Y=${data['y']}, Z=${data['z']}');
      
      // 计算设备倾斜角度
      double pitch = 180 * atan(data['x'] / sqrt(pow(data['y'], 2) + pow(data['z'], 2))) / pi;
      double roll = 180 * atan(data['y'] / sqrt(pow(data['x'], 2) + pow(data['z'], 2))) / pi;
      
      print('倾斜角度: Pitch=$pitch°, Roll=$roll°');
    }, onError: (error) {
      print('传感器错误: $error');
    }, onDone: () {
      print('传感器监听结束');
    });
  }
  
  // 在Widget中使用
  Widget buildSensorWidget() {
    return StreamBuilder<Map<String, dynamic>>(
      stream: sensorStream,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          final data = snapshot.data!;
          return Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Text('X: ${data['x'].toStringAsFixed(2)}'),
              Text('Y: ${data['y'].toStringAsFixed(2)}'),
              Text('Z: ${data['z'].toStringAsFixed(2)}'),
              // 展示
              CustomPaint(
                painter: AccelerometerPainter(data),
                size: Size(200, 200),
              ),
            ],
          );
        } else if (snapshot.hasError) {
          return Text('error: ${snapshot.error}');
        } else {
          return CircularProgressIndicator();
        }
      },
    );
  }
}

EventChannel实际应用场景如下:

  • 加速度计、陀螺仪等
  • GPS位置实时变化
  • 网络连接状态变化
  • 蓝牙设备发现和数据接收
  • 下载进度实时更新

五、自定义插件

5.1 插件项目结构

一个标准的Flutter插件目录结构如下:

bash 复制代码
flutter_custom_plugin/
├── android/                     # Android平台代码
│   ├── build.gradle             # Android构建配置
│   ├── src/main/
│   │   ├── kotlin/
│   │   │   └── com/xxxx/flutter_custom_plugin/
│   │   │       └── FlutterCustomPlugin.kt
│   │   └── AndroidManifest.xml
├── ios/                       # iOS平台代码
│   ├── Classes/
│   │   └── FlutterCustomPlugin.swift
│   └── flutter_custom_plugin.podspec
├── lib/                       # Dart库代码
│   ├── flutter_custom_plugin.dart
│   └── src/
│       └── implementation.dart
├── xxxx/                   # 案例应用
│   ├── android/
│   ├── ios/
│   └── lib/main.dart
├── pubspec.yaml              # 插件配置
└── README.md                 # 文档

5.2 以网络状态监听插件为例

第一步:创建插件项目
bash 复制代码
flutter create --template=plugin --org=com.xxxx--platforms=android,ios flutter_network_plugin
cd flutter_network_plugin
第二步:编写Dart API
dart 复制代码
// lib/flutter_network_plugin.dart
library flutter_network_plugin;

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

/// 网络类型枚举
enum NetworkType {
  wifi,
  mobile,
  ethernet,
  bluetooth,
  vpn,
  none,
  unknown,
}

/// 网络状态类
class NetworkStatus {
  final bool isConnected;
  final NetworkType type;
  final String? subtype;
  final bool isRoaming;
  final String? extraInfo;
  
  NetworkStatus({
    required this.isConnected,
    required this.type,
    this.subtype,
    this.isRoaming = false,
    this.extraInfo,
  });
  
  /// 从Map转换
  factory NetworkStatus.fromMap(Map<dynamic, dynamic> map) {
    return NetworkStatus(
      isConnected: map['isConnected'] ?? false,
      type: _parseNetworkType(map['type']),
      subtype: map['subtype'],
      isRoaming: map['isRoaming'] ?? false,
      extraInfo: map['extraInfo'],
    );
  }
  
  /// 转换为Map
  Map<String, dynamic> toMap() {
    return {
      'isConnected': isConnected,
      'type': type.toString().split('.').last,
      'subtype': subtype,
      'isRoaming': isRoaming,
      'extraInfo': extraInfo,
    };
  }
  
  /// 解析网络类型
  static NetworkType _parseNetworkType(String? type) {
    switch (type?.toLowerCase()) {
      case 'wifi':
        return NetworkType.wifi;
      case 'mobile':
        return NetworkType.mobile;
      case 'ethernet':
        return NetworkType.ethernet;
      case 'bluetooth':
        return NetworkType.bluetooth;
      case 'vpn':
        return NetworkType.vpn;
      case 'none':
        return NetworkType.none;
      default:
        return NetworkType.unknown;
    }
  }
  
  @override
  String toString() {
    return 'NetworkStatus{isConnected: $isConnected, type: $type, subtype: $subtype}';
  }
}

/// 网络状态插件
class FlutterNetworkPlugin {
  static const MethodChannel _methodChannel =
      MethodChannel('flutter_network_plugin/method');
  
  static const EventChannel _eventChannel =
      EventChannel('flutter_network_plugin/event');
  
  /// 单例
  static final FlutterNetworkPlugin _instance = FlutterNetworkPlugin._internal();
  factory FlutterNetworkPlugin() => _instance;
  FlutterNetworkPlugin._internal();
  
  /// 获取当前网络状态
  static Future<NetworkStatus> get currentStatus async {
    try {
      final Map<dynamic, dynamic> status =
          await _methodChannel.invokeMethod('getCurrentStatus');
      return NetworkStatus.fromMap(Map<String, dynamic>.from(status));
    } on PlatformException catch (e) {
      print('获取网络状态失败: ${e.message}');
      return NetworkStatus(
        isConnected: false,
        type: NetworkType.unknown,
      );
    }
  }
  
  /// 检查是否有网络连接
  static Future<bool> get isConnected async {
    final status = await currentStatus;
    return status.isConnected;
  }
  
  /// 网络状态变化流
  static Stream<NetworkStatus> get onStatusChanged {
    return _eventChannel
        .receiveBroadcastStream()
        .map((data) => NetworkStatus.fromMap(Map<String, dynamic>.from(data)))
        .handleError((error) {
          print('网络状态监听错误: $error');
        });
  }
  
  /// 初始化插件
  static Future<void> initialize() async {
    try {
      await _methodChannel.invokeMethod('initialize');
    } on PlatformException catch (e) {
      print('初始化插件失败: ${e.message}');
    }
  }
  
  /// 释放资源
  static Future<void> dispose() async {
    try {
      await _methodChannel.invokeMethod('dispose');
    } on PlatformException catch (e) {
      print('释放插件资源失败: ${e.message}');
    }
  }
}
第三步:实现Android端代码
kotlin 复制代码
// android/src/main/kotlin/com/xxxx/flutter_network_plugin/FlutterNetworkPlugin.kt
package com.example.flutter_network_plugin

import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.os.Build
import androidx.annotation.RequiresApi
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result

/** FlutterNetworkPlugin */
class FlutterNetworkPlugin : FlutterPlugin, MethodCallHandler {
    private lateinit var context: Context
    private lateinit var methodChannel: MethodChannel
    private lateinit var eventChannel: EventChannel
    
    // 事件发送器
    private var eventSink: EventChannel.EventSink? = null
    
    // 网络相关
    private var connectivityManager: ConnectivityManager? = null
    private var networkCallback: ConnectivityManager.NetworkCallback? = null
    
    override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
        context = flutterPluginBinding.applicationContext
        methodChannel = MethodChannel(
            flutterPluginBinding.binaryMessenger,
            "flutter_network_plugin/method"
        )
        methodChannel.setMethodCallHandler(this)
        
        eventChannel = EventChannel(
            flutterPluginBinding.binaryMessenger,
            "flutter_network_plugin/event"
        )
        eventChannel.setStreamHandler(object : EventChannel.StreamHandler {
            override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
                eventSink = events
                // 立即发送当前状态
                sendCurrentStatus()
            }
            
            override fun onCancel(arguments: Any?) {
                eventSink = null
            }
        })
    }
    
    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
        methodChannel.setMethodCallHandler(null)
        unregisterNetworkCallback()
    }
    
    override fun onMethodCall(call: MethodCall, result: Result) {
        when (call.method) {
            "getCurrentStatus" -> {
                result.success(getCurrentNetworkStatus())
            }
            "initialize" -> {
                initializeNetworkMonitoring()
                result.success(null)
            }
            "dispose" -> {
                unregisterNetworkCallback()
                result.success(null)
            }
            else -> result.notImplemented()
        }
    }
    
    /**
     * 获取当前网络状态
     */
    private fun getCurrentNetworkStatus(): Map<String, Any> {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            getNetworkStatusModern()
        } else {
            getNetworkStatusLegacy()
        }
    }
    
    @RequiresApi(Build.VERSION_CODES.M)
    private fun getNetworkStatusModern(): Map<String, Any> {
        connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE)
                as ConnectivityManager
        
        val activeNetwork = connectivityManager?.activeNetwork
        val networkCapabilities = connectivityManager?.getNetworkCapabilities(activeNetwork)
        
        val isConnected = networkCapabilities != null &&
                (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ||
                        networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) ||
                        networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) ||
                        networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) ||
                        networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN))
        
        val type = when {
            networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) == true -> "wifi"
            networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) == true -> "mobile"
            networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) == true -> "ethernet"
            networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) == true -> "bluetooth"
            networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_VPN) == true -> "vpn"
            else -> "none"
        }
        
        return mapOf(
            "isConnected" to isConnected,
            "type" to type,
            "isRoaming" to false,
            "extraInfo" to null
        )
    }
    
    @Suppress("DEPRECATION")
    private fun getNetworkStatusLegacy(): Map<String, Any> {
        val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE)
                as ConnectivityManager
        
        val activeNetworkInfo = cm.activeNetworkInfo
        val isConnected = activeNetworkInfo?.isConnected == true
        val type = when (activeNetworkInfo?.type) {
            ConnectivityManager.TYPE_WIFI -> "wifi"
            ConnectivityManager.TYPE_MOBILE -> "mobile"
            ConnectivityManager.TYPE_ETHERNET -> "ethernet"
            ConnectivityManager.TYPE_BLUETOOTH -> "bluetooth"
            ConnectivityManager.TYPE_VPN -> "vpn"
            else -> "none"
        }
        
        return mapOf(
            "isConnected" to isConnected,
            "type" to type,
            "isRoaming" to activeNetworkInfo?.isRoaming ?: false,
            "extraInfo" to activeNetworkInfo?.extraInfo
        )
    }
    
    /**
     * 初始化网络监控
     */
    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    private fun initializeNetworkMonitoring() {
        connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE)
                as ConnectivityManager
        
        val networkRequest = NetworkRequest.Builder()
            .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
            .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
            .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET)
            .addTransportType(NetworkCapabilities.TRANSPORT_BLUETOOTH)
            .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
            .build()
        
        networkCallback = object : ConnectivityManager.NetworkCallback() {
            override fun onAvailable(network: Network) {
                super.onAvailable(network)
                sendCurrentStatus()
            }
            
            override fun onLost(network: Network) {
                super.onLost(network)
                sendCurrentStatus()
            }
            
            override fun onCapabilitiesChanged(
                network: Network,
                networkCapabilities: NetworkCapabilities
            ) {
                super.onCapabilitiesChanged(network, networkCapabilities)
                sendCurrentStatus()
            }
        }
        
        connectivityManager?.registerNetworkCallback(networkRequest, networkCallback!!)
    }
    
    /**
     * 取消网络监控
     */
    private fun unregisterNetworkCallback() {
        networkCallback?.let {
            connectivityManager?.unregisterNetworkCallback(it)
            networkCallback = null
        }
    }
    
    /**
     * 发送当前状态到Dart
     */
    private fun sendCurrentStatus() {
        val status = getCurrentNetworkStatus()
        eventSink?.success(status)
    }
}
第四步:实现iOS端代码
swift 复制代码
// ios/Classes/FlutterNetworkPlugin.swift
import Flutter
import Foundation
import SystemConfiguration
import Network

public class FlutterNetworkPlugin: NSObject, FlutterPlugin {
    private var eventSink: FlutterEventSink?
    private var monitor: NWPathMonitor?
    private let queue = DispatchQueue(label: "NetworkMonitor")
    
    public static func register(with registrar: FlutterPluginRegistrar) {
        let instance = FlutterNetworkPlugin()
        
        // 方法通道
        let methodChannel = FlutterMethodChannel(
            name: "flutter_network_plugin/method",
            binaryMessenger: registrar.messenger()
        )
        registrar.addMethodCallDelegate(instance, channel: methodChannel)
        
        // 事件通道
        let eventChannel = FlutterEventChannel(
            name: "flutter_network_plugin/event",
            binaryMessenger: registrar.messenger()
        )
        eventChannel.setStreamHandler(instance)
    }
    
    public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        switch call.method {
        case "getCurrentStatus":
            result(getCurrentNetworkStatus())
        case "initialize":
            initializeNetworkMonitoring()
            result(nil)
        case "dispose":
            disposeNetworkMonitoring()
            result(nil)
        default:
            result(FlutterMethodNotImplemented)
        }
    }
    
    // 获取当前网络状态
    private func getCurrentNetworkStatus() -> [String: Any] {
        var zeroAddress = sockaddr_in()
        zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
        zeroAddress.sin_family = sa_family_t(AF_INET)
        
        guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, {
            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                SCNetworkReachabilityCreateWithAddress(nil, $0)
            }
        }) else {
            return [
                "isConnected": false,
                "type": "none",
                "isRoaming": false,
                "extraInfo": nil
            ]
        }
        
        var flags: SCNetworkReachabilityFlags = []
        if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
            return [
                "isConnected": false,
                "type": "none",
                "isRoaming": false,
                "extraInfo": nil
            ]
        }
        
        let isReachable = flags.contains(.reachable)
        let needsConnection = flags.contains(.connectionRequired)
        let isConnected = isReachable && !needsConnection
        
        var type = "none"
        if flags.contains(.isWWAN) {
            type = "mobile"
        } else if flags.contains(.connectionRequired) {
            // 需要连接但不可达
        } else {
            type = "wifi"
        }
        
        return [
            "isConnected": isConnected,
            "type": type,
            "isRoaming": false,
            "extraInfo": nil
        ]
    }
    
    // 初始化网络监控
    @available(iOS 12.0, *)
    private func initializeNetworkMonitoring() {
        monitor = NWPathMonitor()
        
        monitor?.pathUpdateHandler = { [weak self] path in
            guard let self = self else { return }
            
            let isConnected = path.status == .satisfied
            var type = "none"
            
            if path.usesInterfaceType(.wifi) {
                type = "wifi"
            } else if path.usesInterfaceType(.cellular) {
                type = "mobile"
            } else if path.usesInterfaceType(.wiredEthernet) {
                type = "ethernet"
            } else if path.usesInterfaceType(.loopback) {
                type = "loopback"
            } else if path.usesInterfaceType(.other) {
                type = "other"
            }
            
            let status: [String: Any] = [
                "isConnected": isConnected,
                "type": type,
                "isRoaming": false,
                "extraInfo": nil
            ]
            
            // 发送到Dart
            DispatchQueue.main.async {
                self.eventSink?(status)
            }
        }
        
        monitor?.start(queue: queue)
    }
    
    // 释放网络监控
    private func disposeNetworkMonitoring() {
        monitor?.cancel()
        monitor = nil
    }
}

extension FlutterNetworkPlugin: FlutterStreamHandler {
    public func onListen(withArguments arguments: Any?, 
                        eventSink events: @escaping FlutterEventSink) -> FlutterError? {
        eventSink = events
        
        // 立即发送当前状态
        let status = getCurrentNetworkStatus()
        events(status)
        
        // 初始化监控
        if #available(iOS 12.0, *) {
            initializeNetworkMonitoring()
        }
        
        return nil
    }
    
    public func onCancel(withArguments arguments: Any?) -> FlutterError? {
        eventSink = nil
        disposeNetworkMonitoring()
        return nil
    }
}
第五步:配置插件
yaml 复制代码
# pubspec.yaml
name: flutter_network_plugin
description: A Flutter plugin for monitoring network status.
version: 1.0.0
homepage: https://github.com/xxxx/flutter_network_plugin

environment:
  sdk: ">=2.17.0 <3.0.0"
  flutter: ">=1.17.0"

dependencies:
  flutter:
    sdk: flutter

dev_dependencies:
  flutter_test:
    sdk: flutter

flutter:
  plugin:
    platforms:
      android:
        package: com.example.flutter_network_plugin
        pluginClass: FlutterNetworkPlugin
      ios:
        pluginClass: FlutterNetworkPlugin
第六步:编写使用案例
dart 复制代码
// xxxx/lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_network_plugin/flutter_network_plugin.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  NetworkStatus _currentStatus = NetworkStatus(
    isConnected: false,
    type: NetworkType.unknown,
  );
  StreamSubscription<NetworkStatus>? _subscription;

  @override
  void initState() {
    super.initState();
    _initNetworkPlugin();
  }

  Future<void> _initNetworkPlugin() async {
    // 初始化插件
    await FlutterNetworkPlugin.initialize();
    
    // 获取当前状态
    final status = await FlutterNetworkPlugin.currentStatus;
    setState(() {
      _currentStatus = status;
    });
    
    // 监听状态变化
    _subscription = FlutterNetworkPlugin.onStatusChanged.listen((status) {
      setState(() {
        _currentStatus = status;
      });
    });
  }

  @override
  void dispose() {
    _subscription?.cancel();
    FlutterNetworkPlugin.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('网络状态插件'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              // 连接状态指示器
              Container(
                width: 100,
                height: 100,
                decoration: BoxDecoration(
                  shape: BoxShape.circle,
                  color: _currentStatus.isConnected ? Colors.green : Colors.red,
                ),
                child: Icon(
                  _currentStatus.isConnected ? Icons.wifi : Icons.wifi_off,
                  size: 50,
                  color: Colors.white,
                ),
              ),
              SizedBox(height: 20),
              // 状态信息
              Text(
                _currentStatus.isConnected ? '已连接' : '未连接',
                style: TextStyle(
                  fontSize: 24,
                  fontWeight: FontWeight.bold,
                ),
              ),
              SizedBox(height: 10),
              Text(
                '网络类型: ${_currentStatus.type.toString().split('.').last}',
                style: TextStyle(fontSize: 18),
              ),
              if (_currentStatus.subtype != null)
                Text(
                  '子类型: ${_currentStatus.subtype}',
                  style: TextStyle(fontSize: 16),
                ),
              SizedBox(height: 30),
              // 刷新按钮
              ElevatedButton(
                onPressed: () async {
                  final status = await FlutterNetworkPlugin.currentStatus;
                  setState(() {
                    _currentStatus = status;
                  });
                },
                child: Text('刷新状态'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

六、优化

6.1 性能优化技巧

1. 减少跨平台调用次数
dart 复制代码
// 不推荐:多次调用
Future<void> saveUser(User user) async {
  await _channel.invokeMethod('saveName', user.name);
  await _channel.invokeMethod('saveAge', user.age);
  await _channel.invokeMethod('saveEmail', user.email);
}

// 推荐
Future<void> saveUser(User user) async {
  await _channel.invokeMethod('saveUser', {
    'name': user.name,
    'age': user.age,
    'email': user.email,
  });
}
2. 使用Isolate处理耗时任务
dart 复制代码
Future<void> processLargeData(List<dynamic> data) async {
  // 在Isolate中处理,避免阻塞UI
  final result = await compute(_processInIsolate, data);
  await _channel.invokeMethod('saveResult', result);
}

static List<dynamic> _processInIsolate(List<dynamic> data) {
  // 耗时处理逻辑
  return processedData;
}
3. 内存管理
kotlin 复制代码
// Android侧:及时释放资源
override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {
    methodChannel.setMethodCallHandler(null)
    eventSink?.endOfStream()
    eventSink = null
}

6.2 错误处理

1. 统一的错误处理
dart 复制代码
class PluginError implements Exception {
  final String code;
  final String message;
  final dynamic details;
  
  PluginError({
    required this.code,
    required this.message,
    this.details,
  });
  
  @override
  String toString() => 'PluginError($code): $message';
}

Future<T> safeInvoke<T>(String method, [dynamic arguments]) async {
  try {
    final result = await _channel.invokeMethod(method, arguments);
    return result as T;
  } on PlatformException catch (e) {
    throw PluginError(
      code: e.code,
      message: e.message ?? 'Unknown error',
      details: e.details,
    );
  } catch (e) {
    throw PluginError(
      code: 'UNKNOWN',
      message: e.toString(),
      details: e,
    );
  }
}
csharp 复制代码
### 6.3 平台差异处理策略

#### 1. 功能检测与降级
```dart
class PlatformFeatureDetector {
  static Future<bool> isFeatureAvailable(String feature) async {
    try {
      return await _channel.invokeMethod('checkFeature', feature);
    } on PlatformException {
      return false;
    }
  }
  
  static Future<T> withFallback<T>({
    required Future<T> Function() primary,
    required Future<T> Function() fallback,
    String? feature,
  }) async {
    if (feature != null && !await isFeatureAvailable(feature)) {
      return await fallback();
    }
    
    try {
      return await primary();
    } catch (e) {
      PluginLogger.error('Primary method failed', e);
      return await fallback();
    }
  }
}
2. 条件编译
dart 复制代码
// 使用条件导入处理平台差异
export 'src/network_plugin_interface.dart';

// 在platform目录下
// network_plugin_android.dart
// network_plugin_ios.dart
// network_plugin_web.dart

总结

至此,Flutter的插件开发以及如何与Android/iOS进行原生交互就全部介绍完了,我们掌握了如下知识点:

  • Platform Channel原理-Flutter与原生平台通信的底层机制
  • MethodChannel使用-双向方法调用的实现
  • EventChannel-原生到Dart的数据流传输
  • 自定义插件开发完整流程
  • 处理Android/iOS的差异
  • 提升插件性能的技巧

插件开发是Flutter开发的重要技能,能够帮助我们深入理解Flutter架构,充分利用各平台的能力。遇到问题时,多查阅官方文档,多调试。


如果这篇文章对你有帮助,别忘了一键三连~~~,有任何问题或建议,欢迎在评论区留言讨论。下期见!

相关推荐
kirk_wang1 小时前
鸿蒙UI组件与Flutter Widget混合开发:原理、实践与踩坑指南
flutter·移动开发·跨平台·arkts·鸿蒙
2501_915921431 小时前
混合开发应用安全方案,在多技术栈融合下构建可持续、可回滚的保护体系
android·安全·ios·小程序·uni-app·iphone·webview
Sheffi661 小时前
RunLoop Mode 深度剖析:为什么滚动时 Timer 会“失效“?
ios·objective-c
AllBlue2 小时前
unity嵌入安卓界面,如何显示状态
android·unity·游戏引擎
西西学代码2 小时前
flutter---进度条(2)
前端·javascript·flutter
QuantumLeap丶2 小时前
《Flutter全栈开发实战指南:从零到高级》- 21 -响应式设计与适配
android·javascript·flutter·ios·前端框架
晚霞的不甘2 小时前
实战精要:构建企业级 Flutter + OpenHarmony 工业物联网(IIoT)监控平台
物联网·flutter
2501_915106322 小时前
Charles抓包怎么用 Charles抓包工具详细教程、网络调试方法、HTTPS配置与手机抓包实战
网络·ios·智能手机·小程序·https·uni-app·webview
等你等了那么久3 小时前
Flutter路由3分钟学会
flutter·dart