Flutter で Image Picker を使って連続して写真を撮る

この記事はこちら↓の続きです。

 - **[Flutter で Image Picker（0.8.2 以降）を使って撮った写真をファイル保存する](https://qiita.com/hmatsu47/items/e4f7e310e88376d54009)**

**[image_picker](https://pub.dev/packages/image_picker)** を使うと Flutter アプリで写真撮影を簡単に実装できて非常に便利なのですが、個人的には

 - **1 枚写真を撮影するごとに元の画面に戻らないといけない（あらためて「撮影」などのリンクをタップしてカメラを起動する）のが面倒**

と感じることがあります。

（単純に`pickImage()`の呼び出しをループさせるだけだと、2 回目の呼び出しで「呼び出し元の画面（ウィジェット）がない」みたいなことを言われて怒られます。）

そのため、写真の保存後に引き続き撮影を続ける方法を考えてみました（注：手抜きです）。

# 準備

基本的には[前回の記事](https://qiita.com/hmatsu47/items/e4f7e310e88376d54009)のとおりですが、現時点で使用するライブラリのバージョンが少し進んでいますのでそれに合わせます。

```yaml:pubspec.yaml（関連部分）
  image_picker: ^0.8.4+4
  cross_file: ^0.3.2
  image_gallery_saver: ^1.7.1
  path_provider: ^2.0.6
```

iOS の場合の`Info.plist`は変更なしです。

```xml:Info.plist（image_picker関連）
	<key>NSPhotoLibraryUsageDescription</key>
	<string>This app requires to access your photo library</string>
	<key>NSCameraUsageDescription</key>
	<string>This app requires to add file to your camera</string>
	<key>NSMicrophoneUsageDescription</key>
	<string>This app requires to add file to your photo library your microphone</string>
```

```xml:Info.plist（path_providerファイル保存先関連）
	<key>UISupportsDocumentBrowser</key>
	<true/>
	<key>LSSupportsOpeningDocumentsInPlace</key>
	<true/>
```

# コード例

```dart:パッケージインポート
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';

import 'package:flutter/material.dart';

import 'package:cross_file/cross_file.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
```

写真撮影・保存（それぞれ 1 回分）は前の記事と同じです。

```dart:写真撮影
  final ImagePicker _picker = ImagePicker();
（中略）
  Future<XFile?> _takePhoto() {
    return _picker.pickImage(
        source: ImageSource.camera,
        maxWidth: 1600,
        maxHeight: 1600,
        imageQuality: 85);
  }
```

```dart:写真を保存する
  Future<String> _savePhoto(XFile photo) async {
    final Uint8List buffer = await photo.readAsBytes();
    final String savePath = '$_imagePath/${photo.name}';
    final File saveFile = File(savePath);
    saveFile.writeAsBytesSync(buffer, flush: true, mode: FileMode.write);
    // 画像ギャラリーにも保存（オプション）
    await ImageGallerySaver.saveImage(buffer, name: photo.name);

    return saveFile.path;
  }
```

写真撮影→保存の呼び出し元を変更します。

```dart:呼び出し元
  Timer? _timer;
（中略）
  String photoPath = '';
  final XFile? photo = await _takePhoto();

　　　　while (photo != null) {
    // キャンセルするまで続けて撮影する
    photoPath = await _savePhoto(photo);
    File file = File(photoPath);
    await showDialog(
        context: context,
        builder: (BuildContext builderContext) {
          _timer = Timer(const Duration(seconds: 1), () async {
            photo = await _takePhoto();
            Navigator.of(context).pop();
          });
          return AlertDialog(
            actionsAlignment: MainAxisAlignment.center,
            content: SingleChildScrollView(
              child: Image.file(file, width: 240.0, fit: BoxFit.scaleDown),
            ),
          );
        }).then((val) {
      if (_timer!.isActive) {
        _timer!.cancel();
      }
    });
  }
```

`Duration`の時間や表示する写真のサイズなどは好みに合わせて調整します。

写真撮影して「保存」（Use Photo）を選ぶと`Duration`の時間アラートダイアログで撮影した写真を表示し、その後自動的に写真撮影画面に遷移します。

※わたしの自作アプリではここ↓で使っています。

 - **[hmatsu47 / maptool / display_symbol_info.dart](https://github.com/hmatsu47/maptool/blob/61eeef0b024afef17d0a1a3bf7494590bd7bbcaa/lib/display_symbol_info_page.dart#L192)**