I created my first flutter app…

Smriti Arora
Technology at Nineleaps
23 min readSep 3, 2021

--

One day, I was randomly surfing the internet just curious about any new languages. I came across this word: FLUTTER. So, some basic questions hit up my mind like What is flutter? Why flutter? Is it worth learning? I also had all these questions hovering in my mind like most of you when you hear about any new language. Then I became really curious and wanted to learn it for a really long time but couldn’t get a chance.

Image source: https://c.tenor.com/E3SGDcyydMMAAAAM/batman-hmm.gif

And then one day, I finally took up a full-fledged course on flutter and tried my hand on it.

This article is for someone who know some theory about flutter but want a guiding tutorial to create a complete app in flutter. At the end of this article, you’ll have a fully functional android and an iOS mobile app with just one codebase. That’s the magic of cross-platform languages. Okay…?? Now, what’s that though…🤔

So many questions and doubts already… 😅 Before we start with the introduction, let me tell you, this article is not just a follow along tutorial, but is also filled with Try it yourself tasks and an activity at the end. So, do stick along till the end.

The complete project is also available on github for your reference. You can click here to visit it:

Task Manager App Github link

If you want some introduction before you can start coding you can refer to this article:

Intro article link

About the app.

This is just an introductory flutter tutorial, but trust me flutter has a LOT to offer. This is just to give you a taste of how it feels like working in a flutter. We are making a simple task manager app.

  • Starting with a task category screen as the home screen. You can add tasks categories here.
  • On-click of each category you go to a task list screen. Where you can find all the tasks under that category. You can add and delete a tasks here.
  • Also, you can mark a task as a favorite by clicking on the favorite icon and vice versa.
  • There is favorite button on the category screen which gives you access to the favorites screen containing your favorite tasks.
  • Lastly, we will save all this data in a local SQL database.

This is how the app looks:

App look and feel

And here is a simple flowchart to explain the app flow:

App flowchart

Let’s Begin…

First, navigate to the main.dart file in the lib folder and let's start with removing the starter code. So now you have an empty main.dart file.

First of all, we create a stateless widget named HomePage. This the first widget that comes on our screen when we run the app. To create the homepage we write st in the file and we shall see a dropdown menu showing different options. In it, selected the Flutter stateless widget option. We will name this widget as HomePage.

So here we have:

  1. A constructor which has an optional argument named key, which is used to uniquely identify and refer to a widget in your tree, and
  2. The build method.

Now, the StatelessWidget is part of the material.dart package in flutter. So that needs to be imported, to do so we add the below line on top of our file. import ‘package:flutter/material.dart’;

Next, we add the main function to our file as already discussed, it is mandatory to have it in our main.dart file. Inside the main function, we call the runApp() function and pass our widget inside this, it does all the heavy weightlifting of running the app with the widget we pass in it.

This is how it looks after following the above steps:

Starting with the first screen…

Before we start with our first screen. Let’s create task and category data models and dbHelper class perform operations on db.

Category Model

  • Create a class named CategoryModel.dart and add the properties we want each of our categories to have.
  • Our category has an id for unique identification, and a name.

This is pretty much we want our category to hold.

Now, we add a constructor to initialize these values. Since both of these values are a mandatory to have, we add a required keyword in front of both the values. This is how the complete class looks.

Note : The final keyword signifies that these values will be assigned on runtime and cannot be changed once initialized. The curly braces inside the constructor make it a constructor with named arguments.

CategoryModel.dart

Task Model

  • Create a class named TaskModel.dart and add the properties we want each of our tasks to have.
  • Our task has an id for unique identification, a name, an optional description, an integer property isFavorite which is 0 if the task is not a favorite and 1 if it is, now why an integer, you will get to know it later in the article and a category name to which it is associated.

Now, Similar to category model, we add a constructor to initialize these values. Since all values except description is mandatory to have, we add a required keyword in front of the values. This is how the complete class looks.

Note: Since a task may or may not have a description, so, desc’s data type has a ? in front of it to declare that it is an optional.

TaskModel.dart

DbHelper

Here we are going to use SQL lite to store data. Since it is an external dependency we have to add it to our app. To do so, we add it under the dependencies section of the pubspec.yaml file.

Note: We should keep in mind that spacing and indentation plays a very important role in pubspec.yaml.

For that, add the sqflite library to pubspec.yaml. Add this line, under the dependencies section:

sqflite: ^2.0.0+3

Along with that we also need to add the path library, which will help us in getting the path to our db. Add this line, under the dependencies section:

path: ^1.8.0

Apart from this I’ve added another dependency which is used to generate app icons for flutter. Let me know in the comments section, if you would want an article on that as well.

Also, these version numbers may keep on changing as per the latest stable release.

So finally, this is how the file looks:

  1. Setting up db: First, we import the added third party libraries into our dbHelper file. Add these lines on top:

import ‘package:path/path.dart’ as path;

import ‘package:sqflite/sqflite.dart’ as sql;

Now, we can use the functions from these packages by using these import names.

Next, we will maintain two different dbs for category and tasks. We create db for adding the category table, to do so we add this function in dbHelper class.

Setup Category Database

Now, let’s dive into this function and try to understand it:

  1. The static keyword is added here to make the function accessible using class name.
  2. The function returns a Future of type sql.Database. A future is a data type in dart which is used to represent an incoming value, or an error, that will be available at some point of time in the future.
  3. setupCategoryTable is the name of the function.
  4. async keyword converts the function into an asynchronous block.

Inside the function,

  1. We first get path for storing our database by using the getDatabasesPath function from the sql package.
  2. Next, we return the a database by using openDatabase function which takes the database path. We already got the path in step 1, to make it unique we append db name by using the join function from the path package. In the first argument, we pass the obtained db path and then pass db name in second argument, which is category.db in this case.
  3. The openDatabase function either opens an existing database with passed parameters or creates a new one in case the db doesn’t already exist. For this we define the onCreate closure in the function. Inside it we run the create query for db and return the db with the version.

Similarly, we set up the task table. Try doing it by yourself…

If it doesn’t work out you can take a look into the complete project on github. You can find the link at the end of the article.

2. Write functions: To insert data in the db,

  1. We first open it using the setup Function that we created in step 1. It will a return the db reference.
  2. On that db reference, we call the insert function, which takes three argument, table name, data map and an optional argument which is conflict algorithm, which decides what to do in case the data already exists in db. Here, we set it to .replace, which means that duplicate entry will be replaced.

This is how the function looks:

Insert category function.

Now, try to write it for inserting tasks.

3. Read Functions: To get data from the db,

  1. We first open the db, like we did in write function.
  2. Next, we will query all the categories to display them on screen. To do so, we call the .query function on the db reference obtained from step 1. It takes table name as query.
  3. Finally, we return the data obtained in the form of CategoryModel object by using map function and .toList function which converts the iterables obtained from map function to a List of Category Model objects.
Get Category function.

Similarly, we can write a get function for tasks as well. But there is a catch in that. As per the requirement of our app, we only fetch the tasks under certain category. To do so, we have to filter the data we obtain from the query. For that, we have to pass values to where and whereArgs parameters of query function. In the where argument we pass the name of category column from the db i.e. categoryName and in whereArgs we pass the actual value of the category(in array form) of which we want to fetch the tasks. This is how the complete functions look like:

4. Delete functions: To remove data from the db,

  1. We first open the db.
  2. Then, run the delete function on db handle. The delete function takes where and whereArgs where we send the primary key of the category, i.e. id.
  3. Also, along with removing the category we also remove the associated tasks.

This is how the complete function looks like:

Delete category function.

We also have another independent function for removing tasks. TIY…😅

5. Update function: This function is required to update the favorite status of the task in db. To do so,

  1. We first open the db.
  2. Call the update method on db. It takes table name, updated task, where and whereArgs.

This is how function looks like:

Finally done with the Model and Db classes… We are half way through…Yayy!! 🎉🎉🎉🎉🎉

Let’s move on to creating UI to display data.

First, in the main.dart file, return a Material App widget in the build method in place of the container widget. This widget creates a basic app skeleton for your app.

Task Categories screen

  1. Now, our first screen is going to be the Categories screen. For that, let’s create a file named taskCategoriesScreen.dart under a screens folder in lib folder.
  2. Next, we create a stateful widget, a hack for writing it is, write st it will give you options to create both, we select Stateful here. Now enter the name of your class, which is TaskCategories in this case.
  3. Now, there will be two classes, one is the class with name TaskCategories that extends the Stateful Widget and another is the class with name _TaskCategoriesState. As you can see the build method is in the state class, so the widget tree will be created in the state class and not in the class.
  4. Before, creating the widgets we will create two variables, one an empty List of category Model, named categories, which holds the list of categories from db and a boolean named isLoading, which indicates if the data is still getting loaded from the db or not.
  5. Next, we create a function called fetchData, which fetches data from the category table in the db. Inside the function, we first call the getCategoryData function from the dbHelper class and save the data in categories list that we created. Once data is fetched we change the isLoading variable to false and then call the setState function, which will refresh the UI and hence we can see the fetched data.
  6. Now, we want to call this function as soon as we enter the class. So, we call it inside the initState lifecycle method.
  7. Inside the build method, return the Scaffold widget, this widget gives standard app features like provides elements like an Appbar and a drawer.
  8. Next, we need to have an Appbar, for that, in the appBar parameter of Scaffold we create a new App Bar.
  9. As per the app design, we want to have a favorite icon in the left of the app bar and a plus icon on the right. These are basically Icon Buttons. To add them as per design, we create an IconButton in the leading parameter of AppBar. Here, I choose .favorite icon to indicate my fav button.
  10. Next, we want to go to the favorite screen on click of the favorite button. To do so, we can either refer a function in the onPressed param of or we can directly write an anonymous function. Now, to navigate to new screen, we use:

Navigator.push(context,MaterialPageRoute(builder: (ctx) => FavoriteScreen()));

where FavoriteScreen is the favorite Screen class which we shall create further in the course.

Finally this, is how the complete leading param of AppBar looks:

Leading parameter of App bar.

7. Next, we add the title of our screen in the title param. To add any text on screen, we use the Text widget. It takes a compulsory string parameter which is the string we want to display, which in this case is ‘Task Categories’. Add this line to your title param:

Text widget.

8. Now, we want to add the plus icon, which helps add new categories to our category list. Similar to the favorite icon, this is also an Icon button. Try to add it by yourself. On press of this button we want to show a dialog box which has a text field to write category name and two buttons, one to add the category and other to cancel button, to remove the dialog from the screen.

9. To show the dialog on screen, we use the showDialog method, it takes the current build context, which we get from the build method and a builder, which takes the widget that we want to show in the dialog and returns the context to the dialog. To create dialog design. We want a title, a textfield and two action buttons. For that, we add a text widget in the title param, textfield with a textfield controller we can declare it after the variable declarations at the start and for two actions we add two elevated buttons,

first,

an Add button to add category db, to do so we use the insert function that we created in the in DBHelper class. For id, we use the current timestamp and convert it to string. For name, we fetch it from the textfield controller by using it’s .text property. Now, once the data is inserted in db we will remove the dialog from the screen. To remove, we use pop function from the Navigator class and pass the context of dialog box. This is how the complete function looks:

onPressed function of add category

Done with the app bar… 🎉🎉

Next, is the body of the screen, to show that we will create a widget tree, inside the body param of scaffold. Before we start, in flutter we have an amazing thing about widgets that they can also be shown on screen conditionally using if-else, switch and even ternary operators. Now let’s dive-in,

  1. First, we check if the data is still getting loaded. If yes, we show a CirularProgressIndicator widget, which as the name suggest shows and infinitely circling widget on screen.
  2. If the loading has finished, we check if there are any categories in the list. If no, we show a text message in the center stating that there are no categories so you can start to add one.
  3. If the categories are loaded, then we show them using the .builder form of list view as we may not know the exact no. of categories since they can be added again and again. Now this builder form, takes two compulsory arguments, itemCount, which we can get by calling .length function on the categories list, and itemBuilder, which returns context and index of the items in the list and takes the widget that we want to show in the list.
  4. Here, I want to show my list as cards. So I return a Card widget with ListTile as it’s child, now what’s that though? 🤔 A List Tile is a predefined template widget for displaying elements in a list. It has several arguments like, title, trailing, leading etc. Here, we just want a title, which is the category name and trailing element which is a delete button to remove category. So, we add the text widget for title and icon button for delete action. For writing on press of delete button, we call the deleteCategory function from db helper. Also, Don’t forget to call fetch data function to refresh the data present in categories list after the delete is successfull.

5. Moving forward, on tap of the category we want to go on the task list screen to do so, we use the onTap param of list tile widget and inside it, we use the Navigator class’ push function just like we did for the favorites screen.

This is how the complete body looks like:

Body of category screen.

FINALLY! DONE WITH OUR FIRST WORKING SCREEN…. KUDOS GUYS… 🎉🎉🎉🎉

Till now we have a category screen with app bar options well working and the categories show up from db with tap and delete functions.

Now, before we move on, there is a very important tip to working with widget trees, that is,

Break down the widget tree into smaller reusable widgets so as to reduce the tree size which makes the code cleaner, less repetitive and more understandable and these widgets are called custom widgets.

Now, there are two screens left to be made, the task list and favorites screen. There is one common thing between them that is both of them show task list with almost the same styling apart from some little tweaks like, the there is no category label in the task list since we are already inside a category and there is no favoriting button in the favorites screen like in the task list, etc.

So, before moving on to the screen, we will create a custom widget which we can use both on the task list as well as the favorites screen. Cool Isn’t it? 😎

Without further ado, let’s dive in…

Task card

  1. Create a widgets folder, inside it, create a file name taskCard.dart.
  2. This is going to be a stateless widget, since there aren’t going to be any changes in the card on the fly.
  3. Let’s create the widgets first. Both the screens use list’s so we can use ListTile here. In the build method, return the List Tile widget. But I also wanted to give it a card type look, so, we will wrap the ListTile widget inside the card widget.
  4. Let’s design the common features first. The name and delete button is always there on the card. So, in the title param we create a text widget with the name of the task, and in the trailing param we create an Icon button for the delete function, I’ve also configured the button to make it red in colour by using the color param of Icon.

But we don’t have any details about the task how are we going to show the details? 🤔

Here, we need single task which we want to display we can pass this task from the task list screen. To receive that, we will create a variable of type TaskModel, let’s name it taskItem. Also we use the final keyword which states that the variable will be initialised only once during the course of the app. We will receive the value, inside the constructor of the class. We can write this constructor same as we did for models. Now, we can use the . operator to access the properties.

5. Now, deleting an item affects the list as a whole and not the data inside the card. So, we have to do this deletion process in the task list screen. But the function has to be triggered from the task card. To serve the purpose, we can pass the reference of the function from task list screen by passing the name of the function in constructor like we did for task list item.

So we declare a final variable of type Function named deleteItem. The definition of this will be in the task list class. Also add this to the constructor parameter list. This function shall also require the item that is to be deleted. So, in the onPressed of the delete icon button we will write the name of this function reference and pass the selected taskItem in the brackets as argument.

Note: We create an anonymous function in onPressed because if we directly pass the reference with the brackets in front of it, the function will get executed at the time of the creation of the card.

We’re done with the common features of the card.🎊🎊 This is how the card looks by now:

Common task card

Remember there were some tweaks that we had to do in our task card acc. to the screen we are on. Let’s do that.

Additions for Task List screen:

  1. First, let’s see what else do we want on our task list screen. We want a the description below title and a favorite icon at the start of the card to indicate if that task is a favorite or not. We can simply add the description in the subtitle param in Text widget.
  2. For, fav button, we will add an icon button with a favorite icon in the leading parameter of list tile.
  3. Next, the icon should also toggle as the status changes for that I’ve created a function named getFavIcon which returns an icon as per the state of favorite property.
Favorite Icon toggle configuration function.

3. So in the icon param, we call this function.

4. And, in onPressed, we do it in the same way as we did it for delete function. Create the reference, but the reference here will be optional since we would not receive a toggle favorite function from favorite screen, add the receiver in constructor param list and pass reference in the onPressed of icon.

Additions for favorite screen:

In favorite screen, we need to show category name in place of description and we don’t need the favorite icon.

  1. First, we will create a boolean which will indicate if it is favorite screen or not, let’s name it isFavoriteScreen and also add it to the constructor param list.
  2. Since we don’t need a fav icon here, we’ll check if it is the favorite screen, if yes we’ll pass null to the leading param of list tile, else we will pass the icon button that we need for task list screen. This is how it looks:
Leading param of List tile

The toggleFavoriteStatus has a ! mark because it has to be unwrapped since it’s an optional as discussed earlier.

3. Another thing is showing the category name, to do so we’ll simply put a check in the subtitle text widget is the screen is favorites screen we’ll show ‘category name’ else we’ll show the description of task.

Done with the task card creation. Now, let’s quickly place it in our screens and finish the app.

Task List Screen

This class has almost similar functions like the category class like adding and deleting. So, this section will be a comparatively faster and you can also try to create the UI by yourself first.

  1. Create a new file in the screens folder named taskListScreen.dart. This will be a stateful widget as there are list addition, deletions which needs updating on the screen.
  2. We would need the category name to be displayed on the app bar title as well as while saving it in the db. We will pass the category name from the category list. We will pass it in constructor, for that we create a variable of type string and receive the argument in constructor when we initialise it in the category screen class. Note: Since the constructor is in the screen class, we create this variable in the screen class and not in the state class.
  3. We create a list of objects of type TaskModel for storing the data obtained from db. Let’s call it taskList. We’ll also create an isLoading variable to indicate loading state
  4. Now, we need to fetch the data as soon as we enter the screen. To do so, we create the fetch data function like we did in category class. You need to call the getTaskData function from the DbHelper class and pass the category name in the parameter. Also, you cannot directly call the categoryName variable since it is not in the state class. There is a way to call variables from the main class by using the widget keyword, like this, widget.categoryName. Try to implement it by yourself.

This is the implementation here for your reference:

  1. We will call this function whenever we do any updating to the db, so as to fetch the updated data.
  2. So, we make the first call to this function in the initState function. Now, we have the task list with us. Let’s show it on the screen.
  3. In the build method, first we return a scaffold, inside it in app bar param, we create AppBar with title as ‘category’s task’. We have the category name with us, we will join the string ‘task’. We use $ sign, which is string interpolation operator in dart. In the string param in text widget, we write this line: ‘${widget.categoryName} tasks’
  4. Next, we want a reset action in the app bar. So we add a text button in the actions array of app bar widget. Now, on pressing the button we want to reset the list i.e. delete all the tasks for the corresponding category. For this we need to create a helper function the Db class. It is similar to the delete task function that we already have, the only difference is, this time, the where parameter will be category name in place of id. Try it yourself!
  5. Next, we have a list of tasks here and a round button to add tasks.
  6. Now, in body param, we do the primary checks using isLoading variable and if there is data in the list like we did in in category screen and then show the list of tasks using ListView.builder. Easy!
  7. Now, the widget that we are going to return in the builder method is our custom widget, TaskCard . So, we have the data that we need to pass apart from the two function references. So, Let’s create those functions.
  8. Delete function: Create a function which takes the selected task element that we pass from task card. Now, we call the deleteTask function from dbHelper class. Then, refresh the data using fetchData. That’s it! Now pass this function reference in deleteItem param of task card.
  9. Update Favorite function: Create a function which takes the selected task element as parameter. Now, first we fetch the current fav status, then toggle the status from 0–>1 or 1–>0. Next, we call the update task function from the dbHelper class and pass a map with all the elements of task same as before but in the isFavorite key we pass the new status. Then fetch the updated data using fetchData function. Now pass this function reference in toggleFavoriteStatus param of task card.
  10. Now, the only thing left is the add button at the bottom. For this we add a FloatingActionButton in the floatingActionButton param of scaffold. FloatingActionButton creates a circular floating action button. It is same as any other button in terms of implementation, same child and onPressed params. In child, we give the add icon and for on pressed we create a function which shows up the bottom sheet as per the design.
  11. showNewTransactionSheet function: In this function we will design the Add Task sheet. This function will take the current build context. The sheet that slides in on click of the button is called bottom sheet and is created by using showModalBottomSheet widget. It takes the current context in the context param, takes the widget that we want to show in the builder param. The builder param also returns the context to the sheet, we don’t need it so we can use underscore there.
  12. Inside the builder function, we want a text saying ‘Add task’, two textfields to enter name and description and two buttons beside each other like we did for add category dialog.
  13. First, we create a column and set its crossAxisAlignment property to CrossAxisAlignment.start this will align all the elements of the column to the left of it.
  14. Now, in the children array, we add a text heading ‘Add Task’. We want to align it to center, so we wrap it in the text widget. We also want to hightlight the text, to do it we use style property. This is how the complete text widget looks:

14. We would also want some space in between the heading and textfield. So, to add it we use the Sizedbox widget, it basically creates an empty space of specified height or width. So, we add this line SizedBox(height: 8), below the heading widget.

15. Now, we add two textfield widgets with controller property which will take two textfieldediting controllers and a decoration property. This property will set a label on the field. The decoration property takes InputDecoration as parameter. In it, we set it’s labelText property as Name and Description. This is how it looks:

Textfield for adding new task

16. Next is adding Add and Cancel buttons. For that we embed two elevated buttons inside a row and add them to the columns’ children array. The functions of add and cancel button are same as that of the add category dialog buttons. Try to implement it by yourself!

17. Although we are done with the creation of sheet elements, but there is a problem. This sheet arrives from the bottom of the screen and so will the keyboard do, when we write data in text fields. So eventually, the keyboard will cover whole of our sheet. So, to fix this, do the following steps:

  • Wrap the column widget inside Padding widget and in it’s padding arguments give the values as follows: EdgeInsets.only(left: 10, right: 10, top: 10, bottom: MediaQuery.of(context).viewInsets.bottom + 10), this will set fixed spaces on left, right and top and a variable spacing in bottom as per the current view parameters of bottom of the screen.

Media Query is used to get the details about the context passed inside it.

  • Now, wrap this padding widget inside the SingleChildScrollView widget which will make the contents of the view scrollable.

We’re done with the task list screen as well.

Now, we are only left with the creation of favorite screen, now since this screen has almost the same design and similar functions that we have already discussed, so I will leave this as a task for you guys to test your learnings while trying to implement the screen.

Also, if you get stuck somewhere while doing the Try it yourself tasks in the tutorial or the final activity you can always refer to the complete project on github. Here is the link to the project:

Task Manager Github Project Link

Also, apart from what I have explained here, you’ll find extra things in the code which like setting theme for the app, setting app icon, showing splash screen, styling widgets (like changing colors, fonts), including images in your app, etc. I’ll try to create a separate article for that. For now, you can just ignore those properties and stick to default styling of the app.

My Experience

I’m majorly an iOS app developer, I’ve also got an opportunity to work on react native which is also a cross-platform language like Flutter, but I really liked how things are much tidier and easy to understand in a flutter. So, If you want to learn a cross-platform language, I really think flutter is definitely worth a try…

Please share your views about the article in comments, let’s grow together as a community 👫👭👫👭 and also give it some claps if it was helpful… ✨✨🎉🎊

--

--

Smriti Arora
Technology at Nineleaps

iOS developer, content creator, music enthusiast ✨✨💫💫