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.
