Mobx Observer not working as expected when using FutureBuilder

3,536

So today i managed to implement a workaround for my problem by introducing a class-member holding the items, and react on receiving them by calling itemTracker.updateAmount() inside an initState()-method.

class OfferPage extends StatefulWidget {
  final Future<List<Offer>> data = ApiService.getOffers();
  OfferPageState createState() => OfferPageState();
}

class OfferPageState extends State<OfferPage> {
  @override
  void initState() {
    widget.data.then((offers) => itemTracker.updateAmount(offers.length));
    super.initState();
  }
// ...

//the new Scaffold body part
body: ListView(
          shrinkWrap: true,
          padding: const EdgeInsets.all(20.0),
          children: <Widget>[
            FutureBuilder(
// no longer create a request, but instead use the existing request's future
                future: widget.data,
                builder: (BuildContext context, AsyncSnapshot snapshot) {
                  switch (snapshot.connectionState) {
                    case ConnectionState.none:
                    case ConnectionState.waiting:
                      return Center(
                        child: CircularProgressIndicator(),
                      );
                    default:
                      return _createOfferList(context, snapshot);
                  }
                }),
          ],
        ),

The _createOfferList-method remains exactly the same albeit without the call to updateAmount(), since this part is now handled inside initState().

Share:
3,536
Paez Rice
Author by

Paez Rice

Updated on December 13, 2022

Comments

  • Paez Rice
    Paez Rice over 1 year

    When trying to create a Listview of items inside the body of a Scaffold, I am unable to update the displaying of the amount of items in that list in another place (Appbar) with mobx (version: 0.3.5) and flutter_mobx (version: 0.3.0+1).

    I already tried to solve this with the Provider package (3.0.0+1), but this didn't work out as well, so I guess I am missing something about the nature of FutureBuilder and Flutter's build-process.

    // the mobx-store
    ItemTracker itemTracker = ItemTracker();
    
    class StartPage extends StatefulWidget {
      StartPageState createState() => StartPageState();
    }
    
    class StartPageState extends State<StartPage> {
      Widget build(BuildContext context) {
        return SafeArea(
          child: Scaffold(
            appBar: AppBar(
    // this Observer doesn't update
              leading: Observer(
                  builder: (_) => Text('Items: ' + itemTracker.amount.toString())),
              title: Text('MyApp',
                  style: TextStyle(
                    color: Colors.deepPurpleAccent[50],
                    fontSize: 16,
                    fontWeight: FontWeight.bold,
                  )),
              centerTitle: true,
            ),
            body: ListView(
              shrinkWrap: true,
              padding: const EdgeInsets.all(20.0),
              children: <Widget>[
                FutureBuilder(
                    future: ApiService.getOffers(),
                    builder: (BuildContext context,
                        AsyncSnapshot<List<Offer>> snapshot) {
                      switch (snapshot.connectionState) {
                        case ConnectionState.none:
                        case ConnectionState.waiting:
                          return Center(
                            child: CircularProgressIndicator(),
                          );
                        default:
                          return snapshot.hasError
                              ? Text('Error: ${snapshot.error}')
                              : _createOfferList(context, snapshot);
                      }
                    }),
              ],
            ),
          ),
        );
      }
    
      Widget _createOfferList(BuildContext context, AsyncSnapshot snapshot) {
    // after receiving the response, the amount of items should get written to 
    // itemTracker.amount - the Observer-part should be rerendered, but it won't
        itemTracker.updateAmount(snapshot.data.length);
        print(snapshot.data.length.toString());
        return ListView.builder(
            itemCount: snapshot.data.length,
            shrinkWrap: true,
            primary: false,
            itemBuilder: (BuildContext context, int index) {
              return Card(
                  color: Colors.lightBlue[50],
                  child: ListTile(
                    leading: Image.asset(snapshot.data[index].image),
                    title: Text(snapshot.data[index].name),
                    subtitle: Text(snapshot.data[index].description ?? ''),
                    isThreeLine: true,
                    trailing: Padding(
                      padding: EdgeInsets.only(top: 8.0, right: 4.0),
                      child: Icon(
                        Icons.explore,
                        color: Colors.green[200],
                        size: 30,
                      ),
                    ),
                    onTap: () => Navigator.push(
                        context,
                        MaterialPageRoute(
                            builder: (context) =>
                                OfferDetailPage(snapshot.data[index]))),
                  ));
            });
      }
    }
    

    so the upper part of the code fetches the items from ApiService.getOffers and then calls the _createOfferList to build the Listview. Inside this method, the itemTracker-instance's mobx-observable amount gets updated by the mobx-action updateAmount(int newValue) with the length of the snapshot's data-list.
    In the Scaffold above, I'm then using an Observer to track and display the amount of items.
    Aside from the amount not getting updated, the App works as expected and the List gets rendered. If I navigate forth and back in the app, the amount of list-items gets displayed correctly, but it never happens on the initial page-load.

    edit: just as a side-note: I didn't show off the ItemTracker class-definition and the corresponding generated class, since all it does is use an observable-annotation for the amount-member and an action-annotation for the method that just takes an int-parameter and writes it to this.amount.