Hello everyone! Today we will create our first Flutter app: a ToDo List App. We will learn how to use a lot of components and add logic to our application.
The Logic
Before diving into developing the app, let's think of what it will offer.
We mainly need to build 2 things: a group of buttons to add an item / clear the list and the list.
Our list is going to be made of many items, each item has a checkbox and a Text.
We'll have 3 buttons: one to add an item to the list, one to clear the list, and another one to remove the done items.
Now that we understand how our app is going to be built, well, let's build it!
Project Initialization and Structure
First of all head into your preferred editor (I am using VSCode) and create a new Flutter project with this command:
$ flutter create <name of your application>
Ctrl + Shift + p
and search for "Flutter". You will have an item called "Flutter: New Project". If you click it you will create your project in less than a second.After you create your project, head over to the lib folder and create 4 new folders: models/
, utils/
, views/
and widgets/
. You will learn about the use of them in a future post. For now, create them.
Step 1: The Home Page and App Bar
First of all head over to the main.dart file and delete what's not necessary. Then create a file in your views/
folder named homepage.dart. In that file create a simple Flutter Stateful Widget:
// always import the material library
class HomePage extends StatefulWidget {
const HomePage({super.key});
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
@override
Widget build(BuildContext context) {
return const Scaffold();
}
}
Now head over to the main.dart file, import the homepage.dart, and set the main file to return your Home Page Widget:
...
Widget build(BuildContext context) {
return const HomePage();
}
...
Ok, after this we can start by creating our AppBar. I chose a simple AppBar with a text title and a pink color:
// views\homepage.dart
appBar: AppBar(
title: const Text(
"TODOAll",
style: TextStyle(
color: Colors.white,
),
),
centerTitle: true,
backgroundColor: Colors.pinkAccent,
),
Step 2: The ToDo Items and the List
We can now start defining our ToDo List. We will first create a class called ToDo Item which will contain the information of every item in the list. After that, we create a list of these ToDo Items. Create a new file models\
called todo_item.dart and create the class:
class TodoItem {
bool checked = false;
String text = "";
TodoItem({required this.checked, required this.text});
}
After this create another file in the same folder called todo_list.dart:
import './todo_item.dart';
List<TodoItem> TodoList = [];
We will use this list to build and display the items on the page. However, we can't just display the items with this list. We first need to give them a design, a UI.
Step 3: Building the ToDo Item Widget
We will now create a widget for our ToDo Item. In the widgets\
folder create a file called todo_card.dart and copy this code:
import 'package:flutter/material.dart';
import '../models/todo_item.dart';
class TodoCard extends StatefulWidget {
final TodoItem todoItem;
const TodoCard({Key? key, required this.todoItem}) : super(key: key);
@override
State<TodoCard> createState() => _TodoCardState();
}
class _TodoCardState extends State<TodoCard> {
@override
Widget build(BuildContext context) {
return Container(
width: double.infinity,
color: Colors.transparent,
child: Row(
children: [
Expanded(
flex: 1,
child: Checkbox(
value: widget.todoItem.checked,
onChanged: (value) {
setState(() {
widget.todoItem.checked = value!;
});
},
),
),
Expanded(
flex: 3,
child: Text(
widget.todoItem.text,
style: TextStyle(
color: widget.todoItem.checked ? Colors.grey : Colors.black,
decoration: widget.todoItem.checked
? TextDecoration.lineThrough
: TextDecoration.none,
),
),
)
],
),
);
}
}
Let's analyze it:
We import the class from todo_item.dart so we can access the class's properties;
Our item will be a container. In the container, we will have a row of our checkbox and a text.
We set the dynamic dimensions for the row, the text will occupy more space than the checkbox.
Step 4: Showing the List
To show the list we need to iterate over the todo_list and for each ToDoItem create a widget ToDoCard to display the information. To do so, head to the utils\
folder create a file called build_todoCards.dart, and write this code:
import 'package:flutter/material.dart';
import '../models/todo_item.dart';
import '../widgets/todo_card.dart';
List<Widget> buildTodoCards(List<TodoItem> todoList) {
List<Widget> todoCards = [];
for (var todoItem in todoList) {
final todoCard = TodoCard(
todoItem: todoItem,
);
todoCards.add(todoCard);
}
return todoCards;
}
We will use the list of widgets it returns on our home page. So, go to the home page file, import the build_todoCards.dart, and add this after the AppBar:
body: Padding(
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 0),
child: SingleChildScrollView(
child: Column(
children: buildTodoCards(todoList), // we pass the todoList we imported from the file in utils\
),
),
),
We displayed our list, however it is empty and we don't have anything to add an item to it.
Step 5: Add an Item
To add an item we need a button that, when pressed, shows a dialog with an Input field. Once we click "Add" we call a function that adds another ToDo Item to the list with the text we inserted. First of all, create a button on the home page:
...
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add, color: Colors.pinkAccent),
),
Then, create a function that shows a dialog (you can find this quickly on the internet):
Future<void> _showAddTodoDialog(BuildContext context) async {
String newTodoText = "";
await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text("Add Todo"),
content: TextField(
autofocus: true,
onChanged: (value) {
newTodoText = value;
},
decoration: const InputDecoration(labelText: "Todo text"),
),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context); // Close the dialog
},
child: const Text("Cancel"),
),
TextButton(
onPressed: () {
final newTodoItem = TodoItem(checked: false, text: newTodoText);
addTodoItem(newTodoItem);
Navigator.pop(context); // Close the dialog
},
child: const Text("Add"),
),
],
);
},
);
}
Once we have the function, let's call it in our button:
FloatingActionButton(
onPressed: () {
_showAddTodoDialog(context); // add this
},
child: const Icon(Icons.add, color: Colors.pinkAccent),
),
In our _showAddTodoDialog function we called another function named addTodoItem. You can define it in this way:
void addTodoItem(TodoItem todoItem) {
setState(() {
todoList.add(todoItem);
});
}
And now the app will work fine, but, can we add more things?
Final Step: Remove / Clear the List
We can add a few more things to our app:
A button to clear our list, making it easy to restart everything;
A button to remove all the done items so we only see what we have to do;
To add more floating action buttons we need to transform our code a bit:
floatingActionButton: Column(
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: () {
_showAddTodoDialog(context);
},
child: const Icon(Icons.add, color: Colors.pinkAccent),
),
const SizedBox(height: 16),
FloatingActionButton(
onPressed: () {
clearDoneItems();
},
child: const Icon(Icons.delete, color: Colors.pinkAccent),
),
const SizedBox(height: 16),
FloatingActionButton(
onPressed: () {
clearList();
},
child: const Icon(Icons.clear_all, color: Colors.pinkAccent),
),
],
),
Let's now write the functions we called from the buttons:
void clearList() {
setState(() {
todoList.clear();
});
}
void clearDoneItems() {
setState(() {
todoList.removeWhere((todoItem) => todoItem.checked);
});
}
Great! You've completed the app. If you'd like to share it with your friends, you can create an APK file with this command:
$ flutter build apk
That's it for today, hope to see you in the next post!