Simple ToDo Application with Provider

 

Hello,
Today I would like to talk about and illustrate a structure that I found very difficult, complex and took a lot of time to understand during my Flutter learning process. Our topic is the Provider structure in Flutter. I have previously talked about the “State” logic in Flutter, the differences between stateful and stateless widgets. I leave a reminder link below:

Flutter and Widget Concept

We use Stateful Widgets if there will be changes in the application as a result of user interaction or for other reasons. That is, at points that should contain a State object. And with very simple logic, if there is a “State” in the middle, it needs to be managed, that is, we need to choose and use one of the State Management methods so that we can track the state of the State and instantly show the changing interface to the user as a result of interaction. I want to talk about the Provider structure, which is one of these methods. Let’s start if we are ready.

What is Provider ?

Provider is a package used for state management in Flutter, and is used to manage and share state in specific regions or across the application. Provider provides a simple and efficient way to recreate, update and share state.

The encyclopaedic information I used above can be found everywhere. I will show how to use it with a short and simple todo application.

When our project is completed, it will look like the following:

Basic ToDoApp with Provider

Step 1:

First, we create and name a new Flutter application. After the project is created, we open the pubspec.yaml file and add the Provider package here.

dependencies:
flutter:
sdk: flutter
provider: ^6.0.0 //The version number may change
//according to the date you created the application.

Step 2:

The second step is to create a model representing the state. Since our application is TodoApp, we can create a Todo class:

class Todo {
//We will check the todo items with checkbox.
String title;
bool isDone;

Todo({
required this.title,
this.isDone = false, //checkbox will initially come unchecked
});

void toggleDone() {
isDone = !isDone; //method to add tick if there is no tick
//when checkbox is clicked and delete if there is tick
}
}

Step 3:

In the next stage, we need to create a class that manages State and extend it with “ChangeNotifier”. ChangeNotifier is a class that listens and notifies state changes. By calling the notifyListeners() method of this class, we notify this change to all components (widgets) that listen for changes. If we go from our example, let’s say the user wanted to cross out an item he added to the Todo list and ticked the checkbox, thanks to this class, the change is notified to the user interface instantly and the checkbox tick appears and the item is crossed out.

class TodoProvider with ChangeNotifier {
List<Todo> _todos = [];

List<Todo> get todos => _todos;

void addTodo(String title) { //Method to add from CRUD operations
_todos.add(Todo(title: title));
notifyListeners(); //method that allows changes to be reflected
//to the interface instantly
}

void toggleTodoStatus(int index) {
_todos[index].toggleDone();
notifyListeners();
}
}

Step 4:

In this step, we integrate the Provider structure into our main.dart file. ChangeNotifierProvider creates an instance of the ChangeNotifier class (we can think of it as getting an instance) and declares State to subcomponents.

void main() {
runApp(
ChangeNotifierProvider(
create: (context) => TodoProvider(),
child: MyApp(),
),
);
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: TodoListScreen(),
);
}
}

Step 5:

Finally, we create another file called todoListScreen.dart and redirect the MaterialApp’s home page to it.

class TodoListScreen extends StatelessWidget {
final TextEditingController _controller = TextEditingController();

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('To-Do List'),
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
Expanded(
child: TextField(
controller: _controller,
decoration: InputDecoration(labelText: 'New To-Do'),
),
),
IconButton(
icon: Icon(Icons.add),
onPressed: () {
if (_controller.text.isNotEmpty) {
Provider.of<TodoProvider>(context, listen: false)
.addTodo(_controller.text);
_controller.clear();
}
},
),
],
),
),
Expanded(
child: Consumer<TodoProvider>(
builder: (context, todoProvider, child) {
return ListView.builder(
itemCount: todoProvider.todos.length,
itemBuilder: (context, index) {
final todo = todoProvider.todos[index];
return ListTile(
title: Text(
todo.title,
style: TextStyle(
decoration: todo.isDone
? TextDecoration.lineThrough
: TextDecoration.none,
),
),
trailing: Checkbox(
value: todo.isDone,
onChanged: (value) {
todoProvider.toggleTodoStatus(index);
},
),
);
},
);
},
),
),
],
),
);
}
}

There are two uses I need to explain here. The first one is Consumer.

Expanded widget with Consumer is the place where todo items will be displayed. Here, the Consumer allows us to display the todo items right below when the user adds an item. Because Consumer listens to the provider (TodoProvider) using the builder method and automatically rebuilds the widget when the state changes.

The second use is the Provider.of<T>(context) construct. This method accesses the provider of the specified type, which in our example is TodoProvider. Here we have a method called addTodo and using this method new todo items can be added to the list. Here we want to access only the state without listening to the state change, so we give the listen parameter as false. If we give true, it also listens to these changes, but it may cause performance problems because it is unnecessary here.

Finally, we have experienced state management by combining the simple To-Do List application with the Provider structure. We can further develop this project, add persistent data storage or more advanced user interface features.

I tried to explain the Provider structure in a very short and simple way. I hope it was useful.

Good coding to all of us.

Selin.

Hiç yorum yok: