After migrating to null safety I'm getting an error on ListView as "The argument type 'Object?' can't be assigned to the parameter type 'List'." I'm getting error on return ListView(children: snapshot.data,);
Can anyone help me to fix this error and build a ListView for activityfeeditem in my app?
Here is my code for 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,
),
),
);
}
I have tried many ways but I counldn't figure out how I can fix this
New code for list
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(),
);
}),
));
}
Chage your 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>[];
}}
change you FutureBuilder
as follows
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();
You can also use as
.
ListView(
children: object as List<Widget>,
)
I see that you are using a StreamBuilder instead of FutureBuilder, but for what its worth, I believe I have found a solution to the original FutureBuilder problem.
First of all: Using the following print statements, you can troubleshoot the issue better, I found that I was Querying for paths that didnt exist with combinations of wrong.doc(userId) and.doc(ownerId) in posts.dart so the deserialization process wasn't working correctly for me when switching between users during debugging (used a provider package to remedy this eventually) but the below print statements did help me identify some issues that I had (that may or may not have contributed to the problem for you, but worth the look).
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;
Then I found that the deserialization process wasn't working correctly due to the difference between 'likes' and 'comments', due to the 'likes' having 7 inputs and 'comments' having 8 inputs. Comments have the extra 'commentData' input which I set up manually for the 'likes' in the addLikeToActivityFeed() part, and set it to an empty string as such:
'commentData': '',
Finally, I added a Dynamic type to the FutureBuilder to get rid of the => argument type 'Object?' can't be assigned to the parameter type 'List' error...
Container(
child: FutureBuilder<dynamic>(
future: getActivityFeed(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
return ListView(
children: snapshot.data,
);
},
),
),
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.