Search code examples
flutterandroid-widgetsetstate

Flutter - Dart - setState does not reload properly


I have a list of games displaying on my screen and I want to update that list. When I click the button for the first time, it doesn't work, but when I click it at the second time, the list gets updated.

I tried to add _loadData() after the setState, but also it didn't work.

Here is my class:

class _HomePageState extends State<HomePage> {
  bool _isLoading = true;
  int _currentPage = 0;

  @override
  void initState() {
    super.initState();
    Provider.of<GameController>(
      context,
      listen: false,
    ).loadGames(_currentPage).then((value) {
      setState(() => _isLoading = false);
    });
  }

  _loadData() async {
    await Provider.of<GameController>(
      context,
      listen: false,
    ).loadGames(_currentPage);
  }

  @override
  Widget build(BuildContext context) {
    final gameController = Provider.of<GameController>(context, listen: false);
    final List<Game> loadGames = gameController.games;
    final deviceSize = MediaQuery.of(context).size;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Games'),
        centerTitle: true,
        actions: const [LogoutButton()],
      ),
      body: _isLoading
          ? const Center(child: CircularProgressIndicator())
          : Column(
              mainAxisSize: MainAxisSize.max,
              children: [
                Expanded(
                  child: SizedBox(
                    height: deviceSize.height * 0.75,
                    child: SingleChildScrollView(
                      child: Column(
                        children: [
                          GameList(loadGames),
                          Container(
                            margin: const EdgeInsets.only(bottom: 10.0),
                            child: ElevatedButton(
                              onPressed: () {
                                print(_currentPage);
                                setState(() {
                                  _currentPage++;
                                  _loadData();
                                });
                                print(_currentPage);
                              },
                              child: const Text('Get more...'),
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                ),
              ],
            ),
    );
  }
}

And here is my function to load the games

  Future<void> loadGames(int currentPage) async {

    final url = '${Constants.urlGame}?page=$currentPage';
    SharedPreferences prefs = await SharedPreferences.getInstance();
    final token = prefs.getString('token');
    final response = await http.get(
      Uri.parse(url),
      headers: {'Authorization': '$token'},
    );

    if (response.statusCode == 200) {
      final jsonResponse = jsonDecode(response.body);
      final gameList = GameList.fromJson(jsonResponse);
      final List<Game> games = gameList.games;
      _games.clear();
      _games.addAll(games);
      notifyListeners();
    } else {
      print(
        response.statusCode,
      );
    }
  }

Solution

  • _loadData() which is an async operation should not be called inside the setState method.

    According to the documentation itself:

    It must not return a future (the callback cannot be async), since then it would be unclear when the state was actually being set.

    Instead, you can perform the operation first, and call setState after.

      _currentPage++;
      _loadData();
      setState(() {});