Home > Blockchain >  Accessing nested property in AsyncSnapshot
Accessing nested property in AsyncSnapshot

Time:01-16

So, I am learning to use the FutureBuilder class in Flutter. My code works, but I wonder if I could improve the way I access the data from the snapshot. My FutureBuilder is like that:

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: FutureBuilder<List<ListCard>>(
          future: items,
          builder: (context, snapshot) {
            return RefreshIndicator(
              onRefresh: _pullRefresh,
              child: _listView(snapshot),
            );
          },
        ),
      ),
    );
  }

items is a class property of type Future<List<ListCard>>. _listView is the method where I work on the snapshot. There are 2 errors in this code:

  Widget _listView(AsyncSnapshot<List<ListCard>> snapshot) {
    if (snapshot.data != null) {
      return ListView.builder(
        itemCount: snapshot.data.length, // Error when accessing the length property
        itemBuilder: (context, index) {
          return snapshot.data[index]; // Error when accessing the index
        },
      );
    } else {
      return const Center(child: CircularProgressIndicator());
    }
  }

The two errors are actually the same. They say:

The property 'length' can't be unconditionally accessed because the receiver can be 'null'. Try making the access conditional (using '?.') or adding a null check to the target ('!').dart(unchecked_use_of_nullable_value)

I understand that snapshot.data is of type List<ListCard>?, therefore its value can either be null or a list of type List<ListCard>. In case the value is null, Flutter will display the CircularProcessIndicator. If it's not null, then the ListView.builder widget will be returned. If we reach that point, snapshot.data is always a list (it's been tested in the if statement) and it should therefore have a length property.

I also tried:

Widget _listView(AsyncSnapshot<List<ListCard>> snapshot) {
  if (snapshot.data is List<ListCard>) {
  //...
}

but it gives exactly the same error. Same story with:

Widget _listView(AsyncSnapshot<List<ListCard>> snapshot) {
  if (snapshot.hasData) {
  //...
}

However I can get rid of these errors by adding the ! operator:

//...
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
  return snapshot.data![index];
},
//...

I could also do a type assertion such as List<ListCard> data = snapshot.data as List<ListCard>; but I don't want to go that way.

Is it possible to make this work without using the ! operator?

Thanks!

CodePudding user response:

I presume the problem you are facing is because snapshot can not be promoted to non nullable.

In your case you will have to go using "!" after a null-check.

Remember

Type promotion is only applicable to local variables... Promotion of an instance variable is not sound, because it could be overridden by a getter that runs a computation and returns a different object each time it is invoked. Cf. dart-lang/language#1188 for discussions about a mechanism which is similar to type promotion but based on dynamic checks, with some links to related discussions.

For further information on this topic see this answer.

CodePudding user response:

The way you handle your builder is wrong, try this:

builder: (context, snapshot) {
    switch (snapshot.connectionState) {
        case ConnectionState.waiting:
          return Text('Loading....');
        default:
          if (snapshot.hasError) {
            return Text('Error: ${snapshot.error}');
          } else {
            List<ListCard> data = snapshot.data ?? [];
            return RefreshIndicator(
              onRefresh: _pullRefresh,
              child: _listView(data),
            );
          }
      }
},

here I set default value for data, if it gets null I will pass empty list.

  • Related