Saltar al contenido principal

Tomar una foto usando la cámara

Cómo usar un plugin de cámara en dispositivos móviles.

Muchas aplicaciones requieren trabajar con las cámaras del dispositivo para tomar fotos y videos. Flutter proporciona el plugin camera para este propósito. El plugin camera proporciona herramientas para obtener una lista de las cámaras disponibles, mostrar una vista previa proveniente de una cámara específica y tomar fotos o videos.

Esta receta demuestra cómo usar el plugin camera para mostrar una vista previa, tomar una foto y mostrarla usando los siguientes pasos:

  1. Añade las dependencias requeridas.
  2. Obtén una lista de las cámaras disponibles.
  3. Crea e inicializa el CameraController.
  4. Usa un CameraPreview para mostrar la transmisión de la cámara.
  5. Toma una foto con el CameraController.
  6. Muestra la foto con un Widget Image.

1. Añade las dependencias requeridas

#

Para completar esta receta, debes añadir tres dependencias a tu aplicación:

camera

Proporciona herramientas para trabajar con las cámaras en el dispositivo.

path_provider

Encuentra las rutas correctas para almacenar imágenes.

path

Crea rutas que funcionen en cualquier plataforma.

Para añadir los paquetes como dependencias, ejecuta flutter pub add:

flutter pub add camera path_provider path

2. Obtén una lista de las cámaras disponibles

#

A continuación, obtén una lista de las cámaras disponibles usando el plugin camera.

dart
// 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. Crea e inicializa el CameraController

#

Una vez que tengas una cámara, sigue estos pasos para crear e inicializar un CameraController. Este proceso establece una conexión con la cámara del dispositivo que te permite controlar la cámara y mostrar una vista previa de la transmisión de la cámara.

  1. Crea un StatefulWidget con una clase State complementaria.
  2. Añade una variable a la clase State para almacenar el CameraController.
  3. Añade una variable a la clase State para almacenar el Future devuelto por CameraController.initialize().
  4. Crea e inicializa el controlador en el método initState().
  5. Destruye (dispose) el controlador en el método dispose().
dart
// 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. Usa un CameraPreview para mostrar la transmisión de la cámara

#

A continuación, usa el Widget CameraPreview del paquete camera para mostrar una vista previa de la transmisión de la cámara.

Usa un FutureBuilder exactamente para este propósito.

dart
// 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. Toma una foto con el CameraController

#

Puedes usar el CameraController para tomar fotos utilizando el método takePicture(), el cual devuelve un XFile, una abstracción de File simplificada y multiplataforma. Tanto en Android como en iOS, la nueva imagen se almacena en sus respectivos directorios de caché, y la ruta (path) a esa ubicación se devuelve en el XFile.

En este ejemplo, crea un FloatingActionButton que tome una foto usando el CameraController cuando el usuario toque el botón.

Tomar una foto requiere 2 pasos:

  1. Asegúrate de que la cámara esté inicializada.
  2. Usa el controlador para tomar una foto y asegúrate de que devuelva un Future<XFile>.

Es una buena práctica envolver estas operaciones en un bloque try / catch para manejar cualquier error que pueda ocurrir.

dart
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. Muestra la foto con un Widget Image

#

Si tomas la foto con éxito, puedes mostrar la imagen guardada usando un Widget Image. En este caso, la foto se almacena como un archivo en el dispositivo.

Por lo tanto, debes proporcionar un File al constructor Image.file. Puedes crear una instancia de la clase File pasando la ruta creada en el paso anterior.

dart
Image.file(File('path/to/my/picture.png'));

Ejemplo completo

#
dart
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)),
    );
  }
}