🐈

Flutter: 枚数に上限を設けてギャラリーから写真を選択する

2022/03/09に公開約4,300字

Flutterでギャラリーから写真を選択する際、選択可能な枚数に上限を設けたいケースがありますが、標準ライブラリであるImagePickerのpickMultiImageメソッドは上限枚数を指定できないため実装することが困難です。
そこで今回はサードパーティー製のライブラリを使用して、枚数に上限を設けてギャラリーから写真を選択する処理を実装する方法について解説していきたいと思います。

環境

  • macOS Big Sur 11.5.2
  • iPhone11 iOS 15.31
  • Flutter 2.8.1
  • Dart 2.15.1

使用するライブラリ

multi_image_picker2 ... ギャラリーから写真を選択する処理で使用
https://pub.dev/packages/multi_image_picker2/example

flutter_absolute_path ... AssetをFileに変換する処理で使用
https://pub.dev/packages/flutter_absolute_path/versions

dependencies:
  multi_image_picker2: ^5.0.2

  flutter_absolute_path:
    git:
      url: [url_to_fork_repository]

flutter_absolute_pathはgithubにアップロードされているものはnull_safetyに対応していますが、
pubdevに公開されているバージョンがnull_safetyに対応していないので、

  1. 実行時に --no-sound-null-safety をつける
  2. 元のリポジトリからフォークして使用する

のどちらかの対応を行う必要があります。
今回は2番の方法を採用しました。

iOSの設定

カメラやライブラリへのアクセスを可能にするため、info.plistに下記内容を追記します。

<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>

androidに必要な設定

同様にAndroidManifest.xmlの<application>タグに下記内容を追記します。

<application
            android:requestLegacyExternalStorage="true">

日本語化

iOS端末でmulti_image_pickerの日本語化を行うために、xcodeで設定を行います。
xcodeでプロジェクトを開き、「Runner」 → 「info」 → 「Custom iOS Target Properties」 → の順に選択し、ValueをJapanに変更します。

https://storage.googleapis.com/zenn-user-upload/b35ccb64bf3c-20220306.png

ギャラリーから写真を選択する

MultiImagePickerのpickImagesメソッドを呼び出して、ギャラリーから写真を選択します。
pickImagesは戻り値として、Assetクラスのリストを返却します。

引数のmaxImagesに値を渡すことで、選択できる上限枚数を指定することができます。
また選択を完了するボタンのラベルは日本語化してもDoneのままなので、変更したい場合はdoneButtonTitleに変更後のラベルを指定したCupertinoOptionsを生成し、pickImagesの引数に渡してあげる必要があります。

List<Asset> assets = await MultiImagePicker.pickImages(
  maxImages: 4,
  cupertinoOptions: const CupertinoOptions(
    doneButtonTitle: '保存',
  ),
);

AssetをFileに変換する

Assetはこのままと扱いづらいので、Fileに変換しましょう。
AssetをFileに変換する際は、FlutterAbsolutePathのgetAbsolutePathメソッドを使用します。

AssetのidentifierをgetAbsolutePathメソッドに引数として渡すことで、絶対パスを取得することができます。
ここで取得した絶対パスを使用してFileを生成することで、AssetをFileに変換することができます。

final files = <File>[];

await Future.wait(
  assets.map(
    (e) async {
      final path = await FlutterAbsolutePath.getAbsolutePath(e.identifier!);
      final file = File(path!);
      files.add(file);
    },
  ),
);

関数化する

ここまでで処理の実装は完了しましたので、関数化して使いまわせるようにしましょう。

import 'dart:io';

import 'package:multi_image_picker2/multi_image_picker2.dart';
import 'package:flutter_absolute_path/flutter_absolute_path.dart';

class ImageUtils {
  static Future<List<File>> pickLimitedMultiImage({
    int maxImages = 4,
  }) async {
    List<Asset> assets = await MultiImagePicker.pickImages(
      maxImages: maxImages,
      cupertinoOptions: const CupertinoOptions(
        doneButtonTitle: '保存',
      ),
    );

    final files = <File>[];

    await Future.wait(
      assets.map(
        (e) async {
          final path = await FlutterAbsolutePath.getAbsolutePath(e.identifier!);
          final file = File(path!);
          files.add(file);
        },
      ),
    );

    return files;
  }
}

使ってみる

最後に関数を使用したWidgetを作成してみましょう。
今回は、ギャラリーから画像を選択し任意の処理を行うボタンを作成します。

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:playport/utils/image_utils.dart';

class ImageSelectButton extends StatelessWidget {
  const ImageSelectButton({
    Key? key,
    this.maxImages = 4,
    required this.onPressed,
  }) : super(key: key);

  final int maxImages;
  final Function(List<File>) onPressed;

  
  Widget build(BuildContext context) {
    return IconButton(
      icon: const Icon(Icons.photo_library_outlined),
      onPressed: () async {
        List<File>? files = await ImageUtils.pickLimitedMultiImage(
          maxImages: maxImages,
        );

        if (files != null) {
          onPressed(files);
        }
      },
    );
  }
}

GitHubで編集を提案

Discussion

ログインするとコメントできます