HarmonyOS鸿蒙Next中Flutter框架多设备开发指导-拍照场景

HarmonyOS鸿蒙Next中Flutter框架多设备开发指导-拍照场景

1.1 场景概述

在多设备系统开发中,不同设备的屏幕形态各异,如直板机、折叠屏、平板、PC等,同时系统状态栏、导航栏、软键盘等元素也会占据屏幕空间。为了确保相机界面在各种设备和场景下都能正常显示,提供流畅的用户体验。本文将详细介绍 RN 相机组件的实现原理、适配指导以及具体的场景案例。

1.1.1 使用场景

在拍照录像应用、视频会议、扫码支付等应用中,相机功能是很常见的功能,希望在不同设备上相机界面都能正常显示和交互。下面是不同设备上相机界面的差异,包括:直板机、折叠屏(阔折叠、双折叠、三折叠)、平板、PC。

1.1.2 常见问题

开发过程中相机界面在不同设备上会存在差异,可能会出现以下问题:

  • 相机预览画面在不同设备上显示比例不一致
  • 操作按钮在不同设备上位置错位
  • 横竖屏切换时相机方向调整不正确
  • 折叠屏设备上相机界面被折痕遮挡

1.1.3 多设备适配

1.1.3.1 相机多设备尺寸自适应适配

说明

根据断点布局实现相机界面的自适应:

图1 横向断点sm,适配竖屏直板机,效果如下

图1

图2 横向断点md,适配横屏平板,效果如下

图2

图3 横向断点lg,适配折叠屏,效果如下

图3

图4 横向断点xl,适配PC,效果如下

图4

1.1.3.2 相机多设备窗口变化适配

适配点1: 基于断点实现组件自适应在多设备布局适配

适配点1

适配点2: 组件尺寸自适应覆盖拉伸、均分、占比、缩放、延伸、隐藏、折行

适配点2

1.2 开发指导

1.2.1 Flutter开发

1.2.1.1 关键能力

官方库camera插件

在yaml里面增加camera依赖(path_provider插件获取应用文档目录)

path_provider:
  git:
    url: https://gitcode.com/openharmony-tpc/flutter_packages.git
    path: packages/path_provider/path_provider
camera:
  git:
    url: https://gitcode.com/openharmony-tpc/flutter_packages.git
    path: packages/camera/camera

1.2.1.2 指导案例

  1. 初始化可用摄像头列表
Future<void> _fetchData() async {
  try {
    WidgetsFlutterBinding.ensureInitialized();
    var _cameras1 = await CameraPlatform.instance.availableCameras();
    setState(() {
      _cameras = _cameras1;
    });
  } on CameraException catch (e) {
    _logError(e.code, e.description);
  }
}
  1. 创建并初始化cameraController
final CameraController cameraController = CameraController(
  cameraDescription,
  kIsWeb ? ResolutionPreset.max : ResolutionPreset.medium,
  enableAudio: enableAudio,
  imageFormatGroup: ImageFormatGroup.jpeg,
);

await cameraController.initialize();
  1. 显示相机预览
CameraPreview(
  controller!,
  child: LayoutBuilder(
    builder: (BuildContext context, BoxConstraints constraints) {
    return GestureDetector(
      behavior: HitTestBehavior.opaque,
      onScaleStart: _handleScaleStart,
      onScaleUpdate: _handleScaleUpdate,
      onTapDown: (TapDownDetails details) =>
      onViewFinderTap(details, constraints),
    );
  })
)
  1. 拍照
Future<XFile?> takePicture() async {
  final CameraController? cameraController = controller;
  if (cameraController == null || !cameraController.value.isInitialized) {
    showInSnackBar('Error: select a camera first.');
    return null;
  }
  
  if (cameraController.value.isTakingPicture) {
    // A capture is already pending, do nothing.
    return null;
  }
  
  try {
    final XFile file = await cameraController.takePicture();
    return file;
  } on CameraException catch (e) {
    _showCameraException(e);
    return null;
  }
}
  1. 生命周期管理

确保在dispose()中释放相机资源

@override
void dispose() {
  _ambiguate(WidgetsBinding.instance)?.removeObserver(this);
  cameraController.dispose();
  controller.dispose();
  super.dispose();
}

1.2.1.3 示例代码

import 'package:camera/camera.dart';
import 'package:flutter/material.dart';

late List<CameraDescription> _cameras;

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  _cameras = await availableCameras();
  runApp(const CameraApp());
}

/// CameraApp is the Main Application.
class CameraApp extends StatefulWidget {
  /// Default Constructor
  const CameraApp({super.key});

  @override
  State<CameraApp> createState() => _CameraAppState();
}

class _CameraAppState extends State<CameraApp> {
  late CameraController controller;

  @override
  void initState() {
    super.initState();
    controller = CameraController(_cameras[0], ResolutionPreset.max);
    controller
    .initialize()
    .then((_) {
      if (!mounted) {
        return;
      }
      setState(() {});
    })
    .catchError((Object e) {
      if (e is CameraException) {
        switch (e.code) {
          case 'CameraAccessDenied':
          // Handle access errors here.
          break;
          default:
          // Handle other errors here.
          break;
        }
      }
    });
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if (!controller.value.isInitialized) {
      return Container();
    }
    return MaterialApp(home: CameraPreview(controller));
  }
}

相机的Sample示例代码地址:example,开发者可以通过该地址查看完整的示例代码,并根据自己的需求进行修改和扩展。


更多关于HarmonyOS鸿蒙Next中Flutter框架多设备开发指导-拍照场景的实战教程也可以访问 https://www.itying.com/category-92-b0.html

2 回复

在HarmonyOS Next上,使用Flutter框架实现多设备拍照场景,推荐集成 camera_ohos 插件。通过PlatformView桥接ArkUI相机组件,调用startImageCapturetakePicture方法捕获图片。需配置ohos.permission.CAMERA权限,并处理设备旋转、折叠屏适配(监听屏幕状态)。拍照结果通过ImagePicker或文件路径回传,支持多摄像头切换。

更多关于HarmonyOS鸿蒙Next中Flutter框架多设备开发指导-拍照场景的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在HarmonyOS Next中使用Flutter开发多设备相机应用,适配关键点在于基于断点的响应式布局窗口变化监听

1. 断点适配多设备屏幕 利用MediaQuery获取窗口宽度,划分断点(如sm竖屏手机、md横屏平板、lg折叠屏、xl PC),根据不同断点切换相机界面的操作栏、预览窗格等组件的布局方式(占比、均分、隐藏等),确保在不同设备上控件位置和大小合理。

2. 窗口变化与预览比例适配LayoutBuilder包裹CameraPreview,根据约束动态计算预览区域的尺寸和宽高比。监听MediaQuery.of(context).orientation处理横竖屏切换,调整相机预览方向。关键代码示例:

LayoutBuilder(
  builder: (context, constraints) {
    final aspectRatio = controller.value.aspectRatio;
    final previewHeight = constraints.maxWidth / aspectRatio;
    return CameraPreview(controller,
      child: SizedBox(
        width: constraints.maxWidth,
        height: previewHeight.clamp(0.0, constraints.maxHeight),
      ),
    );
  },
)

3. 生命周期管理dispose()中调用controller.dispose()释放相机资源,避免内存泄漏。

4. 基本开发流程

  • 添加依赖:在pubspec.yaml中引入camerapath_provider插件。
  • 初始化availableCameras()获取设备摄像头列表,CameraController初始化并设置分辨率。
  • 拍照controller.takePicture()返回XFile
  • 预览CameraPreview显示实时画面。

完整示例可从官方Git仓库获取。

回到顶部