Flutter无法访问REST API返回对象数组字段的问题排查
解决Flutter中访问REST API返回字段的报错问题
从REST API获取对象数组后,在Flutter中访问posts、comments、likes和status字段时失败,报错:The getter 'data' isn't defined for the type 'List',错误出现在代码行snapshot.data!.data[index].posts[postPosition].url和snapshot.data!.data[index].status。
问题根源
snapshot.data本身就是List<Posts>类型,无需额外调用.data属性,多写的这层调用直接导致报错- Post类字段名不匹配:API返回的图片地址字段是
post,代码中错误使用了.url - Posts类中多余的
data和lengthgetter完全无用,反而造成混淆 - 嵌套ListView未处理滚动冲突,会导致界面滚动异常
API返回数据
[ { "_id": "6304e73ecdc5d350cc33e902", "userId": "6304e42231ef2e7a4dec924d", "posts": [ { "postType": "image", "post": "https://www.theskinnybeep.com/wp-content/uploads/2019/01/Versace-Man-2019.jpg", "_id": "6304e73ecdc5d350cc33e903" }, { "postType": "image", "post": "https://www.theskinnybeep.com/wp-content/uploads/2019/01/Versace-Man-2019.jpg", "_id": "6304e73ecdc5d350cc33e904" } ], "status": "testing status", "comments": [ { "commentText": "Testing comment", "commentAt": "2022-08-23T14:41:55.646Z", "commentReplys": [ { "userId": "6304e02d481e08d44e618d41", "replyText": "Testing comment", "replyAt": "2022-08-23T14:41:55.646Z", "replyLikes": [ { "userId": "6304e02d481e08d44e618d41", "_id": "6304e73ecdc5d350cc33e907", "isNotified": true }, { "userId": "6304e42231ef2e7a4dec924d", "isNotified": true, "_id": "6305f8d07d513ce62b9c099f" } ], "_id": "6304e73ecdc5d350cc33e906" }, { "userId": "6304e02d481e08d44e618d41", "replyText": "reply text testing", "replyAt": "2022-08-23T15:57:51.259Z", "_id": "6304f90191c32e0deac663b8", "replyLikes": [ { "userId": "6304e42231ef2e7a4dec924d", "isNotified": true, "_id": "6305f8d07d513ce62b9c099f" } ] } ], "commentLikes": [ { "userId": "6304e42231ef2e7a4dec924d", "isNotified": true, "_id": "6305f8f67d513ce62b9c09a2" } ], "commentId": "bc174de0-22f1-11ed-9c5d-23d89a83ff32", "_id": "6304e73ecdc5d350cc33e905" }, { "commentText": "Testing comment", "commentAt": "2022-08-23T15:02:11.123Z", "commentId": "90928740-22f4-11ed-b912-e99836187b6d", "_id": "6304ec67825b5926f0f074cf", "commentReplys": [ { "userId": "6304e02d481e08d44e618d41", "replyText": "reply text testing", "replyAt": "2022-08-23T15:57:51.259Z", "_id": "6304f90191c32e0deac663b8", "replyLikes": [ { "userId": "6304e42231ef2e7a4dec924d", "isNotified": true, "_id": "6305f8d07d513ce62b9c099f" } ] } ], "commentLikes": [ { "userId": "6304e42231ef2e7a4dec924d", "isNotified": true, "_id": "6305f8f67d513ce62b9c09a2" } ] }, { "commentText": "Testing comment", "commentAt": "2022-08-23T15:02:11.123Z", "commentId": "90928740-22f4-11ed-b912-e99836187b6d", "_id": "6304ec81825b5926f0f074d1", "commentReplys": [ { "userId": "6304e02d481e08d44e618d41", "replyText": "reply text testing", "replyAt": "2022-08-23T15:57:51.259Z", "_id": "6304f90191c32e0deac663b8", "replyLikes": [ { "userId": "6304e42231ef2e7a4dec924d", "isNotified": true, "_id": "6305f8d07d513ce62b9c099f" } ] } ], "commentLikes": [ { "userId": "6304e42231ef2e7a4dec924d", "isNotified": true, "_id": "6305f8f67d513ce62b9c09a2" } ] } ], "likes": [ { "userId": "6304e42231ef2e7a4dec924d", "_id": "63052dc7a1728d463769681b" } ], "__v": 0 }, { "_id": "63070a03584ed0febe5b5a5f", "status": "testing", "posts": [], "comments": [], "likes": [] } ]
修正后的Flutter代码
Widget build(BuildContext context) { return SizedBox( height: 600, child: FutureBuilder<List<Posts>>( future: fetchPost(), builder: ((context, snapshot) { if (snapshot.hasData) { return ListView.builder( scrollDirection: Axis.vertical, shrinkWrap: true, itemCount: snapshot.data!.length, itemBuilder: (context, index) { final postItem = snapshot.data![index]; return Column( children: [ Text(postItem.status ?? ''), if (postItem.posts.isNotEmpty) ListView.builder( scrollDirection: Axis.vertical, shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), // 解决嵌套滚动冲突 itemCount: postItem.posts.length, itemBuilder: (context, postPosition) { final singlePost = postItem.posts[postPosition]; return Image.network(singlePost.post ?? ''); }, ), ], ); }, ); } else { return const CircularProgressIndicator(); } }), ), ); }
修正后的Posts及关联类
import 'dart:convert'; import 'dart:core'; List<Posts> postsFromJson(String str) => List<Posts>.from(json.decode(str).map((x) => Posts.fromJson(x))); String postsToJson(List<Posts> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson()))); class Posts { Posts({ this.id, this.userId, required this.posts, this.status, this.comments, this.likes, }); String? id; String? userId; List<Post> posts; String? status; List<Comment>? comments; List<Like>? likes; factory Posts.fromJson(Map<String, dynamic> json) => Posts( id: json["_id"], userId: json["userId"], posts: List<Post>.from(json["posts"].map((x) => Post.fromJson(x))), status: json["status"], comments: json["comments"] != null ? List<Comment>.from(json["comments"].map((x) => Comment.fromJson(x))) : null, likes: json["likes"] != null ? List<Like>.from(json["likes"].map((x) => Like.fromJson(x))) : null, ); Map<String, dynamic> toJson() => { "_id": id, "userId": userId, "posts": List<dynamic>.from(posts.map((x) => x.toJson())), "status": status, "comments": comments != null ? List<dynamic>.from(comments!.map((x) => x.toJson())) : null, "likes": likes != null ? List<dynamic>.from(likes!.map((x) => x.toJson())) : null, }; } class Post { Post({ this.postType, this.post, this.id, }); String? postType; String? post; String? id; factory Post.fromJson(Map<String, dynamic> json) => Post( postType: json["postType"], post: json["post"], id: json["_id"], ); Map<String, dynamic> toJson() => { "postType": postType, "post": post, "_id": id, }; } class Comment { Comment({ this.commentText, this.commentAt, this.commentReplys, this.commentLikes, this.commentId, this.id, }); String? commentText; DateTime? commentAt; List<CommentReply>? commentReplys; List<Like>? commentLikes; String? commentId; String? id; factory Comment.fromJson(Map<String, dynamic> json) => Comment( commentText: json["commentText"], commentAt: DateTime.parse(json["commentAt"]), commentReplys: json["commentReplys"] != null ? List<CommentReply>.from(json["commentReplys"].map((x) => CommentReply.fromJson(x))) : null, commentLikes: json["commentLikes"] != null ? List<Like>.from(json["commentLikes"].map((x) => Like.fromJson(x))) : null, commentId: json["commentId"], id: json["_id"], ); Map<String, dynamic> toJson() => { "commentText": commentText, "commentAt": commentAt?.toIso8601String(), "commentReplys": commentReplys != null ? List<dynamic>.from(commentReplys!.map((x) => x.toJson())) : null, "commentLikes": commentLikes != null ? List<dynamic>.from(commentLikes!.map((x) => x.toJson())) : null, "commentId": commentId, "_id": id, }; } class CommentReply { CommentReply({ this.userId, this.replyText, this.replyAt, this.replyLikes, this.id, }); String? userId; String? replyText; DateTime? replyAt; List<Like>? replyLikes; String? id; factory CommentReply.fromJson(Map<String, dynamic> json) => CommentReply( userId: json["userId"], replyText: json["replyText"], replyAt: DateTime.parse(json["replyAt"]), replyLikes: json["replyLikes"] != null ? List<Like>.from(json["replyLikes"].map((x) => Like.fromJson(x))) : null, id: json["_id"], ); Map<String, dynamic> toJson() => { "userId": userId, "replyText": replyText, "replyAt": replyAt?.toIso8601String(), "replyLikes": replyLikes != null ? List<dynamic>.from(replyLikes!.map((x) => x.toJson())) : null, "_id": id, }; } class Like { Like({ this.userId, this.id, this.isNotified, }); String? userId; String? id; bool? isNotified; factory Like.fromJson(Map<String, dynamic> json) => Like( userId: json["userId"], id: json["_id"], isNotified: json["isNotified"], ); Map<String, dynamic> toJson() => { "userId": userId, "_id": id, "isNotified": isNotified, }; }
关键修正说明
- 移除多余的
.data调用:snapshot.data!直接是List<Posts>,通过[index]即可获取单个Posts对象 - 匹配字段名:将代码中访问图片地址的
.url改为API返回的.post - 清理无用代码:删除Posts类中无意义的
data和lengthgetter - 解决滚动冲突:给嵌套的ListView添加
NeverScrollableScrollPhysics(),避免滚动手势冲突 - 空值安全处理:对可能为null的字段添加空值判断,防止运行时异常
内容的提问来源于stack exchange,提问作者Jeevan Shrestha
相关产品推荐
相关产品推荐

