Provider exposes incorrect values with Streams

190

Looks like this is precisely why ProxyProvider was introduced

https://pub.dev/documentation/provider/latest/provider/ProxyProvider-class.html Quoting the important note:

As opposed to the builder parameter of Provider, builder may be called more than once. It will be called once when the widget is mounted, then once whenever any of the InheritedWidget which ProxyProvider depends emits an update

By wrapping AnotherWidget with MultiProvider containing ProxyProvider (as below) solves the problem

child: MultiProvider(
                        providers: [
                            Provider.value(value: _messages[index].documentID),
                            ProxyProvider<String, Parameters>(
                              builder: (context, documentId, parameters) => Parameters(documentId),
                            ),
                          ],
                          child: AnotherWidget()));
Share:
190
Vinayakaram Nagarajan
Author by

Vinayakaram Nagarajan

Updated on December 14, 2022

Comments

  • Vinayakaram Nagarajan
    Vinayakaram Nagarajan over 1 year

    I am using Provider with to expose some parameters to Widgets deep down a tree. The Widgets are built from Streambuilder, i.e. Firebase documents

    However, Provider does not seem to be exposing the correct value

    A very simplified version of my code:

    import 'package:flutter/material.dart';
    import 'package:cloud_firestore/cloud_firestore.dart';
    import 'package:provider/provider.dart';
    
    class Parameters {
      Parameters(this.documentId);
      final String documentId;
    }
    
    class Widget1 extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return StreamBuilder<QuerySnapshot>(
            stream: mystream, //Query the Firebase for a bunch of Documents
            builder: (context, snapshot) {
              if (snapshot.hasError) {
                return Text('Error processing data feed');
              }
              switch (snapshot.connectionState) {
                case ConnectionState.waiting:
                  {
                    return CircularProgressIndicator();
                  }
                  break;
                default:
                  {
                    var _messages = snapshot.data.documents;
    
                    return ListView.builder(
                        itemCount: _messages.length,
                        itemBuilder: (context, index) {
                          return Provider<Parameters>(
                              builder: (context) =>
                                  Parameters(_messages[index].documentID),
                              child: AnotherWidget());
                        });
                  }
              }
            });
      }
    }
    
    class AnotherWidget extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        Parameters param = Provider.of<Parameters>(context);
        return Text(param.documentId);
      }
    }
    

    This works perfectly fine when the Widgets are built for the first time. However, when a new document is inserted AnotherWidget shows the document Id of previous document

    How do I get the correct document ID when the stream is updated?

    Note: I am aware I can simply pass the document as a parameter. However I need the parameter deep down the widget tree of AnotherWidget