6

I'm obtaining JSON data over HTTP and displaying it in a ListView. Since it's HTTP, it's all async.

Here's what I'd like to do:

var index = new ListView.builder(
  controller: _scrollController,
  itemBuilder: (ctx, i) async {
    _log.fine("loading post $i");
    var p = await _posts[i];
    return p == null ? new PostPreview(p) : null;
  },
);

Unfortunately, this doesn't work since IndexedWidgetBuilder has to be a synchronous function. How do I use a Future to build a child for an IndexedWidgetBuilder? It doesn't seem like there's a way to wait for the future to complete synchronously.

Previously, I was loading the data into an array and the IndexedWidgetBuilder function only checked to see if the list elements existed before returning the child widget.

var index = new ListView.builder(
  controller: _scrollController,
  itemBuilder: (ctx, i) {
    _log.fine("loading post $i");
    return _posts.length > i ? new PostPreview(_posts[i]) : null;
  },
);

This works, but I would like to completely separate the view from the data and asynchronously request the JSON as needed.

This also seems, in my limited experience, like it might be a common use-case. Could an async version of IndexWidgetBuilder be added to flutter?

Community
  • 1
  • 1
perlatus
  • 531
  • 2
  • 6
  • 12

1 Answers1

9

You can wait for asynchronous computations using a FutureBuilder. I'd probably change your PostPreview to take a Future as a constructor argument and put the FutureBuilder there, but if you want to leave PostPreview as-is, here's how to modify your itemBuilder.

var index = new ListView.builder(
  controller: _scrollController,
  itemBuilder: (ctx, i) {
    return new FutureBuilder(
      future: _posts[i],
      builder: (context, snapshot) {
        return snapshot.connectionState == ConnectionState.done
               ? new PostPreview(snapshot.data)
               : new Container(); // maybe a show placeholder widget?
    );
  },
);

The nice thing about FutureBuilder is that it takes care of the scenarios where the async request completes and your State has already been disposed.

Collin Jackson
  • 110,240
  • 31
  • 221
  • 152
  • It's worth noting that I tried this, and it did work. However, it become too complex and slow (not really due to the FutureBuilder, but my general design). I ended up just adding a "load more" button rather than trying to hide the pagination from the UI. – perlatus May 12 '17 at 23:55