The argument type 'Future<List<User>>' can't be assigned to the parameter type 'Future<void> Function()' in onRefresh when using RefreshIndicator
I am having a Future that is fetching and populating users below
Future<List<User>> _fetchUsersListUsingLoop() async {
try {
var response = await http.get(
Uri.parse(
"https://api.json-generator.com/templates/Eh5AlPjYVv6C/data"),
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer tltsp6dmnbif01jy9xfo9ssn4620u89xhuwcm5t3",
});
List<User> usersList = [];
for (var u in json.decode(response.body)) {
User user = User(u["email"], u["about"], u["name"], u["picture"],
u["index"], u["imageFetchType"]);
usersList.add(user);
}
return usersList;
} catch (e) {
log("FetchUsersListUsingLoopException $e");
rethrow;
}
}
And below is how i am using future
in FutureBuilder to call _fetchUsersListUsingLoop()
and then use ListView.builder inside RefreshIndicator to show users in the list
body: SizedBox(
child: FutureBuilder(
future: _fetchUsersListUsingLoop(),
builder: (BuildContext context, AsyncSnapshot asyncSnapshot) {
if (asyncSnapshot.data == null) {
return const Center(child: CircularProgressIndicator());
} else {
return RefreshIndicator(
// background color
backgroundColor: Colors.white,
// refresh circular progress indicator color
color: Colors.green,
onRefresh: _fetchUsersListUsingLoop(),
child: ListView.builder(
itemCount: asyncSnapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
contentPadding: const EdgeInsets.all(10),
leading: CircleAvatar(
backgroundImage:
NetworkImage(asyncSnapshot.data[index].picture),
),
title: Text(asyncSnapshot.data[index].name),
subtitle: Text(
"${asyncSnapshot.data[index].email} \nUsing NetworkImage with backgroundImage"),
);
},
));
}
}),
),
I am getting The argument type 'Future<List<User>>' can't be assigned to the parameter type 'Future<void> Function()'.
error in this line onRefresh: _fetchUsersListUsingLoop(),
How can i be able to call _fetchUsersListUsingLoop()
again when i swipe down to refresh
Solution 1:
Instead of calling _fetchUsersListUsingLoop
you should use setState
to cause a rebuild so that FutureBuilder
fetches the data again.
onRefresh: () => setState(() {})
That being said it's best to store your future in initState
and update this future when needed by calling setState
. This is from documentation:
The future must have been obtained earlier, e.g. during State.initState, State.didUpdateWidget, or State.didChangeDependencies. It must not be created during the State.build or StatelessWidget.build method call when constructing the FutureBuilder. If the future is created at the same time as the FutureBuilder, then every time the FutureBuilder's parent is rebuilt, the asynchronous task will be restarted.
Future<List<User>>? _usersFuture;
@override
void initState() {
super.initState();
_usersFuture = _fetchUsersListUsingLoop();
}
Then:
onRefresh: () {
setState(() {
_usersFuture = _fetchUsersListUsingLoop();
});
}
Solution 2:
When you do this
onRefresh: _fetchUsersListUsingLoop(),
You are telling dart to call _fetchUsersListUsingLoop
and expect it to return a function, then to call said function, what you want to do instead is tell it to just call _fetchUsersListUsingLoop
:
onRefresh: _fetchUserListUsingLoop,
or ìf that doesn't work:
onRefresh: () async => _fetchUserListUsingLoop(),
But the problem is that this will not actually work, why? Because the future builder is already completed and nothing will change that, to fix your issue; you can reassign the future like this:
You need to declare a future:
late Future<List<User>> _user;
child: FutureBuilder(
future: _users,
builder: (context, asyncSnapshot) {
...
onRefresh: _updateUsersFuture,
...
Then somewhere in your class you do:
Future<void> _updateUsersFuture() async {
final newUsers = await _fetchUserListUsingLoop();
setState(() => _users = newUsers);
}
and on initstate:
@override
void initState() {
_users = _fetchUsersListUsingLoop();
super.initState();
}