Flutter跨平台相机测试工具testcamera鸿蒙化使用指南
Flutter for OpenHarmony相机插件testcamera实现了在鸿蒙系统上的相机预览功能。该插件通过Flutter的Texture组件与鸿蒙原生相机API交互,支持OpenHarmony API 9+平台,提供简洁的API接口和自动权限处理。使用需配置相机和麦克风权限,通过MethodChannel实现原生与Flutter的通信,Texture组件渲染相机画面。开发者可通过引入G

一、插件介绍
testcamera是一个专为OpenHarmony优化的Flutter相机测试工具,用于在Flutter应用中实现相机预览功能。该工具通过Flutter的Texture组件与鸿蒙原生相机API交互,提供了简单易用的相机预览接口,适配鸿蒙系统的相机能力。
主要功能特点:
- 支持OpenHarmony API 9+平台
- 实现相机预览功能
- 自动处理相机权限申请
- 支持Texture纹理渲染
- 适配鸿蒙系统的原生相机API
- 提供简洁的Flutter API接口
二、环境准备
系统要求:
- OpenHarmony API 9+
- Flutter SDK 3.0+
- DevEco Studio 3.0+
三、插件使用
3.1 依赖引入
由于该三方库为自定义修改版本,需要以git形式引入。在项目的pubspec.yaml文件中添加以下配置:
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
camera:
git:
url: "https://atomgit.com/flutter/plugins"
path: "packages/camera/camera"
然后执行flutter pub get命令获取依赖。
3.2 权限配置
在OpenHarmony项目中,需要在module.json5文件中声明相机和麦克风权限:
{
"module": {
"requestPermissions": [
{"name": "ohos.permission.CAMERA"},
{"name": "ohos.permission.MICROPHONE"}
]
}
}
3.3 API调用示例
3.3.1 相机预览页面实现
创建一个相机预览页面,使用Texture组件显示相机画面:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class CameraPage extends StatefulWidget {
const CameraPage({super.key});
_CameraPageState createState() => _CameraPageState();
}
class _CameraPageState extends State<CameraPage> {
final MethodChannel _channel = MethodChannel('CameraControlChannel');
int textureId = -1;
void initState() {
super.initState();
newTexture();
startCamera();
}
void dispose() {
super.dispose();
if (textureId >= 0) {
_channel.invokeMethod('unregisterTexture', {'textureId': textureId});
}
}
void startCamera() async {
await _channel.invokeMethod('startCamera');
}
void newTexture() async {
int id = await _channel.invokeMethod('registerTexture');
setState(() {
this.textureId = id;
});
}
Widget getTextureBody(BuildContext context) {
return Container(
width: 500,
height: 500,
child: Texture(
textureId: textureId,
),
);
}
Widget build(BuildContext context) {
Widget body = textureId >= 0 ? getTextureBody(context) : Text('loading...');
print('build textureId :$textureId');
return Scaffold(
appBar: AppBar(
title: Text("相机预览"),
),
body: Container(
color: Colors.white,
height: 500,
child: Center(
child: body,
),
),
);
}
}
3.3.2 应用入口配置
在应用入口文件中配置相机预览页面:
import 'package:flutter/material.dart';
import 'CameraPage.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return MaterialApp(
title: '相机测试应用',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const CameraPage(),
);
}
}
3.3.3 鸿蒙原生插件实现
鸿蒙原生插件实现了相机的初始化、预览和权限处理等功能:
import { FlutterPlugin, FlutterPluginBinding } from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin';
import { TextureRegistry } from '@ohos/flutter_ohos/src/main/ets/view/TextureRegistry';
import MethodChannel from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel';
import Log from '@ohos/flutter_ohos/src/main/ets/util/Log';
import { checkPermissions, reqPermissionsFromUser, permissions } from './PermissionUtil';
import { getCameraManager, getCameraDevices, getCameraInput, getSupportedOutputCapability, getPreviewOutput, getCaptureSession, beginConfig, setSessionCameraInput, setSessionPreviewOutput, startSession } from './CameraUtil';
import { common } from '@kit.AbilityKit';
const TAG = "CameraPlugin";
export class CameraPlugin implements FlutterPlugin {
private binding: FlutterPluginBinding | null = null;
private mMethodChannel: MethodChannel | null = null;
private textureRegistry: TextureRegistry | null = null;
private textureId: number = -1;
private surfaceId: number = -1;
getUniqueClassName(): string {
return TAG;
}
onAttachedToEngine(binding: FlutterPluginBinding): void {
Log.e(TAG, "CameraPlugin onAttachedToEngine");
this.binding = binding;
this.mMethodChannel = new MethodChannel(binding.getBinaryMessenger(), "CameraControlChannel");
this.mMethodChannel.setMethodCallHandler((call, result) => {
let method: string = call.method;
Log.e(TAG, "Received '" + method + "' message.");
switch (method) {
case "registerTexture":
this.registerCameraTexture();
result.success(this.textureId);
break;
case "startCamera":
this.startCamera();
result.success(null);
break;
case "unregisterTexture":
this.unregisterTexture(call.argument("textureId"));
result.success(null);
break;
}
});
this.textureRegistry = binding.getTextureRegistry();
}
onDetachedFromEngine(binding: FlutterPluginBinding): void {
this.binding = null;
this.mMethodChannel = null;
}
registerCameraTexture(): void {
Log.i(TAG, "start register Camera texture in flutter engine");
this.textureId = this.textureRegistry!.getTextureId();
this.surfaceId = this.textureRegistry!.registerTexture(this.textureId)!.getSurfaceId();
}
unregisterTexture(textureId: number): void {
this.textureRegistry!.unregisterTexture(textureId);
}
startCamera() {
checkPermissions(permissions).then((value: boolean) => {
if (value) {
this.startSession();
} else {
reqPermissionsFromUser(permissions, getContext(this) as Context).then((value: boolean) => {
if (value) {
this.startSession();
} else {
console.log(`[camera test] 授权失败`);
}
});
}
});
}
startSession() {
console.log(`[camera test] 已经授权,相机开始拍摄`);
let cameraManager = getCameraManager(getContext(this) as common.BaseContext);
let cameraDevices = getCameraDevices(cameraManager);
let cameraInput = getCameraInput(cameraDevices[0], cameraManager);
if (cameraInput != null) {
getSupportedOutputCapability(cameraDevices[0], cameraManager, cameraInput)
.then((supportedOutputCapability) => {
if (supportedOutputCapability != undefined) {
let previewOutput = getPreviewOutput(cameraManager, supportedOutputCapability, this.surfaceId.toString());
let captureSession = getCaptureSession(cameraManager);
if (captureSession != undefined && previewOutput != undefined && cameraInput != null) {
beginConfig(captureSession);
setSessionCameraInput(captureSession, cameraInput);
setSessionPreviewOutput(captureSession, previewOutput);
startSession(captureSession);
}
}
});
}
}
}
3.4 相机工具类
相机工具类封装了鸿蒙原生相机API的调用:
import camera from '@ohos.multimedia.camera';
import { BusinessError } from '@ohos.base';
import common from '@ohos.app.ability.common';
// 获取相机管理器
export function getCameraManager(context: common.BaseContext): camera.CameraManager {
let cameraManager: camera.CameraManager = camera.getCameraManager(context);
return cameraManager;
}
// 获取相机设备列表
export function getCameraDevices(cameraManager: camera.CameraManager): Array<camera.CameraDevice> {
let cameraArray: Array<camera.CameraDevice> = cameraManager.getSupportedCameras();
if (cameraArray != undefined && cameraArray.length <= 0) {
console.error("[camera test] cameraManager.getSupportedCameras error");
return [];
}
return cameraArray;
}
// 获取相机输入流
export function getCameraInput(cameraDevice: camera.CameraDevice, cameraManager: camera.CameraManager): camera.CameraInput | null {
let cameraInput: camera.CameraInput | null = null;
try {
cameraInput = cameraManager.createCameraInput(cameraDevice);
cameraInput.on('error', cameraDevice, (error: BusinessError) => {
console.info(`[camera test] Camera input error code: ${error.code}`);
});
} catch (error) {
let err = error as BusinessError;
console.error('[camera test] Failed to createCameraInput errorCode = ' + err.code);
}
return cameraInput;
}
// 获取相机预览输出流
export function getPreviewOutput(cameraManager: camera.CameraManager, cameraOutputCapability: camera.CameraOutputCapability, surfaceId: string): camera.PreviewOutput | undefined {
let previewProfilesArray: Array<camera.Profile> = cameraOutputCapability.previewProfiles;
let previewOutput: camera.PreviewOutput | undefined = undefined;
try {
previewOutput = cameraManager.createPreviewOutput(previewProfilesArray[0], surfaceId);
} catch (error) {
let err = error as BusinessError;
console.error("[camera test] Failed to create the PreviewOutput instance. error code: " + err.code);
}
return previewOutput;
}
// 启动相机会话
export async function startSession(captureSession: camera.CaptureSession): Promise<void> {
try {
await captureSession.commitConfig();
} catch (error) {
let err = error as BusinessError;
console.error(`[camera test] Failed to commitConfig. error: ${JSON.stringify(err)}`);
}
try {
await captureSession.start()
} catch (error) {
let err = error as BusinessError;
console.error(`[camera test] Failed to start. error: ${JSON.stringify(err)}`);
}
}
四、测试方法
4.1 编译运行应用
使用DevEco Studio编译并运行应用,应用启动后会自动请求相机权限,权限通过后会显示相机预览画面。
4.2 权限测试
首次运行应用时,会弹出相机和麦克风权限请求对话框,用户需要点击"允许"才能使用相机功能。
4.3 功能测试
应用启动后,会在屏幕中央显示一个500x500的相机预览区域,显示当前相机的实时画面。
五、常见问题与解决方案
5.1 相机权限被拒绝
可能原因:
- 用户拒绝了相机权限
- 应用未在module.json5中声明相机权限
解决方案:
- 重新运行应用,点击"允许"授予相机权限
- 检查module.json5中是否正确声明了相机和麦克风权限
5.2 相机预览不显示
可能原因:
- Texture组件未正确初始化
- 相机设备被其他应用占用
- 相机API调用失败
解决方案:
- 检查Texture组件的textureId是否正确设置
- 关闭其他可能占用相机的应用
- 查看日志,排查相机API调用失败的原因
5.3 应用崩溃
可能原因:
- 相机权限未正确处理
- 相机API调用异常
- Texture组件使用不当
解决方案:
- 检查权限处理逻辑
- 查看日志,定位崩溃原因
- 确保Texture组件在dispose时正确释放资源
六、总结
testcamera是一个专为OpenHarmony平台优化的Flutter相机测试工具,通过本指南,你可以了解如何:
- 以git形式引入自定义修改的相机依赖
- 在OpenHarmony项目中配置相机权限
- 使用Flutter的Texture组件实现相机预览
- 处理相机权限申请
- 与鸿蒙原生相机API交互
该工具为开发者提供了在OpenHarmony平台上使用Flutter实现相机功能的完整示例,帮助他们快速集成相机功能到自己的应用中。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐
所有评论(0)