🌊

Bad state: cannot get a field on a DocumentSnapshotPlatform which does

2023/08/26に公開

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