Take a picture using the 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.
This recipe demonstrates how to use the camera plugin to display a preview,
take a photo, and display it using the following steps:
- Add the required dependencies.
- Get a list of the available cameras.
- Create and initialize the
CameraController. - Use a
CameraPreviewto display the camera's feed. - Take a picture with the
CameraController. - Display the picture with an
Imagewidget.
1. Add the required dependencies
#To complete this recipe, you need to add three dependencies to your app:
cameraProvides tools to work with the cameras on the device.
path_providerFinds the correct paths to store images.
pathCreates paths that work on any platform.
To add the packages as dependencies, run flutter pub add:
$ flutter pub add camera path_provider path
2. Get a list of the available cameras
#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;
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.
- Create a
StatefulWidgetwith a companionStateclass. - Add a variable to the
Stateclass to store theCameraController. -
Add a variable to the
Stateclass to store theFuturereturned fromCameraController.initialize(). - Create and initialize the controller in the
initState()method. - Dispose of the controller in the
dispose()method.
// 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. Use a CameraPreview to display the camera's feed
#
Next, use the CameraPreview widget from the camera package to
display a preview of the camera's feed.
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 const Center(child: CircularProgressIndicator());
}
},
)
5. Take a picture with the 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>.
It is good practice to wrap these operations in a try / catch block in order
to handle any errors that might occur.
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. Display the picture with an Image widget
#
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.
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';
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)),
);
}
}
Unless stated otherwise, the documentation on this site reflects Flutter 3.38.0. Page last updated on 2025-8-19. View source or report an issue.