【Flutter】デバイスからの画像選択とカメラの起動

こんにちは！
[MYJLab Advent Calendar 2021](https://qiita.com/advent-calendar/2021/myjlab)の18日目はてるが担当します。宜しくお願いします！
昨日は@yuzu_m さんの[「OpenCVで画像に文字を描画してみた」](https://qiita.com/yuzu_m/items/341298006b3b30ca4379)でした。うさぎかわいいですね🤩
今回は画像に便乗して、最近ハマっているFlutterを使った「デバイスからの画像選択とカメラの起動」について紹介をしていきたいと思います。


## 準備
### パッケージのインストール
image_pickerパッケージを使います。pubspec.yamlのdependenciesに追記してください。

```
dependencies:
  image_picker: ^0.8.4+4
```
vscodeにDartとFlutterの拡張機能を入れていれば、変更を保存するだけでパッケージのインストールが完了します。

それ以外の場合は、下記リンクを参考にしながらパッケージをインストールしてください。
https://pub.dev/packages/image_picker/install

### ファイルを2つ作成
- test.dart
- done.dart

ファイル名は適当です😎コピペで使えるコードを記述したのち解説を書きます。

## ①ライブラリ選択ボタンとカメラ起動ボタンの追加

### コード（test.dart）
```
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'done.dart';

class test extends StatelessWidget {const test({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
    final ImagePicker _picker = ImagePicker(); return Scaffold(

    body: Center(
        child: 
          Column( 
            children: [
              ElevatedButton(
                  child: Text('デバイスのライブラリから取得'),
                  onPressed: () async { final XFile? image =
                      await _picker.pickImage(source: ImageSource.gallery); 
                      if (image != null)
                      Navigator.push(context, MaterialPageRoute(builder: (context) => done(image),),); 
                  },
              ),
              ElevatedButton(
                  child: Text('カメラで撮影する'),
                  onPressed: () async {
                  final XFile? image = await _picker.pickImage(
                  source: ImageSource.camera, );
                  if (image != null) Navigator.push(
                      context, MaterialPageRoute(
                          builder: (context) => done(image),
                      ), 
                  );
                  }, 
              ),
            ], 
          ),
      ), 
    );
  } 
}
```

### 画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1790148/74298a0d-1faa-44a1-8a92-1f946b2a5f85.png)

## ②ギャラリーから選択もしくは撮影した画像の表示

### コード（done.dart）
```
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';

class done extends StatelessWidget {
  const done(this.image, {Key? key}) : super(key: key);
  final XFile image;

  @override
  Widget build(BuildContext context) {
    return Scaffold( 

      body: Column(
      children: [ 
        Center(
          child: Image.file( File(image.path),), 
        ),
        Padding(
          padding: const EdgeInsets.all(12), child: Text('画像が選択されました!'),
        ), 
        ElevatedButton(
          child: Text('完了'),
          onPressed: () async { 
              Navigator.pushNamed(context, '/');
          },
        ), 
      ],
    ), );
  } 
}
```



## 解説
```
final ImagePicker _picker = ImagePicker();
```
この部分でImagePickerをインスタンス化します。



端末のライブラリからの画像取得であれば、
_picker.pickImage(source: ImageSource.gallery) pickImageメソッドのsourceにImageSource.galleryを指定。カメラの起動であれば、ImageSource.cameraを指定します。 


あとは XFile? imageにカメラまたはライブラリから取得した画像データが格納されるので次のページのクラスへ引数を渡し、コンストラクタを使ってパスを受け取ります。

```
Image.file(
 File(image.path),
), 
```

あとは煮るなり焼くなりこっちのものです🔥

ちなみにですが、「①ライブラリ選択ボタンとカメラ起動ボタンの追加」に記述したコードにはasyncとawaitの非同期処理があります。

```
onPressed: () async { final XFile? image =
    await _picker.pickImage(source: ImageSource.gallery); 
    if (image != null)
    Navigator.push(context, MaterialPageRoute(builder: (context) => done(image),),); 
},
```
これはギャラリーからの選択が終わるまで次の処理が行われないようにするためです。



# 補足
iOSシミュレーターでは画像のライブラリ取得とカメラ起動に失敗します。
iOSで実装したい時は実機にビルドをする必要があります。また、iOSアプリからユーザ端末の操作をするには操作許可を得る必要があるので、ios/Runner/info.plistファイルに確認のメッセージと対応するkeyを追記する必要があります。
 
# 参考
- [[Flutter] 初心者でもプラグインで簡単！カメラと写真を読み込むimage_pickerの使い方](https://masamarun.com/flutter-image_picker/)
- [【Flutter】【Dart】Image Pickerで画像を選択する](https://note.com/hatchoutschool/n/n071d5d93e948)