
I don't know why you say your code is not working, but here you can see that even the prints perform as they should. Your example might be oversimplified. If you add a delay to that Future (which is a real case scenario, cause fetching data and waiting for it does take a few seconds sometimes), then the code does indeed display 0.
The reason why your code works right now is that the Future returns the list instantly before the build method starts rendering Widgets. That's why the first thing that shows up on the screen is 4.
If you add that .delayed() to the Future, then it does indeed stop working, because the list of numbers is retrieved after some time and the build renders before the numbers are updated.
Problem explanation
SetState in your code is not called properly. You either do it like this (which in this case makes no sense because you use "await", but generally it works too)
_initializeController() async {
setState(() {
List<int> newNumbersList = await _getAsyncNumberList();
print("Number list was updated to list of length ${newNumbersList.length}");
numbers = newNumbersList;
});
}
or like this
_initializeController() async {
List<int> newNumbersList = await _getAsyncNumberList();
print("Number list was updated to list of length ${newNumbersList.length}");
numbers = newNumbersList;
setState(() {
/// this thing right here is an entire function. You MUST HAVE THE AWAIT in
/// the same function as the update, otherwise, the await is callledn, and on
/// another thread, the other functions are executed. In your case, this one
/// too. This one finishes early and updates nothing, and the await finishes later.
});
}
Suggested solution
This will display 0 while waiting 5 seconds for the Future to return the new list with the data and then it will display 4. If you want to display something else while waiting for the data, please use a FutureBuilder Widget.
FULL CODE WITHOUT FutureBuilder:
class Scan extends StatefulWidget {
@override
_ScanState createState() => _ScanState();
}
class _ScanState extends State<Scan> {
List<int> numbers;
@override
void initState() {
super.initState();
_initializeController();
}
@override
Widget build(BuildContext context) {
print('Build was scheduled');
return Center(
child: Text(numbers == null ? '0' : numbers.length.toString()));
}
Future<List<int>> _getAsyncNumberList() {
return Future.delayed(Duration(seconds: 5), () => [1, 2, 3, 4]);
}
_initializeController() async {
List<int> newNumbersList = await _getAsyncNumberList();
print(
"Number list was updated to list of length ${newNumbersList.length}");
numbers = newNumbersList;
setState(() {});
}
}
I strongly recommend using this version, since it displays something to the user the whole time while waiting for the data and also has a failsafe if an error comes up. Try them out and pick what is best for you, but again, I recommend this one.
FULL CODE WITH FutureBuilder:
class _ScanState extends State<Scan> {
List<int> numbers;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
print('Build was scheduled');
return FutureBuilder(
future: _getAsyncNumberList(),
builder: (BuildContext context, AsyncSnapshot<List<int>> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting: return Center(child: Text('Fetching numbers...'));
default:
if (snapshot.hasError)
return Center(child: Text('Error: ${snapshot.error}'));
else
/// snapshot.data is the result that the async function returns
return Center(child: Text('Result: ${snapshot.data.length}'));
}
},
);
}
Future<List<int>> _getAsyncNumberList() {
return Future.delayed(Duration(seconds: 5), () => [1, 2, 3, 4]);
}
}
Here is a more detailed example with a full explanation of how FutureBuilder works. Take some time and carefully read through it. It's a very powerful thing Flutter offers.