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

Flutter 学习之旅 之 flutter 在 Android 端进行简单的打开前后相机预览 / 拍照保存

Flutter 学习之旅 之 flutter 在 Android 端进行简单的打开前后相机预览 / 拍照保存

目录

Flutter 学习之旅 之 flutter 在 Android 端进行简单的打开前后相机预览 / 拍照保存

一、简单介绍

二、简单介绍 camera

三、安装 camera

四、简单案例实现

五、关键代码


一、简单介绍

Flutter 是一款开源的 UI 软件开发工具包,由 Google 开发和维护。它允许开发者使用一套代码同时构建跨平台的应用程序,包括移动设备(iOS 和 Android)、Web 和桌面平台(Windows、macOS 和 Linux)。

Flutter 使用 Dart 编程语言,它可以将代码编译为 ARM 或 Intel 机器代码以及 JavaScript,从而实现快速的性能。Flutter 提供了一个丰富的预置小部件库,开发者可以根据自己的需求灵活地控制每个像素,从而创建自定义的、适应性强的设计,这些设计在任何屏幕上都能呈现出色的外观和感觉。

二、简单介绍 camera

网址:camera | Flutter package

一个用于iOS、Android和Web的Flutter插件,允许访问设备摄像头。

三、安装 camera

1、直接运行命令

使用 Flutter:flutter pub add camera

使用 Flutter 安装权限管理插件:flutter pub add permission_handler

使用 Flutter 安装图片保存插件:flutter pub add permission_handler

2、或者在 pubspec.yaml 添加

dependencies:camera: ^0.11.1permission_handler: ^11.4.0saver_gallery: ^4.0.1

四、简单案例实现

1、这里使用 Android Studio 进行创建 Flutter 项目

2、创建一个 application 的 Flutter 项目

3、工程创建后如下

4、添加权限

    <uses-permission android:name="android.permission.CAMERA"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

5、编写代码实现打开相机,预览,拍照功能

6、连接设备,运行项目,简单效果如下

五、关键代码

import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart' show getExternalStorageDirectory;
import 'package:path/path.dart' as path show join;
import 'package:permission_handler/permission_handler.dart';// 应用入口点
Future<void> main() async {// 确保 Flutter 的 WidgetsBinding 已初始化WidgetsFlutterBinding.ensureInitialized();// 请求存储权限await requestPermissions();// 获取设备上所有可用的摄像头列表final cameras = await availableCameras();runApp(MaterialApp(theme: ThemeData.dark(), // 使用暗色主题home: TakePictureScreen(cameras: cameras), // 传入摄像头列表),);
}// 请求存储权限的函数
Future<void> requestPermissions() async {// 检查存储权限的状态var status = await Permission.storage.status;if (!status.isGranted) {// 如果权限未开启,则请求权限await Permission.storage.request();}
}// TakePictureScreen 页面
class TakePictureScreen extends StatefulWidget {// 构造函数,传入摄像头列表const TakePictureScreen({super.key, required this.cameras});final List<CameraDescription> cameras;@overrideTakePictureScreenState createState() => TakePictureScreenState();
}// TakePictureScreen 的状态管理
class TakePictureScreenState extends State<TakePictureScreen> {late CameraController _controller; // 摄像头控制器late Future<void> _initializeControllerFuture; // 初始化摄像头的 Futureint _selectedCameraIndex = 0; // 当前选中的摄像头索引@overridevoid initState() {super.initState();// 初始化当前选中的摄像头_initializeCamera(widget.cameras[_selectedCameraIndex]);}// 初始化摄像头的函数void _initializeCamera(CameraDescription camera) {_controller = CameraController(camera, // 摄像头描述ResolutionPreset.medium, // 设置分辨率);_initializeControllerFuture = _controller.initialize(); // 初始化摄像头}// 切换摄像头的函数void _switchCamera() {setState(() {// 切换到下一个摄像头_selectedCameraIndex = (_selectedCameraIndex + 1) % widget.cameras.length;// 释放当前摄像头资源_controller.dispose();// 初始化新的摄像头_initializeCamera(widget.cameras[_selectedCameraIndex]);});}@overridevoid dispose() {// 释放摄像头资源_controller.dispose();super.dispose();}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(title: const Text('Take a picture')), // 页面标题body: FutureBuilder<void>(future: _initializeControllerFuture, // 监听摄像头初始化的 Futurebuilder: (context, snapshot) {if (snapshot.connectionState == ConnectionState.done) {// 如果摄像头初始化完成,显示摄像头预览return CameraPreview(_controller);} else {// 如果摄像头初始化未完成,显示加载动画return const Center(child: CircularProgressIndicator());}},),floatingActionButton: Column(mainAxisAlignment: MainAxisAlignment.end,children: [// 拍照按钮FloatingActionButton(onPressed: () async {try {await _initializeControllerFuture; // 确保摄像头已初始化final image = await _controller.takePicture(); // 拍照print("Image path: ${image.path}");// 读取图片文件为字节数据final file = File(image.path);final imageBytes = await file.readAsBytes();print("Image bytes length: ${imageBytes.length}");// 手动保存图片到指定路径final saveResult = await saveImageManually(imageBytes);ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text(saveResult)),);} catch (e) {print("Error taking picture: $e");}},child: const Icon(Icons.camera_alt), // 拍照图标),const SizedBox(height: 16),// 切换摄像头按钮FloatingActionButton(onPressed: _switchCamera, // 切换摄像头child: const Icon(Icons.switch_camera), // 切换摄像头图标),],),);}
}// 手动保存图片到指定路径的函数
Future<String> saveImageManually(Uint8List imageBytes) async {// 获取外部存储路径final directory = await getExternalStorageDirectory();if (directory == null) {throw Exception("Failed to get external storage directory.");}// 拼接目标路径final picturesDir = path.join(directory.path, "Pictures", "CameraApp");// 确保目标目录存在await Directory(picturesDir).create(recursive: true);// 生成文件名final fileName = "camera_image_${DateTime.now().millisecondsSinceEpoch}.jpg";final filePath = path.join(picturesDir, fileName);// 写入文件final file = File(filePath);await file.writeAsBytes(imageBytes);print("Image saved manually to: $filePath");return "Image saved to: $filePath";
}

代码说明

  1. 代码结构清晰:每个函数和逻辑块都有详细的注释,解释了其功能和实现方式。

  2. 关键点解释

    • 如何初始化摄像头。

    • 如何切换摄像头。

    • 如何保存图片到指定路径。

  3. 错误处理:在关键操作中添加了错误处理逻辑,并打印了错误信息。

  4. 用户交互:通过 SnackBar 提示用户图片保存成功或失败。

相关文章:

Flutter 学习之旅 之 flutter 在 Android 端进行简单的打开前后相机预览 / 拍照保存

Flutter 学习之旅 之 flutter 在 Android 端进行简单的打开前后相机预览 / 拍照保存 目录 Flutter 学习之旅 之 flutter 在 Android 端进行简单的打开前后相机预览 / 拍照保存 一、简单介绍 二、简单介绍 camera 三、安装 camera 四、简单案例实现 五、关键代码 一、简单…...

【deepseek第一课】从0到1介绍 采用ollama安装deepseek私有化部署,并实现页面可视化

【deepseek第一课】从0到1介绍 采用ollama安装deepseek私有化部署,并实现页面可视化 1. ollama安装1.1 linux安装1.2 windows安装2. deepSeek支持的7种蒸馏模型2.1 蒸馏模型介绍2.2 7种模型特点2.3 安装deepseek-r1:14b模型3. openwebui图形化页面安装4. java连接大模型的三…...

【Vue3 Teleport 技术解析:破解弹窗吸附与滚动列表的布局困局】

&#x1f31f; Vue3 Teleport 技术解析&#xff1a;破解弹窗吸附与滚动列表的布局困局 &#x1f30d; 背景&#xff1a;传统组件嵌套的布局之痛 在传统前端开发中&#xff0c;组件往往被严格限制在父级 DOM 结构中&#xff0c;这导致三大典型问题&#xff1a; 层级监禁 &…...

鸿蒙HarmonyOS 开发简介

鸿蒙开发入门教程 一、技术简介 鸿蒙操作系统&#xff08;HarmonyOS&#xff09;是面向万物互联时代的全场景分布式操作系统&#xff0c;具备分布式软总线、分布式数据管理、分布式任务调度等核心能力&#xff0c;能让设备间实现无缝连接与协同&#xff0c;为用户提供统一、流…...

VBA技术资料MF276:在集合中使用键

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…...

谈谈 Node.js 中的文件系统(fs)模块,如何进行文件读写操作?

Node.js 文件系统&#xff08;fs&#xff09;模块深度解析与实践指南 一、模块基础与核心能力 Node.js 的 fs 模块提供了完整的文件系统操作能力&#xff0c;涵盖 50 个方法&#xff0c;主要分为以下类型&#xff1a; 文件读写&#xff1a;基础 CRUD 操作目录操作&#xff1…...

Python Cookbook-2.18 从指定的搜索路径寻找文件

任务 给定一个搜索路径(一个描述目录信息的字符串)&#xff0c;需要根据这个路径和请求的文件名找到第一个符合要求的文件。 解决方案 需要循环指定的搜索路径中的目录: import os def search_file(filename,search path&#xff0c;pathsepos.pathsep): """…...

安装Git(小白也会装)

一、官网下载&#xff1a;Git 1.依次点击&#xff08;红框&#xff09; 不要安装在C盘了&#xff0c;要炸了&#xff01;&#xff01;&#xff01; 后面都 使用默认就好了&#xff0c;不用改&#xff0c;直接Next&#xff01; 直到这里&#xff0c;选第一个 这两种选项的区别如…...

工学一体化教育模式的核心内涵及实践意义探究

工学一体化是一种将理论教学与实践操作深度融合的教育模式&#xff0c;旨在通过工作过程与学习过程的有机结合&#xff0c;培养具备综合职业能力和创新能力的技能人才。 一、工学一体化的核心内涵 工学一体化教学模式强调“在工作中学习、在学习中工作”&#xff0c;其核心在于…...

前端正则表达式完全指南:从入门到实战

文章目录 第一章&#xff1a;正则表达式基础概念1.1 什么是正则表达式1.2 正则表达式工作原理1.3 基础示例演示 第二章&#xff1a;正则表达式核心语法2.1 元字符大全表2.2 量词系统详解2.3 字符集合与排除 第三章&#xff1a;前端常用正则模式3.1 表单验证类3.1.1 邮箱验证3.1…...

Chromium项目相关

Chromium项目相关 Chromium 是一个开源浏览器项目&#xff0c;旨在为所有用户构建一种更安全、更快速、更稳定的方式来体验 Web。 自 Google 在 2008 年宣布 Chromium 项目以来&#xff0c;他们一直很高兴能够在开源 Web 浏览器的良好基础上进行构建&#xff0c;并为富 Web 平…...

自动驾驶测试场景相关概念

自动驾驶测试场景 一、概念二、分类2.1、按照场景的抽象程度可分为&#xff1a;功能场景、逻辑场景、具体场景。2.2.、​按功能划分2.3、 ​按环境复杂度2.3、按场景类型 三、要素四、挑战与趋势4.1、长尾场景覆盖​4.2、伦理决策测试​4.3、车路协同测试​4.4、联邦学习驱动​…...

给小白的oracle优化工具,了解一下

有时懒得分析或语句太长&#xff0c;可以尝试用oracle的dbms_sqldiag包进行sql优化&#xff0c; --How To Use DBMS_SQLDIAG To Diagnose Query Performance Issues (Doc ID 1386802.1) --诊断SQL 性能 SET ECHO ON SET LINESIZE 132 SET PAGESIZE 999 SET LONG 999999 SET SER…...

DMA发送全部历史记录数据到串口

背景 博主参与的项目中&#xff0c;有个读取全部历史记录的功能&#xff0c;如果下位机在主程序中将全部历史记录单纯地通过串口传输会比较占用cpu资源&#xff0c;影响主程序中别的功能。最后商量得出以下实现方案&#xff1a; 定义两个发送缓冲区DMATxbuf1和DMATxbuf2&…...

基因型—环境两向表数据分析——品种生态区划分

参考资料&#xff1a;农作物品种试验数据管理与分析 用于品种生态区划分的GGE双标图有两种功能图&#xff1a;试点向量功能图和“谁赢在哪里”功能图。双标图的具体模型基于SD定标和h加权和试点中心化的数据。本例中籽粒产量的GGE双标图仅解释了G和GE总变异的53.6%&#xff0c;…...

电路中如何计算电容容值大小

一个例题&#xff1a; 【电路中电容容值是怎么算出来的&#xff1f;】https://www.bilibili.com/video/BV1RQ4y1c7i1?vd_source3cc3c07b09206097d0d8b0aefdf07958...

c++中迭代器和指针有什么区别?

在 C 中&#xff0c;迭代器和指针虽然在某些场景下有相似的行为&#xff0c;但它们在设计目的、功能和使用场景上有本质区别。以下是详细对比和最佳实践&#xff1a; 一、核心区别对比表 特征指针迭代器本质原生数据类型&#xff0c;直接存储内存地址类对象&#xff0c;抽象容…...

GPT大语言模型与搜索引擎:技术本质与应用场景的深度解析

引言 在人工智能和自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;GPT&#xff08;Generative Pre-trained Transformer&#xff09;大语言模型和搜索引擎是两个备受关注的技术。尽管它们都涉及到信息检索和生成&#xff0c;但它们在技术原理、应用场景和用户体验上…...

FreeRTOS-中断管理

实验目的 创建一个队列及一个任务&#xff0c;按下按键 KEY1 触发中断&#xff0c;在中断服务函数里向队列里发送数据&#xff0c;任务则阻塞接 收队列数据。 实验代码 实验结果 这样就实现了&#xff0c;使用中断往队列的发送信息&#xff0c;用任务阻塞接收信息...

计算机毕业设计SpringBoot+Vue.js音乐网站(源码+文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…...

更换k8s容器运行时环境为docker

更换k8s容器运行时环境为docker k8s-V1.24之后容器运行时默认是containerd&#xff0c;若想改为熟悉的docker作为运行时&#xff0c;需要做以下操作 在每个节点安装containerd、docker; 每个节点安装cri-docker&#xff1b; 调整kubelet配置并重启验证。 1.安装docker、con…...

知识图谱-资源网

知识图谱-资源网 http://openkg.cn/datasets-type/https://www.ownthink.com/knowledge.html...

CTF-web: Rust 的过程宏

Rust 的过程宏&#xff08;Procedural Macros&#xff09;是一种强大的元编程工具&#xff0c;允许你在编译时对代码进行操作和生成。与属性宏和派生宏不同&#xff0c;过程宏可以接收并处理任意 Rust 代码&#xff0c;生成新的代码片段。这里有一个简单的例子来说明 Rust 的过…...

小程序Three Dof识别 实现景区AR体验

代码工程 GitCode - 全球开发者的开源社区,开源代码托管平台 dof...

Windows 11 下正确安装 Docker Desktop 到 D 盘的完整教程

文章目录 Windows 11 在 D 盘正确安装 Docker Desktop 的完整教程**前言****准备工作****1. 手动创建 Docker 相关目录**&#xff08;⚠️ **这一步非常重要**&#xff0c;否则会报错&#xff09;**2. 下载 Docker Desktop 安装程序****3. 使用管理员权限打开终端** **安装 Doc…...

锐评当前主流的各大编程语言

喵~ 让本喵来爪爪锐评一波编程语言吧&#xff01;(ฅ•ω•ฅ)✧ &#x1f40d; Python “简单到能拿爪子写代码喵~” 但缩进强迫症克星&#xff01;写起来像在撸猫&#xff0c;跑起来…有时候像猫主子突然打翻水杯&#xff08;GIL锁警告&#xff09;~ 库多到能埋人&#xff0…...

2.数据结构:1.Tire 字符串统计

1.Tire 字符串统计 #include<algorithm> #include<cstring> #include<iostream>using namespace std;const int N100010; int son[N][26];//至多 N 层&#xff0c;每一层至多 26 个节点&#xff08;字母&#xff09; int cnt[N];//字符串至多 N 个&#xff…...

2020 年英语(一)考研真题 笔记(更新中)

Section I Use of English&#xff08;完型填空&#xff09; 原题 Directions&#xff1a;Read the following text. Choose the best word (s) for each numbered blank and mark A, B, C or D on the ANSWER SHEET. (10 points) Even if families are less likely to si…...

YOLO11改进加入ResNet网络

文章目录 1.改进目的2.demo引入2.1代码2.2 结果展示2.3 BottleNeck详解 1.改进目的 原始YOLO11模型训练好以后&#xff0c;检测结果mAP结果很低&#xff0c;视频检测结果很差&#xff0c;于是想到改进网络&#xff0c;这里介绍改进主干网络。 2.demo引入 2.1代码 # File: 2…...

硬编码(三)经典变长指令一

我们在前两节的硬编码中学习了定长指令&#xff0c;接下来学习变长指令 对于定长指令&#xff0c;我们通过opcode便可知该指令的长度&#xff0c;但是对于变长指令却是不可知的。变长指令长度由opcode&#xff0c;ModR/M&#xff0c;SIB共同决定。变长指令通常在需要操作内存的…...