【Flutter】
cameraパッケージの基本とTips

記事イメージ

当社で開発中に新アプリ(iOS/Android)にてcameraパッケージ(バージョン0.9.8+1)を使用しました。

従来はimage_pickerをよく利用していましたが、cameraパッケージの方が汎用的に利用できそうです。

せっかくなので今回得られた知見などを共有します。

cameraパッケージの概要

端末のカメラへのアクセスを環境ごとにラップしてくれるライブラリです。

プレビュー(カメラに写っているリアルタイムの映像をウィジェットに表示)する機能や、ストリーミング、写真撮影など汎用的な機能を備えています。

image_pickerとの違い

Flutterでカメラを扱える他のパッケージとしてはimage_pickerが存在します。

image_pickerは組み込みのカメラビューを呼び出しする一方、cameraパッケージは端末カメラからの入力をウィジェットに組み込んでプレビューやストリーミングが可能な点が異なります。

組み込みのカメラビューは安定的ですが、連続使用や機械学習モデルへのリアルタイム処理などカスタマイズ的な用途には対応できませんので、cameraパッケージを利用することになります。`

導入方法

公式ページの通りですが、次のコマンド・コードで導入します。


$ flutter pub add camera


import 'package:camera/camera.dart';

写真撮影機能を実装する基本的コード

カメラは端末の方向を参照するため、main関数にWidgetsFlutterBinding.ensureInitializedを追加します。

Dart
void main() {
  WidgetsFlutterBinding.ensureInitialized(); //追加
  runApp(MyApp());
}

まずはカメラオブジェクトを初期化します。

以降の各操作はここで生成したcameraControllerオブジェクトを使用します。インスタンス変数に格納するなどして使いまわしましょう。

カメラの解像度はCameraControllerオブジェクトのコストラクタでResolutionPreset列挙体を使用して指定します。

指定値によってはiOSとAndroidで縦横比やサイズが異なるので注意しましょう。ResolutionPreset enum(pub.dev)に詳細が記載されています。

Dart
final cameras = await availableCameras();
final camera = cameras.first;
//↓で解像度を指定
final cameraController = CameraController(camera, ResolutionPreset.high);
await cameraController.initialize();

UI上にプレビュー(カメラに写っているリアルタイム画像)を表示します。

CameraPreview関数がプレビューを表示するウィジェットを生成します。

Dart
@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text("分類作業カメラ"),
    ),
    //カメラに映る映像をリアルタイムで表示
    body: CameraPreview(cameraController),
  );
}

そして、撮影は次のように行います。ボタンウィジェット等から呼び出しましょう。

Dart
//XFile型で結果を得られます。
final photo = await cameraController!.takePicture();

cameraパッケージいろいろテクニック

正方形の写真を撮りたい場合の実現方法

別記事に分離しました。

関連記事:【Flutter】cameraパッケージで正方形カメラを構築

CameraExceptionへの対応

CameraAccessDenied, User denied the camera access request.(ユーザーにカメラアクセスを拒絶された)

CameraException(CameraAccessDenied, User denied the camera access request.)

カメラを使用する場合、ユーザーにアクセス許可を求めます。拒絶された場合にフェール・セーフをしっかり組んでおく必要があります。

少々調べましたが、明確に判定するのが難しそうなのでtry-catchで処理するのがよいのではないでしょうか。

Dart
try {
  final cameras = await availableCameras();
  _camera = cameras.first;
  _appStatus = await AppStatus().load();
  _cameraController = CameraController(_camera!, ResolutionPreset.high);
  await _cameraController!.initialize();
} catch (e) {
  //処理不能の場合はnullをセットしておくことで、判定を容易にできます。この辺はお好みで。ぬるぽには気をつけましょう。
  _cameraController = null;
}

Previous capture has not returned yet(前の処理が終わってない)

takePictureを連続して使用すると、以下のように「前の処理がまだ終わっていないよ」という例外が投げられます。

したがって、takePictureを呼ぶ前に前回処理が完了していることを確認するプロセスが必要と言えます。

CameraException(Previous capture has not returned yet., takePicture was called before the previous capture returned.)

takePictureの戻り値を得ていることを確認して実行タイミングを制御すればOKです。

ここもアイディア次第ですが、インスタンス変数で以下のようにして対処可能です。

Dart
try {
    //インスタンス変数cameraLockで連続実行を制御します。
    if (this.cameraLock) {
        return;
    }
    this.cameraLock = true;
    final photo = await _cameraController!.takePicture();

    //do something

} finally {
    this.cameraLock = false;
}

カメラのみ使用し、マイクへのアクセスが不要な場合の処置

CameraControllerオブジェクトは、デフォルトではマイク(Microphone)へもアクセスするため、ユーザーへはカメラとマイク両方へのアクセス許可を求めることになります。

カメラのUsageとして音声が不要な場合も多いと思います。その場合は、次のコードのようにCameraControllerコンストラクタでenableAudio: falseを指定する必要があります。

尚、enableAudioプロパティは読み取り専用であるため、オブジェクト作成後は変更できません。

Dart
_cameraController = CameraController(
    _camera!,
    ResolutionPreset.high,
    enableAudio: false  //←マイクへのアクセス防止にこれが必要
);

撮影時フラッシュの制御

setFlashMode関数でフラッシュのオンオフ自動を制御できます。イニシャライズ後でないと効果が発揮されないので注意しましょう。

また、一部のiPadなどカメラはあるけどフラッシュ機能が搭載されていない端末でsetFlashModeを呼ぶと例外がスローされます。try-catchなどで適切に対処しましょう。

Dart
//先にイニシャライズが必要。
_cameraController!.initialize();
//Enumでフラッシュ制御を指定
_cameraController?.setFlashMode(FlashMode.always);

記事筆者へのお問い合わせ、仕事のご依頼

当社では、IT活用をはじめ、業務効率化やM&A、管理会計など幅広い分野でコンサルティング事業・IT開発事業を行っております。

この記事をご覧になり、もし相談してみたい点などがあれば、ぜひ問い合わせフォームまでご連絡ください。

皆様のご投稿をお待ちしております。

記事筆者へ問い合わせする

※ご相談は無料でお受けいたします。
IT活用経営を実現する - 堺財経電算合同会社