鸿蒙版Flutter库torch_light手电筒功能深度适配

鸿蒙版Flutter库torch_light手电筒功能深度适配:跨平台开发者的光明之路

本项目作者:kirk/坚果

适配仓库地址

作者仓库:https://github.com/svprdga/torch_light#

在数字化浪潮的推动下,跨平台开发框架如 Flutter 凭借其高效、便捷的特性,成为了开发者们的宠儿。而鸿蒙系统的崛起,更是为跨平台开发注入了新的活力。为了助力开发者在鸿蒙生态中快速实现 torch_light来处理设备手电筒的插件功能,本文将深入浅出地为大家解析如何适配 torch_light 三方库至鸿蒙平台。

一、适配鸿蒙版 torch_light 三方库

(一)版本选择与仓库简介

我们先去 pub 上查看最新版本,我们选择以 0.0.10版本为基础进行适配。torch_light 是一个简单的 Flutter 插件来处理设备手电筒的插件,其 GitHub 仓库为https://github.com/svprdga/torch_light#,我们的目标是将这个插件适配到鸿蒙平台。

(二)引入背景与使用场景

在 OpenHarmony 北向生态的发展过程中,许多已经适配了 Flutter 的厂商在接入 OpenHarmony 时,都希望能够继续使用 torch_light 来实现手电筒功能。因此,我们提供了这个适配方案,采用插件化的适配器模式,帮助生态伙伴快速实现产品化。

本方案适用于已经支持 Flutter 框架的设备在移植到 OpenHarmony 系统过程中,作为一个备选方案。

(三)使用文档与插件库使用

适配 OpenHarmony 平台的详细使用指导可以参考:Flutter使用指导文档

在项目中使用该插件库时,只需在 pubspec.yaml 文件的 dependencies 中新增如下配置:

dependencies:torch_light:git:url: "git@gitcode.com:nutpi/flutter_torch_light.git"path: ""

然后在项目根目录运行 flutter pub get,即可完成依赖添加

接下来是具体的适配过程。

二、适配过程详解

(一)准备工作

确保已经配置好了 Flutter 开发环境,具体可参考 Flutter 配置指南。同时,从 官方插件库 下载待适配的三方插件。本指导书, 以适配 torch_light 为例

image-20250417200546042

(二)插件目录结构

下载并解压插件后,我们会看到以下目录结构:

  • lib :对接 Dart 端代码的入口,由此文件接收到参数后,通过 channel 将数据发送到原生端。
  • android :安卓端代码实现目录。
  • ios :iOS 原生端实现目录。
  • example :一个依赖于该插件的 Flutter 应用程序,用于说明如何使用它。
  • README.md :介绍包的文件。
  • CHANGELOG.md :记录每个版本中的更改。
  • LICENSE :包含软件包许可条款的文件。

(三)创建插件的鸿蒙模块

在插件目录下,打开 Terminal,执行以下命令来创建一个鸿蒙平台的 Flutter 模块:

flutter create . --org com.svprdga.torchlight --template=plugin --platforms=ohos

步骤:

  1. 用vscode/trae打开刚刚下载好的插件。

  2. 打开Terminal,cd到插件目录下。

  3. 执行命令flutter create . --template=plugin --platforms=ohos 创建一个ohos平台的flutter模块。

第一个问题,修改sdk的版本,适配旧版本。

我们做好修改就好。

(四)在根目录下添加鸿蒙平台配置

在项目根目录的 pubspec.yaml 文件中,添加鸿蒙平台的相关配置:

name: torch_light
description: A Flutter plugin to check if the device has a torch / flashlight, and to turn it on and off.
version: 1.1.0
homepage: https://davidserrano.io/
repository: https://github.com/svprdga/torch_lightenvironment:sdk: ">=2.17.5 <4.0.0"flutter: ">=2.3.0"dependencies:flutter:sdk: flutterdev_dependencies:flutter_test:sdk: flutterlint: 2.3.0flutter:plugin:platforms:android:package: com.svprdga.torchlightpluginClass: TorchLightPluginios:pluginClass: TorchLightPluginohos:package: com.svprdga.torchlightpluginClass: TorchLightPlugin

(五)编写鸿蒙插件的原生 ArkTS模块

1. 创建鸿蒙插件模块

使用 DevEco Studio 打开鸿蒙项目。

2. 修改相关配置文件

ohos 目录内的 oh-package.json5 文件中添加 libs/flutter.har 依赖,并创建 .gitignore 文件,添加以下内容以忽略 libs 目录:

/node_modules
/oh_modules
/local.properties
/.preview
/.idea
/build
/libs
*.har
/.cxx
/.test
/BuildProfile.ets
/oh-package-lock.json5

oh-package.json5 文件内容如下:

{"name": "torch_light","version": "1.0.0","description": "Please describe the basic information.","main": "index.ets","author": "","license": "Apache-2.0","dependencies": {"@ohos/flutter_ohos": "file:./har/flutter.har"}
}

ohos 目录下创建 index.ets 文件,导出配置:

import TorchLightPlugin from './src/main/ets/components/plugin/TorchLightPlugin';
export default TorchLightPlugin;
3. 编写 ETS 代码

文件结构和代码逻辑可以参考安卓或 iOS 的实现,鸿蒙的 API 文档可以参考 :https://gitcode.com/openharmony-sig/flutter_packages/tree/master/packages/path_provider/path_provider_android

ohos的api可以参考:https://gitcode.com/openharmony/docs

以下是 TorchLightPlugin.ets 文件的代码示例:

  private channel: MethodChannel | null = null;private nativeEventIsTorchAvailable: string = "torch_available";private errorIsTorchAvailable: string = "torch_available_error";private nativeEventEnableTorch: string = "enable_torch";private errorEnableTorchExistentUser: string = "enable_torch_error_existent_user";private errorEnableTorch: string = "enable_torch_error";private errorEnableTorchNotAvailable: string = "enable_torch_not_available";private nativeEventDisableTorch: string = "disable_torch";private errorDisableTorchExistentUser: string = "disable_torch_error_existent_user";private errorDisableTorch: string = "disable_torch_error";private errorDisableTorchNotAvailable: string = "disable_torch_not_available";

这里我主要参考的是

三、手电筒使用

手电筒模式的使用是通过操作手机启用手电筒功能,使设备的手电筒功能持续保持常亮状态。

在使用相机应用并操作手电筒功能时,存在以下几种情况说明:

  • 当使用后置摄像头并设置闪光灯模式FlashMode关闭时,手电筒功能无法启用。
  • 当使用前置摄像头时,手电筒可以正常启用并保持常亮状态。
  • 从前置摄像头切换至后置摄像头时,如果手电筒原本处于开启状态,它将会被自动关闭。

详情请参考Camera API参考。

开发步骤

导入camera接口,接口中提供了相机相关的属性和方法,导入方法如下。

import { camera } from '@kit.CameraKit';
import { BusinessError } from '@kit.BasicServicesKit';

通过CameraManager类中的isTorchSupported方法,检测当前设备是否支持手电筒功能。

function isTorchSupported(cameraManager: camera.CameraManager) : boolean {let torchSupport: boolean = false;try {torchSupport = cameraManager.isTorchSupported();} catch (error) {let err = error as BusinessError;console.error('Failed to torch. errorCode = ' + err.code);}console.info('Returned with the torch support status:' + torchSupport);return torchSupport;
}

通过CameraManager类中的isTorchModeSupported方法,检测是否支持指定的手电筒模式TorchMode。

function isTorchModeSupported(cameraManager: camera.CameraManager, torchMode: camera.TorchMode) : boolean {let isTorchModeSupport: boolean = false;try {isTorchModeSupport = cameraManager.isTorchModeSupported(torchMode);} catch (error) {let err = error as BusinessError;console.error('Failed to set the torch mode. errorCode = ' + err.code);}return isTorchModeSupport;
}

通过CameraManager类中的setTorchMode方法,设置当前设备的手电筒模式。以及通过CameraManager类中的getTorchMode方法,获取当前设备的手电筒模式。

在使用getTorchMode方法前,需要先注册监听手电筒的状态变化,请参考状态监听。

function setTorchModeSupported(cameraManager: camera.CameraManager, torchMode: camera.TorchMode) : void {cameraManager.setTorchMode(torchMode);let isTorchMode = cameraManager.getTorchMode();console.info(`Returned with the torch mode supportd mode: ${isTorchMode}`);
}

状态监听

在相机应用开发过程中,可以随时监听手电筒状态,包括手电筒打开、手电筒关闭、手电筒不可用、手电筒恢复可用。手电筒状态发生变化,可通过回调函数获取手电筒模式的变化。

通过注册torchStatusChange事件,通过回调返回监听结果,callback返回TorchStatusInfo参数,参数的具体内容可参考相机管理器回调接口实例TorchStatusInfo。

function onTorchStatusChange(cameraManager: camera.CameraManager): void {cameraManager.on('torchStatusChange', (err: BusinessError, torchStatusInfo: camera.TorchStatusInfo) => {if (err !== undefined && err.code !== 0) {console.error(`Callback Error, errorCode: ${err.code}`);return;}console.info(`onTorchStatusChange, isTorchAvailable: ${torchStatusInfo.isTorchAvailable}, isTorchActive: ${torchStatusInfo.isTorchActive}, level: ${torchStatusInfo.torchLevel}`);});
}

完整的代码

import {FlutterPlugin,FlutterPluginBinding,MethodCall,MethodCallHandler,MethodChannel,MethodResult,
} from '@ohos/flutter_ohos';
import { camera } from '@kit.CameraKit';
import { BusinessError } from '@kit.BasicServicesKit';/** TorchLightPlugin **/
export default class TorchLightPlugin implements FlutterPlugin, MethodCallHandler {private channel: MethodChannel | null = null;private channelName = "com.svprdga.torchlight/main";private NATIVE_EVENT_TORCH_AVAILABLE = "torch_available";private NATIVE_EVENT_ENABLE_TORCH = "enable_torch";private NATIVE_EVENT_DISABLE_TORCH = "disable_torch";private cameraManager: camera.CameraManager | undefined = undefined;constructor() {}getUniqueClassName(): string {return "TorchLightPlugin"}onAttachedToEngine(binding: FlutterPluginBinding): void {this.channel = new MethodChannel(binding.getBinaryMessenger(), this.channelName);this.channel.setMethodCallHandler(this)}onDetachedFromEngine(binding: FlutterPluginBinding): void {if (this.channel != null) {this.channel.setMethodCallHandler(null)}}onMethodCall(call: MethodCall, result: MethodResult): void {this.cameraManager=camera.getCameraManager(getContext());switch (call.method) {case this.NATIVE_EVENT_TORCH_AVAILABLE : this.isTorchAvailable(result);      break;case this.NATIVE_EVENT_ENABLE_TORCH :  this.enableTorch(result);      break;case this.NATIVE_EVENT_DISABLE_TORCH :  this.disableTorch(result);      break;}}//检查设备是否有可用的手电筒isTorchAvailable(result: MethodResult){if(this.cameraManager!==null){registerTorchStatusChange(this.cameraManager!)}result.success(this.cameraManager!==null?isTorchSupported(this.cameraManager!):false);}//启用手电筒enableTorch(result: MethodResult){if(this.cameraManager!==null){if(isTorchSupported(this.cameraManager!)&&isTorchModeSupported(this.cameraManager!,camera.TorchMode.ON)){setTorchMode(this.cameraManager!,camera.TorchMode.ON)}}result.success(null)}//禁用手电筒disableTorch(result: MethodResult){if(this.cameraManager!==null){if(isTorchSupported(this.cameraManager!)&&isTorchModeSupported(this.cameraManager!,camera.TorchMode.ON)){setTorchMode(this.cameraManager!,camera.TorchMode.OFF)unregisterTorchStatusChange(this.cameraManager!)}}result.success(null)}}
///检测是否支持手电筒
function isTorchSupported(cameraManager: camera.CameraManager) : boolean {let torchSupport: boolean = false;try {torchSupport = cameraManager.isTorchSupported();} catch (error) {let err = error as BusinessError;console.error('Failed to torch. errorCode = ' + err.code);}console.info('Returned with the torch support status:' + torchSupport);return torchSupport;
}
///检测是否支持指定的手电筒模式TorchMode
function isTorchModeSupported(cameraManager: camera.CameraManager, torchMode: camera.TorchMode) : boolean {let isTorchModeSupport: boolean = false;try {isTorchModeSupport = cameraManager.isTorchModeSupported(torchMode);} catch (error) {let err = error as BusinessError;console.error('Failed to set the torch mode. errorCode = ' + err.code);}return isTorchModeSupport;
}
//返回设备当前手电筒模式
function getTorchMode(cameraManager: camera.CameraManager): camera.TorchMode | undefined {let torchMode: camera.TorchMode | undefined = undefined;torchMode = cameraManager.getTorchMode();return torchMode;
}function onTorchStatusChange(cameraManager: camera.CameraManager): void {cameraManager.on('torchStatusChange', (err: BusinessError, torchStatusInfo: camera.TorchStatusInfo) => {if (err !== undefined && err.code !== 0) {console.error(`Callback Error, errorCode: ${err.code}`);return;}console.info(`onTorchStatusChange, isTorchAvailable: ${torchStatusInfo.isTorchAvailable}, isTorchActive: ${torchStatusInfo.isTorchActive}, level: ${torchStatusInfo.torchLevel}`);});
}
///设置手电筒模式
function setTorchMode(cameraManager: camera.CameraManager, torchMode: camera.TorchMode): void {try {cameraManager.setTorchMode(torchMode);} catch (error) {// 失败返回错误码error.code并处理let err = error as BusinessError;console.error(`The setTorchMode call failed. error code: ${err.code}`);}
}
function callback(err: BusinessError, torchStatusInfo: camera.TorchStatusInfo): void {if (err !== undefined && err.code !== 0) {console.error(`Callback Error, errorCode: ${err.code}`);return;}console.info(`onTorchStatusChange, isTorchAvailable: ${torchStatusInfo.isTorchAvailable}, isTorchActive: ${torchStatusInfo.isTorchActive}, level: ${torchStatusInfo.torchLevel}`);
}function registerTorchStatusChange(cameraManager: camera.CameraManager): void {cameraManager.on('torchStatusChange', callback);
}
function unregisterTorchStatusChange(cameraManager: camera.CameraManager): void {cameraManager.off('torchStatusChange');
}

四、编写 Example

1. 创建 Example 应用

在插件根目录下创建一个名为 example 的文件夹,用于存放示例应用。在 example 文件夹中,创建一个鸿蒙平台的 Flutter 应用,用于验证插件功能。

2. 签名与运行

使用 Deveco Studio 打开 example > ohos 目录,单击 File > Project Structure > Project > Signing Configs,勾选 Automatically generate signature,等待自动签名完成。然后运行以下命令:

flutter pub getflutter build hap --debug

如果应用正常启动,说明插件适配成功。如果没有,欢迎大家联系坚果派一起支持。

五、总结

通过以上步骤,我们成功地将 torch_light 三方库适配到了鸿蒙平台。这个过程涉及到了解插件的基本信息、配置开发环境、创建鸿蒙模块、编写原生代码以及测试验证等多个环节。希望这篇博客能够帮助到需要进行 torch_light 鸿蒙适配的开发者们,让大家在鸿蒙生态的开发中更加得心应手。

六、参考

  • [如何使用Flutter与OpenHarmony通信 FlutterChannel](https://gitcode.com/openharmony-sig/flutter_samples/blob/master/ohos/docs/04_development/如何使用Flutter与OpenHarmony通信 FlutterChannel.md)
  • [开发module](https://gitcode.com/openharmony-sig/flutter_samples/blob/master/ohos/docs/04_development/如何使用混合开发 module.md)
  • 开发package
  • 开发plugin
  • [开发FFI plugin](https://gitcode.com/openharmony-sig/flutter_samples/blob/master/ohos/docs/04_development/开发FFI plugin.md)
  • developing-packages
  • 适配仓库地址
  • 手电筒

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/diannao/84364.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【信息系统项目管理师】一文掌握高项常考题型-项目进度类计算

更多内容请见: 备考信息系统项目管理师-专栏介绍和目录 文章目录 一、进度类计算的基本概念1.1 前导图法1.2 箭线图法1.3 时标网络图1.4 确定依赖关系1.5 提前量与滞后量1.6 关键路径法1.7 总浮动时间1.8 自由浮动时间1.9 关键链法1.10 资源优化技术1.11 进度压缩二、基本公式…

深入了解linux系统—— 操作系统的路径缓冲与链接机制

前言 在之前学习当中&#xff0c;我们了解了被打开的文件是如何管理的&#xff1b;磁盘&#xff0c;以及ext2文件系统是如何存储文件的。 那我们要打开一个文件&#xff0c;首先要先找到这个文件&#xff0c;操作系统又是如何去查找的呢&#xff1f; 理解操作系统搜索文件 …

Docker Hub仓库介绍

Docker Hub仓库全解析&#xff1a;从公共市场到私有化部署指南 一、Docker Hub公共镜像市场 1.1 核心功能解析 全球最大容器镜像库&#xff1a;累计托管超500万镜像核心服务矩阵&#xff1a; #mermaid-svg-CAMkhmtSWKEUw7z0 {font-family:"trebuchet ms",verdana,a…

redis使用RDB文件恢复数据

设置存盘间隔为120秒且10个key改变数据自动存盘使用RDB文件恢复数据 IP地址主机名192.168.10.170redis170 [rootredis170 ~]# yum install -y redis [rootredis170 ~]# systemctl start redis步骤一&#xff1a;设置存盘间隔为120秒且10个key改变自动存盘 [rootredis170 ~]#…

SpringBoot多环境配置文件切换

resources下application.yml、application-dev.yml、application-prod.yml多个配置文件。 spring:profiles:active: devspring:profiles:active: prod一般都是通过修改spring.profiles.active值来修改加载不同环境的配置信息&#xff0c;可以把切换的dev/prod放到pom.xml文件来…

Java 并发编程高级技巧:CyclicBarrier、CountDownLatch 和 Semaphore 的高级应用

Java 并发编程高级技巧&#xff1a;CyclicBarrier、CountDownLatch 和 Semaphore 的高级应用 一、引言 在 Java 并发编程中&#xff0c;CyclicBarrier、CountDownLatch 和 Semaphore 是三个常用且强大的并发工具类。它们在多线程场景下能够帮助我们实现复杂的线程协调与资源控…

【Java多线程】多线程状态下如何安全使用ArrayList以及哈希表

&#x1f50d; 开发者资源导航 &#x1f50d;&#x1f3f7;️ 博客主页&#xff1a; 个人主页&#x1f4da; 专栏订阅&#xff1a; JavaEE全栈专栏 多线程安全使用ArrayList 手动加锁 日常中最常用的方法&#xff0c;使用synchronized进行加锁&#xff0c;把代码打包成一份&a…

InnoDB引擎底层解析(二)之InnoDB的Buffer Pool(三)

Buffer Pool 实例 我们上边说过&#xff0c;Buffer Pool 本质是 InnoDB 向操作系统申请的一块连续的内存空间&#xff0c;在多线程环境下&#xff0c;访问 Buffer Pool 中的各种链表都需要加锁处理&#xff0c;在Buffer Pool特别大而且多线程并发访问特别高的情况下&#xff0…

Netty学习专栏(三):Netty重要组件详解(Future、ByteBuf、Bootstrap)

文章目录 前言一、Future & Promise&#xff1a;异步编程的救星1.1 传统NIO的问题1.2 Netty的解决方案1.3 代码示例&#xff1a;链式异步操作 二、ByteBuf&#xff1a;重新定义数据缓冲区2.1 传统NIO ByteBuffer的缺陷2.2 Netty ByteBuf的解决方案2.3 代码示例&#xff1a;…

Vue3逐步抛弃虚拟Dom,React如何抉择

虚拟DOM&#xff1a;前端界的替死鬼 这玩意儿就是个前端开发的充气娃娃&#xff01; 你以为它很牛逼&#xff1f;无非是给真DOM当替死鬼&#xff01; 每次数据变&#xff0c;虚拟DOM先搁内存里自嗨一顿&#xff0c;diff算法跟便秘似的算半天&#xff0c;最后才敢碰真DOM。 说白…

分布式锁总结

文章目录 分布式锁什么是分布式锁&#xff1f;分布式锁的实现方式基于数据库(mysql)实现基于缓存(redis)多实例并发访问问题演示项目代码(使用redis)配置nginx.confjmeter压测复现问题并发是1&#xff0c;即不产生并发问题并发30测试,产生并发问题(虽然单实例是synchronized&am…

解决自签名证书HTTPS告警:强制使用SHA-256算法生成证书

解决自签名证书HTTPS告警&#xff1a;强制使用SHA-256算法生成证书 一、问题场景 在使用OpenSSL生成和配置自签名证书时&#xff0c;常遇到以下现象&#xff1a; 浏览器已正确导入根证书&#xff08;.pem文件&#xff09;&#xff0c;但访问HTTPS站点时仍提示不安全连接或证…

线上 Linux 环境 MySQL 磁盘 IO 高负载深度排查与性能优化实战

目录 一、线上告警 二、问题诊断 1. 系统层面排查 2. 数据库层面分析 三、参数调优 1. sync_binlog 参数优化 2. innodb_flush_log_at_trx_commit 参数调整 四、其他优化建议 1. 日志文件位置调整 2. 生产环境核心参数配置模板 3. 突发 IO 高负载应急响应方案 五、…

window 显示驱动开发-初始化和 DMA 缓冲区创建

若要指示 GPU 支持 GDI 硬件加速&#xff0c;显示微型端口驱动程序的 DriverEntry 函数实现必须使用指向驱动程序实现的 DxgkDdiRenderKm 函数的指针填充 DRIVER_INITIALIZATION_DATA 结构的 DxgkDdiRenderKm 成员。 DirectX 图形内核子系统调用 DxgkDdiRenderKm 函数&#xf…

Go语言实战:使用 excelize 实现多层复杂Excel表头导出教程

Go 实现支持多层复杂表头的 Excel 导出工具 目录 项目介绍依赖说明核心结构设计如何支持多层表头完整使用示例总结与扩展 项目介绍 在实际业务系统中&#xff0c;Excel 文件导出是一项常见功能&#xff0c;尤其是报表类需求中常见的复杂多级表头&#xff0c;常规表格组件往…

机器视觉6-halcon高级教程

机器视觉6-halcon高级教程 双目立体视觉原理视差外极线几何双目标定 双目立体视觉之Halcon标定一&#xff0e;标定结果二.Halcon标定过程1.获取左右相机图像中标定板的区域;2.提取左右相机图像中标定板的MARK点坐标和摄像机外部参数;3.执行双目标定;4.获取非标准外极线几何到标…

板凳-------Mysql cookbook学习 (六)

2025年Pytorch-gpu版本安装&#xff08;各种情况适用自己的安装需求&#xff0c;亲测绝对有效&#xff0c;示例安装torch2.6.0&#xff0c;过程详细面向小白&#xff09;_torch gpu版本-CSDN博客 https://blog.csdn.net/OpenSeek/article/details/145795127 2.2 查错 import s…

Spring boot和SSM项目对比

目录对比 springboot目录 project├─src│ ├─main│ │ ├─java│ │ │ ├─com.example.demo│ │ │ │ ├─config // 存放SpringBoot的配置类│ │ │ │ ├─controller // 存放控制器类│ │ │ │ ├─entity // 存…

《关于浔川社团退出DevPress社区及内容撤回的声明》

《关于浔川社团退出DevPress社区及内容撤回的声明》 尊敬的DevPress社区及读者&#xff1a; 经浔川社团内部决议&#xff0c;我社决定自**2025年5月26日**起正式退出DevPress社区&#xff0c;并撤回所有由我社成员在该平台发布的原创文章。相关事项声明如下&#xff1a; …

Python性能优化利器:__slots__的深度解析与避坑指南

核心场景&#xff1a;当需要创建数百万个属性固定的对象时&#xff0c;默认的__dict__字典存储会造成巨大内存浪费。此时__slots__能通过元组结构取代字典&#xff0c;显著提升内存效率&#xff08;实测节省58%内存&#xff09;&#xff01; 底层原理&#xff1a;为何能节省内…