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.