実現したいこと
- エラーを解決させて理想の挙動を得る。
前提
Flutterで個人開発をしており、解決法としてはnullの非許容化だったり、nullの場合の処理を実装するなどの対応をとれば解決することは把握しているが、デバッグする可能性のあるものがあまりに多くなる可能性があり、やるべきことを整理したく備忘としてこちらに書かせていただきました。同じシチュエーションに陥った場合の最適な解決法などを知りたいです。有識者の方々にアドバイスをいただきたいです。追加してほしいものがあればコメント欄にてお願いいたします。
発生している問題・エラーメッセージ
[VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: type 'Null' is not a subtype of type 'Map<String, dynamic>' in type cast #0 AccountFirestore.getUser (package:chat_app/firestore/account_firestore.dart:38:35) <asynchronous suspension> #1 _UserProfilePageState.getUserProfile (package:chat_app/pages/user_profile_page.dart:29:20) <asynchronous suspension> [VERBOSE-2:dart_vm_initializer.cc(41)] Unhandled Exception: type 'Null' is not a subtype of type 'Map<String, dynamic>' in type cast #0 AccountFirestore.getUser (package:chat_app/firestore/account_firestore.dart:38:35) <asynchronous suspension> #1 _UserProfilePageState.getUserProfile (package:chat_app/pages/user_profile_page.dart:29:20) <asynchronous suspension>
該当のソースコード
user_profile_page.dart
1import 'package:chat_app/firestore/account_firestore.dart'; 2import 'package:chat_app/model/account.dart'; 3import 'package:flutter/cupertino.dart'; 4import 'package:flutter/foundation.dart'; 5import 'package:flutter/material.dart'; 6 7class UserProfilePage extends StatefulWidget { 8 final String userId; 9 const UserProfilePage({super.key, required this.userId}); 10 11 @override 12 State<UserProfilePage> createState() => _UserProfilePageState(); 13} 14 15class _UserProfilePageState extends State<UserProfilePage> { 16 // Account myAccount = Authentication.myAccount!; 17 // File? image; 18 // String imagePath = ''; 19 20 Account? userAccount; 21 22 @override 23 void initState() { 24 super.initState(); 25 getUserProfile(); 26 } 27 28 Future<void> getUserProfile() async { 29 bool success = await AccountFirestore.getUser(widget.userId); 30 if (success) { 31 setState(() { 32 userAccount = getUserAccountFromFirestore(); 33 }); 34 } else { 35 if (kDebugMode) { 36 print("ERROR"); 37 } 38 } 39 } 40 41 Account getUserAccountFromFirestore() { 42 return Account( 43 id: widget.userId, 44 name: userAccount?.name ?? "", 45 imagePath: userAccount?.imagePath ?? "", 46 selfIntroduction: userAccount?.selfIntroduction ?? "", 47 userId: userAccount?.userId ?? "", 48 ); 49 } 50 51 @override 52 Widget build(BuildContext context) { 53 return Scaffold( 54 appBar: AppBar( 55 backgroundColor: CupertinoColors.white, 56 elevation: 0, 57 title: const Text( 58 'Profile', 59 style: TextStyle( 60 color: CupertinoColors.black, 61 fontSize: 20.0, 62 fontWeight: FontWeight.bold, 63 ), 64 ), 65 actions: [ 66 IconButton( 67 icon: const Icon(Icons.more_horiz), 68 color: CupertinoColors.systemGrey, 69 onPressed: () {}, 70 ), 71 ], 72 ), 73 body: userAccount != null ? buildUserProfile() : buildLoading(), 74 ); 75 } 76 77 Widget buildLoading() { 78 return const Center( 79 child: CircularProgressIndicator( 80 strokeWidth: 4.0, 81 valueColor: AlwaysStoppedAnimation<Color>( 82 CupertinoColors.activeGreen, 83 ), 84 ), 85 ); 86 } 87 88 Widget buildUserProfile() { 89 return Center( 90 child: Column( 91 mainAxisAlignment: MainAxisAlignment.center, 92 children: [ 93 CircleAvatar( 94 backgroundImage: NetworkImage(userAccount!.imagePath), 95 radius: 50, 96 ), 97 const SizedBox( 98 height: 16, 99 ), 100 Text( 101 userAccount!.name, 102 style: const TextStyle( 103 fontSize: 24, 104 fontWeight: FontWeight.bold, 105 ), 106 ), 107 const SizedBox( 108 height: 8, 109 ), 110 Text( 111 userAccount!.selfIntroduction, 112 style: const TextStyle( 113 fontSize: 16, 114 ), 115 ), 116 ], 117 ), 118 ); 119 } 120} 121
account_firestore.dart
1import 'package:chat_app/firestore/post_firestore.dart'; 2import 'package:chat_app/model/account.dart'; 3import 'package:chat_app/utils/authentication.dart'; 4import 'package:cloud_firestore/cloud_firestore.dart'; 5import 'package:flutter/foundation.dart'; 6 7class AccountFirestore { 8 static final _firestoreInstance = FirebaseFirestore.instance; 9 static final CollectionReference account = 10 _firestoreInstance.collection('account'); 11 12 static Future<dynamic> setUser(Account newAccount) async { 13 try { 14 await account.doc(newAccount.id).set({ 15 'name': newAccount.name, 16 'user_id': newAccount.userId, 17 'self_introduction': newAccount.selfIntroduction, 18 'image_path': newAccount.imagePath, 19 'created_time': Timestamp.now(), 20 'updated_time': Timestamp.now(), 21 }); 22 if (kDebugMode) { 23 print("Account registration completed."); 24 } 25 return true; 26 } on FirebaseException catch (e) { 27 if (kDebugMode) { 28 print("Account Registration Failed. $e"); 29 } 30 return false; 31 } 32 } 33 34 static Future<dynamic> getUser(String uid) async { 35 try { 36 DocumentSnapshot documentSnapshot = await account.doc(uid).get(); 37 Map<String, dynamic> data = 38 documentSnapshot.data() as Map<String, dynamic>; 39 if (data != null) { 40 Account myAccount = Account( 41 id: uid, 42 name: data['name'], 43 imagePath: data['image_path'], 44 selfIntroduction: data['self_introduction'], 45 userId: data['user_id'], 46 createdTime: data['created_time'], 47 updatedTime: data['updated_time'], 48 ); 49 Authentication.myAccount = myAccount; 50 if (kDebugMode) { 51 print("User acquisition succeeded."); 52 } 53 return true; 54 } else { 55 if (kDebugMode) { 56 print("User data is null."); 57 } 58 return false; 59 } 60 } on FirebaseException catch (e) { 61 if (kDebugMode) { 62 print("User acquisition failed. $e"); 63 } 64 return false; 65 } 66 } 67 68 static Future<dynamic> updateUser(Account updateAccount) async { 69 try { 70 await account.doc(updateAccount.id).update({ 71 'name': updateAccount.name, 72 'image_path': updateAccount.imagePath, 73 'user_id': updateAccount.userId, 74 'self_introduction': updateAccount.selfIntroduction, 75 'update_time': Timestamp.now(), 76 }); 77 if (kDebugMode) { 78 print("User information updated successfully."); 79 } 80 return true; 81 } on FirebaseException catch (e) { 82 if (kDebugMode) { 83 print("Failed to update user information. $e"); 84 } 85 return true; 86 } 87 } 88 89 static Future<Map<String, Account>?> getPostUserMap( 90 List<String> accountIds) async { 91 Map<String, Account> map = {}; 92 try { 93 await Future.forEach(accountIds, (String accountId) async { 94 var doc = await account.doc(accountId).get(); 95 Map<String, dynamic> data = doc.data() as Map<String, dynamic>; 96 Account postAccount = Account( 97 id: accountId, 98 name: data['name'], 99 imagePath: data['image_path'], 100 selfIntroduction: data['self_introduction'], 101 userId: data['user_id'], 102 ); 103 map[accountId] = postAccount; 104 }); 105 if (kDebugMode) { 106 print( 107 "Successful acquisition of information about the user who submitted the article."); 108 } 109 return map; 110 } on FirebaseException catch (e) { 111 if (kDebugMode) { 112 print("Failure to obtain information about the submitting user. $e"); 113 } 114 return null; 115 } 116 } 117 118 static Future<dynamic> deleteUser(String accountId) async { 119 account.doc(accountId).delete(); 120 PostFirestore.deletePosts(accountId); 121 } 122} 123 124
試したこと
Unhandled Exception: type 'Null' is not a subtype of type 'Map<String, dynamic>' in type castでググり、AccountFirestore.getUserメソッド内のdocumentSnapshot.data()メソッドが返す値がMap<String, dynamic>型ではなくnullであることが原因であることが判明。
補足情報(FW/ツールのバージョンなど)
[✓] Flutter (Channel stable, 3.10.5, on macOS 13.4 22F66 darwin-x64,
locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version
32.1.0-rc1)
[✓] Xcode - develop for iOS and macOS (Xcode 14.3.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.2)
[✓] VS Code (version 1.79.1)
[✓] Connected device (3 available)
[✓] Network resources

バッドをするには、ログインかつ
こちらの条件を満たす必要があります。