使用相机拍照
许多应用程序需要使用设备的摄像头来拍摄照片和视频。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. 使用 CameraPreview
显示相机的画面
#接下来,使用 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
拍照
#您可以使用 CameraController
通过 takePicture()
方法拍照,该方法返回一个 XFile
,这是一个跨平台的简化 File
抽象。在 Android 和 IOS 上,新图像都存储在各自的缓存目录中,并且该位置的 path
将在 XFile
中返回。
在此示例中,创建一个 FloatingActionButton
,当用户点击按钮时,使用 CameraController
拍照。
拍照需要两个步骤
- 确保相机已初始化。
- 使用控制器拍照,并确保它返回一个
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. 使用 Image
小部件显示图片
#如果成功拍照,则可以使用 Image
小部件显示保存的图片。在这种情况下,图片将作为文件存储在设备上。
因此,您必须向 Image.file
构造函数提供一个 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)),
);
}
}