[flutter✖️firebase] auth✖️riverpod✖️mvvm 新規登録
どうも、初心者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