赞
踩
flutter开发实战-自定义相机camera功能。
Flutter 本质上只是一个 UI 框架,运行在宿主平台之上,Flutter 本身是无法提供一些系统能力,比如使用蓝牙、相机、GPS等,因此要在 Flutter 中调用这些能力就必须和原生平台进行通信。
实现相机功能,我们使用的是camera插件。
在pubspec.yaml引入插件
# Camera相机拍照等
camera: ^0.10.5+2
image: ^4.0.17
在iOS的info.plist文件增加相机、麦克风权限
<key>NSCameraUsageDescription</key>
<string>your usage description here</string>
<key>NSMicrophoneUsageDescription</key>
<string>your usage description here</string>
在Android的android/app/build.gradle调整
minSdkVersion 21
处理详情权限获取,以下是权限错误的类型
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), ); } }
通过重写didChangeAppLifecycleState方法来处理生命周期更改,如下所示:
使用WidgetsBindingObserver
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
final CameraController? cameraController = controller;
// App state changed before we got the chance to initialize.
if (cameraController == null || !cameraController.value.isInitialized) {
return;
}
if (state == AppLifecycleState.inactive) {
cameraController.dispose();
} else if (state == AppLifecycleState.resumed) {
_initializeCameraController(cameraController.description);
}
}
这个需要使用到具体Container的宽高和aspectRatio做处理
if (controller != null && controller!.value.isInitialized) { // 设备像素比 double deviceRatio = 1.0; if (widget.width! > widget.height!) { deviceRatio = widget.width! / widget.height!; } else { deviceRatio = widget.height! / widget.width!; } // 相机纵横比 final double aspectRatio = controller!.value.aspectRatio; double scale = aspectRatio / deviceRatio; return Container( key: _cameraContainerGlobalKey, width: widget.width, height: widget.height, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( color: Colors.transparent, borderRadius: BorderRadius.circular(20.r), ), child: Stack( alignment: Alignment.center, clipBehavior: Clip.hardEdge, children: [ Container( width: widget.width, height: widget.height, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( color: Colors.transparent, ), child: RepaintBoundary( key: _cameraViewGlobalKey, child: Transform.scale( scale: scale * aspectRatio, child: AspectRatio( aspectRatio: aspectRatio, child: Center( child: CameraPreview( controller!, ), ), ), ), ), ),),); }
使用拍照功能,需要用到CameraController
Future<XFile?> takePicture() async { final CameraController? cameraController = controller; if (cameraController == null || !cameraController.value.isInitialized) { print('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(); print("takePicture file:${file.toString()}"); return file; } on CameraException catch (e) { print("takePicture exception:${e.toString()}"); return null; } }
获取到File,可以得到图片的path。
暂停及恢复预览
if (!cameraController.value.isPreviewPaused) {
await cameraController.pausePreview();
}
if (cameraController.value.isPreviewPaused) {
await cameraController.resumePreview();
}
裁剪图片这里使用的是插件:image
引入插件
image: ^4.0.17
实现裁剪
if (file != null) { // 保存到相册 // await SaveToAlbumUtil.saveLocalImage(file.path); RenderBox renderBox = _cameraContainerGlobalKey.currentContext! .findRenderObject() as RenderBox; // offset.dx , offset.dy 就是控件的左上角坐标 Offset offset = renderBox.localToGlobal(Offset.zero); //获取size Size size = renderBox.size; // 创建文件path String imageDir = await PathUtil.createDirectory("local_images"); String imagePath = '$imageDir/${TimeUtil.currentTimeMillis()}.png'; // 获取当前设备的像素比 double dpr = ui.window.devicePixelRatio; print("devicePixelRatio:${dpr}"); print( "offset:(${offset.dx},${offset.dy})--size:(${size.width},${size.height})"); File? targetFile = await ImageUtil.cropImage(file.path, imagePath, x: (dpr * offset.dx).floor(), y: (dpr * offset.dy).floor(), width: (dpr * size.width).ceil(), height: (dpr * size.height).ceil()); if (targetFile != null) { await SaveToAlbumUtil.saveLocalImage(targetFile.path); } }
裁剪结果如图所示
flutter开发实战-自定义相机camera功能,拍照及图片裁剪功能.
学习记录,每天不停进步。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。