🌊
Bad state: cannot get a field on a DocumentSnapshotPlatform which does
FutureProviderで使用すると起きた!
Firestoreから、userコレクションのnameフィールドの値を取得しようとしたら、以下のエラーが発生する。
error code
コードを何度か書き換えると、エラー連発しました😱
Bad state: cannot get a field on a DocumentSnapshotPlatform which does not exist
The operator '[]' isn't defined for the type 'Object'.
Try defining the operator '[]'.dartundefined_operator
Type: String
final name = userSnapshot.data()!['name'] as String;
解決策
FutureProviderに例外処理を追加して、エラー対応をする。
// uidを取得するためのProvider
final uidProvider = Provider((ref) => ref.watch(authProvider).currentUser?.uid);
// ログイン状態を取得するためのProvider
final authStateProvider = StreamProvider<User?>((ref) {
return ref.watch(authProvider).authStateChanges();
});
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:kenty_app/provider/auth_provider.dart';
import 'package:kenty_app/provider/firestore_provider.dart';
final userFutureProvider = FutureProvider<DocumentSnapshot?>((ref) async {
try {
final uid = ref.read(uidProvider); // ref.watch() から ref.read() へ変更
final snapshot = await ref.read(fireStoreProvider).collection('user').doc(uid).get();
if (!snapshot.exists) { // ドキュメントが存在しない場合はnullを返す
return null;
}
return snapshot;
} catch (e) {
// エラーが発生した場合は、プロバイダーのエラーを投げます。
// FutureProviderはこのエラーを捉えて、.error部分で処理します。
throw Exception("Failed to fetch user data: $e");
}
});
削除のロジックが書いてあるコード
趣味で書いてるコードです。よくないかも?
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:kenty_app/domain/users/users.dart';
import 'package:kenty_app/provider/auth_provider.dart';
import 'package:kenty_app/provider/firestore_provider.dart';
base class UserBase {
Ref ref;
UserBase(this.ref);// refはUserRepositoryのref
}
// UserBaseを継承したUserRepositoryを使うためのProvider
final userRepositoryProvider = Provider((ref) => UserRepository(ref));
base class UserRepository extends UserBase {
UserRepository(super.ref);//super.refはUserBaseのref
// userコレクションに.setでデータを追加する
Future<void> createUser(Users users) async {
try {
final uid = ref.watch(uidProvider);
await ref
.read(fireStoreProvider)
.collection('user')
.doc(uid)
.set(users.toJson());
} catch (e) {
throw e.toString();
}
}
// // userコレクションのデータを.getで取得する
// Future<DocumentSnapshot> readUser() async {
// try {
// final uid = ref.watch(uidProvider);
// return await ref
// .read(fireStoreProvider)
// .collection('user')
// .doc(uid)
// .get();
// } catch (e) {
// throw e.toString();
// }
// }
// userコレクションのデータを.updateで更新する
Future<void> updateUser(Users users) async {
try {
final uid = ref.watch(uidProvider);
await ref
.read(fireStoreProvider)
.collection('user')
.doc(uid)
.update(users.toJson());
} catch (e) {
throw e.toString();
}
}
// userコレクションのデータを.deleteで削除する
Future<void> deleteUser() async {
try {
final uid = ref.watch(uidProvider);
await ref
.read(fireStoreProvider)
.collection('user')
.doc(uid)
.delete();
} catch (e) {
throw e.toString();
}
}
}
モデルクラス
Freezedを使用してます。いるのかなって感じです。実験用で使ってみました。
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:flutter/foundation.dart';
part 'users.freezed.dart';
part 'users.g.dart';
// flutter pub run build_runner watch --delete-conflicting-outputs
class Users with _$Users {
const factory Users({
('') String name,
}) = _Users;
factory Users.fromJson(Map<String, dynamic> json) => _$UsersFromJson(json);
}
view側の修正
Firestoreから値が取得できなかった時の、エラー対応を追加する。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:kenty_app/provider/user/user_base.dart';
import 'package:kenty_app/provider/user/user_provider.dart';
class userInfo extends ConsumerWidget {
const userInfo({super.key});
Widget build(BuildContext context, WidgetRef ref) {
final users = ref.watch(userFutureProvider);
return Scaffold(
appBar: AppBar(
title: const Text('ユーザー情報'),
),
body: Container(
width: MediaQuery.of(context).size.width,
child: Column(
children: [
const SizedBox(height: 20),
const Text('自己紹介', style: TextStyle(fontSize: 20)),
users.when(
data: (userSnapshot) {
// ドキュメントがnullか、データがnullの場合の処理
if (userSnapshot == null || userSnapshot.data() == null) {
return const Center(child: Text('ユーザー情報が存在しません。'));
}
final dataMap = userSnapshot.data() as Map<String, dynamic>;
final name = dataMap['name'] as String;
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
const CircleAvatar(
radius: 20,
backgroundColor: Colors.grey,
),
const SizedBox(width: 20),
Text(
name,
style: const TextStyle(fontSize: 20),
),
],
),
);
},
error: (e, s) => Center(child: Text(e.toString())), // エラーメッセージを表示
loading: () => const Center(child: CircularProgressIndicator()),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () async {
await ref.read(userRepositoryProvider).deleteUser();
// ignore: unused_result
ref.refresh(userFutureProvider);
},
child: const Text('ユーザを削除')),
],
),
),
);
}
}
スクリーンショット
エラー対応したら、データを取得できました🙌
最後に
短い記事になりましたが、同じエラーにハマった人のお役に立てるかなと思い記事を書いてみました。
Discussion