Home > Software design >  Flutter set state not updating my UI with new data
Flutter set state not updating my UI with new data

Time:09-29

I have a ListView.builder widget wrapped inside a RefreshIndicator and then a FutureBuilder. Refreshing does not update my list, I have to close the app and open it again but the refresh code does the same as my FutureBuilder.

Please see my code below, when I read it I expect the widget tree to definitely update.

@override
void initState() {
  super.initState();
  taskListFuture= TaskService().getTasks();
}

@override
  Widget build(BuildContext context) {
    return Consumer<TaskData>(builder: (context, taskData, child) {
      return FutureBuilder(
          future: taskListFuture,
          builder: (context, snapshot) {
            if (snapshot.connectionState == ConnectionState.done) {
              taskData.tasks = (snapshot.data as ApiResponseModel).responseBody;
              return RefreshIndicator(
                onRefresh: () async {
                  var responseModel = await TaskService().getTasks();
                  setState(() {
                    taskData.tasks = responseModel.responseBody;
                  });
                },
                child: ListView.builder(
...
...

Let me know if more code is required, thanks in advance!

Points

  • I am using a StatefulWidget
  • Task data is a class that extends ChangeNotifier
  • When I debug the refresh I can see the new data in the list, but the UI does not update

getTasks()

  Future<ApiResponseModel> getTasks() async {
    try {
      var _sharedPreferences = await SharedPreferences.getInstance();
      var userId = _sharedPreferences.getString(PreferencesModel.userId);

      var response = await http.get(
        Uri.parse("$apiBaseUrl/$_controllerRoute?userId=$userId"),
        headers: await authorizeHttpRequest(),
      );

      var jsonTaskDtos = jsonDecode(response.body);
      var taskDtos= List<TaskDto>.from(
          jsonTaskDtos.map((jsonTaskDto) => TaskDto.fromJson(jsonTaskDto)));
      return ApiResponseModel(
          responseBody: taskDtos,
          isSuccessStatusCode: isSuccessStatusCode(response.statusCode));
    } catch (e) {
      return null;
    }
  }

CodePudding user response:

The issue here seems to be that you are updating a property that is not part of your StatefulWidget state.

setState(() {
  taskData.tasks = responseModel.responseBody;
});

That sets a property part of TaskData. My suggestion is to only use the Consumer and refactor TaskService so it controls TaskData or similar. Something like:

Consumer<TaskService>(builder: (context, service, child) {
  return RefreshIndicator(
    onRefresh: () {
     service.getTasks();
    },
  child: ListView()); //Uses service.data to build the list items
}

and make sure to call notifyListeners() in the service.getTasks() method to make the Consumer rebuild

CodePudding user response:

I think (someone will correct me if I'm wrong) the problem is that you are using the FutureBuilder, once it's built, you need to refresh to whole widget for the FutureBuilder to listen to changes. I can suggest a StreamBuilder that listens to any changes provided from the data model/api/any kind of stream of data. Or better yet, you can use some sort of state management like Provider and use Consumer from the Provider package that notifies the widget of any changes that may occurred.

  • Related