Create a simple todo app with Flutter provider

Sourav Sarkar Emon
5 min readFeb 27, 2022

Learning state management with a real example to-do app is a great choice for it. I will show you how to create a very simple to-do app with Flutter provider. Here we gonna use the Provider package to manage the state of the app.

Prerequisites:

Flutter installed properly in your system and basic understanding of flutter & dart.

Create a blank flutter project:

flutter create todo_app

Add provider package

flutter pub add provider

Create todo model

To make your project organized create another folder named model inside lib folder. Create a todo_model.dart file inside that model folder. And this is our model file.

We will be working on a single model if your project is big and you need more then feel free to create them.

Here my model looks like that:

class TodoModel {String todoTitle;
bool completed;
TodoModel({required this.todoTitle, this.completed = false});// to toggle the task
void toggleCompleted() {
completed = !completed;
}
}

Create todo model provider

In this section, we have to create another class that extends provider package’s specific class changeNotifier This class helps us to change the state of our app and it will notify us to re-render our app.

In our TodoProvider class, our _task list should be empty or you can put one or two dummy tasks to check the UI. Then we have to create some methods that will help us to modify the tasks in _task list. These methods are get allTasks, addTask, toggleTask, deleteTask. In every method, we must define notifyListeners() at the end to notify all the clients that the object has changed. You can follow my code.

import 'package:flutter/material.dart';
//import dart:collection for UnmodifiableListView class
import 'dart:collection';
import '../model/todo_model.dart';class TodoProvider with ChangeNotifier {//you can put one or two dummy task
List<TodoModel> _tasks = [];
//to get all the tasks
UnmodifiableListView<TodoModel> get allTasks => UnmodifiableListView(_tasks);
//all new added task must be uncompleted
void addTask(String task) {
_tasks.add(TodoModel(todoTitle: task, completed: false));
notifyListeners();
}
//we are not working with task id that why we are working with every tasks index number to modifyvoid toggleTask(TodoModel task) {
final taskIndex = _tasks.indexOf(task);
_tasks[taskIndex].toggleCompleted();
notifyListeners();
}
void deleteTask(TodoModel task) {
_tasks.remove(task);
notifyListeners();
}
}

Complete your main.dart file

First, you have to wrap MaterialApp with changeNotifierProvider() class. This changeNotifierProvider is used is exactly like a vanilla provider. Now we have to create a new instance of TodoProvider().

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:simple_todo_app/providers/todo_provider.dart';
import './screens/my_home_page.dart';
import './providers/todo_provider.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: ((context) => TodoProvider()),
child: MaterialApp(
title: 'Simple ToDo App',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.deepPurple,
),
home: const MyHomePage(),
),
);
}
}

Complete your homepage screen

Our homepage will be a stateful widget because we gonna use textEditingController() in it. For adding listener to our controller we have to create a initState() method. And we must dispose it via dispose() method. We are gonna show a dialog in our floatingActionButton. Users can add new tasks via textField in this dialog. _textFieldController will modify this textField behind the scene. Now create a _submit() function to add textField’s text as a task. In this function, tasks will be added by Provider.of() method. And don’t forget to add Navigator.pop() to close the dialog. We should also clear the textField by _textFieldController.clear() method.

check my code for better understanding.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/todo_provider.dart';
import '../widgets/todo_action.dart';
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final _textFieldController = TextEditingController();
String newTask = '';
//creating initState() for adding listener to controller
@override
void initState() {
super.initState();
_textFieldController.addListener(() {
newTask = _textFieldController.text;
});
}
//disposing the controller
@override
void dispose() {
_textFieldController.dispose();
super.dispose();
}
void _submit() {
//we aren't interested in updating our ui so listen will be false
Provider.of<TodoProvider>(context, listen: false).addTask(newTask);
//cancelling the dialog
Navigator.pop(context);
_textFieldController.clear();
}
@override
Widget build(BuildContext context) {
Future<void> _showAddTextDialog() async {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("Add a new Task"),
content: TextField(
autofocus: true,
controller: _textFieldController,
decoration: const InputDecoration(hintText: "Add New Task"),
onSubmitted: (_) => _submit(),
),
actions: [
ElevatedButton(
onPressed: _submit,
child: const Text("Submit"),
style: ElevatedButton.styleFrom(
minimumSize: const Size(120, 40)),
)
],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
actionsAlignment: MainAxisAlignment.center,
);
});
}
return Scaffold(
appBar: AppBar(
title: const Text("ToDo App"),
),
body: TodoAction(),
floatingActionButton: FloatingActionButton(
onPressed: (() {
_showAddTextDialog();
}),
child: const Icon(Icons.add),
tooltip: "Add a todo",
),
);
}
}

Complete tasks every action

Now it's time to work with the last and final widget called TodoAction. Here our main goal is to show all tasks, toggle, and delete them. ListView.builder is a great option for all to do. Use ListTile as child widget of itemBuilder. In ListTile there is one checkbox as leading to toggle the task, a title to show the task title, and a trailing to delete the complete task.

You will get a clear idea after checking my code:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../providers/todo_provider.dart';class TodoAction extends StatelessWidget {
const TodoAction({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
//store the value in a variable
final task = Provider.of<TodoProvider>(context);
return ListView.builder(
itemCount: task.allTasks.length,
itemBuilder: ((context, index) => ListTile(
leading: Checkbox(
// toggle the task as index item
value: task.allTasks[index].completed,
onChanged: ((_) => task.toggleTask(task.allTasks[index])),
),
//show all the task title
title: Text(task.allTasks[index].todoTitle),
trailing: IconButton(
onPressed: () {
//delete task as index item
task.deleteTask(task.allTasks[index]);
},
icon: const Icon(Icons.delete)),
)),
);
}
}

Wrap up

Now your app is ready. Congratulations you successfully created a todo app with flutter provider. If you follow my code along your app will look like this

todo app home page
todo app home page
todo app add new task section
todo app add new task section

You can find my GitHub code here: https://github.com/souemon16/simple_todo_app

--

--