Update State without reloading a widget in Flutter

Viewed 38

I have a widget on a screen that receives its data from API calls. The API call is made inside the init method of the Navigation Bar so that continuous API calls can be prevented when going back and forth between screens. Although this works fine, I'm facing a real challenge in trying to get the state of the widget updated when new data is added to that particular API that the widget relies on for displaying data. I would therefore need to know how to display the updated data that I added to the Database by making a post request on a different screen. The only way this happens now is by way of reloading the entire app or by killing it. Any help will be appreciated.

This is the NavBar where the API is getting called. I usually make all the API calls at once here and something I have done here too.

NavBar

class CustomBottomNavigationState extends State<CustomBottomNavigation> {
  bool isLoading = true;

  int index = 2;

  final screens = [
    MenuScreen(),
    LeaveScreen(),
    // TaskList(),
    HomeScreen(),
    // PaySlipScreen(),
    TaskList(),

    Claimz_category(),
    // ClaimzScreen()
  ];

  @override
  void initState() {
    // TODO: implement initState
    Provider.of<LeaveRequestViewModel>(context, listen: false)
        .getLeaveRequest()
        .then((value) {
      Provider.of<AnnouncementViewModel>(context, listen: false)
          .getAllAnouncements()
          .then((value) {
        Provider.of<TodaysTaskList>(context, listen: false)
            .getTodaysTasks()     //This is the API call in question
            .then((value) {
          setState(() {
            isLoading = false;
          });
        });
      });
    });
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    final items = ['The icons are stored here'];

    // TODO: implement build
    return SafeArea(
      child: Scaffold(
        body: isLoading
            ? const Center(
                child: CircularProgressIndicator(),
              )
            : screens[index],
        extendBody: true,
        bottomNavigationBar: Container(
          decoration: const BoxDecoration(
              borderRadius: BorderRadius.only(
                  topLeft: Radius.circular(200),
                  topRight: Radius.circular(200)),
              boxShadow: [
                BoxShadow(
                    color: Colors.transparent,
                    blurRadius: 10,
                    offset: Offset(1, 2))
              ]),
          child: CurvedNavigationBar(
              items: items,
              index: index,
              height: 60,
              color: const Color.fromARGB(255, 70, 70, 70),
              backgroundColor: Colors.transparent,
              onTap: (index) => setState(() {
                    this.index = index;
                  })),
        ),
      ),
    );
  }
}

ToDoList widget(This the widget where the updates never reflect without reloading)

class ToDoListState extends State<ToDoList> {

  @override
  Widget build(BuildContext context) {
    final toDoList = Provider.of<TodaysTaskList>(context).getToDoList;   //This is the getter method that stores the data after it has been fetched from API

    // TODO: implement build
    return ContainerStyle(
      height: SizeVariables.getHeight(context) * 0.35,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Padding(
            padding: EdgeInsets.only(
                top: SizeVariables.getHeight(context) * 0.015,
                left: SizeVariables.getWidth(context) * 0.04),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Container(
                  // color: Colors.red,
                  child: FittedBox(
                    fit: BoxFit.contain,
                    child: Text(
                      'To do list',
                      style: Theme.of(context).textTheme.caption,
                    ),
                  ),
                ),
              ],
            ),
          ),
          SizedBox(height: SizeVariables.getHeight(context) * 0.01),
          Padding(
            padding: EdgeInsets.only(
                left: SizeVariables.getWidth(context) * 0.04,
                top: SizeVariables.getHeight(context) * 0.005,
                right: SizeVariables.getWidth(context) * 0.04),
            child: SizedBox(
              height: SizeVariables.getHeight(context) * 0.25,
              child: Container(
                // color: Colors.red,
                child: toDoList['today'].isEmpty
                    ? Center(
                        child: Lottie.asset('assets/json/ToDo.json'),
                      )
                    : ListView.separated(
                        physics: const NeverScrollableScrollPhysics(),
                        itemBuilder: (context, index) => Row(
                              children: [
                                Icon(Icons.circle,
                                    color: Colors.white,
                                    size:
                                        SizeVariables.getWidth(context) * 0.03),
                                SizedBox(
                                    width:
                                        SizeVariables.getWidth(context) * 0.02),
                                FittedBox(
                                  fit: BoxFit.contain,
                                  child: Text(
                                      toDoList['today'][index]['task_name'],    //This is where it is used
                                      overflow: TextOverflow.ellipsis,
                                      style: Theme.of(context)
                                          .textTheme
                                          .bodyText1),
                                )
                              ],
                            ),
                        separatorBuilder: (context, index) => Divider(
                              height: SizeVariables.getHeight(context) * 0.045,
                              color: Colors.white,
                              thickness: 0.5,
                            ),
                        itemCount: toDoList['today'].length > 4
                            ? 4
                            : toDoList['today'].length),
              ),
            ),
          )
        ],
      ),
    );
  }
}

The other widget where the date gets added

class _TaskListState extends State<TaskList> {

  @override
  Widget build(BuildContext context) {
    var floatingActionButton;
    return Scaffold(
      backgroundColor: Colors.black,
      floatingActionButton: Container(
        ....
        ....,
          child: FloatingActionButton(
            backgroundColor: Color.fromARGB(255, 70, 69, 69),
            onPressed: openDialog,    //This is the method for posting data
            child: Icon(Icons.add),
          ),
        ),
      ),
      body: Container(
        ....
        ....
        ....
      ),
    );
  }

  Future<dynamic> openDialog() => showDialog(
        context: context,
        builder: (context) => AlertDialog(
          backgroundColor: Color.fromARGB(255, 87, 83, 83),

          content: Form(
            key: _key,
            child: TextFormField(
              controller: taskController,
              maxLines: 5,
              style: Theme.of(context).textTheme.bodyText1,
              decoration: InputDecoration(
                border: InputBorder.none,
              ),
              validator: (value) {
                if (value!.isEmpty || value == '') {
                  return 'Please Enter Task';
                } else {
                  input = value;
                }
              },
            ),
          ),
          actions: [
            InkWell(
                onTap: () async {
                  showDatePicker(
                          context: context,
                          initialDate: DateTime.now(),
                          firstDate: DateTime(2010),
                          lastDate:
                              DateTime.now().add(const Duration(days: 365)))
                      .then((date) {
                    setState(() {
                      _dateTime = date;
                    });
                    print('Date Time: ${dateFormat.format(_dateTime!)}');
                  });
                },
                child: const Icon(Icons.calendar_month, color: Colors.white)),
            TextButton(
              child: Text(
                "Add",
                style: Theme.of(context).textTheme.bodyText1,
              ),
              onPressed: () async {
                Map<String, dynamic> _data = {
                  'task': taskController.text,
                  'task_date': dateFormat.format(_dateTime!).toString()
                };

                print(_data);

                if (_key.currentState!.validate()) {
                  await Provider.of<ToDoViewModel>(context, listen: false)
                      .addToDo(_data, context)         //This is the post method
                      .then((_) {
                    Navigator.of(context).pop();
                    Provider.of<TodaysTaskList>(context, listen: false)
                        .getTodaysTasks();             //I did this here again to re-initialize the data. I was under the impression that the new data would get initialized for the widget to reflect it on the other screen.
                  });
                }
              },
            ),
          ],
        ),
      );
  void add() {
    Navigator.of(context).pop();
  }
}

The Get API Call

class TodaysTaskList with ChangeNotifier {
  

  Map<String, dynamic> _getToDoList = {};

  Map<String, dynamic> get getToDoList {
    return {..._getToDoList};
  }

  Future<void> getTodaysTasks() async {
    SharedPreferences localStorage = await SharedPreferences.getInstance();

    var response = await http.get(Uri.parse(AppUrl.toDoList), headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ${localStorage.getString('token')}'
    });

    if (response.statusCode == 200) {
      _getToDoList = json.decode(response.body);
    } else {
      _getToDoList = {};
    }
    print('TO DO LIST: $_getToDoList');
    notifyListeners();
  }
}

Please let me know for additional input.

1 Answers

i think it's because you didn't call the provider to update your state correctly as i see that you declare new variable to store your provider like this

 final toDoList = Provider.of<TodaysTaskList>(context).getToDoList;

then you use it like this

Text(
  toDoList['today'][index]['task_name'],    //This is where it is used
  overflow: TextOverflow.ellipsis,
  style: Theme.of(context)
  .textTheme
  .bodyText1),
  )

it's not updating the state, you should wrap the widget that need to be updated with Consumer

Consumer<TodaysTaskList>(
      builder: (context, data, child) {
        return _Text(
                 data.[your_list]['today'][index]['task_name'], 
                  overflow: TextOverflow.ellipsis,
                  style: Theme.of(context).textTheme.bodyText1),
          );                     
      },
    );
Related