Flutter 从入门到精通(水)

226 阅读2分钟

第12章:Flutter 与原生插件开发

Flutter 提供了插件机制,允许开发者将平台原生功能(Java/Kotlin、Swift/Objective-C)封装为 Dart 接口,供 Flutter 使用。你可以开发:

  • 私有插件:在你的项目中复用
  • 公共插件:发布到 pub.dev,供社区使用

一、创建插件项目

使用命令行创建插件:

flutter create --template=plugin --platforms=android,ios flutter_battery_plugin

目录结构说明:

flutter_battery_plugin/
  ├── lib/flutter_battery_plugin.dart   # Dart接口
  ├── android/src/.../FlutterBatteryPlugin.kt
  ├── ios/Classes/FlutterBatteryPlugin.swift

二、Dart 端接口定义

// lib/flutter_battery_plugin.dart
import 'package:flutter/services.dart';

class FlutterBatteryPlugin {
  static const MethodChannel _channel = MethodChannel('flutter_battery_plugin');

  static Future<int?> getBatteryLevel() async {
    final level = await _channel.invokeMethod<int>('getBatteryLevel');
    return level;
  }
}

三、Android 原生实现(Kotlin)

// android/src/main/kotlin/com/example/flutter_battery_plugin/FlutterBatteryPlugin.kt
package com.example.flutter_battery_plugin

import android.content.Context
import android.os.BatteryManager
import android.os.Build
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel

class FlutterBatteryPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
  private lateinit var channel: MethodChannel
  private lateinit var context: Context

  override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
    context = flutterPluginBinding.applicationContext
    channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_battery_plugin")
    channel.setMethodCallHandler(this)
  }

  override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: MethodChannel.Result) {
    if (call.method == "getBatteryLevel") {
      val batteryManager = context.getSystemService(Context.BATTERY_SERVICE) as BatteryManager
      val level = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
      result.success(level)
    } else {
      result.notImplemented()
    }
  }

  override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
    channel.setMethodCallHandler(null)
  }
}

四、iOS 原生实现(Swift)

// ios/Classes/FlutterBatteryPlugin.swift
import Flutter
import UIKit

public class FlutterBatteryPlugin: NSObject, FlutterPlugin {
  public static func register(with registrar: FlutterPluginRegistrar) {
    let channel = FlutterMethodChannel(name: "flutter_battery_plugin", binaryMessenger: registrar.messenger())
    let instance = FlutterBatteryPlugin()
    registrar.addMethodCallDelegate(instance, channel: channel)
  }

  public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
    if call.method == "getBatteryLevel" {
      let device = UIDevice.current
      device.isBatteryMonitoringEnabled = true
      result(Int(device.batteryLevel * 100))
    } else {
      result(FlutterMethodNotImplemented)
    }
  }
}

五、在主项目中引用插件(本地依赖)

在你的主 App 项目中:

dependencies:
  flutter_battery_plugin:
    path: ../flutter_battery_plugin

使用:

import 'package:flutter_battery_plugin/flutter_battery_plugin.dart';

Future<void> _getBattery() async {
  final level = await FlutterBatteryPlugin.getBatteryLevel();
  print('Battery Level: $level%');
}

六、发布插件到 pub.dev(可选)

  1. 填写 pubspec.yaml 中的 namedescriptionversionhomepage
  2. 添加 README.mdCHANGELOG.mdLICENSE
  3. 注册 pub.dev 账号
  4. 登录并发布:
dart pub publish

七、常见问题解析

❗ 插件无法识别 MethodChannel

  • 检查通道名称必须一致(Dart 与原生)
  • 原生端是否实现了 onMethodCall

❗ Android 端电池读取失败(0 或 -1)

  • 需使用真实设备或模拟器设置支持电池状态
  • 请检查是否有正确的上下文和权限

❗ iOS 端未返回电池电量

  • UIDevice.current.isBatteryMonitoringEnabled 是否开启
  • iOS 模拟器不支持电池状态,需要真机调试