迁移到 null 安全:无法将参数类型 'Object?' 分配给参数类型 'List<Widget>'
Migrating to null safety: The argument type 'Object?' can't be assigned to the parameter type 'List<Widget>'
迁移到空安全后,我在 ListView 上收到错误消息“无法将参数类型 'Object?' 分配给参数类型 'List'。”
我在 return ListView(children: snapshot.data,);
上遇到错误
谁能帮我解决这个错误并在我的应用程序中为 activityfeeditem 构建一个 ListView?
这是我的 activity_feed.dart,
代码
class ActivityFeed extends StatefulWidget {
@override
_ActivityFeedState createState() => _ActivityFeedState();
}
class _ActivityFeedState extends State<ActivityFeed> {
getActivityFeed() async {
QuerySnapshot snapshot = await activityFeedRef
.doc(currentUser!.id)
.collection('feedItems')
.orderBy('timestamp', descending: true)
.limit(50)
.get();
List<ActivityFeedItem> feedItems = [];
snapshot.docs.forEach((doc) {
feedItems.add(ActivityFeedItem.fromDocument(doc));
print('Activity Feed Item: ${doc.data}');
});
return feedItems;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.deepPurple[50],
appBar: header(context, titleText: "Activity Feed"),
body: Container(
child: FutureBuilder(
future: getActivityFeed(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
return ListView(
children: snapshot.data,
// Here I'm getting error on `snapshot.data`
);
},
),
),
);
}
}
Widget? mediaPreview;
String? activityItemText;
class ActivityFeedItem extends StatelessWidget {
final String? username;
final String? userId;
final String? type; // 'like', 'follow', 'comment'
final String? mediaUrl;
final String? postId;
final String? userProfileImg;
final String? commentData;
final Timestamp? timestamp;
ActivityFeedItem({
this.username,
this.userId,
this.type,
this.mediaUrl,
this.postId,
this.userProfileImg,
this.commentData,
this.timestamp,
});
factory ActivityFeedItem.fromDocument(DocumentSnapshot doc) {
return ActivityFeedItem(
username: doc['username'],
userId: doc['userId'],
type: doc['type'],
postId: doc['postId'],
userProfileImg: doc['userProfileImg'],
commentData: doc['commentData'],
timestamp: doc['timestamp'],
mediaUrl: doc['mediaUrl'],
);
}
showPost(context) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PostScreen(postId: postId, userId: userId)));
}
configureMediaPreview(context) {
if (type == "like" || type == 'comment') {
mediaPreview = GestureDetector(
onTap: () => showPost(context),
child: Container(
height: 50.0,
width: 50.0,
child: AspectRatio(
aspectRatio: 16 / 9,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: CachedNetworkImageProvider(mediaUrl!),
),
),
)),
),
);
} else {
mediaPreview = Text('');
}
if (type == 'like') {
activityItemText = "liked your post";
} else if (type == 'follow') {
activityItemText = "is following you";
} else if (type == 'comment') {
activityItemText = 'replied: $commentData';
} else {
activityItemText = "Error: Unknown type '$type'";
}
}
@override
Widget build(BuildContext context) {
configureMediaPreview(context);
return Padding(
padding: EdgeInsets.only(bottom: 2.0),
child: Container(
color: Colors.white54,
child: ListTile(
title: GestureDetector(
onTap: () => showProfile(context, profileId: userId),
child: RichText(
overflow: TextOverflow.ellipsis,
text: TextSpan(
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
),
children: [
TextSpan(
text: username,
style: TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(
text: ' $activityItemText',
),
]),
),
),
leading: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(userProfileImg!),
),
subtitle: Text(
timeago.format(timestamp!.toDate()),
overflow: TextOverflow.ellipsis,
),
trailing: mediaPreview,
),
),
);
}
}
showProfile(BuildContext context, {String? profileId}) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Profile(
profileId: profileId,
),
),
);
}
我尝试了很多方法,但我不知道如何解决这个问题
列表的新代码
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.deepPurple[50],
appBar: header(context, titleText: "Activity Feed"),
body: Container(
child: StreamBuilder<QuerySnapshot>(
stream: activityFeedRef
.doc(currentUser!.id)
.collection('feedItems')
.orderBy('timestamp', descending: true)
.limit(50)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: circularProgress(),
);
} else
return ListView(
children: snapshot.data!.docs.map((doc) {
return Card(
child: ListTile(
title: GestureDetector(
onTap: () =>
showProfile(context, profileId: doc['userId']),
child: RichText(
overflow: TextOverflow.ellipsis,
text: TextSpan(
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
),
children: [
TextSpan(
text: doc['username'],
style: TextStyle(
fontWeight: FontWeight.bold),
),
TextSpan(
text: ' $activityItemText',
),
]),
),
),
leading: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(
doc['userProfileImg']!),
),
subtitle: Text(
timeago.format(doc['timestamp']!.toDate()),
overflow: TextOverflow.ellipsis,
),
trailing: mediaPreview,
),
);
}).toList(),
);
}),
));
}
改变你的getActivityFeed
Future<List<ActivityFeedItem>> getActivityFeed() async {
try{
QuerySnapshot snapshot = await activityFeedRef
.doc(currentUser!.id)
.collection('feedItems')
.orderBy('timestamp', descending: true)
.limit(50)
.get();
List<ActivityFeedItem> feedItems = [];
snapshot.docs.forEach((doc) {
feedItems.add(ActivityFeedItem.fromDocument(doc));
print('Activity Feed Item: ${doc.data}');
});
return feedItems;
}
catch (error) {
print(error);
return <ActivityFeedItem>[];
}}
改变你FutureBuilder
如下
FutureBuilder<List<ActivityFeedItem>>(
future: getActivityFeed(),
builder: (BuildContextcontext, AsyncSnapshot<List<ActivityFeedItem>> snapshot) {
if (snapshot.hasError){
return Center(child: Text("You have an error in loading
data"));
}
if (snapshot.hasData) {
return ListView(
children: snapshot.data!,
);
}
return CirclularProgressIndicator();
您也可以使用as
。
ListView(
children: object as List<Widget>,
)
我看到您使用的是 StreamBuilder 而不是 FutureBuilder,但就其价值而言,我相信我已经找到了解决原始 FutureBuilder 问题的方法。
首先:使用以下打印语句,您可以更好地解决问题,我发现我正在查询不存在的路径,其中包含错误的 .doc(userId) 和 .doc(ownerId) in posts.dart 因此在调试期间在用户之间切换时反序列化过程对我来说无法正常工作(最终使用提供程序包来解决这个问题)但是下面的打印语句确实帮助我确定了我遇到的一些问题(可能或可能没有为您解决问题,但值得一看)。
getActivityFeed() async {
QuerySnapshot snapshot = await FirebaseFirestore.instance
.collection('feed')
.doc(_auth.currentUser!.uid)
.collection('feedItems')
.orderBy('timestamp', descending: true)
.limit(50)
.get();
List<ActivityFeedItem> feedItems = [];
snapshot.docs.forEach((doc) {
feedItems.add(ActivityFeedItem.fromDocument(doc));
print('Activity Feed Item: ${doc.id}');
print('Activity Feed Item: ${doc.data()}');
});
// return feedItems;
return snapshot.docs;
然后我发现由于 'likes' 和 'comments' 之间的差异,反序列化过程无法正常工作,因为 'likes' 有 7 个输入而 'comments' 有 8 个输入。评论有额外的 'commentData' 输入,我在 addLikeToActivityFeed() 部分为 'likes' 手动设置,并将其设置为空字符串,如下所示:
'commentData': '',
最后,我在FutureBuilder中添加了一个Dynamic类型来摆脱=>参数类型'Object?'无法分配给参数类型'List'的错误...
Container(
child: FutureBuilder<dynamic>(
future: getActivityFeed(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
return ListView(
children: snapshot.data,
);
},
),
),
迁移到空安全后,我在 ListView 上收到错误消息“无法将参数类型 'Object?' 分配给参数类型 'List'。”
我在 return ListView(children: snapshot.data,);
上遇到错误
谁能帮我解决这个错误并在我的应用程序中为 activityfeeditem 构建一个 ListView?
这是我的 activity_feed.dart,
代码class ActivityFeed extends StatefulWidget {
@override
_ActivityFeedState createState() => _ActivityFeedState();
}
class _ActivityFeedState extends State<ActivityFeed> {
getActivityFeed() async {
QuerySnapshot snapshot = await activityFeedRef
.doc(currentUser!.id)
.collection('feedItems')
.orderBy('timestamp', descending: true)
.limit(50)
.get();
List<ActivityFeedItem> feedItems = [];
snapshot.docs.forEach((doc) {
feedItems.add(ActivityFeedItem.fromDocument(doc));
print('Activity Feed Item: ${doc.data}');
});
return feedItems;
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.deepPurple[50],
appBar: header(context, titleText: "Activity Feed"),
body: Container(
child: FutureBuilder(
future: getActivityFeed(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
return ListView(
children: snapshot.data,
// Here I'm getting error on `snapshot.data`
);
},
),
),
);
}
}
Widget? mediaPreview;
String? activityItemText;
class ActivityFeedItem extends StatelessWidget {
final String? username;
final String? userId;
final String? type; // 'like', 'follow', 'comment'
final String? mediaUrl;
final String? postId;
final String? userProfileImg;
final String? commentData;
final Timestamp? timestamp;
ActivityFeedItem({
this.username,
this.userId,
this.type,
this.mediaUrl,
this.postId,
this.userProfileImg,
this.commentData,
this.timestamp,
});
factory ActivityFeedItem.fromDocument(DocumentSnapshot doc) {
return ActivityFeedItem(
username: doc['username'],
userId: doc['userId'],
type: doc['type'],
postId: doc['postId'],
userProfileImg: doc['userProfileImg'],
commentData: doc['commentData'],
timestamp: doc['timestamp'],
mediaUrl: doc['mediaUrl'],
);
}
showPost(context) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PostScreen(postId: postId, userId: userId)));
}
configureMediaPreview(context) {
if (type == "like" || type == 'comment') {
mediaPreview = GestureDetector(
onTap: () => showPost(context),
child: Container(
height: 50.0,
width: 50.0,
child: AspectRatio(
aspectRatio: 16 / 9,
child: Container(
decoration: BoxDecoration(
image: DecorationImage(
fit: BoxFit.cover,
image: CachedNetworkImageProvider(mediaUrl!),
),
),
)),
),
);
} else {
mediaPreview = Text('');
}
if (type == 'like') {
activityItemText = "liked your post";
} else if (type == 'follow') {
activityItemText = "is following you";
} else if (type == 'comment') {
activityItemText = 'replied: $commentData';
} else {
activityItemText = "Error: Unknown type '$type'";
}
}
@override
Widget build(BuildContext context) {
configureMediaPreview(context);
return Padding(
padding: EdgeInsets.only(bottom: 2.0),
child: Container(
color: Colors.white54,
child: ListTile(
title: GestureDetector(
onTap: () => showProfile(context, profileId: userId),
child: RichText(
overflow: TextOverflow.ellipsis,
text: TextSpan(
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
),
children: [
TextSpan(
text: username,
style: TextStyle(fontWeight: FontWeight.bold),
),
TextSpan(
text: ' $activityItemText',
),
]),
),
),
leading: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(userProfileImg!),
),
subtitle: Text(
timeago.format(timestamp!.toDate()),
overflow: TextOverflow.ellipsis,
),
trailing: mediaPreview,
),
),
);
}
}
showProfile(BuildContext context, {String? profileId}) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => Profile(
profileId: profileId,
),
),
);
}
我尝试了很多方法,但我不知道如何解决这个问题
列表的新代码
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.deepPurple[50],
appBar: header(context, titleText: "Activity Feed"),
body: Container(
child: StreamBuilder<QuerySnapshot>(
stream: activityFeedRef
.doc(currentUser!.id)
.collection('feedItems')
.orderBy('timestamp', descending: true)
.limit(50)
.snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return Center(
child: circularProgress(),
);
} else
return ListView(
children: snapshot.data!.docs.map((doc) {
return Card(
child: ListTile(
title: GestureDetector(
onTap: () =>
showProfile(context, profileId: doc['userId']),
child: RichText(
overflow: TextOverflow.ellipsis,
text: TextSpan(
style: TextStyle(
fontSize: 14.0,
color: Colors.black,
),
children: [
TextSpan(
text: doc['username'],
style: TextStyle(
fontWeight: FontWeight.bold),
),
TextSpan(
text: ' $activityItemText',
),
]),
),
),
leading: CircleAvatar(
backgroundImage: CachedNetworkImageProvider(
doc['userProfileImg']!),
),
subtitle: Text(
timeago.format(doc['timestamp']!.toDate()),
overflow: TextOverflow.ellipsis,
),
trailing: mediaPreview,
),
);
}).toList(),
);
}),
));
}
改变你的getActivityFeed
Future<List<ActivityFeedItem>> getActivityFeed() async {
try{
QuerySnapshot snapshot = await activityFeedRef
.doc(currentUser!.id)
.collection('feedItems')
.orderBy('timestamp', descending: true)
.limit(50)
.get();
List<ActivityFeedItem> feedItems = [];
snapshot.docs.forEach((doc) {
feedItems.add(ActivityFeedItem.fromDocument(doc));
print('Activity Feed Item: ${doc.data}');
});
return feedItems;
}
catch (error) {
print(error);
return <ActivityFeedItem>[];
}}
改变你FutureBuilder
如下
FutureBuilder<List<ActivityFeedItem>>(
future: getActivityFeed(),
builder: (BuildContextcontext, AsyncSnapshot<List<ActivityFeedItem>> snapshot) {
if (snapshot.hasError){
return Center(child: Text("You have an error in loading
data"));
}
if (snapshot.hasData) {
return ListView(
children: snapshot.data!,
);
}
return CirclularProgressIndicator();
您也可以使用as
。
ListView(
children: object as List<Widget>,
)
我看到您使用的是 StreamBuilder 而不是 FutureBuilder,但就其价值而言,我相信我已经找到了解决原始 FutureBuilder 问题的方法。
首先:使用以下打印语句,您可以更好地解决问题,我发现我正在查询不存在的路径,其中包含错误的 .doc(userId) 和 .doc(ownerId) in posts.dart 因此在调试期间在用户之间切换时反序列化过程对我来说无法正常工作(最终使用提供程序包来解决这个问题)但是下面的打印语句确实帮助我确定了我遇到的一些问题(可能或可能没有为您解决问题,但值得一看)。
getActivityFeed() async {
QuerySnapshot snapshot = await FirebaseFirestore.instance
.collection('feed')
.doc(_auth.currentUser!.uid)
.collection('feedItems')
.orderBy('timestamp', descending: true)
.limit(50)
.get();
List<ActivityFeedItem> feedItems = [];
snapshot.docs.forEach((doc) {
feedItems.add(ActivityFeedItem.fromDocument(doc));
print('Activity Feed Item: ${doc.id}');
print('Activity Feed Item: ${doc.data()}');
});
// return feedItems;
return snapshot.docs;
然后我发现由于 'likes' 和 'comments' 之间的差异,反序列化过程无法正常工作,因为 'likes' 有 7 个输入而 'comments' 有 8 个输入。评论有额外的 'commentData' 输入,我在 addLikeToActivityFeed() 部分为 'likes' 手动设置,并将其设置为空字符串,如下所示:
'commentData': '',
最后,我在FutureBuilder中添加了一个Dynamic类型来摆脱=>参数类型'Object?'无法分配给参数类型'List'的错误...
Container(
child: FutureBuilder<dynamic>(
future: getActivityFeed(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
return ListView(
children: snapshot.data,
);
},
),
),