Developing a TODO app with Flutter – Part 2

In this series of tutorials, I will walk you through the process of creating a TODO app with the Flutter framework. By the end, you will have a cross-platform TODO mobile application, which allows you to add, edit, remove and complete tasks, while persisting the data on Google Firebase. The series spans across 3 parts:

Part 1 – Setting up the development environment and creating the project
Part 2 – Implementing core functionality
Part 3 – Connecting your app to Google Firebase

Where we left off

After you have finished part 1 of this series, you should have Android Studio and Flutter SDK installed, Android Emulator configured and sample project building and running.

screenshot showing android studio and android emulator running sample flutter app
Your screen after completing part 1

Flutter 101

The core concept of Flutter is widgets. In Flutter, everything (almost) is a widget. Widget is something that gets drawn on the screen. If you are familiar with React or Vue, you can think of widgets as components, as they are essentially the same thing.

A widget can either be stateless or stateful. Stateless means that the widget does not have any internal functionality built into it. An example of a stateless widget is a Text widget, which renders a string of text, or an Icon widget, which renders an icon. Stateful means that the component has some internal state, which allows more complex interaction. An example of a stateful component may be an input field that lets the user enter text or a dropdown that lets the user select a value from a list.

Widgets can be combined with one another, thus allowing us to build very complex user interfaces. In our TODO app, I will try to keep things simple to let you fully grasp the features of Flutter.

Data structure

Before we begin creating the UI for our app, it is important to consider what data structure is going to be used to store the tasks in the TODO list. As Dart is an object-oriented language, it makes sense to create a class to hold the required information about the task. Create a file called task.dart in /lib and write the following code:

Here, we are defining a class Task that is going to be used to store our TODO tasks. Every task has a name and can either be completed or not. These properties are defined in lines 4-5. Note the underscore in front of the names: it makes them private. It is a good practice to keep the class properties private and write getters/setters for them. Getters and setters for _name are defined on lines 13-14 and for _completed on lines 17-18.

Displaying the tasks

After we have defined a data structure for tasks, it would be great to display them. We will start by deleting all the code from main.dart file. This code is used to check if the environment is set up correctly, but to learn Flutter, you should be able to write all the code yourself. After deleting, write the following code in it:

We start by importing the material package. This package is provided by Flutter and has all the common UI widgets, conforming to Material Design guidelines. On line 6, we define the main function, which acts as the entry point for the application. Flutter will start by executing this function. It asks Flutter to run the app with TODOApp widget as the root widget.

On line 10, we define the TODOApp widget. It is defined by extending the StatelessWidget class. To specify what exactly has to be rendered on the screen, we override the build method on line 15. The build method returns a Widget, which is composed of several basic widgets from the material package. The app is wrapped in MaterialApp which provides some layout and actions for a common material app. Then it is wrapped in a Scaffold, which gives you some basic layout for a specific screen within the app. In Scaffold, we specify the AppBar, which is the header at the top of the screen and body. In the body, we output the ‘Hello world’ in the center of the screen. Right now, the app should look like this:

screenshot of android emulator running the hello world app

To display the list of tasks, we first have to create it. As there is no interface to add new tasks, we will fill it with some dummy ones. Carefully examine the following code which goes in main.dart:

We begin by importing the task.dart file we created earlier to use the Task class. Then, in the TODOApp class, the tasks property is defined on line 10. Its type is List and we assign some dummy values to it. In the build method, instead of rendering Hello world we use Listview.builder to build a list of values on line 25. It accepts the number of items in the list and the method to build them. Your screen should now look like this:

screenshot of android emulator running the app. there are 3 tasks displayed

Now, we have 1 screen that is responsible for displaying the tasks. But we also need to create tasks, so we are going to need another screen. Flutter has navigation capabilities built-in the MaterialApp class. Write the following code in main.dart:

Here, we have moved the displaying capabilities out of TODOApp class into TODOList on line 32. We also created a TODOCreate class on line 62 that will hold the form to create tasks. Back in the TODOApp class, we define routes and initialRoute to handle the navigation logic.

To navigate between routes, Navigator class is used. To create new tasks, we have added a button to the TODOList class on line 53. In its onPressed event, we call Navigator.pushNamed to navigate to TODOCreate. Now, the app should look like this:

screenshot of android emulator running the app. there are 3 tasks displayed
screenshot of android emulator running the app. the create a new task screen is opened

Stateful components

As of right now, our app cannot support the creation and editing of new tasks. To implement it, we have to first introduce state to the application. State will hold the tasks and notify the widgets when they are updated. Rewrite the TODOApp class as follows:

We start by creating a stateful widget TODO and state for it, the TODOState on lines 11 and 21. Every stateful widget has to have a state associated with it. State holds all the information needed by the widget and handles the building (line 32). Stateful widget has to connect a state to itself, as shown in line 15. The app looks the same right now, as we did not change any functionality yet.

Creating a task

Finally, we are at the point when we can implement task creation. This will be done in TODOCreate class. TODOState will provide a callback that is going to be used by TODOCreate to signal a new task creation. Callback is a function that is passed into another function so it can be executed later. Change your code in main.dart as follows:

Be begin by defining a callback function in TODOState on line 27. It takes a name and inserts a new task to the list. Be sure to wrap any operations on state with setState, otherwise Flutter will not update the UI. In the build function on line 43, this callback is passed into TODOCreate.

As TODOCreate handles user input, it also has to be converted to a stateful component. In TODOCreateState, we define a TextEditingController, which gives access to the value of the TextField. In the build function on line 105, controller is connected to the TextField. There is also a button to save the new task on line 112. It called the callback provided to the widget with widget.onCreate and goes back to the list screen with Navigator.pop. Now the app should look like this:

screenshot of android emulator running the app. the create a new task screen is opened
screenshot of android emulator running the app. there is 1 task in the list

Completing the task

Congratulations, we are almost done! The last thing left to implement is the ability to complete a task. Modify TODOState and TODOList classes like this:

The callback function on line 36 is very similar to the previous one. It takes a Task object and triggers its completed value. This callback is passed into TODOList and hooked up to the CheckboxListTile. CheckboxListTile is built into material package and lets you easily create a list with checkboxes. Your app should now look like this:

screenshot of android emulator running the app. there are 3 tasks in the list with checkboxes

🎉🎉🎉 We are done with part 2 of this tutorial! You can read part 1 here, and in the next one, we will connect our app to Google Firebase to enable authentication and data persistence. All the code up to now is available on Github.

Get new content delivered to your mailbox:

one comment

leave a comment