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

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

目录

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

一、简单介绍

[二、简单介绍 camera](#二、简单介绍 camera)

[三、安装 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 添加

Dart 复制代码
dependencies:
  camera: ^0.11.1
  permission_handler: ^11.4.0
  saver_gallery: ^4.0.1

四、简单案例实现

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

2、创建一个 application 的 Flutter 项目

3、工程创建后如下

4、添加权限

XML 复制代码
    <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、连接设备,运行项目,简单效果如下

五、关键代码

Dart 复制代码
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;

  @override
  TakePictureScreenState createState() => TakePictureScreenState();
}

// TakePictureScreen 的状态管理
class TakePictureScreenState extends State<TakePictureScreen> {
  late CameraController _controller; // 摄像头控制器
  late Future<void> _initializeControllerFuture; // 初始化摄像头的 Future
  int _selectedCameraIndex = 0; // 当前选中的摄像头索引

  @override
  void 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]);
    });
  }

  @override
  void dispose() {
    // 释放摄像头资源
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Take a picture')), // 页面标题
      body: FutureBuilder<void>(
        future: _initializeControllerFuture, // 监听摄像头初始化的 Future
        builder: (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 提示用户图片保存成功或失败。

相关推荐
wayne2141 小时前
最新Flutter导航拦截PopScope使用
flutter
缘来的精彩1 小时前
Android framwork 详细开发指南
android·c++·framwork
清霜之辰1 小时前
2025年如何实现安卓、iOS、鸿蒙跨平台开发
android·ios·跨平台·harmonyos
妄想出头的工业炼药师2 小时前
Basler acA1920-40gc
相机
Zender Han3 小时前
Flutter状态管理框架GetX最新版详解与实践指南
android·flutter·ios
王的博客5 小时前
Android -- 使用Sharepreference保存List储存失败,原因是包含Bitmap,drawable等类型数据
android
开开心心就好6 小时前
手机隐私数据彻底删除工具:回收或弃用手机前防数据恢复
android·运维·python·智能手机·pdf·自动化·软件需求
m0_748232396 小时前
【PHP】部署和发布PHP网站到IIS服务器
android·服务器·php
雾里看山7 小时前
【MySQL】索引(中)
android·数据库·mysql