Tesshu's Blog

Flutter製のアプリでカメラを使う

February 15, 2020

flutterでカメラを使ったアプリを使いたいと思い、flutterでカメラを扱う方法を調べました。

参考サイト:
https://flutter.dev/docs/cookbook/plugins/picture-using-camera https://pub.dev/packages/camera

flutterが提供しているcameraプラグインを使用するだけで簡単にカメラの利用が可能になります。

1. プラグイン導入

まず以下の3つのプラグインをインストール。

  • path_provider
  • path
  • camera

プラグインの詳細ページで最新バージョンをチェック。 [projectRepository]/pubspec.yamlのdependenciesにインストールしたいプラグインを追加する。

    dependencies:
      flutter:
        sdk: flutter
+     path_provider: ^1.6.0
+     path: ^1.6.4
+     camera: ^0.5.7+

I/flutter (23927): [CameraDescription(0, CameraLensDirection.back, 90), CameraDescription(1, CameraLensDirection.front, 90)]

追加したら、コマンドで $ flutter packages getを実行し、プラグインをインストール。

アンドロイドで使用する場合は、minSdkVersionの変更が必要。 [projectRepository]/app/build.gradleを修正。

    android {
      ・・・
      defaultConfig {
+       minSdkVersion 21
        ・・・
      }
    }

2. 使用できるカメラのリストを取得

main.dartのmain()を下記のように編集。 cameraプラグインを利用し、利用可能なカメラのリストを取得。そしてリストから特定のカメラを取得。

+   import 'package:camera/camera.dart';
-   void main() => runApp(MyApp());
+   
+   Future<void> main() async {
+     WidgetsFlutterBinding.ensureInitialized();
+     final cameras = await availableCameras();
+     final firstCamera = cameras.first;
+   
+     runApp(
+       MaterialApp(
+         theme: ThemeData.dark(),
+         home: MyApp()),
+     );
+   }

3. CameraControllerの生成と初期化

2.で取得したカメラの情報を受け取るScreenを作成。 受け取ったカメラの情報を利用して、Controllerを生成・初期化。

+    // カメラのリストと、イメージを保存するディレクトリーを取り入れるスクリーン。
+    class TakePictureScreenState extends State<TakePictureScreen> {
+      CameraController _controller;
+      Future<void> _initializeControllerFuture;
+    
+      @override
+      void initState() {
+        super.initState();
+        // カメラからの情報を表示させるためのCameraControllerを生成
+        _controller = CameraController(
+          // main()で取得した特定のカメラを引数にとる。
+          widget.camera,
+          // 解像度を指定(low, medium, high, veryHigh, ultraHigh, max)
+          ResolutionPreset.medium,
+        );
+    
+        // cameracontrollerを初期化。Futureが返ってくる。
+        _initializeControllerFuture = _controller.initialize();
+      }
+    
+      @override
+      void dispose() {
+        _controller.dispose();
+        super.dispose();
+      }
+    
+      @override
+      Widget build(BuildContext context) {
+        // 次のステップで処理を追加
+      }
+    }

4. CarmeraPreview()メソッドを使って、カメラからの取得した映像を表示させる。

3.で作成したtakePictureScreenState()のbuild()を修正します。

  @override
  Widget build(BuildContext context) {
+   return Scaffold(
+     appBar: AppBar(title: Text("Take a picture")),
+     // カメラのプレビューを表示させる前にカメラコントローラーの初期化が完了するのを待たなければならない。
+     // FutureBuilderを使用することで、コントローラの初期化が完了するまでローディング中のスピナーを表示させる。
+     body: FutureBuilder<void>(
+       future: _initializeControllerFuture,
+       builder: (context, snapshot) {
+         if (snapshot.connectionState == ConnectionState.done) {
+           return CameraPreview(_controller);
+         } else {
+           //初期化が完了してない場合は、ローディングスピナーを表示させる。
+           return Center(child: CircularProgressIndicator());
+         }
+       },
+     ),
+   );
  }

この時点での$ flutter runを実行して、エミュレーターで確認してみると、以下のような画面が表示されます。optionキーを押しながらマウスを動かすとカメラの方向を変えることができます。 emulator1

5. 撮影・保存

takePictureScreenState()のbuild()にfloatingActionButtonを追加し、撮影する機能を追加します。

+ import 'package:path/path.dart';
+ import 'package:path_provider/path_provider.dart';
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: ・・・,
      body: ・・・,
+     floatingActionButton: FloatingActionButton(
+       child: Icon(Icons.camera_alt),
+       onPressed: () async {
+         try {
+           // Controllerが初期化されていることを保証するための処理
+           await _initializeControllerFuture;
+     
+           // pathパッケージを使ってイメージが保存されるパスを構成する
+           final path = join(
+             // 撮影した画像を一時的に保存する場所を確保。
+             (await getTemporaryDirectory()).path,
+             '${DateTime.now()}.png',
+           );
+     
+           await _controller.takePicture(path);
+         } catch (e) {
+           print(e);
+         }
+       },
+     )
+   );
  }

6. 撮影した画像を表示させる。

6-1. 画像を表示させるためのwidgetを作成

// File()を使うために必要
import 'dart:io';
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')),
      // イメージはデバイスにファイルとして保存されており、'image.file'とイメージ保存先のパスを使って、イメージを表示させることができる。
      body: Image.file(File(imagePath)),
    );
  }
}

6-2. 撮影が完了したら画面を遷移する処理追加

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: ・・・,
      body: ・・・,
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.camera_alt),
        onPressed: () async {
          try {
            ・・・
            await _controller.takePicture(path);
+           // 画面遷移処理を追加
+           Navigator.push(
+             context,
+             MaterialPageRoute(
+               builder: (context) => DisplayPictureScreen(imagePath: path),
+             ),
+           );
          } catch (e) {
            print(e);
          }
        },
      )
    );
  }
}

7. 動作確認

エミュレーターを起動し、$ flutter runでアプリを実行。

FloatingButtonを押下し、写真を撮影。 screen

画面遷移+撮影した画像が表示できることが確認できました。イェイ✌️ takePic

8.番外編

cameraプラグインのavailableCameras()で何が取得できるのか気になったので、debug

I/flutter (23927): [CameraDescription(0, CameraLensDirection.back, 90), CameraDescription(1, CameraLensDirection.front, 90)]

バックのカメラとフロントのカメラが入っているようです。

フロントのカメラを使ったらどうなるのか確認してみました。main()のカメラ情報取得箇所を修正。

   Future<void> main() async {
     WidgetsFlutterBinding.ensureInitialized();
     final cameras = await availableCameras();
-      final firstCamera = cameras.first;
       // 配列の最後の要素(フロントカメラ)を取得
+      final firstCamera = cameras.last;
     runApp(
       MaterialApp(
         theme: ThemeData.dark(),
         home: MyApp()),
     );
   }

これで実行してみるとこうなりました。エミュレータなので、フロントカメラからの情報が取得できたのかわかりませんが、バックカメラとは違う画面が取得できました。マリオ?

frontCamera

Written by Tesshu Ohkura who lives and works in Tokyo building useful things. Click Here or the thumbnail to know me more!