State Management in Flutter - Provider Journey with Todoey App

ValerianaGitđź’™ - Apr 17 '21 - - Dev Community

Part 1

Use Provider to get data across the app

1 - Install Provider in .yaml file and get packages
2 - Build mindmap of a simple Provider implementation
3 - Build mindmap for Todoey app and Provider implementation

Alt Text
First areas of struggle -

  • How to use Provider with ListView and its tiles(parts 3 and 4)
  • Buttons updating state (part 2) 4 - Start writing code ###Create TaskData class Properties tasks = []; (lift from tasks list)

Getter for taskCount

int get taskCount {
return tasks.length;
}

Void addNewTasks() { // addTask to list ; notifyListeners(); }

void addNewTasks(String newTaskTitle) {
final task = Task(name: newTaskTitle);
tasks.add(task);
notifyListeners();
}

Update root to access data at highest level

Got an error The argument type 'Widget Function(BuildContext)' can't be assigned to the parameter type 'Widget Function(BuildContext, Widget).
Turns out using builder is deprecated and we must use create instead. (April 16/ 2021).

Tasks Screen

Update Text widget to provide number of tasks
'${Provider.of<TaskData>(context).tasks.length}',
Comment out SetState in AddTaskScreen
Convert to stateless widget since we are no longer using setState
Update TaskListobject to not need any property constructors

Tasks List

Import provider and task_data
Delete tasks property, won’t be needed anymore, the constructor is not needed either
Replace ToDoItem constructor data, depending on which property you are trying to access
Provider.of<TaskData>(context).tasks[index].name
Also comment out setState
Convert to stateless widget since we are no longer using setState

The Consumer Widget from Provider

To not have to repeat all the places where Provider.of<TaskData>(context).tasks[index].name is going to be accessed.
Wrap highest level widget where these will be used
In our case in Tasks List is the List View widget
End up with this
return Consumer<TaskData>(
builder: (context, taskData, child) {
Return ListView….;
},
child: ListView.builder(

Now taskData can replace Provider.of<TaskData>(context)

Part 2

Add Tasks to list functionality

AddTasks Screen

Button
onPressed: () {
//newValue comes from textfield input
Provider.of<TaskData>(context, listen: false)
.addNewTasks(newTaskTitle);
Navigator.pop(context);
},

Part 3

Functionality for tile’s state

Task Data

void updateTask(Task task) {
task.toggleDone();
}

Task Tile

Tasks List (nothing specific to Provider, except for the use of updateTask)
final task = taskData.tasks[index];
return TodoItem(
newTodoItem: task.name,
tapped: task.isDone,
iconCallBack: (bool checkBoxState) {
TaskData().updateTask(task);
});

With current setup, it should work, but Tasks Screen only updated when the button is pressed in the Add Tasks Screen. I think it is related to changes / updates in Provider. Related to that I had to set the listen: false . .. Maybe it is because we are using Consumer

SUCCESS! Because of changes made to the newer versions of Provider package, where we need to set the listen property to false. Even though we are inside Consumer, we still need to access updateTask calling through explicitly calling Provider to be able to set the listen property to false. This way the widget can rerender.

final task = taskData.tasks[index];
return TodoItem(
newTodoItem: task.name,
tapped: task.isDone,
iconCallBack: (checkBoxState) {
Provider.of&lt;TaskData&gt;(context, listen: false)
.updateTask(task);
});

Enter fullscreen mode Exit fullscreen mode




Part 4

Delete Tasks

Alt Text

1 create new method in TaskData model to delete the task

void deleteTask(Task task) {
_tasks.remove(task);
notifyListeners();
}

2 - create a new callback in the TodoItem constructor

final Function longPressCallBack;
TodoItem({this.newTodoItem, this.tapped, this.iconCallBack, this.longPressCallBack});

3 - ListTile has a property - onLongPress, which is where we’ll call the new property in the constructor

return ListTile(
onLongPress: longPressCallBack,

4 - Update TodoItem constructor in the TasksList Widget

longPressCallBack: () {
Provider.of<TaskData>(context, listen: false)
.deleteTask(task);
},);

5 - The TaskScreen creates a new TasksList Widget as a child, so it should get all this information and update. DONE!

. . . . . . . . . . . . . . . . . . . .