【Flutter x Supabase】 画像のアップロードのやり方＋おまけつき

![スクリーンショット 2023-12-01 12.12.50（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/223d2ec8-07ce-15e6-967d-d235f0dca93c.png)



:::note info
この記事は、<b>株式会社ゆめみの23卒 Advent Calendar 2023</b>のアドベントカレンダーになります！！
色々な種類の記事が投稿されるので、お楽しみに🤗
:::

https://qiita.com/advent-calendar/2023/yumemi-23-graduation

こんにちは。いせりゅーです。今回は、Flutter x Supabaseで画像のアップロードする方法に加えて、必要そうな記載を書いてみました。(あくまでも一例です)

:::note warn
Supabaseを学習し始めてからまもないため、間違えた実装を記載するかも知れません。その場合はコメントなどで教えていただけると幸いです🙇
:::

１、カメラかカメラロールか選択するSheetを出す。
２、ImagePickerで写真を取得する
３、画像をアップロードする。

こんな感じで進めていきます〜〜！！


# カメラorカメラロールか選択するSheetを出す。

写真をアップロードする場合において、「カメラ」か「カメラロール」の２種類で画像をアップロードすることが多いと思います。
これの表現方法はいくらでもありますが、今回は、Sheetを出す形にしてみました。iOS風にしたかったため`showCupertinoModalPopup`を使用してみました。

```dart
  void onTapImage() {
    showCupertinoModalPopup(
      context: context,
      builder: (context) => CupertinoActionSheet(
        actions: [
          CupertinoActionSheetAction(
            isDefaultAction: true,
            onPressed: () {
              Navigator.pop(context);
              camera();
            },
            child: const Text(
              'カメラ',
              style: TextStyle(
                fontWeight: FontWeight.bold,
                color: Colors.black,
              ),
            ),
          ),
          CupertinoActionSheetAction(
            onPressed: () {
              Navigator.pop(context);
              album();
            },
            child: const Text(
              'アルバム',
              style: TextStyle(
                fontWeight: FontWeight.bold,
                color: Colors.black,
              ),
            ),
          ),
          CupertinoActionSheetAction(
            isDestructiveAction: true,
            onPressed: () =>  Navigator.pop(context),
            child: const Text('閉じる'),
          ),
        ],
      ),
    );
  }

  void camera() {
  
  }

  void album() {
  
  }
  
```

# ImagePickerで写真を取得する

次に画像を取得する処理を記載します。今回は、image_pickerを導入しました。

https://pub.dev/packages/image_picker


### Androidのセットアップ

```xml: android/app/src/main/AndroidManifest.xml

<uses-permission android:name="android.permission.CAMERA" />

```

```gradle:android/app/build.gradle

defaultConfig {
  applicationIdSuffix appIdSuffix
  // minSdkVersion 19
  minSdkVersion 21
  targetSdkVersion flutter.targetSdkVersion
  versionCode flutterVersionCode.toInteger()
  versionName flutterVersionName
  resValue "string", "app_name", appName
}

```

### iOSのセットアップ

```plist:ios/Runner/Info.plist
<key>NSPhotoLibraryUsageDescription</key>
<string>写真の投稿のために許可が必要になります</string>
<key>NSCameraUsageDescription</key>
<string>写真の投稿のために許可が必要になります</string>
```

### 画像を取得する

```dart
final picker = ImagePicker();
String uploadImage = '';

 // カメラからの画像を取得する
 Future<void> camera() async {
    try {
      final image = await ImagePicker().pickImage(source: ImageSource.camera);
      if (image == null) {
        return;
      }
      imageBytes = await image.readAsBytes();
      uploadImage = image.name;
      print('画像の添付が成功しました');
    } on PlatformException catch (error) {
      print(error);
    }
  }

  Future<void> album() async {
    try {
      final image = await ImagePicker().pickImage(source: ImageSource.gallery);
      if (image == null) {
        return;
      }
      imageBytes = await image.readAsBytes();
      uploadImage = image.name;
      print('画像の添付が成功しました');
    } on PlatformException catch (e) {
      state = state.copyWith(status: e.message!);
    }
  }
```

# 画像をアップロードする。

では、Supabaseに画像をアップロードしていこうと思います。
その前にSupabase側で設定が必要そうですね。こちらを参考にしてみてください（今回は割愛させていただきます）
今回は、画像のPathを`id/画像のパス`にしてみました。理由としては、どのユーザーが投稿したかをわかりやすくするためでした。（もっといい方法があれば知りたい😇）

https://zenn.dev/dk96424/articles/d7efb641e01445

https://www.youtube.com/watch?v=SZ09mPSNu4k

```dart
final supabase = Supabase.instance.client;![スクリーンショット 2023-12-01 12.12.50（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/4d213fd7-f6ff-a7c5-444c-7bf7eb0f5bfa.png)


Future<void> upload() async {
    loading.state = true;
    final user = supabase.auth.currentUser?.id;
    try {
      await supabase.storage.from('food').uploadBinary('/$user/$uploadImage', imageBytes);
    } on PostgrestException catch (error) {
      print(error);
    }
  }
```

# ソースコード

```dart
  final picker = ImagePicker();
  String uploadImage = '';
  final supabase = Supabase.instance.client;

  void onTapImage() {
    showCupertinoModalPopup(
      context: context,
      builder: (context) => CupertinoActionSheet(
        actions: [
          CupertinoActionSheetAction(
            isDefaultAction: true,
            onPressed: () {
              Navigator.pop(context);
              camera();
            },
            child: const Text(
              'カメラ',
              style: TextStyle(
                fontWeight: FontWeight.bold,
                color: Colors.black,
              ),
            ),
          ),
          CupertinoActionSheetAction(
            onPressed: () {
              Navigator.pop(context);
              album();
            },
            child: const Text(
              'アルバム',
              style: TextStyle(
                fontWeight: FontWeight.bold,
                color: Colors.black,
              ),
            ),
          ),
          CupertinoActionSheetAction(
            isDestructiveAction: true,
            onPressed: () =>  Navigator.pop(context),
            child: const Text('閉じる'),
          ),
        ],
      ),
    );
  }

 Future<void> camera() async {
    try {
      final image = await ImagePicker().pickImage(source: ImageSource.camera);
      if (image == null) {
        return;
      }
      imageBytes = await image.readAsBytes();
      uploadImage = image.name;
      print('画像の添付が成功しました');
    } on PlatformException catch (error) {
      print(error);
    }
  }

  Future<void> album() async {
    try {
      final image = await ImagePicker().pickImage(source: ImageSource.gallery);
      if (image == null) {
        return;
      }
      imageBytes = await image.readAsBytes();
      uploadImage = image.name;
      print('画像の添付が成功しました');
    } on PlatformException catch (e) {
      state = state.copyWith(status: e.message!);
    }
  }

  Future<void> upload() async {
    loading.state = true;
    final user = supabase.auth.currentUser?.id;
    try {
      await supabase.storage.from('food').uploadBinary('/$user/$uploadImage', imageBytes);
    } on PostgrestException catch (error) {
      print(error);
    }
  }

```


# 最後に

自分もまだまだ学習中ですが、Supabaseのいいところが毎日見つかってきてとてもいいですね。個人的にはこれからの発展に期待したいですね😁


# ちょっとした宣伝

株式会社ゆめみの23卒のメンバーでアドベントカレンダーを作成しています。
新卒のフレッシュな記事がたくさん投稿予定なので、少しでも興味があれば購読いただけると幸いです！！


https://qiita.com/advent-calendar/2023/yumemi-23-graduation



[![YUMEMI New Grad Advent Calendar 2023](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/672609/6d8a7098-9aef-a19a-54fe-83a2d493e033.png)](https://qiita.com/advent-calendar/2023/yumemi-23-graduation)