使用 Camera 插件实现拍照功能
- Directions
- 步骤
- 1. Add the required dependencies
- 1. 添加所需依赖
- 2. Get a list of the available cameras
- 2. 获取可用相机列表
- 3. Create and initialize the CameraController
- 3. 创建并初始化 CameraController
- 4. Use a CameraPreview to display the camera’s feed
- 4. 在 initState 方法中创建并初始化控制器
- 5. Take a picture with the CameraController
- 5. 使用 CameraController 拍照
- 6. Display the picture with an Image widget
- 6. 在 dispose 方法中销毁控制器
- Complete example
- 完整样例
很多应用都需要使用到设备的相机模块拍摄图片和视频。因此,Flutter 提供了 camera
插件。
camera
插件提供了一系列可用的相机,并使用特定的相机展示相机预览、拍照、录视频。
Many apps require working with the device’s cameras to
take photos and videos. Flutter provides the camera
plugin
for this purpose. The camera
plugin provides tools to get a list of the
available cameras, display a preview coming from a specific camera,
and take photos or videos.
这个章节将会讲解如何使用 camera
插件去展示相机预览、拍照并显示。
This recipe demonstrates how to use the camera
plugin to display a preview,
take a photo, and display it using the following steps:
步骤
Directions
-
添加所需依赖
Add the required dependencies.
-
获取可用相机列表
Get a list of the available cameras.
-
创建并初始化
CameraController
Create and initialize the
CameraController
. -
使用
CameraPreview
展示相机的帧流Use a
CameraPreview
to display the camera’s feed. -
使用
CameraController
拍摄一张图片Take a picture with the
CameraController
. -
使用
Image
组件展示图片Display the picture with an
Image
widget.
1. 添加所需依赖
1. Add the required dependencies
为了完成这个章节,你需要向你的应用添加三个依赖:
To complete this recipe, you need to add three dependencies to your app:
camera
提供使用设备相机模块的工具
camera
Provides tools to work with the cameras on the device.
path_provider
寻找存储图片的正确路径
path_provider
Finds the correct paths to store images.
path
创建适配任何平台的路径
path
Creates paths that work on any platform.
dependencies:
flutter:
sdk: flutter
camera:
path_provider:
path:
2. 获取可用相机列表
2. Get a list of the available cameras
接着,你可以使用 camera
插件获取可用相机列表。
Next, get a list of available cameras using the camera
plugin.
// 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;
CameraController
3. 创建并初始化
CameraController
3. Create and initialize the 在选择了一个相机后,你需要创建并初始化 CameraController
。在这个过程中,与设备相机建立了连接并允许你控制相机并展示相机的预览帧流。
Once you have a camera, use the following steps to
create and initialize a CameraController
.
This process establishes a connection to
the device’s camera that allows you to control the camera
and display a preview of the camera’s feed.
实现这个过程,请依照以下步骤:
To achieve this, please:
-
创建一个带有
State
类的StatefulWidget
组件Create a
StatefulWidget
with a companionState
class. -
添加一个变量到
State
类来存放CameraController
Add a variable to the
State
class to store theCameraController
. -
添加另外一个变量到
State
类中来存放CameraController.initialize()
返回的Future
Add a variable to the
State
class to store theFuture
returned fromCameraController.initialize()
. -
在
initState()
方法中创建并初始化控制器Create and initialize the controller in the
initState()
method. -
在
dispose()
方法中销毁控制器Dispose of the controller in the
dispose()
method.
// A screen that takes in a list of cameras and the Directory to store images.
class TakePictureScreen extends StatefulWidget {
final CameraDescription camera;
const TakePictureScreen({
Key key,
@required this.camera,
}) : super(key: key);
@override
TakePictureScreenState createState() => TakePictureScreenState();
}
class TakePictureScreenState extends State<TakePictureScreen> {
// Add two variables to the state class to store the CameraController and
// the Future.
CameraController _controller;
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.
}
}
initState
方法中创建并初始化控制器
4. 在
CameraPreview
to display the camera’s feed
4. Use a 接着,你能够使用 camera
中的 CameraPreview
组件来展示相机预览帧流。
Next, use the CameraPreview
widget from the camera
package to
display a preview of the camera’s feed.
你可以使用 FutureBuilder
完成这个任务。
Use a FutureBuilder
for exactly this purpose.
// 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 Center(child: CircularProgressIndicator());
}
},
)
CameraController
拍照
5. 使用
CameraController
5. Take a picture with the 你可以使用 CameraController
的
takePicture()
方法拍照。在这个示例中,创建了一个浮动按钮 FloatingActionButton
,用户点击这个按钮,就能通过 CameraController
来拍摄图片。
You can use the CameraController
to take pictures using the
takePicture()
method. In this example,
create a FloatingActionButton
that takes a picture
using the CameraController
when a user taps on the button.
保存一张图片,需要经过一下三个步骤:
Saving a picture requires 3 steps:
-
确保相机模块已经被初始化完成
Ensure the camera is initialized
-
创建图片需要被保存的路径
Construct a path that defines where the picture should be saved
-
使用控制器拍摄一张图片并保存结果到上述路径
Use the controller to take a picture and save the result to the path
最好把这些操作都放在 try / catch
方法块中来处理可能发生的异常。
It is good practice to wrap these operations in a try / catch
block in order
to handle any errors that might occur.
FloatingActionButton(
child: Icon(Icons.camera_alt),
// 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;
// Construct the path where the image should be saved using the path
// package.
final path = join(
// Store the picture in the temp directory.
// Find the temp directory using the `path_provider` plugin.
(await getTemporaryDirectory()).path,
'${DateTime.now()}.png',
);
// Attempt to take a picture and log where it's been saved.
await _controller.takePicture(path);
} catch (e) {
// If an error occurs, log the error to the console.
print(e);
}
},
)
dispose
方法中销毁控制器
6. 在
Image
widget
6. Display the picture with an 如果你能成功拍摄图片,你就可以使用 Image
组件展示被保存的图片。在这个示例中,这张图片是以文件的形式存储在设备中。
If you take the picture successfully, you can then display the saved picture
using an Image
widget. In this case, the picture is stored as a file on
the device.
因此,你需要提供一个 File
给 Image.file
构造函数。你能够通过传递你在上一步中创建的路径来创建一个 File
类的实例。
Therefore, you must provide a File
to the Image.file
constructor.
You can create an instance of the File
class by passing the path created in
the previous step.
Image.file(File('path/to/my/picture.png'))
完整样例
Complete example
import 'dart:async';
import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:path/path.dart' show join;
import 'package:path_provider/path_provider.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 {
final CameraDescription camera;
const TakePictureScreen({
Key key,
@required this.camera,
}) : super(key: key);
@override
TakePictureScreenState createState() => TakePictureScreenState();
}
class TakePictureScreenState extends State<TakePictureScreen> {
CameraController _controller;
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: Text('Take a picture')),
// 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 Center(child: CircularProgressIndicator());
}
},
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.camera_alt),
// 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;
// Construct the path where the image should be saved using the
// pattern package.
final path = join(
// Store the picture in the temp directory.
// Find the temp directory using the `path_provider` plugin.
(await getTemporaryDirectory()).path,
'${DateTime.now()}.png',
);
// Attempt to take a picture and log where it's been saved.
await _controller.takePicture(path);
// If the picture was taken, display it on a new screen.
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DisplayPictureScreen(imagePath: path),
),
);
} catch (e) {
// If an error occurs, log the error to the console.
print(e);
}
},
),
);
}
}
// A widget that displays the picture taken by the user.
class DisplayPictureScreen extends StatelessWidget {
final String imagePath;
const DisplayPictureScreen({Key key, this.imagePath}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: 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)),
);
}
}