🐹

[flutter✖️firebase] auth✖️riverpod✖️mvvm 新規登録

2024/04/30に公開

どうも、初心者flutterプログラマーちゃきです!

firebaseauthでの新規登録のシンプルな備忘録です。

MVVM構造で以下のように処理を分担させることで、コードを見直しやすくなります!

Model → firebaseでの処理
ViewModel  → repositoryの処理を監視し、viewに返す
View  → クライアントユーザーの入力の処理

はじめに

1、authで新規登録
2、cloudstoreで新規登録
の順で、firebaseAuthとcloudstoreの両方でユーザーを新規作成します。

Authでの処理

Auth関連のrepositoryとcontrollerのproviderを作成

・ Modelのレポジトリでは、currentUserをstate(状態)に持たせます。
(全てのfirebaseauthの処理でcurrentUserを使用するから)

@riverpod
class AuthRepo extends _$AuthRepo {
  @override
  User? build() {
    return firebaseauth.instance.currentUser;
  }
}

・ ViewModelでは、AsyncValueをstateに持たせることで、レポジトリに処理を依頼している際に
処理の状態を表示できるようにします。(処理中にグルグルを表示)

@riverpod
class AuthController extends _$AuthController {
  @override
  AsyncValue build() {
    return const AsyncData(null);
  }
}

Model(firebaseの処理)

FirebaseAuthException catch(e){}とtry{}catch(e){}の二つを使うことで、
エラーが起きた際に、authのエラーとその他のエラーを区別できるようにする。
新規登録createUserWithEmailAndPassword()がうまく行った際には、プロバイダーのstateを更新して、currentUserをプロバイダー内で共有させる。

  Future<String> createUser({
    required String email,
    required String password,
  }) async {

    try {
      await ref.read(authInstanceProvider).createUserWithEmailAndPassword(
            email: email,
            password: password,
          );
      state = firebaseauth.instance.currentUser;
      return 'success';
    } on FirebaseAuthException catch (e) {
      return FirebaseAuthErrorExt.fromCode(e.code);
    } catch (e) {
      return 'error';
    }
  }

ViewModel(Modelの処理の監視)

処理中はAsyncLoading();でグルグルマークを表示、
処理が終わるとAsyncData(null);で状態をリセットします。
そして、modelから受け取ったString型の処理の結果を返します。

 Future<String> createUser({
    required String email,
    required String password,
  }) async {
    state = const AsyncLoading();
    final String result = await ref
        .read(authRepoProvider.notifier)
        .createUser(email: email, password: password);
    state = const AsyncData(null);
    return result;
  }

View(ViewModelへ処理を依頼)

Future<void> _createUser({
    required WidgetRef ref,
    required String email,
    required String password,
    required String name,
    required BuildContext context,
  }) async {
    //① authで新規登録する
    final signInResult = await ref
        .read(authControllerProvider.notifier)
        .createUser(email: emailController.text, password: passController.text);
    //② authで登録できた場合はcloudStoreでユーザー作成。
    if (signInResult == 'success') {
      //③ ユーザーアカウントのインスタンスを作成。
      UserAccount userAccount = UserAccount(
        userId: firebaseauth.instance.currentUser!.uid,
        email: email,
        name: name,
        createdAt: Timestamp.now(),
      );
      // ④ cloudStoreでユーザーデータ保存 
      await ref
          .read(userAccountControllerProvider.notifier)
          .createUserAccount(userAccount: userAccount);

      //authで登録できなかった場合はエラーの原因を表示する
    } else {
      print(signInResult);
    }
  }

Cloudstoreでの処理

cloudstoreでユーザーコレクションを扱うプロバイダーを作成

・ Modelのstateが重要です⭐️stateでユーザーアカウント型のデータを返すようにします
(viewmodelはauthの場合と同じなので割愛)

@riverpod
class UserAccountRepo extends _$UserAccountRepo {
  @override
  CollectionReference<UserAccount> build() {
    return firebaseauth.instance
        .collection('userAccount')
        .withConverter<UserAccount>(
          fromFirestore: (snapshot, _) =>
              UserAccount.fromJson(snapshot.data()!),
          toFirestore: (UserAccount value, _) => value.toJson(),
        );
  }
}
@riverpod
class UserAccountRepo extends _$UserAccountRepo {
  @override
  CollectionReference<UserAccount> build() {
    return ref
        .read(fireStoreInstanceProvider)
        .collection(FirebaseUserAccountKey.userCollection)
        .withConverter<UserAccount>(
          fromFirestore: (snapshot, _) =>
              UserAccount.fromJson(snapshot.data()!),
          toFirestore: (UserAccount value, _) => value.toJson(),
        );
  }
}

View(ViewModelへ処理を依頼)

Future<void> _createUser({
    required WidgetRef ref,
    required String email,
    required String password,
    required String name,
    required BuildContext context,
  }) async {
    //① authで新規登録する
    final signInResult = await ref
        .read(authControllerProvider.notifier)
        .createUser(email: emailController.text, password: passController.text);
    //② authで登録できた場合はcloudStoreでユーザー作成。
    if (signInResult == 'success') {
      //③ ユーザーアカウントのインスタンスを作成。
      UserAccount userAccount = UserAccount(
        userId: firebaseauth.instance.currentUser!.uid,
        //⭐️ここで、firebaseauthから得られたuserIdを設定する。
        email: email,
        name: name,
        createdAt: Timestamp.now(),
      );
      // ④ cloudStoreでユーザーデータ保存 
      await ref
          .read(userAccountControllerProvider.notifier)
          .createUserAccount(userAccount: userAccount);

      //authで登録できなかった場合はエラーの原因を表示する
    } else {
      print(signInResult);
    }
  }

ViewModel(Modelの処理の監視)

Future<void> createUserAccount({required UserAccount userAccount}) async {
    state = const AsyncLoading();
    await ref
        .read(userAccountRepoProvider.notifier)
        .createUserAccount(userAccount);
    state = const AsyncData(null);
  }

Model(firebaseの処理)

・ ProviderでUser型のデータを返せるように処理したため、とてもシンプルなコードとなっています!

Future<void> createUserAccount(UserAccount addAccount) async {
    await state.doc(addAccount.userId).set(addAccount);
  }

おわりに

新規登録ができると、authとcloudstoreでのデータ作成の基礎ができていいですね☺️
また今度、ログイン、メールアドレス変更、パスワード変更、パスワード確認などの記事も追加してauthマスターになるぞ💪

Discussion