skip to Main Content

I have an archaic solution for my problem, but it’s quickly become cumbersome and not as robust. I want to switch to a riverpod provider and am studying how to do so, but I want to put this question out there at the same time. For simplicity, my app is similar to BlaBlaCar. Lots of trips listed, and then users can request to be picked up. The general flow goes like this:

enter image description here

To call the same document ID from Firestore, my solution has been just to pass a detailDocument parameter over and over. In my trips page, I have a streambuilder that displays the document:

StreamBuilder<QuerySnapshot<Object?>> tripStreamBuilder() {
    return StreamBuilder<QuerySnapshot>(
      stream: tripStream.snapshots(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return const Center(child: CircularProgressIndicator());
        }
        if (snapshot.hasError) {
          return Center(child: Text('Error: ${snapshot.error}'));
        }
        return ListView(
          shrinkWrap: true,
          physics: const NeverScrollableScrollPhysics(),
          children: snapshot.data!.docs.map((DocumentSnapshot document) {
            Map<String, dynamic> data =
                document.data()! as Map<String, dynamic>;
            return Card(
              child: ListTile(
                // leading: Text((data['departureDate']).toString()),
                title: Text('From ${data['departure']}'),
                subtitle: Text('To ${data['arrival']}'),
                trailing: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child: Column(
                    children: [
                      Text((data['departureDate']).toString()),
                      Text('Leaving at ${data['departureTime']}'),
                    ],
                  ),
                ),

                onTap: () {
                  Navigator.of(context, rootNavigator: true)
                      .push(MaterialPageRoute(
                          builder: (context) => TripDetails(
                                detailDocument: document,
                              )));
                },
              ),
            );
          }).toList(),
        );
      },
    );

From here, I just keep making detailDocument a parameter. For exmaple in my trip details:

class TripDetails extends ConsumerStatefulWidget {
  const TripDetails({super.key, required this.detailDocument});
  final DocumentSnapshot detailDocument;

And jointrip class:

class JoinTrip extends ConsumerStatefulWidget {
  const JoinTrip({
    super.key,
    required this.detailDocument,
  });
  final DocumentSnapshot detailDocument;

So my question is how can I pass the document ID into a riverpod provider? And one that gets disposed and reset as users click on different trips.

2

Answers


  1. Chosen as BEST ANSWER

    Ok so not sure if this is the best solution, but I ended up making a StateNotifierProvider out of the DocumentSnapshot class from Firestore.

    class DocumentSnapshotNotifier extends StateNotifier<DocumentSnapshot?> {
      DocumentSnapshotNotifier() : super(null);
    
      DocumentSnapshot? get documentSnapshot => state;
    
      set documentSnapshot(DocumentSnapshot? documentSnapshot) {
        state = documentSnapshot;
      }
    
      void updateDocumentSnapshot(DocumentSnapshot? documentSnapshot) {
        state = documentSnapshot;
      }
    }
    
    final documentProvider = StateNotifierProvider<
        DocumentSnapshotNotifier,
        DocumentSnapshot?>((ref) => DocumentSnapshotNotifier());
    

    Then once a user clicks on a trip, I pass the value to the provider via

      onTap: () {
                      ref
                          .read(documentProvider.notifier)
                          .updateDocumentSnapshot(document);
    

    Then I can just call it for whatever field in the doc I need.

    Center(
                    child: Text(
                        'Departure Place: ${ref.read(documentProvider)!['departure']}')),
              ),
              Center(
                  child:
                      Text('Arrival Place: ${ref.read(documentProvider)!['arrival']}')),
              Center(
                  child: Text(
                      'Departure Date: ${ref.read(documentProvider)!['departureDate']}'))
    

    It's working for now...


  2. You shouldn’t pass the id of your document to pages. You can check this example from the official documentation.

    In you case, you can have a provider that saves the id. So whenever you need to fetch or do something about the same id, you watch its value and when you need to change it, you can change the id in that provider.

    Let’s say we have two providers as follow:

    // id provider
    final tripIdProvider = StateProvider<String?>((ref) => null);
    
    // Another provider fetching the document for an id
    final tripProvider = Provider((ref) {
      final id = ref.watch(tripIdProvider);
      // code to call the Firebase and returning the result for instance.
    });
    

    tripIdProvider has the latest clicked id, and tripProvider will watch this id and updates and fetch the new values if the id changes. It fetches the document of that id from the Firebase.

    Now if user clicks another object having another id, in the callback of pressing/tapping that object, you can do something like this:

    ...
    onPressed:(){
     ref.read(tripIdProvider.notifier).state = id;
    }
    
    

    This will change the id and because tripProvider is watching that providers’ value, it will be triggered again and will fetch new data for the new id.

    It’s also good to check stateNotifier.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search