Flutter 中文文档:使用 Camera 插件实现拍照功能

640?wx_fmt=jpeg

不少应用都须要使用到设备的相机模块拍摄图片和视频。所以,Flutter 提供了 camera 插件。camera 插件提供了一系列可用的相机,并使用特定的相机展现相机预览、拍照、录视频。javascript

这个章节将会讲解如何使用 camera 插件去展现相机预览、拍照并显示。java

步骤app

1. 添加所需依赖
2. 获取可用相机列表
3. 建立并初始化  CameraController
4. 使用  CameraPreview  展现相机的帧流
5. 使用  CameraController  拍摄一张图片
6. 使用  Image  组件展现图片

1. 添加所需依赖

为了完成这个章节,你须要向你的应用添加三个依赖:less

  • camera - 提供使用设备相机模块的工具async

  • path_provider - 寻找存储图片的正确路径ide

  • path - 建立适配任何平台的路径函数

dependencies:
  flutter:
    sdk: flutter
  camera:
  path_provider:
  path:

2. 获取可用相机列表工具

接着,你可使用 camera 插件获取可用相机列表。ui

 
 
3. 建立并初始化 CameraController

在选择了一个相机后,你须要建立并初始化  CameraController 在这个过程当中,与设备相机创建了链接并容许你控制相机并展现相机的预览帧流。
实现这个过程,请依照如下步骤:

(1)建立一个带有 State 类的 StatefulWidget 组件this

(2)添加一个变量到 State 类来存放 CameraController

(3)添加另一个变量到 State 类中来存放 CameraController.initialize() 返回的 Future

(4)在 initState() 方法中建立并初始化控制器

(5)在 dispose() 方法中销毁控制器

 
 

请注意
若是你没有初始化  CameraController ,你就 不能 使用相机预览和拍照。

4. 在 initState 方法中建立并初始化控制器

接着,你可以使用  camera  中的  CameraPreview  组件来展现相机预览帧流。
请记住 在使用相机前,请确保控制器已经完成初始化。 所以,你必定要等待前一个步骤建立  _initializeControllerFuture()  执行完毕才去展现  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 Center(child: CircularProgressIndicator());
    }
  },
)
5. 使用 CameraController 拍照
你可使用  CameraController  的  takePicture  方法拍照。 在这个示例中,建立了一个浮动按钮  FloatingActionButton ,用户点击这个按钮,就能经过  CameraController  来拍摄图片。
保存一张图片,须要通过一下三个步骤:

(1)确保相机模块已经被初始化完成

(2)建立图片须要被保存的路径

(3)使用控制器拍摄一张图片并保存结果到上述路径

最好把这些操做都放在  try / catch  方法块中来处理可能发生的异常。
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);
    }
  },
),
)
6. 在 dispose 方法中销毁控制器
若是你能成功拍摄图片,你就可使用  Image  组件展现被保存的图片。 在这个示例中,这张图片是以文件的形式存储在设备中。
所以,你须要提供一个  File  给  Image.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';
import 'package:path/path.dart' show join;
import 'package:path_provider/path_provider.dart';

Future<void> main() async {
  // 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)),
    );
  }
}

640?wx_fmt=png