跳转至

第13章 Flutter与原生交互

Flutter与原生交互图

学习目标:掌握Flutter与原生Android/iOS的通信机制,实现平台特定功能。

预计学习时间:5-7天 实践时间:2-3天


目录

  1. 平台通道概述
  2. MethodChannel基础通信
  3. EventChannel事件流
  4. BasicMessageCodec自定义编解码
  5. FFI外部函数接口
  6. 实践练习

1. 平台通道概述

1.1 通信架构

Text Only
Flutter Layer          Platform Layer
     │                       │
     │  MethodChannel        │
     │  EventChannel         │
     │  BasicMessageChannel  │
     │◄─────────────────────►│
     │                       │
   Dart Code           Android/iOS

1.2 通信方式对比

方式 方向 适用场景
MethodChannel 双向 方法调用
EventChannel 原生→Flutter 事件流
BasicMessageChannel 双向 自定义编解码

2. MethodChannel基础通信

2.1 Flutter端

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

class BatteryService {
  // 通道名称必须与原生端一致,作为双方通信的唯一标识
  static const platform = MethodChannel('samples.flutter.dev/battery');

  // Flutter → 原生:调用原生方法获取电量
  static Future<String> getBatteryLevel() async {
    try {
      final int result = await platform.invokeMethod('getBatteryLevel');
      return 'Battery level: $result%';
    } on PlatformException catch (e) {
      // 原生端抛出异常时会被封装为 PlatformException
      return 'Failed to get battery level: ${e.message}';
    }
  }

  // 原生 → Flutter:注册原生端主动调用 Flutter 的处理器
  static void setupHandler() {
    platform.setMethodCallHandler((call) async {
      switch (call.method) {
        case 'showToast':
          final String message = call.arguments;
          // 显示Toast
          return null;
        default:
          throw MissingPluginException(); // 未实现的方法抛出此异常
      }
    });
  }
}

2.2 Android端(Kotlin)

Kotlin
class MainActivity : FlutterActivity() {
    private val CHANNEL = "samples.flutter.dev/battery"

    // Flutter 引擎初始化时注册平台通道
    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        // 创建 MethodChannel 并设置方法调用处理器
        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
            .setMethodCallHandler { call, result ->
                when (call.method) {
                    "getBatteryLevel" -> {
                        val batteryLevel = getBatteryLevel()
                        result.success(batteryLevel) // 返回成功结果给 Flutter
                    }
                    else -> result.notImplemented() // 未识别的方法调用
                }
            }
    }

    // 通过 Android 系统服务获取电池电量
    private fun getBatteryLevel(): Int {
        val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
        return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
    }
}

3. EventChannel事件流

3.1 传感器数据流示例

Dart
// Flutter端:通过 EventChannel 接收原生端持续推送的传感器数据
class SensorService {
  static const EventChannel _eventChannel =
      EventChannel('samples.flutter.dev/sensor');

  // 将原生事件流转换为 Dart Stream,实现响应式数据订阅
  static Stream<AccelerometerEvent> get accelerometerEvents {
    return _eventChannel.receiveBroadcastStream().map(
      (event) => AccelerometerEvent(
        x: event[0], // 原生端发送的数组,按 [x, y, z] 排列
        y: event[1],
        z: event[2],
      ),
    );
  }
}

// 使用
StreamBuilder<AccelerometerEvent>(
  stream: SensorService.accelerometerEvents,
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      return Text('X: ${snapshot.data!.x}');
    }
    return CircularProgressIndicator();
  },
)

3.2 Android端

Kotlin
class MainActivity : FlutterActivity() {
    private val SENSOR_CHANNEL = "samples.flutter.dev/sensor"
    private var sensorEventSink: EventChannel.EventSink? = null

    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        EventChannel(flutterEngine.dartExecutor.binaryMessenger, SENSOR_CHANNEL)
            .setStreamHandler(object : EventChannel.StreamHandler {
                // Flutter 端开始监听时触发,启动传感器并保存事件发送器
                override fun onListen(arguments: Any?, events: EventChannel.EventSink?) {
                    sensorEventSink = events  // 通过 EventSink 向 Flutter 推送数据
                    startSensor()
                }

                // Flutter 端取消监听时触发,释放资源
                override fun onCancel(arguments: Any?) {
                    sensorEventSink = null
                    stopSensor()
                }
            })
    }
}

4. BasicMessageCodec自定义编解码

Dart
// 自定义消息通道
class CustomMessageChannel {
  static const BasicMessageChannel<dynamic> _channel =
      BasicMessageChannel(
        'samples.flutter.dev/custom',
        StandardMessageCodec(),
      );

  static Future<void> sendCustomData(Map<String, dynamic> data) async {
    final response = await _channel.send(data);
    print('Response: $response');
  }
}

5. FFI外部函数接口

5.1 调用C/C++库

Dart
import 'dart:ffi';
import 'package:ffi/ffi.dart';

// 加载动态库:Android 加载 .so 文件,iOS 直接使用进程内符号
final DynamicLibrary nativeLib = Platform.isAndroid
    ? DynamicLibrary.open('libnative.so')
    : DynamicLibrary.process();

// 定义C函数签名(需要两套:Native 类型用于 FFI 查找,Dart 类型用于实际调用)
typedef NativeAddFunc = Int32 Function(Int32 a, Int32 b); // C 层签名
typedef DartAddFunc = int Function(int a, int b);          // Dart 层签名

// 通过符号名查找 C 函数,并转换为可直接调用的 Dart 函数
final add = nativeLib
    .lookup<NativeFunction<NativeAddFunc>>('native_add') // 按符号名查找
    .asFunction<DartAddFunc>();                           // 转为 Dart 函数

// 使用
void main() {
  final result = add(10, 20);
  print('Result: $result'); // 30
}

6. 实践练习

练习1:电池信息获取

任务:实现获取电池电量和充电状态

要求: - MethodChannel通信 - 显示电量百分比 - 显示充电状态

练习2:原生相机调用

任务:调用原生相机并返回图片

要求: - 打开原生相机 - 拍摄照片 - 返回图片路径给Flutter


本章小结

核心要点

  1. MethodChannel用于双向方法调用
  2. EventChannel用于原生向Flutter发送事件流
  3. FFI可直接调用C/C++库,性能更高
  4. 平台通道是Flutter扩展能力的关键

下一步

完成本章学习后,请进入第14章:Android系统服务深度解析


本章完成时间:预计5-7天