Issue
I have a JSON method that returns a List
after it is completed,
Future<List<Feed>> getData() async {
List<Feed> list;
String link =
"https://example.com/json";
var res = await http.get(link);
if (res.statusCode == 200) {
var data = json.decode(res.body);
var rest = data["feed"] as List;
list = rest.map<Feed>((json) => Feed.fromJson(json)).toList();
}
return list;
}
I then call this, in my initState()
which contains a list hello, that will filter out the JSON list, but it shows me a null
list on the screen first and after a few seconds it loads the list.
getData().then((usersFromServer) {
setState(() {. //Rebuild once it fetches the data
hello = usersFromServer
.where((u) => (u.category.userJSON
.toLowerCase()
.contains('hello'.toLowerCase())))
.toList();
users = usersFromServer;
filteredUsers = users;
});
});
This is my FutureBuilder
that is called in build()
method, however if I supply the hello list before the return statement, it shows me that the method where()
was called on null (the list method that I am using to filter out hello()
)
FutureBuilder(
future: getData(),
builder: (context, snapshot) {
return snapshot.data != null ?
Stack(
children: <Widget>[
CustomScrollView(slivers: <Widget>[
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
child: puzzle[0],
);
},
childCount: 1,
),
)
]),
],
)
:
CircularProgressIndicator();
});
Solution
You are calling your getData method multiple times. Don’t do that. Your UI waits for one call and your code for the other. That’s a mess. Call it once and wait for that call.
You need to define the future to wait for in your state:
Future<void> dataRetrievalAndFiltering;
Then in your initstate
, assign the whole operation to this future:
(note that I removed the setState completely, it’s not needed here anymore)
dataRetrievalAndFiltering = getData().then((usersFromServer) {
hello = usersFromServer.where((u) => (u.category.userJSON.toLowerCase().contains('hello'.toLowerCase()))).toList();
users = usersFromServer;
filteredUsers = users;
});
Now your FurtureBuilder
can actually wait for that specific future, not for a new Future you generate by calling your method again:
FutureBuilder(
future: dataRetrievalAndFiltering,
builder: (context, snapshot) {
Now you have one Future, that you can wait for.
Answered By – nvoigt
Answer Checked By – David Goodson (FlutterFixes Volunteer)