在许多应用场景中,手电筒功能不仅需要开关灯,还希望可以调节亮度。Flutter 本身没有提供直接操作手电筒的 API,但我们可以通过 Platform Channel 调用 Android/iOS 原生接口,实现开关灯和亮度控制。
本文我们将详细讲解实现思路和方法,让读者可以自己写出一个类似的插件
功能需求
-
开/关手电筒
-
调节亮度(0~1 的百分比)
-
获取当前亮度和手电状态
-
获取最大亮度等级(Android 13+ 有真实等级,iOS 默认 100)
-
自动开关灯逻辑:亮度 > 0 自动开灯,亮度 = 0 自动关灯
-
跨平台支持:Android 13+ 支持亮度,旧版本 Android 仅开灯;iOS 支持 0~1 小数亮度
实现原理
Android 原理实现
Android 使用 Camera2 API 控制手电筒:
开关手电筒
cameraManager.setTorchMode(cameraId, true); // 开灯
cameraManager.setTorchMode(cameraId, false); // 关灯
调节亮度(Android 13+)
cameraManager.turnOnTorchWithStrengthLevel(cameraId, torchLevel);
iOS原理实现
iOS 使用 AVCaptureDevice 控制摄像头 LED:
开关手电筒
FlashlightBrightness.instance.flashOn(); // 开灯
FlashlightBrightness.instance.flashOff(); // 关灯
调节亮度
FlashlightBrightness.instance.setBrightness(0.5); // 50%亮度
实现步骤
创建一个Flutter 插件
flutter create --template=plugin --platforms=android,ios -a java flashlight_brightness
Flutter插件lib配置
1.文件预览
lib/
├─ flashlight_brightness.dart # 插件主入口,提供给用户的 API
├─ flashlight_brightness_method_channel.dart # MethodChannel 平台实现(Android/iOS 调用)
└─ flashlight_brightness_platform_interface.dart # 抽象平台接口,定义公共方法
作用:
-
flashlight_brightness.dart:Flutter 层调用统一接口,无需关心平台差异。
-
flashlight_brightness_platform_interface.dart:定义接口,支持未来可能增加其他平台(web、macOS 等)。
-
flashlight_brightness_method_channel.dart:通过 MethodChannel 调用 Android/iOS 原生代码,实现接口。
2.创建平台接口
在 lib/flashlight_brightness_platform_interface.dart:
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'flashlight_brightness_method_channel.dart';
/// @class FlashlightBrightnessPlatform
/// @description 定义插件接口,支持多平台实现
abstract class FlashlightBrightnessPlatform extends PlatformInterface {
FlashlightBrightnessPlatform() : super(token: _token);
static final Object _token = Object();
static FlashlightBrightnessPlatform _instance =
MethodChannelFlashlightBrightness();
/// 默认实例,MethodChannel 实现
static FlashlightBrightnessPlatform get instance => _instance;
static set instance(FlashlightBrightnessPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
/// 获取 Android/iOS 版本(字符串)
Future<String?> getPlatformVersion() {
throw UnimplementedError('getPlatformVersion() has not been implemented.');
}
/// 初始化插件
Future<void> init() {
throw UnimplementedError('init() has not been implemented.');
}
/// 查询手电筒是否点亮
Future<bool?> isTorched() {
throw UnimplementedError('isTorched() has not been implemented.');
}
/// 打开手电筒
Future<void> flashOn() {
throw UnimplementedError('flashOn() has not been implemented.');
}
/// 关闭手电筒
Future<void> flashOff() {
throw UnimplementedError('flashOff() has not been implemented.');
}
/// 获取亮度百分比 0~1
Future<double?> getBrightness() {
throw UnimplementedError('getBrightness() has not been implemented.');
}
/// 设置亮度百分比 0~1
Future<void> setBrightness(double value) {
throw UnimplementedError('setBrightness() has not been implemented.');
}
/// 获取设备最大等级
Future<int?> getMaxLevel() {
throw UnimplementedError('getMaxLevel() has not been implemented.');
}
/// 销毁插件
Future<void> dispose() {
throw UnimplementedError('dispose() has not been implemented.');
}
}
说明:
-
所有方法都是抽象的,必须由具体平台实现。
-
_instance默认指向 MethodChannel 实现。
3.创建 MethodChannel 实现
在 lib/flashlight_brightness_method_channel.dart:
import 'package:flutter/services.dart';
import 'flashlight_brightness_platform_interface.dart';
/// @class MethodChannelFlashlightBrightness
/// @description 通过 MethodChannel 与原生通信实现 FlashlightBrightnessPlatform
class MethodChannelFlashlightBrightness extends FlashlightBrightnessPlatform {
final MethodChannel _channel = const MethodChannel('flashlight_brightness');
@override
Future<String?> getPlatformVersion() async {
return await _channel.invokeMethod('getPlatformVersion');
}
@override
Future<void> init() async {
await _channel.invokeMethod('init');
}
@override
Future<bool?> isTorched() async {
return await _channel.invokeMethod('isTorched');
}
@override
Future<void> flashOn() async {
await _channel.invokeMethod('flashOn');
}
@override
Future<void> flashOff() async {
await _channel.invokeMethod('flashOff');
}
@override
Future<double?> getBrightness() async {
return await _channel.invokeMethod('getPercent');
}
@override
Future<void> setBrightness(double value) async {
if (value < 0) value = 0;
if (value > 1) value = 1;
await _channel.invokeMethod('setPercent', value);
}
@override
Future<int?> getMaxLevel() async {
return await _channel.invokeMethod('maxLevel');
}
@override
Future<void> dispose() async {
await _channel.invokeMethod('dispose');
}
}
说明:
-
使用
MethodChannel调用原生 Android/iOS 方法。 -
参数和返回值都做了简单映射(比如亮度百分比 0~1)。
4.创建插件主入口
在 lib/flashlight_brightness.dart:
import 'flashlight_brightness_platform_interface.dart';
/// @class FlashlightBrightness
/// @description Flutter 端插件封装,提供统一方法给业务层调用
class FlashlightBrightness {
FlashlightBrightness._privateConstructor() {
_platform.init(); // 自动初始化
}
static final FlashlightBrightness instance =
FlashlightBrightness._privateConstructor(); // 单例模式
final FlashlightBrightnessPlatform _platform =
FlashlightBrightnessPlatform.instance;
/// 获取系统版本
Future<String?> getPlatformVersion() => _platform.getPlatformVersion();
/// 初始化插件
Future<void> init() => _platform.init();
/// 手电筒是否点亮
Future<bool?> isTorched() => _platform.isTorched();
/// 打开手电筒
Future<void> flashOn() => _platform.flashOn();
/// 关闭手电筒
Future<void> flashOff() => _platform.flashOff();
/// 获取亮度百分比 0~1
Future<double?> getBrightness() => _platform.getBrightness();
/// 设置亮度百分比 0~1
Future<void> setBrightness(double value) => _platform.setBrightness(value);
/// 获取设备最大等级
Future<int?> getMaxLevel() => _platform.getMaxLevel();
/// 销毁插件
Future<void> dispose() => _platform.dispose();
}
说明:
-
提供统一的 Flutter API,隐藏平台差异。
-
用户只需要导入
flashlight_brightness.dart即可使用插件功能。 -
使用单例模式
instance,确保全局只有一个插件实例。
Android 手电筒亮度控制实现方式
1.文件预览
android/src/main/java/com/example/flashlight_brightness/
├─ FlashlightBrightnessConstants.java # 方法名与关键常量
├─ FlashlightBrightnessController.java # 具体的手电筒实现逻辑
└─ FlashlightBrightnessPlugin.java # Flutter 插件入口,MethodChannel 分发
作用:
- FlashlightBrightnessConstants.java:规范接口,集中管理 MethodChannel 的方法名
- FlashlightBrightnessController.java:手电筒控制逻辑,不影响 MethodChannel
- FlashlightBrightnessPlugin.java:MethodChannel 入口,收 Flutter 调用 → 分发给 Controller → 返回结果给 Flutter
2.定义方法变量
在android/src/main/java/com/example/flashlight_brightness/FlashlightBrightnessConstants.java:
package com.example.flashlight_brightness;
/**
* @class FlashlightBrightnessConstants
* @description 定义插件中使用的所有方法名常量,统一管理 Flutter 与 Android 端通信的 key
* @features
* - 提供插件方法常量
* - 避免字符串硬编码
* - 提供亮度控制相关常量
*/
public final class FlashlightBrightnessConstants {
private FlashlightBrightnessConstants() {} // 私有构造,防止实例化
// 获取 Android 系统版本
public static final String VERSION = "getPlatformVersion";
// 初始化 FlashlightBrightnessController
public static final String INIT = "init";
// 开灯 / 关灯
public static final String FLASH_ON = "flashOn";
public static final String FLASH_OFF = "flashOff";
// 获取 / 设置亮度百分比(0~1)
public static final String SET_PERCENT = "setPercent";
public static final String GET_PERCENT = "getPercent";
// 获取设备支持的最大 torch level
public static final String MAX_LEVEL = "maxLevel";
// 查询手电筒是否已点亮
public static final String IS_TORCHED = "isTorched";
// 销毁 FlashlightBrightnessController
public static final String DISPOSE = "dispose";
}
3.配置手电筒配置逻辑
在android/src/main/java/com/example/flashlight_brightness/FlashlightBrightnessController.java:
主要功能
-
获取亮度等级
- Android 13+:获取真实最大等级
- Android 6~12:等级为1
-
获取手电筒亮度
- 当前的亮度等级/最大亮度等级
-
设置手电筒亮度
-
设置值范围0~1。
- 0:关闭手电筒
-
亮度值*最大亮度等级并取整
package com.example.flashlight_brightness;
import android.annotation.TargetApi;
import android.content.Context;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
import android.os.Build;
import android.util.Log;/**
- @class FlashlightBrightnessController
- @description Android 手电筒控制类,支持亮度控制
- @features
-
- 获取 / 设置亮度(百分比 0~1) -
- 打开 / 关闭手电筒 -
- 获取最大亮度等级 -
- Android 13+ 支持多级亮度控制 -
- Android 6~12 基础开关手电筒兼容
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class FlashlightBrightnessController {private final CameraManager cameraManager;
private String cameraId;private boolean isTorched = false; // 当前是否点亮
private int torchLevel = 1; // 当前亮度等级(整数)
private int maxLevel = 1; // 设备支持的最大亮度等级/**
- Torch 回调监听器
- 用于监听亮度和状态变化
*/
private final CameraManager.TorchCallback torchCallback = new CameraManager.TorchCallback() {
@Override
public void onTorchStrengthLevelChanged(String id, int newStrengthLevel) {
torchLevel = newStrengthLevel;
}
@Override public void onTorchModeChanged(String id, boolean enabled) { isTorched = enabled; }};
/**
- 构造函数
- @param context 应用上下文
- @description 初始化 CameraManager 和 TorchCallback,获取后置摄像头 ID 和最大亮度
*/
public FlashlightBrightnessController(Context context) {
cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
try { for (String id : cameraManager.getCameraIdList()) { CameraCharacteristics cs = cameraManager.getCameraCharacteristics(id); Integer lens = cs.get(CameraCharacteristics.LENS_FACING); if (lens != null && lens == CameraCharacteristics.LENS_FACING_BACK) { cameraId = id; // Android 13+ 获取真实最大等级 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { maxLevel = cameraManager.getTorchStrengthLevel(cameraId); } else { maxLevel = 1; // Android 6~12 无多级亮度 } break; } } // 注册回调监听 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { cameraManager.registerTorchCallback(torchCallback, null); } } catch (Exception e) { Log.e("FlashlightBrightnessController", "Init failed", e); }}
/**
- @return 当前手电筒是否点亮
*/
public boolean isTorched() {
return isTorched;
}
/**
- @return 设备最大亮度等级
*/
public int getMaxTorchLevel() {
return maxLevel;
}
/**
- @return 当前亮度百分比(0~1)
*/
public double getTorchLevelPercent() {
if (maxLevel <= 1)
return isTorched ? 1.0 : 0.0;
return torchLevel / (double) maxLevel;
}
/**
- 设置亮度百分比并自动开灯
- @param percent 亮度百分比,范围 0~1
*/
public void setTorchLevelPercent(double percent) {
// if (percent < 0.0) percent = 0.0;
// if (percent > 1.0) percent = 1.0;
percent = Math.max(0, Math.min(percent, 1)); // 限制在 0~1
if (percent == 0) {
// 如果亮度为 0,直接关灯
flashOff();
torchLevel = 0;
return;
}
int level = (int) Math.round(percent * maxLevel); if (level < 1) level = 1; torchLevel = level; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { try { cameraManager.turnOnTorchWithStrengthLevel(cameraId, torchLevel); isTorched = true; } catch (Exception e) { Log.e("FlashlightBrightnessController", "setTorchLevelPercent failed", e); } } else { flashOn(); // Android 6~12 只能开灯,不支持亮度 }}
/**
- 开灯
*/
public void flashOn() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
return;
if (!isTorched) { try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU && maxLevel > 1) { cameraManager.turnOnTorchWithStrengthLevel(cameraId, torchLevel); } else { cameraManager.setTorchMode(cameraId, true); } isTorched = true; } catch (Exception e) { Log.e("FlashlightBrightnessController", "flashOn failed", e); } }}
/**
- 关灯
*/
public void flashOff() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && isTorched) {
try {
cameraManager.setTorchMode(cameraId, false);
isTorched = false;
} catch (Exception e) {
Log.e("FlashlightBrightnessController", "flashOff failed", e);
}
}
}
/**
- 注销回调并关闭手电筒
*/
public void dispose() {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
cameraManager.unregisterTorchCallback(torchCallback);
}
} catch (Exception ignored) {
}
flashOff();
}
}
-
4.Flutter ↔ Android 通信桥梁配置
在android/src/main/java/com/example/flashlight_brightness/FlashlightBrightnessPlugin.java:
主要作用:
-
Flutter ↔ Android 通信桥梁
-
分发方法到 Controller
package com.example.flashlight_brightness;
import android.content.Context;
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 FlashlightBrightnessPlugin
- @description Flutter 插件入口,实现 MethodChannel 与 Android 原生通信
- @features
-
- 注册 MethodChannel -
- 分发 Flutter 调用到 FlashlightBrightnessController -
- 提供百分比亮度接口 -
- 提供最大亮度等级查询 -
- 管理生命周期(初始化、销毁)
*/
public class FlashlightBrightnessPlugin implements FlutterPlugin, MethodChannel.MethodCallHandler {private MethodChannel channel; private Context context; private FlashlightBrightnessController flashlightController; @Override public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) { channel = new MethodChannel(binding.getBinaryMessenger(), "flashlight_brightness"); channel.setMethodCallHandler(this); context = binding.getApplicationContext(); // 自动初始化 ensureController(); } @Override public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) { switch (call.method) { case FlashlightBrightnessConstants.VERSION: result.success("Android " + Build.VERSION.RELEASE); break; case FlashlightBrightnessConstants.INIT: if (flashlightController == null) { flashlightController = new FlashlightBrightnessController(context); } result.success(true); break; case FlashlightBrightnessConstants.IS_TORCHED: // 自动初始化 ensureController(); result.success(flashlightController.isTorched()); break; case FlashlightBrightnessConstants.FLASH_ON: ensureController(); flashlightController.flashOn(); result.success(true); break; case FlashlightBrightnessConstants.FLASH_OFF: ensureController(); flashlightController.flashOff(); result.success(true); break; case FlashlightBrightnessConstants.GET_PERCENT: ensureController(); result.success(flashlightController.getTorchLevelPercent()); break; case FlashlightBrightnessConstants.SET_PERCENT: ensureController(); Object arg = call.arguments; double percent = 1.0; if (arg instanceof Number) { percent = ((Number) arg).doubleValue(); } flashlightController.setTorchLevelPercent(percent); result.success(true); break; case FlashlightBrightnessConstants.MAX_LEVEL: ensureController(); result.success(flashlightController.getMaxTorchLevel()); break; case FlashlightBrightnessConstants.DISPOSE: if (flashlightController != null) { flashlightController.dispose(); flashlightController = null; } result.success(true); break; default: result.notImplemented(); } } /** * 确保 flashlightController 已初始化 */ private void ensureController() { if (flashlightController == null) { flashlightController = new FlashlightBrightnessController(context); } } @Override public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) { channel.setMethodCallHandler(null); }}
至此,Android配置就完成了
iOS 手电筒亮度控制实现方式
1.文件预览
flashlight_brightness/
└─ ios/
└─ Classes/
├─ FlashlightBrightnessPlugin.swift // Flutter 插件入口
├─ FlashlightBrightnessController.swift // 手电筒控制核心逻辑
└─ FlashlightBrightnessConstants.swift // 方法名常量(与 Android 对应)
作用:
- FlashlightBrightnessConstants.swift :负责统一 iOS MethodChannel 方法名
- FlashlightBrightnessController.swift:控制 iOS 手电筒亮度核心逻辑
- FlashlightBrightnessPlugin.swift:插件入口:接收 Flutter 调用 → 分发给 Controller
2.定义方法变量
在ios/Classes/FlashlightBrightnessConstants.swift
//
// FlashlightBrightnessConstants.swift
// flashlight_brightness
//
// 定义 Flutter 与 iOS 通信的常量方法名
//
import Foundation
enum FlashlightBrightnessConstants {
static let version = "getPlatformVersion"
static let initPlugin = "init"
static let flashOn = "flashOn"
static let flashOff = "flashOff"
static let setPercent = "setPercent"
static let getPercent = "getPercent"
static let maxLevel = "maxLevel"
static let isTorched = "isTorched"
static let dispose = "dispose"
}
3.配置手电筒配置逻辑
在ios/Classes/FlashlightBrightnessController.swift
//
// FlashlightBrightnessController.swift
// flashlight_brightness
//
// 封装手电筒控制逻辑,支持开关和亮度控制
//
import AVFoundation
import UIKit
class FlashlightBrightnessController {
private let device = AVCaptureDevice.default(for: .video)
private var currentBrightness: Float = 1.0
/// 判断设备是否有手电筒
var hasTorch: Bool {
return device?.hasTorch ?? false
}
/// 当前是否点亮
var isTorched: Bool {
guard let device = device else { return false }
return device.isTorchActive
}
/// 最大亮度等级(iOS 恒定为 1.0)
var maxLevel: Int {
return 100
}
/// 获取当前亮度百分比 0~1
var brightness: Float {
return currentBrightness
}
/// 设置亮度百分比 0~1 并自动开灯
func setBrightness(_ percent: Float) {
guard hasTorch, let device = device else { return }
let level = min(max(percent, 0.0), 1.0)
if level == 0 {
// 亮度为0,自动关灯
flashOff()
currentBrightness = 0
return
}
currentBrightness = level
do {
try device.lockForConfiguration()
try device.setTorchModeOn(level: level)
device.unlockForConfiguration()
} catch {
print("Failed to set torch brightness: \(error)")
}
}
/// 打开手电筒(使用当前亮度)
func flashOn() {
guard hasTorch, let device = device else { return }
do {
try device.lockForConfiguration()
if !device.isTorchActive {
try device.setTorchModeOn(level: currentBrightness)
}
device.unlockForConfiguration()
} catch {
print("Failed to turn on torch: \(error)")
}
}
/// 关闭手电筒
func flashOff() {
guard hasTorch, let device = device else { return }
do {
try device.lockForConfiguration()
device.torchMode = .off
device.unlockForConfiguration()
} catch {
print("Failed to turn off torch: \(error)")
}
}
/// 销毁控制器(iOS 不需要额外操作)
func dispose() {
flashOff()
}
4.接收 Flutter 调用 → 分发给 Controller 配置
在ios/Classes/FlashlightBrightnessPlugin.swift
//
// FlashlightBrightnessPlugin.swift
// flashlight_brightness
//
// Flutter 插件入口,注册 MethodChannel 并分发方法
//
import Flutter
import UIKit
public class FlashlightBrightnessPlugin: NSObject, FlutterPlugin {
/// 控制器实例,可变变量
private var controller: FlashlightBrightnessController?
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "flashlight_brightness", binaryMessenger: registrar.messenger())
let instance = FlashlightBrightnessPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
/// 获取控制器,如果未创建则自动初始化
private func getController() -> FlashlightBrightnessController {
if controller == nil {
controller = FlashlightBrightnessController() // 自动初始化
}
return controller!
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
// 使用类属性 controller,不再声明 let
switch call.method {
case FlashlightBrightnessConstants.version:
result("iOS \(UIDevice.current.systemVersion)")
case FlashlightBrightnessConstants.initPlugin:
// 已经自动初始化,无需重新赋值
_ = getController()
result(true)
case FlashlightBrightnessConstants.isTorched:
result(controller?.isTorched ?? false)
case FlashlightBrightnessConstants.flashOn:
getController().flashOn()
result(true)
case FlashlightBrightnessConstants.flashOff:
getController().flashOff()
result(true)
case FlashlightBrightnessConstants.getPercent:
result(getController().brightness)
case FlashlightBrightnessConstants.setPercent:
if let arg = call.arguments as? Double {
getController().setBrightness(Float(arg)) // 自动开灯或关灯
}
result(true)
case FlashlightBrightnessConstants.maxLevel:
result(getController().maxLevel)
case FlashlightBrightnessConstants.dispose:
controller?.dispose()
controller = nil
result(true)
default:
result(FlutterMethodNotImplemented)
}
}
}
至此,iOS配置也就完成了
使用方法
创建单例实例
import 'package:flashlight_brightness/flashlight_brightness.dart';
final flashlight = FlashlightBrightness.instance;
开启/关闭手电筒
await flashlight.flashOn(); // 开灯
await flashlight.flashOff(); // 关灯
设置亮度 (0.0 - 1.0)
// 设置亮度为50%,如果手电未开则会自动开灯
await flashlight.setBrightness(0.5);
// 设置亮度为0,会自动关闭手电筒
await flashlight.setBrightness(0.0);
获取当前亮度
double? brightness = await flashlight.getBrightness(); // 返回0.0~1.0
判断手电筒是否开启
bool? isOn = await flashlight.isTorched();
获取最大亮度等级
int? maxLevel = await flashlight.getMaxLevel(); // Android: 5,iOS: 100
释放资源(可选)
await flashlight.dispose();