使用 Camera 插件实现拍照功能
步骤 1. 添加所需依赖 2. 获取可用相机列表 3. 创建并初始化 CameraController 4. 在 initState 方法中创建并初始化控制器 5. 使用 CameraController 拍照 6. 在 dispose 方法中销毁控制器 完整样例
很多应用都需要使用到设备的相机模块拍摄图片和视频。因此,Flutter 提供了 camera
插件。
camera
插件提供了一系列可用的相机,并使用特定的相机展示相机预览、拍照、录视频。
这个章节将会讲解如何使用 camera
插件去展示相机预览、拍照并显示。
步骤
#-
添加所需依赖
-
获取可用相机列表
-
创建并初始化
CameraController
-
使用
CameraPreview
展示相机的帧流 -
使用
CameraController
拍摄一张图片 -
使用
Image
组件展示图片
1. 添加所需依赖
#为了完成这个章节,你需要向你的应用添加三个依赖:
camera
提供使用设备相机模块的工具
path_provider
寻找存储图片的正确路径
path
创建适配任何平台的路径
运行 flutter pub add
将其添加为依赖:
flutter pub add camera path_provider path
2. 获取可用相机列表
#接着,你可以使用 camera
插件获取可用相机列表。
// Ensure that plugin services are initialized so that `availableCameras()`
// can be called before `runApp()`
WidgetsFlutterBinding.ensureInitialized();
// Obtain a list of the available cameras on the device.
final cameras = await availableCameras();
// Get a specific camera from the list of available cameras.
final firstCamera = cameras.first;
3. 创建并初始化 CameraController
#
在选择了一个相机后,你需要创建并初始化 CameraController
。在这个过程中,与设备相机建立了连接并允许你控制相机并展示相机的预览帧流。
实现这个过程,请依照以下步骤:
-
创建一个带有
State
类的StatefulWidget
组件 -
添加一个变量到
State
类来存放CameraController
-
添加另外一个变量到
State
类中来存放CameraController.initialize()
返回的Future
-
在
initState()
方法中创建并初始化控制器 -
在
dispose()
方法中销毁控制器
// A screen that allows users to take a picture using a given camera.
class TakePictureScreen extends StatefulWidget {
const TakePictureScreen({
super.key,
required this.camera,
});
final CameraDescription camera;
@override
TakePictureScreenState createState() => TakePictureScreenState();
}
class TakePictureScreenState extends State<TakePictureScreen> {
late CameraController _controller;
late Future<void> _initializeControllerFuture;
@override
void initState() {
super.initState();
// To display the current output from the Camera,
// create a CameraController.
_controller = CameraController(
// Get a specific camera from the list of available cameras.
widget.camera,
// Define the resolution to use.
ResolutionPreset.medium,
);
// Next, initialize the controller. This returns a Future.
_initializeControllerFuture = _controller.initialize();
}
@override
void dispose() {
// Dispose of the controller when the widget is disposed.
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
// Fill this out in the next steps.
return Container();
}
}
4. 在 initState
方法中创建并初始化控制器
#
接着,你能够使用 camera
中的 CameraPreview
组件来展示相机预览帧流。
你可以使用 FutureBuilder
完成这个任务。
// You must wait until the controller is initialized before displaying the
// camera preview. Use a FutureBuilder to display a loading spinner until the
// controller has finished initializing.
FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the Future is complete, display the preview.
return CameraPreview(_controller);
} else {
// Otherwise, display a loading indicator.
return const Center(child: CircularProgressIndicator());
}
},
)
5. 使用 CameraController
拍照
#
You can use the CameraController
to take pictures using the
takePicture()
method, which returns an XFile
,
a cross-platform, simplified File
abstraction.
On both Android and IOS, the new image is stored in their
respective cache directories,
and the path
to that location is returned in the XFile
.
In this example, create a FloatingActionButton
that takes a picture
using the CameraController
when a user taps on the button.
Taking a picture requires 2 steps:
- Ensure that the camera is initialized.
- Use the controller to take a picture and ensure
that it returns a
Future<XFile>
.
最好把这些操作都放在 try / catch
方法块中来处理可能发生的异常。
FloatingActionButton(
// Provide an onPressed callback.
onPressed: () async {
// Take the Picture in a try / catch block. If anything goes wrong,
// catch the error.
try {
// Ensure that the camera is initialized.
await _initializeControllerFuture;
// Attempt to take a picture and then get the location
// where the image file is saved.
final image = await _controller.takePicture();
} catch (e) {
// If an error occurs, log the error to the console.
print(e);
}
},
child: const Icon(Icons.camera_alt),
)
6. 在 dispose
方法中销毁控制器
#
如果你能成功拍摄图片,你就可以使用 Image
组件展示被保存的图片。在这个示例中,这张图片是以文件的形式存储在设备中。
因此,你需要提供一个 File
给 Image.file
构造函数。你能够通过传递你在上一步中创建的路径来创建一个 File
类的实例。
Image.file(File('path/to/my/picture.png'));
完整样例
#import 'dart:async';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
Future<void> main() async {
// Ensure that plugin services are initialized so that `availableCameras()`
// can be called before `runApp()`
WidgetsFlutterBinding.ensureInitialized();
// Obtain a list of the available cameras on the device.
final cameras = await availableCameras();
// Get a specific camera from the list of available cameras.
final firstCamera = cameras.first;
runApp(
MaterialApp(
theme: ThemeData.dark(),
home: TakePictureScreen(
// Pass the appropriate camera to the TakePictureScreen widget.
camera: firstCamera,
),
),
);
}
// A screen that allows users to take a picture using a given camera.
class TakePictureScreen extends StatefulWidget {
const TakePictureScreen({
super.key,
required this.camera,
});
final CameraDescription camera;
@override
TakePictureScreenState createState() => TakePictureScreenState();
}
class TakePictureScreenState extends State<TakePictureScreen> {
late CameraController _controller;
late Future<void> _initializeControllerFuture;
@override
void initState() {
super.initState();
// To display the current output from the Camera,
// create a CameraController.
_controller = CameraController(
// Get a specific camera from the list of available cameras.
widget.camera,
// Define the resolution to use.
ResolutionPreset.medium,
);
// Next, initialize the controller. This returns a Future.
_initializeControllerFuture = _controller.initialize();
}
@override
void dispose() {
// Dispose of the controller when the widget is disposed.
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Take a picture')),
// You must wait until the controller is initialized before displaying the
// camera preview. Use a FutureBuilder to display a loading spinner until the
// controller has finished initializing.
body: FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the Future is complete, display the preview.
return CameraPreview(_controller);
} else {
// Otherwise, display a loading indicator.
return const Center(child: CircularProgressIndicator());
}
},
),
floatingActionButton: FloatingActionButton(
// Provide an onPressed callback.
onPressed: () async {
// Take the Picture in a try / catch block. If anything goes wrong,
// catch the error.
try {
// Ensure that the camera is initialized.
await _initializeControllerFuture;
// Attempt to take a picture and get the file `image`
// where it was saved.
final image = await _controller.takePicture();
if (!context.mounted) return;
// If the picture was taken, display it on a new screen.
await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => DisplayPictureScreen(
// Pass the automatically generated path to
// the DisplayPictureScreen widget.
imagePath: image.path,
),
),
);
} catch (e) {
// If an error occurs, log the error to the console.
print(e);
}
},
child: const Icon(Icons.camera_alt),
),
);
}
}
// A widget that displays the picture taken by the user.
class DisplayPictureScreen extends StatelessWidget {
final String imagePath;
const DisplayPictureScreen({super.key, required this.imagePath});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Display the Picture')),
// The image is stored as a file on the device. Use the `Image.file`
// constructor with the given path to display the image.
body: Image.file(File(imagePath)),
);
}
}
除非另有说明,本文档之所提及适用于 Flutter 的最新稳定版本,本页面最后更新时间: 2024-07-03。 查看文档源码 或者 为本页面内容提出建议。