| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- import 'dart:io';
- import 'package:camera/camera.dart' as camera_pkg;
- import 'package:sino_med_cloud/core/utils/logger.dart';
- import 'package:sino_med_cloud/core/utils/toast_utils.dart';
- import 'package:sino_med_cloud/permission/permission_service.dart';
- import 'media_exception.dart';
- // 使用别名避免命名冲突
- typedef CameraController = camera_pkg.CameraController;
- typedef ResolutionPreset = camera_pkg.ResolutionPreset;
- typedef CameraLensDirection = camera_pkg.CameraLensDirection;
- typedef XFile = camera_pkg.XFile;
- /// 专业相机服务
- ///
- /// 适合医疗影像、证件采集、高质量拍照、连拍/自定义UI
- ///
- /// 使用示例:
- /// ```dart
- /// final cameraService = CameraService();
- /// await cameraService.init();
- /// final photo = await cameraService.takePicture();
- /// cameraService.dispose();
- /// ```
- class CameraService {
- CameraController? _controller;
- bool _isInitialized = false;
- bool _isDisposed = false;
- /// 初始化相机
- ///
- /// - [resolutionPreset] 分辨率预设,默认 veryHigh
- /// - [enableAudio] 是否启用音频,默认 false
- /// - [lensDirection] 镜头方向,默认后置摄像头
- ///
- /// 抛出 [MediaCameraException] 如果初始化失败
- Future<void> init({
- ResolutionPreset resolutionPreset = camera_pkg.ResolutionPreset.veryHigh,
- bool enableAudio = false,
- CameraLensDirection lensDirection = camera_pkg.CameraLensDirection.back,
- }) async {
- try {
- if (_isDisposed) {
- throw MediaCameraException('相机服务已释放,无法初始化');
- }
- if (_isInitialized) {
- AppLogger.d('相机已初始化,跳过重复初始化');
- return;
- }
- // 检查相机权限
- final permissionResult = await PermissionService.request(PermissionType.camera);
- if (permissionResult != PermissionResult.granted) {
- if (permissionResult == PermissionResult.permanentlyDenied) {
- ToastUtils.showError('相机权限被永久拒绝,请在设置中开启相机权限');
- // 自动打开设置页面
- await PermissionService.openSetting();
- throw MediaCameraException('相机权限被永久拒绝,已引导用户前往设置');
- } else {
- ToastUtils.showError('需要相机权限才能使用相机');
- throw MediaCameraException('相机权限被拒绝');
- }
- }
- AppLogger.d('开始初始化相机...');
- final cameras = await camera_pkg.availableCameras();
-
- if (cameras.isEmpty) {
- throw MediaCameraException('未找到可用相机');
- }
- final camera = cameras.firstWhere(
- (c) => c.lensDirection == lensDirection,
- orElse: () => cameras.first,
- );
- _controller = camera_pkg.CameraController(
- camera,
- resolutionPreset,
- enableAudio: enableAudio,
- );
- await _controller!.initialize();
- _isInitialized = true;
- AppLogger.d('相机初始化成功: ${camera.name}');
- } catch (e) {
- _isInitialized = false;
- if (e is camera_pkg.CameraException) {
- AppLogger.e('相机初始化失败(原生异常)', e);
- throw MediaCameraException(
- '相机初始化失败: ${e.toString()}',
- originalError: e,
- );
- }
- if (e is MediaCameraException) {
- rethrow;
- }
- AppLogger.e('相机初始化失败', e);
- throw MediaCameraException(
- '相机初始化失败: ${e.toString()}',
- originalError: e,
- );
- }
- }
- /// 获取相机控制器
- ///
- /// 抛出 [MediaCameraException] 如果相机未初始化
- CameraController get controller {
- if (!_isInitialized || _controller == null) {
- throw MediaCameraException('相机未初始化,请先调用 init()');
- }
- return _controller!;
- }
- /// 检查相机是否已初始化
- bool get isInitialized => _isInitialized && _controller?.value.isInitialized == true;
- /// 拍照
- ///
- /// 返回拍摄的照片文件,如果失败返回 null
- ///
- /// 抛出 [MediaCameraException] 如果拍照失败
- Future<File?> takePicture() async {
- try {
- if (!isInitialized) {
- throw MediaCameraException('相机未初始化,无法拍照');
- }
- AppLogger.d('开始拍照...');
- final camera_pkg.XFile file = await _controller!.takePicture();
- final photoFile = File(file.path);
- if (!await photoFile.exists()) {
- throw MediaCameraException('拍照失败:文件不存在');
- }
- AppLogger.d('拍照成功: ${photoFile.path}');
- return photoFile;
- } catch (e) {
- if (e is camera_pkg.CameraException) {
- AppLogger.e('拍照失败(原生异常)', e);
- throw MediaCameraException(
- '拍照失败: ${e.toString()}',
- originalError: e,
- );
- }
- if (e is MediaCameraException) {
- rethrow;
- }
- AppLogger.e('拍照失败', e);
- throw MediaCameraException(
- '拍照失败: ${e.toString()}',
- originalError: e,
- );
- }
- }
- /// 释放相机资源
- ///
- /// 必须在不再使用相机时调用,否则可能导致资源泄漏
- Future<void> dispose() async {
- if (_isDisposed) {
- return;
- }
- try {
- if (_controller != null) {
- await _controller!.dispose();
- _controller = null;
- }
- _isInitialized = false;
- _isDisposed = true;
- AppLogger.d('相机资源已释放');
- } catch (e) {
- AppLogger.e('释放相机资源失败', e);
- }
- }
- }
|