Flutterでカメラを使う
最近 Release Preview 1 がリリースされた Google 製クロスプラットフォーム環境 Flutter。
一度触ってみて、本ブログでも導入部分を紹介しました。
もう少し実際のアプリで使うであろう機能について使い方などを踏み込んで調べてみました。
今回はカメラになります。
なお、今回利用している Camera プラグインはまだ開発中とのことなので、今後 API など変わる可能性はあります。
今回作ったアプリ
カメラのプレビュー画面を表示し、ボタンをタップするとカメラを使って撮影をするという単純なアプリです。
カメラの処理については capture.dart でやってます。
実装内容
1. Flutter 環境の構築
Flutter 環境の構築方法に関しては以前書いた記事を参考にしていただければ。
2. Camera プラグインの導入
カメラを使うために、公式で提供されている camera プラグインを導入します。
Flutter のプロジェクト内にある pubspec.yaml
の dependencies の項目に camera:
を追加します。
また、撮影した画像の出力先のファイルパスを取得するために、path_provider
プラグインも導入しておきます。
1 2 3 4 5 6 7 8 9 10 11 |
name: flutter_demos description: A new Flutter project. dependencies: ... camera: path_provider: ... |
追加したら、Android Studio、IntelliJ を使っている方は右上に表示される Packages get
でプラグインをダウンロードします。
コンソールで行う場合は、flutter packages get
を実行します。
3. プロジェクトでカメラを使えるようにする
iOS、Android のそれぞれでカメラを使う設定を追加します。
iOS の場合は Info.plist 下記キーを設定します。
1 2 3 4 5 |
<key>NSCameraUsageDescription</key> <string>Can I use the camera please?</string> <key>NSMicrophoneUsageDescription</key> <string>Can I use the mic please?</string> |
Android の場合、最小SDKで 21 を指定するようにします。camera プラグインで SDK 21 で提供されている CameraDevice を使っているためです。
android/app/build.gradle
で
1 2 |
minSdkVersion 21 |
を指定します。
Android の “android.permission.CAMERA” パーミッションの指定はアプリの方で行わなくても良いみたいです。プラグインの方で指定されているみたいですね。
4. カメラ制御部の初期化
ここから実際のソースコードの内容に触れていきます。
GitHub の capture.dart も合わせて参照ください。
まずは availableCameras
で利用できるカメラの情報を取得します。
1 2 3 4 5 |
availableCameras().then((cameras) { CameraDescription rearCamera = cameras.firstWhere( (desc) => desc.lensDirection == CameraLensDirection.back, orElse: () => null); |
camera プラグインで利用できる関数の多くは非同期関数なので、then や async/await
の書き方を使って結果を取得します。
availableCameras
で取得できるのは List<CameraDescription>
です。
今の端末の多くはリアカメラとフロントカメラのように複数のカメラを持っていますので、利用できるカメラの情報もリストで返ってくるようになっています。
CameraDescription
にはカメラレンズの方向(フロントかリアか)が含まれていますので、それをもとに使うカメラを決めます。今回はリアカメラを使うことにしました。
使うカメラが決まったら、その CameraDescription
を指定して CameraController
のインスタンスを作成し、initialize
メソッドを呼び出します。
コンストラクタの二つ目の引数はプレビュー画面の解像度をどの程度のものにするかという目安です。
1 2 3 4 5 6 7 8 |
controller = new CameraController(rearCamera, ResolutionPreset.high); controller.initialize().then((_) { if (!mounted) { return; } setState(() {}); }); |
initialize
時に、Android アプリではおなじみのカメラの使用の許可を求めるダイアログが出ます。このあたりをライブラリでやってくれるのは楽で良いですね。
以降の処理はすべて作成した CameraController
のインスタンスに対して行います。
5. プレビュー用 Widget の作成
カメラのプレビュー用に CameraPreview
という Widget がプラグインの方で用意されていますので、それを作成して画面に配置します。
カメラ画像のアスペクト比が controller.value.aspectRatio
で取得できますので、プレビュー画面もその比率で表示するよう AspectRatio Widget を使っています。
1 2 3 4 5 6 7 8 |
preview = new Container( child: new Center( child: new AspectRatio( aspectRatio: controller.value.aspectRatio, child: new CameraPreview(controller)) ) ); |
6. 撮影
撮影は CameraController.takePicture
で行います。引数は画像の保存先のファイルパスです。
1 2 3 4 5 6 7 |
final Directory extDir = await getApplicationDocumentsDirectory(); final String dirPath = '${extDir.path}/Pictures/flutter_test'; await new Directory(dirPath).create(recursive: true); final String filePath = '$dirPath/${_timestamp()}.jpg'; await controller.takePicture(filePath); |
今回は path_provider プラグインで取得できる getApplicationDocumentsDirectory() を使い、アプリケーション内ごとの保存領域に保存するようにしました。
端末のギャラリー(iOSのカメラロール)に保存する方法を探して見たのですが、今のところ自分でプラグインを書くしか無さそう。気が向いたら作ってみます。
トラブルシューティング
基本は camera プラグインを使って実装すればよいという話だけなのですが、わたしの持っている端末の中に、下記エラーを出力してリアカメラが使えない端末がありました。(Zenfone3 Android 7.0)
1 2 3 |
W/CameraDevice-JV-0(17996): Stream configuration failed due to: endConfigure:343: Camera 0: Unsupported set of inputs/outputs provided E/CameraCaptureSession(17996): Session 0: Failed to create capture session; configuration failed |
デバイスで対応していない解像度とか保存形式を指定したときに出るエラーだそう。
camera プラグインではプレビュー用と画像保存用で 2つのサーフェイスを出力しているのですが、複数サーフェイスに対して出力するときは単体とは別の解像度制限があるらしく、それが考慮されてないように思いました。
一応そのあたりを改造版 camera プラグインを GitHub にあげました。もし同じエラーが出てしまったら試してみてください。
下記のように pubspec.yaml でプラグインのリポジトリを指定すると使えます。コードの方での変更は必要ありません。
1 2 3 4 5 6 7 |
dependencies: camera: git: url: https://github.com/ryuta46/plugins.git path: packages/camera |
PR もしてみた けど、 まだ受け入れられるかはわかりません。
無事マージされました!やったぜ。
おわりに
どうにか当初の目的のカメラで撮影する部分まではできたのですが、プラグインの機能がもうちょっと充実してほしいところではあります。(ギャラリーに保存するところとか)
今回少しプラグインをいじってみたりしたのですがプラグインの実装自体は割とわかりやすい感じでした。公開されてるプラグインでどうにもならなければ、自分で Kotlin/Swift で書けばいっかーって感じがしました。
正直 Android も iOS も、UI 部分の実装が一番時間がかかると思っているので、そのあたりだけでも Flutter で共通コードになればありがたいです。
またアプリで使いそうな機能の実装方法調べたら書いていきます。
最後まで読んでいただきありがとうございます。 このブログを「いいな」と感じていただけましたら、Twiter にてフォローいただけるとうれしいです。ブログ更新情報などもお届けします。
Follow @ryuta461
この記事をシェアする