Flutter 从入门到精通(水)

475 阅读1分钟

第9章:Flutter 与原生交互(平台通道)

Flutter 是跨平台框架,但有时我们仍需要访问原生功能,比如:

  • 调用 Android 的传感器或服务
  • 使用 iOS 的特定 API
  • 访问硬件(如蓝牙、相机、定位)

Flutter 提供了 平台通道(Platform Channel) 来实现这些操作。


一、平台通道的工作原理

平台通道是 Flutter 与原生代码(Android/iOS)之间通信的桥梁。

基本流程:

Flutter(Dart) <-- MethodChannel --> 原生平台(Java/Kotlin/Swift)

二、使用 MethodChannel 调用原生代码(示例)

1. Dart 端定义通道

import 'package:flutter/services.dart';

class NativeBridge {
  static const _channel = MethodChannel('com.example.platform');

  static Future<String> getPlatformVersion() async {
    final version = await _channel.invokeMethod<String>('getPlatformVersion');
    return version ?? 'Unknown';
  }
}

2. Android 端实现(Kotlin)

MainActivity.kt 中添加:

class MainActivity: FlutterActivity() {
    private val CHANNEL = "com.example.platform"

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

        MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
            .setMethodCallHandler { call, result ->
                if (call.method == "getPlatformVersion") {
                    result.success("Android ${android.os.Build.VERSION.RELEASE}")
                } else {
                    result.notImplemented()
                }
            }
    }
}

3. iOS 端实现(Swift)

AppDelegate.swift 中添加:

import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
  override func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
  ) -> Bool {
    let controller = window.rootViewController as! FlutterViewController
    let channel = FlutterMethodChannel(name: "com.example.platform",
                                       binaryMessenger: controller.binaryMessenger)

    channel.setMethodCallHandler { call, result in
      if call.method == "getPlatformVersion" {
        result("iOS " + UIDevice.current.systemVersion)
      } else {
        result(FlutterMethodNotImplemented)
      }
    }

    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
  }
}

三、常见原生功能的调用方式(推荐使用插件)

Flutter 提供了大量封装好的插件,你通常无需手写原生代码。

功能推荐插件用途简述
相机camera拍照、录像、预览
相册选择image_picker从相册选择图片/拍照
定位geolocator获取 GPS、定位权限
权限管理permission_handler动态申请相机/麦克风/位置权限等
蓝牙flutter_blue蓝牙设备扫描、连接、传输数据
本地通知flutter_local_notifications显示通知,定时提醒

四、使用示例:使用 image_picker 打开相册

添加依赖

dependencies:
  image_picker: ^1.0.0
  permission_handler: ^11.0.0

示例代码

import 'package:image_picker/image_picker.dart';

final picker = ImagePicker();

Future<void> pickImage() async {
  final pickedFile = await picker.pickImage(source: ImageSource.gallery);
  if (pickedFile != null) {
    print('选择的文件路径: ${pickedFile.path}');
  }
}

五、权限处理(Android/iOS)

Android 添加权限(如相机、定位)

AndroidManifest.xml 中添加:

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

iOS 添加权限说明

Info.plist 中添加:

<key>NSCameraUsageDescription</key>
<string>我们需要访问相机来拍照</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>我们需要访问位置来提供更好的服务</string>

六、常见问题解析

❗ 问题 1:MethodChannel 调用失败,提示未实现

  • 检查通道名是否一致(Flutter 和原生)
  • 检查 call.method 是否拼写正确
  • 检查是否注册了通道(configureFlutterEngine()

❗ 问题 2:原生权限申请失败

  • Android 6.0+ 需要动态申请权限(使用 permission_handler
  • iOS 需要在 Info.plist 中写权限用途说明

❗ 问题 3:插件功能在模拟器无法使用

  • 相机、GPS、蓝牙等功能需真机调试
  • 可以通过 Flutter 的平台判断避免模拟器执行:
import 'dart:io';
if (Platform.isAndroid || Platform.isIOS) {
  // 真机逻辑
}