0

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(),
                  );
              }),
        ));
  }

3 Answers3

2

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();
Lakmal Fernando
  • 1,420
  • 6
  • 14
0

You can also use as.

ListView(
  children: object as List<Widget>,
)
iDecode
  • 22,623
  • 19
  • 99
  • 186
0

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,
                );
              },
            ),
          ),