当前位置: 首页 > ds >正文

Flutter - 原生交互 - 相机Camera - 02

环境

Flutter 3.29

macOS Sequoia 15.4.1

Xcode 16.3

集成

  1. 创建一个带有 State 类的 StatefulWidget 组件
  2. 添加一个变量到 State 类来存放 CameraController
  3. 添加另外一个变量到 State 类中来存放 CameraController.initialize() 返回的 Future
  4. 在 initState() 方法中创建并初始化控制器
  5. 在 dispose() 方法中销毁控制器

创建并初始化 CameraController

新建take_photo.dart文件,输入stf快捷创建一个有状态的Widget,命名为TakePictureScreen

class TakePictureScreen extends StatefulWidget {const TakePictureScreen({super.key, required this.cameras});/// 1.接收调用时传入的摄像头列表final List<CameraDescription> cameras;TakePictureScreenState createState() => TakePictureScreenState();
}class TakePictureScreenState extends State<TakePictureScreen> {/// 2. 相机控制器late CameraController _controller;/// 3. Future类型存放CameraController初始化返回的Futurelate Future<void> _initializeControllerFuture;
}

在 initState 方法中创建并初始化控制器

class TakePictureScreenState extends State<TakePictureScreen> {/// 根据camera的原生代码,从availableCameras获取可用摄像头列表时,后置索引为0/**switch device.position {case .back:lensFacing = .backcase .front:lensFacing = .frontcase .unspecified:lensFacing = .external...reply.append(cameraDescription)*/int frontCamera = 0; ...void initState() {super.initState();/// 设置初始化后置摄像头_controller = CameraController(widget.cameras[frontCamera],/// 清晰度ResolutionPreset.medium,);_initializeControllerFuture = _controller.initialize();}...

预览

使用CameraPreview类实现相机的预览

FutureBuilder<void>(future: _initializeControllerFuture, // initState方法中Controller返回的Future对象builder: (context, snapshot) {// Controller初始化完成if (snapshot.connectionState == ConnectionState.done) {/// 显示预览return CameraPreview(_controller);} else {// 不然显示一个Loading动画return const Center(child: CircularProgressIndicator());}},
),

使用 CameraController 拍照

使用CameraController的takePicture()方法实现拍照功能

class TakePictureScreenState extends State<TakePictureScreen> {
String photoPath = "";
...
Widget build(BuildContext context) {
...
onPressed: () async {try {/// 确认摄像头已初始化await _initializeControllerFuture;/// 拍照并返回图片final image = await _controller.takePicture();/// 检查是否已挂载if (!context.mounted) return;/// 可以持久化存储 /// 设置更新状态,将这次的照片缩略图显示在左下角的预览图中setState(() {photoPath = image.path;});} catch (e) {// If an error occurs, log the error to the console.// print(e);}
},
}
}

发送消息到原生方法

/// FLTCam.m
- (void)captureToFileWithCompletion:(void (^)(NSString *_Nullable,FlutterError *_Nullable))completion
{AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings];// 对照片进行配置,比如是否高清,格式jpg还是heif,闪光灯,存储路径.../// 照片存储后的回调操作,移除该照片的uniqueID__weak typeof(self) weakSelf = self;FLTSavePhotoDelegate *savePhotoDelegate = [[FLTSavePhotoDelegate alloc]initWithPath:pathioQueue:self.photoIOQueuecompletionHandler:^(NSString *_Nullable path, NSError *_Nullable error) {typeof(self) strongSelf = weakSelf;if (!strongSelf) return;dispatch_async(strongSelf.captureSessionQueue, ^{// cannot use the outter `strongSelf`typeof(self) strongSelf = weakSelf;if (!strongSelf) return;[strongSelf.inProgressSavePhotoDelegates removeObjectForKey:@(settings.uniqueID)];});if (error) {completion(nil, FlutterErrorFromNSError(error));} else {NSAssert(path, @"Path must not be nil if no error.");completion(path, nil);}}];.../// 传入配置 + 回调代理对象 + 通知进行拍照[self.capturePhotoOutput capturePhotoWithSettings:settings delegate:savePhotoDelegate];}

在 dispose 方法中销毁控制器


void dispose() {_controller.dispose();super.dispose();
}

开启闪光灯

使用CameraController对象的setFlashMode方法

class TakePictureScreenState extends State<TakePictureScreen> {FlashMode _flashMode = FlashMode.always;
...
onPressed: () {/// 闪光灯不同配置下图标不一样,因此修改后触发buildsetState(() {if (_flashMode == FlashMode.always) {_flashMode = FlashMode.auto;} else if (_flashMode == FlashMode.auto) {_flashMode = FlashMode.off;} else if (_flashMode == FlashMode.off) {_flashMode = FlashMode.always;}_controller.setFlashMode(_flashMode);});
};
}

202506111538.gif

切换摄像头

通过更新CameraController对象的description参数来切换摄像头

 onPressed: () async {if (frontCamera == 1) {/// 重新创建CameraController并设置后置摄像头_controller = CameraController(widget.cameras[0],ResolutionPreset.medium,);/// 初始化完成后更新标记,刷新界面_controller.initialize().then((_) {if (mounted) {frontCamera = 0;setState(() {});}});} else {_controller = CameraController(widget.cameras[1],ResolutionPreset.medium,);_controller.initialize().then((_) {if (mounted) {frontCamera = 1;setState(() {});}});}
},

设置焦距

使用CameraController对象的setZoomLevel方法设置焦距

...
onPressed: () {setState(() {_controller.setZoomLevel(1);_isMinZoomLevel = true;minZoomLevelDesc = "0.5x";});
},

拍照后显示缩略图

使用Align设置缩略图靠左

SizedBox(width: MediaQuery.of(context).size.width,child: Stack(fit: StackFit.loose,children: [Align(alignment: Alignment(-0.8, 0),child: SizedBox(width: 60,height: 60,child:photoPath.isEmpty  // 相片为空,隐藏? Container(): Image.file(File(photoPath)),),
),

202506111614.gif

其它

装修结束,闲居在家

IMG_8130.png

参考

  1. 使用 Camera 插件实现拍照功能
  2. Flutter——最详细Stack(堆叠布局)使用教程
  3. flutter 调用摄像头,前后镜头切换
http://www.xdnf.cn/news/13652.html

相关文章:

  • 编程学习网站大全(C++/OpenCV/QT方向)—— 资源导航与深度评测
  • AI任务相关解决方案8-基于卷积神经网络(CNN)和反向传播神经网络(BPNN)的数字图像水印改进算法
  • git撤回commit
  • 力扣-121.买卖股票的最佳时机
  • 计算机系统概述(5)
  • Bandizip 7.38专业版安装教程【超详细】一键安装教程(永久使用)
  • MySQL 基础笔记
  • RNN:从记忆困境到序列建模革命
  • docker-compose和docker下载
  • 如何在docker desktop上安装mysql
  • 20250611让NanoPi NEO core开发板在Ubuntu core16.04系统下开机自启动的时候拉高GPIOG8
  • 缓冲区(C语言缓冲区+内核缓冲区)一个例子解释他们的关系和作用!!!
  • ElasticSearch 操作索引与映射的API
  • Springboot+idea热更新
  • 【全志V821_FoxPi】2-1 首次编译Tina5.0 SDK
  • Redis:set类型和zset类型
  • Google reCAPTCHA实现
  • 在线客服系统:未来商业沟通的关键武器
  • 使用VirtualBox安装ubuntu22.04虚拟机
  • 怎么设置51la统计?新手快速设置悟空统计
  • 大模型笔记_模型微调 vs RAG
  • 23、Python字符串核心机制解析:驻留原理、对象比较与成员检测实战
  • 亮数据抓取浏览器,亚马逊数据采集实战
  • 【电路物联网】SDN架构与工作原理介绍
  • 护照阅读器在景区的应用
  • Vue 数据代理机制实现
  • 什么是数据交换?有哪些数据交换方式?
  • Rust 学习笔记:关于 Future trait 和 Async 语法的练习题
  • el-select滚动分页加载、模糊搜索
  • 代码填空题技术实现:突破 highlight.js 安全限制的工程实践