Building a cross-platform todo mobile app using Flutter: Pt. 2
Tags: flutter, dart, mobile
Published: 2018-10-30 15:16:29

---
title: Building a cross-platform todo mobile app using Flutter: Pt. 2
published: true
description: 
tags: Flutter, Dart, Mobile
---


![Photo by Randall Ruiz on Unsplash](https://cdn-images-1.medium.com/max/2000/1*w7Zz6iKz6BKWrUaYwYcI8Q.jpeg)
<figcaption>Photo by Randall Ruiz on Unsplash</figcaption>

In the [first part of this series (which you should totally take a look at)](https://dev.to/theoneguilherme/building-a-cross-platform-todo-mobile-app-using-flutter-5e3m), we set up our workspace to build apps with Flutter. In this part, we will take a more in-depth tour on how we can build stuff with it, and even create ourselves a pretty basic to-do app.

As you may remember, we were looking at the entry-point of every Dart application, the `main.dart` file. This file builds a simple counter app with a button that increments a variable, which then gets printed on the screen. That’s pretty useless, isn’t it? Let’s build something better. Let’s build a to-do app.

Before starting, I think we should delete the code from the example app I mentioned above and start from scratch. To do that, make your `main.dart` file look like this one:

{% gist https://gist.github.com/guivazcabral/a8cc92d14afb8c10f1ee289b5301e76b %}

There are some things you should focus on:

* `void main() => runApp(new MyApp());` is the entry-point of the application

* the `MyApp` class, that holds the root of the widget tree

* `MaterialApp()` is a widget provided by the library imported in the first line of the file. It builds a Material Design app.

* the `HomePage` class, a widget that has a State instance of the class `_HomePageState`, which will return more than a `Container()` soon enough.

There are lots of details about this, and we’ll cover them as we go.

To-do lists have to-do items, right? So we need to describe Items using a simple class. To do so, we’ll make a folder called `models` and inside we’ll create a file called `item.dart` that looks like this:

{% gist https://gist.github.com/guivazcabral/d5bc4f0347a9e96d50c814ebf0cef368 %}

Now that we have the model, let’s work on presenting our tasks by using some widgets! By importing Flutter’s Material library, we have access to a bunch of different pre-designed widgets to build our interface.

We need some to-do items to display. First we need to import the Item model we created earlier:

    import 'package:todo/models/item.dart';

And then we place our tasks inside our `_HomePageState` class, like this:

    class _HomePageState extends State<HomePage> {

      List<Item> items = [
        Item('Take a shower'),
        Item('Go shopping'),
        Item('Feed the cat'),
      ];

      ...

Now that we have items to show, let’s grow our widget tree to display them by changing our build method to this:

    @override
    Widget build(BuildContext context) {
      return new Scaffold(
        appBar: new AppBar(
          title: new Text('Awesome Todo'),
        ),
        body: new Container(
          child: new ListView.builder(
            itemCount: items.length,
            itemBuilder: (context, index) {
              return ListTile(
                title: Text(
                  '${items[index].title}',
                ),
                trailing: new IconButton(
                  icon: new Icon(Icons.delete),
                  onPressed: null,
                ),
              );
            },
          ),
        ),
      );
    }

If you’re using Visual Studio Code or IntelliJ IDEA, and have already installed the respective Flutter plugin, you can start the app from the editor by pressing F5 on VS Code or clicking the green play button on IntelliJ IDEA (you also have *hot-reload-on-save* awesomeness). Run the app, and your device or simulator should look like the image below.

![Ew, who can’t remember to take a shower?](https://cdn-images-1.medium.com/max/2000/1*5fc_9fzc9VB2yNRD1WPOOA.png)
<figcaption>Ew, who can’t remember to take a shower?</figcaption>

Let’s go over the widgets we used. The `Scaffold` is the basic structure for our app. It holds, among other things, the `AppBar` which we see on the top, and a `Container` as our body. It could also hold a drawer menu or other things. The `Container` on the body is the parent of a `ListView.builder()`. We could have just used `ListView(children: items)` and everything would be fine. I chose to go with `ListView.builder()` because this way it *automagically* renders its children (`ListTile` in this case) as they are scrolled through, similar to an infinite scroll.

We’ll need to add to-do items to our awesome list. Let’s start by adding a `FloatingActionButton` to our app! We’ll add it to our `Scaffold`, and this should be the result:

    @override
    Widget build(BuildContext context) {
      return new Scaffold(
        appBar: new AppBar(
          title: new Text('Awesome Todo'),
        ),
        body: new Container(
          child: new ListView.builder(
            itemCount: items.length,
            itemBuilder: (context, index) {
              return ListTile(
                title: Text(
                  '${items[index].title}',
                ),
                trailing: new IconButton(
                  icon: new Icon(Icons.delete),
                  onPressed: null,
                ),
              );
            },
          ),
        ),
        floatingActionButton: new FloatingActionButton(
          onPressed: null,
          tooltip: 'Increment',
          child: new Icon(Icons.add),
        ),
      );
    }

![Look at it, so pretty.](https://cdn-images-1.medium.com/max/2000/1*XTQpctyhofeYyxalAVoP2A.png)
<figcaption>Look at it, so pretty.</figcaption>

And now we have a great Floating Action Button on our app!

It is pretty, but dumb: right now, the button does nothing. Neither do the trash cans on the items. Let’s create the methods that will handle those interactions and update our `FloatingActionButton` and `ListTile` widgets.

    _onAddItemPressed() {
      
    }
    
    _onDeleteItem(item) {
      
    }

    floatingActionButton: new FloatingActionButton(
      onPressed: () {
        _onAddItemPressed();
      },
      tooltip: 'Increment',
      child: new Icon(Icons.add),
    ),

    ...

    trailing: new IconButton(
      icon: new Icon(Icons.delete),
      onPressed: () {
        _onDeleteItem(index);
      },
    ),

To delete the Item from the list, we’ll just have to add this to the method we created above:

    _onDeleteItem(item) {
      items.removeAt(item);
      setState(() {});
    }

To add new items, I wanted to make things prettier and a little more fun. We’ll have a drawer coming from the bottom of the screen. That drawer will have a `TextField` that the user can fill and submit a task to the list. To do this, we are going to use a `BottomSheet`, more specifially, the `Persistant` kind. `PersistantBottomSheets` are context-aware, which means that opening them counts as navigating to another route of our app and that can be closed by pressing back in the app or in the device¹ (if running on an Android phone). To do so, we’ll need to set a key on our Scaffold widget. First, we’ll create it:

    final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();

Set it on our Scaffold widget:

    @override
    Widget build(BuildContext context) {
      return new Scaffold(
        key: _scaffoldKey,
        ...

and now we can use it to open the `PersistantBottomSheet`

    _onAddItemPressed() {
      _scaffoldKey.currentState.showBottomSheet<Null>((BuildContext context) {
        return new Container(
          decoration:
              new BoxDecoration(color: Colors.blueGrey),
          child: new Padding(
            padding: const EdgeInsets.fromLTRB(32.0, 50.0, 32.0, 32.0),
          ),
        );
      });
    }

This will show a sliding drawer coming from the bottom, with a `Container`, that has styles from a `BoxDecoration` widget, and has a `Padding` widget as a child (remember when I said that everything is a widget?).

If you go to your device and hit the Floating Button, you’ll see it rise to a small grey box to the left. That’s our `Padding` widget, being empty. Nothing to worry about, as we’re going to insert our `TextField` there. To achieve this, we’ll need a `TextEditingController` to handle our `TextField` and we’ll set it right after our `_scaffoldKey` :

    final TextEditingController _textEditingController =
    new TextEditingController();

and give our `Padding` widget a child.

    child: new Padding(
      padding: const EdgeInsets.fromLTRB(32.0, 50.0, 32.0, 32.0),
      child: new TextField(
        controller: _textEditingController,
        decoration: InputDecoration(
          hintText: 'Please enter a task',
        ),
        onSubmitted: _onSubmit,
      ),
    ),

Right now it won’t build because we are using the _onSubmit function on the `TextField` and we haven’t created it yet. It’s pretty similar to our _onDeleteItem function:

    _onSubmit(String s) {
      if (s.isNotEmpty) {
        items.add(Item(s));
        _textEditingController.clear();
        setState(() {});
      }
    }

Try it, you should now be able to add new tasks and delete old ones.

![Note to self: make more and better GIFs next time.](https://cdn-images-1.medium.com/max/2000/1*m6v8_XYtE43BrGmAR2lRug.gif)
<figcaption>Note to self: make more and better GIFs next time.</figcaption>

And that’s it! With 100 lines of code we built a cross platform to-do app. You can check the whole code [here](https://github.com/guivazcabral/flutter_todo).

The next step is making the data persistent by storing the items in the device’s storage. Stay tuned for the third part of this series!

*¹ You can also close the `PersistantModalSheet` by dragging it down.*

*I love building products and I found my place to do so at [Runtime Revolution](http://www.runtime-revolution.com/). If you are interested in who we are and what we do, make sure to reach out!*