Flutter x Supabaseでプロフィール画像を登録する方法

ます。

1. Flutterプロジェクトの作成と必要なパッケージのインストール

まずはFlutterプロジェクトを作成し、以下の2つのパッケージをインストールしてください。

flutter pub add supabase
flutter pub add image_picker

Supabaseはデータベースとの通信を行うために使用します。image_pickerはユーザーが自分の端末から画像を選択できるようにするために使用します。

2. Supabaseの初期化とログイン処理の実装

まずはSupabaseとの通信を行うための初期化と、ログイン処理の実装を行います。以下のコードをmain.dartに追加してください。

import 'package:flutter/material.dart';
import 'package:supabase/supabase.dart';

const supabaseUrl = 'SUPABASE_URL';
const supabaseAnonKey = 'SUPABASE_ANON_KEY';
const supabase = SupabaseClient(supabaseUrl, supabaseAnonKey);

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final response = await supabase.auth.signIn(
      email: 'EMAIL',
      password: 'PASSWORD'
  );
  if (response.error != null) {
    print(response.error!.message);
    return;
  }
  runApp(App());
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Profile Image Upload',
      debugShowCheckedModeBanner: false,
      home: HomePage(),
    );
  }
}

SUPABASE_URLとSUPABASE_ANON_KEYには、SupabaseのURLとAnonymous Keyをそれぞれ設定してください。また、EMAILとPASSWORDはSupabaseで登録したユーザーアカウントの情報に置き換えてください。ログイン処理が成功した場合、Appウィジェットを起動します。Appウィジェットは今回作るアプリのルートウィジェットとなります。

3. 画像選択機能の実装

ユーザーが自分の端末から画像を選択できるように、以下のように画像選択機能を実装します。

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State {
  final _picker = ImagePicker();
  File? _image;

  Future _pickImage(ImageSource source) async {
    final pickedImage = await _picker.pickImage(source: source);
    if (pickedImage != null) {
      final imageFile = File(pickedImage.path);
      setState(() {
        _image = imageFile;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Profile Image Upload'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (_image != null)
              Image.file(_image!, width: 300.0),
            SizedBox(height: 16.0),
            ElevatedButton.icon(
              onPressed: () => _pickImage(ImageSource.gallery),
              icon: Icon(Icons.photo_library),
              label: Text('Select Image'),
            ),
          ],
        ),
      ),
    );
  }
}

_pickImage()メソッドで画像を選択した後、選択した画像を_stateful widget_内の_image変数に格納しています。選択した画像は、Image.file()ウィジェットを使って表示されます。

4. 画像をストレージにアップロードする処理の実装

次に、選択した画像をストレージにアップロードする処理を実装します。以下のように_stateful widget_の_HomePageStateクラスにuploadImage()メソッドを追加します。

class _HomePageState extends State {
  ...

  Future _uploadImage() async {
    if (_image == null) return;

    final storageResponse = await supabase.storage
        .from('PROFILE_IMAGES')
        .upload('profile.png', _image!.readAsBytesSync(), cacheControl: '3600')
        .onError((error, stackTrace) {
      print('Error uploading image: $error');
      return;
    });

    if (storageResponse.error == null) {
      print('Image uploaded successfully!');

      // TODO: Update user profile with photo_url
    }
  }

  ...
}

ここでは、Supabaseのストレージに_IMAGE-URL_という名前のフォルダを作成し、そこにアップロードします。アップロードには、Supabaseの_storage_ライブラリを使用します。アップロードが成功した場合、コンソールに「Image uploaded successfully!」と表示されます。

5. ユーザーのプロフィール情報を更新する処理の実装

最後に、ユーザーのプロフィール情報を更新する処理を実装します。ここでは、Supabaseの_auth_ライブラリを使用します。以下のようにsetState()メソッドを用いて、ウィジェットを再描画し、アップロードした画像を表示します。

class _HomePageState extends State {
  ...

  Future _updateProfile(String imageUrl) async {
    final user = supabase.auth.currentUser;
    if (user == null) return;

    final response = await supabase.from('profiles')
        .update({'photo_url': imageUrl})
        .eq('id', user.id)
        .execute();

    if (response.error == null) {
      print('Profile updated successfully!');
      setState(() => user.userMetadata.photoUrl = imageUrl);
    }
  }

  ...
  
}

また、以下のように ElevatedButton を追加して、ボタンを押したときに_updateProfile() メソッドが呼び出されるようにしてください。

ElevatedButton.icon(
  onPressed: () async {
    await _uploadImage();
    await _updateProfile('$_supabaseUrl/storage/v1/object/PROFILE_IMAGES/profile.png');
  },
  icon: Icon(Icons.upload),
  label: Text('Upload Image'),
),

6. 完成したコード

最終的に完成したコードは、以下のようになります。

import 'dart:io';

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:supabase/supabase.dart';

const supabaseUrl = 'SUPABASE_URL';
const supabaseAnonKey = 'SUPABASE_ANON_KEY';
const supabase = SupabaseClient(supabaseUrl, supabaseAnonKey);

Future main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final response = await supabase.auth.signIn(
      email: 'EMAIL',
      password: 'PASSWORD'
  );
  if (response.error != null) {
    print(response.error!.message);
    return;
  }
  runApp(App());
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Profile Image Upload',
      debugShowCheckedModeBanner: false,
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State {
  final _picker = ImagePicker();
  File? _image;

  Future _pickImage(ImageSource source) async {
    final pickedImage = await _picker.pickImage(source: source);
    if (pickedImage != null) {
      final imageFile = File(pickedImage.path);
      setState(() {
        _image = imageFile;
      });
    }
  }

  Future _uploadImage() async {
    if (_image == null) return;

    final storageResponse = await supabase.storage
        .from('PROFILE_IMAGES')
        .upload('profile.png', _image!.readAsBytesSync(), cacheControl: '3600')
        .onError((error, stackTrace) {
      print('Error uploading image: $error');
      return;
    });

    if (storageResponse.error == null) {
      print('Image uploaded successfully!');

      // TODO: Update user profile with photo_url
    }
  }

  Future _updateProfile(String imageUrl) async {
    final user = supabase.auth.currentUser;
    if (user == null) return;

    final response = await supabase.from('profiles')
        .update({'photo_url': imageUrl})
        .eq('id', user.id)
        .execute();

    if (response.error == null) {
      print('Profile updated successfully!');
      setState(() => user.userMetadata.photoUrl = imageUrl);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Profile Image Upload'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            if (_image != null)
              Image.file(_image!, width: 300.0),
            SizedBox(height: 16.0),
            ElevatedButton.icon(
              onPressed: () => _pickImage(ImageSource.gallery),
              icon: Icon(Icons.photo_library),
              label: Text('Select Image'),
            ),
            SizedBox(height: 16.0),
            ElevatedButton.icon(
              onPressed: () async {
                await _uploadImage();
                await _updateProfile('$_supabaseUrl/storage/v1/object/PROFILE_IMAGES/profile.png');
              },
              icon: Icon(Icons.upload),
              label: Text('Upload Image'),
            ),
          ],
        ),
      ),
    );
  }
}

コメント

タイトルとURLをコピーしました