How to pass a Firestore document from a stream to next page in Flutter?

Issue

I have a listview where each item is one document from a firestore collection. I would like to tap the item and pass the document information to a details page.

This is how I am retrieving document information within the first stream:

child: Text(streamSnapshot.data.docs[index]['event_title'],

This is how I’m attempting to send the data to the next page:

child: GestureDetector(
                          onTap: () {
                            Navigator.pushNamed(context, EventPage.id, arguments: streamSnapshot.data.docs[index]);
                          },

I’m lost as to how to receive the passed data:

    class _EventPageState extends State<EventPage> {
  @override

final db = FirebaseFirestore.instance;
  Widget build(BuildContext context) {
    final args = ModalRoute.of(context)!.settings.arguments;
    return CupertinoPageScaffold(
      navigationBar: CupertinoNavigationBar(
        middle: Text('event_title'),
      ),
      child: Column(

I know I need a StreamBuilder on the next page, but do you have any insight on how to make that stream show only the passed in document?

Solution

I have worked out an answer to this question. I’m sure there are several ways to do this, but here’s mine:

The key is to pass the firestore document ID to the next page. In this example code, I pass streamSnapshot.data.docs[index].id.toString() as a parameter to a custom widget. I’ve located my named route within that widget.

StreamBuilder(
    stream: FirebaseFirestore.instance
        .collection('events')
        .where('start_date', isGreaterThanOrEqualTo: DateTime.now())
        .snapshots(),
    builder: (context, AsyncSnapshot streamSnapshot) {

      if (!streamSnapshot.hasData) {
        return SizedBox(
          height: 250,
          child: Center(
            child: CircularProgressIndicator(),
          ),
        );
      } else
        return SizedBox(
          height: 250,
          child: ListView.builder(
            scrollDirection: Axis.horizontal,
            itemCount: streamSnapshot.data.docs.length,
            itemBuilder: (ctx, index) =>

                EventListHorizontalTile(

                //passes the document ID as a string down to the horizontally scrollable tile,
                //where we push a named route with the docID string as an argument

                firestoreDocID: streamSnapshot.data.docs[index].id.toString(),

                  image: streamSnapshot.data.docs[index]['main_image'],
                  name: streamSnapshot.data.docs[index]['name'],
              ),
          ),
        );
    }),

I then created a class to pass as an argument through a named route.

class Events {
  final String firestoreDocID;

  Events({
    required this.firestoreDocID,

  });

}

Now, within my EventListHorizontalTile widget:

class EventListHorizontalTile extends StatelessWidget {
  const EventListHorizontalTile({
    Key? key,

    required this.name,
    this.firestoreDocID = '',

  }) : super(key: key);

  final String name;
  final String firestoreDocID;

  @override
  Widget build(BuildContext context) {
return GestureDetector(

        onTap: () {

//Here I am pushing a named route with an argument, using that Events class I made earlier.

          Navigator.pushNamed(context, EventPage.id, arguments: Events(firestoreDocID: firestoreDocID));

        },

//child: The rest of the list tile widget

),

Now we have to write a bit of code in the EventPage to receive the argument.

class EventPage extends StatefulWidget {
  const EventPage({
    Key? key,
  }) : super(key: key);

  static String id = 'EventPage';

  @override
  _EventPageState createState() => _EventPageState();
}

class _EventPageState extends State<EventPage> {
  @override
  Widget build(BuildContext context) {


//This is how we receive the argument.
    final args = ModalRoute.of(context)!.settings.arguments as Events;

    return CupertinoPageScaffold(
      navigationBar: CupertinoNavigationBar(),
      child: SingleChildScrollView(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [

//Some text to see if the string made it.
            Text(args.firestoreDocID),

]),
),
);
}
}

And that’s it! Once you have that document ID in your new page, you can call a Streambuilder like this:

StreamBuilder(
              stream: FirebaseFirestore.instance
                  .collection('events')
                  .doc(args.firestoreDocID)
                  .snapshots(),

Answered By – Ian Carberry

Answer Checked By – Dawn Plyler (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *