使用相機拍照
許多應用程式需要使用裝置的相機來拍攝照片和影片。Flutter 提供了 camera 外掛來實現此目的。camera 外掛提供了獲取可用相機列表、顯示特定相機預覽以及拍攝照片或影片的工具。
本指南演示瞭如何使用 camera 外掛透過以下步驟顯示預覽、拍照並顯示照片:
- 新增必要的依賴項。
- 獲取可用相機列表。
- 建立並初始化
CameraController。 - 使用
CameraPreview顯示相機畫面。 - 使用
CameraController拍照。 - 使用
Imagewidget 顯示照片。
1. 新增必要的依賴項
#要完成本指南,您需要在應用中新增三個依賴項:
camera- 提供與裝置相機互動的工具。
path_provider- 查詢儲存影像的正確路徑。
path- 建立可在任何平臺上使用的路徑。
要將這些包新增為依賴項,請執行 flutter pub add
flutter pub add camera path_provider path2. 獲取可用相機列表
#接下來,使用 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 widget 來顯示相機畫面的預覽。
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 上,新影像都儲存在各自的快取目錄中,並且 XFile 返回指向該位置的 path。
在此示例中,建立一個 FloatingActionButton,當用戶點選該按鈕時,它會使用 CameraController 拍照。
拍照需要 2 個步驟:
- 確保相機已初始化。
- 使用控制器拍照,並確保它返回一個
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 widget 顯示照片
#如果拍照成功,您就可以使用 Image widget 顯示已儲存的照片。在此情況下,照片將作為檔案儲存在裝置上。
因此,您必須為 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<void>(
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)),
);
}
}