Build a note-taking app with Flutter + Firebase — Part II

Implements a plain-text editor with reversible actions & Hero transitions

Yingxin Wu
Mar 7, 2020 · 5 min read
Flutter Keep note editor screenshot
Flutter Keep note editor screenshot

Nice to have you in this second part of the series of . If you haven’t read the previous article, please find it here.

In part I, we’ve built the first screen for the notebook app, . In this article, we’re going to create a note editor with reversible operations supported, and explore the magical animations.

The note editor

There’re many types of notes in Google Keep, including plain-text notes, audio notes, and checklists, with optional image attachments. However, in this example, we’ll focus on the plain-text editor to keep things simple.

The following is a preview of what we’re going to build:

Note editor preview
Note editor preview

In essence, it composites of two text fields, one for the title, another for the plain-text content. Plus, a top AppBar and a ModalBottomSheet provides actions to update either the state or the color of the note.

So let’s start with a StatefulWidget:

note_editor.dart

In the editor state, we keep a copy of the original note (could be an empty one) to check whether the editor is dirty.

And don’t forget to make it accessible from the HomeScreen:

home_screen.dart

Ok, go on building the widget:

editor_state.dart

We’re going to save the note to FireStore before the editor is closed, by using a WillPopScope widget, which fires the onWillPop callback right before the current screen is being dismissed.

NoteEditor#onWillPop

Introducing a ChangeNotifierProvider is to provide the Note object being edited to descendant widgets to keep them aligned with the latest state. For example, the editor should update the background color when the user picks a different one in the ColorPicker widget.

To use ChangeNotifierProvider, the Note class has to extend the ChangeNotifier, and to notifyListeners whenever its state has changed:

note.dart

We’ll see how it works.

Actions like archiving and picking a theme color are organized in a bottom sheet.

One thing that may be confusing is when we showModalBottomSheet, we have to provide the note object again, or the descendant widgets (of the bottom sheet) won’t be able to retrieve it via Consumer or Provider.of. (It’s a different widget tree from the editor body)

NoteEditor#showModalBottomSheet

To understand how a ChangeNotifierProvider works, we take the color picker as an example.

What does the LinearColorPicker do? It renders a horizontal list of available tints for a note:

color_picker.dart

When one of the tints is selected, the picker updates the color property of the note, and that is it.

An ancestor widget (of the ColorPicker) we’ve built previously, which watching the note, gets notified and then refreshes the screen so that we can see the whole editor is tinted with the color we pick.

editor state sync demo
Editor state synchronization

The other actions, such as deletion and archiving, share the same mechanism.

Reversible operations

We can now edit a note by updating the properties including the state. But how about the UX? What if users delete a note by accident?

For dangerous operations like deleting or archiving, a SnackBar could be used to provide a reverse action, e.g., restoring or unarchiving, in addition to a prompt message.

To implement reversible operations more cleanly, we’re going to apply the .

First, we define the command interface, which is responsible for applying an action to a note.

note_command.dart

For this notebook app, actions considered reversible are all about mutating the state of a note. So only one concrete command is needed. However, nothing prevents you from extending it to other situations.

note_state_command.dart

Next, we produce and consume commands, for example:

Using commands

Done, we’ve made the dangerous operations reversible!

Hero transition

Now we have a working note editor, let’s take a step further, by adding a beautiful transition animation between the HomeScreen and the NoteEditor.

We can see that a note item grows to the size of the entire screen, from where it located in the grid. In Flutter, that’s called a Hero Animation.

The usage is simple. First, we wrap the note item in the grid or list with a Hero widget:

Hero note item

Then wrap the editor widget too:

Hero note editor

In the above snippets, the two tags passed to the Hero widgets must be identical.

And the DefaultTextStyle widgets are applied to avoid the big underlined text during screen transition on the iOS platform.

Now we can leave the rest to the Flutter framework.

What noticeable is that the standard screen transition animation is platform-specific. You could make a custom transition as you need, but it is beyond the scope of this article. Please refer to this cookbook.

Tips

In an example app, we don’t bother to apply patterns like BLOC. But there are still ways to keep the code clean and avoid boilerplates.

For example, we can move the reversible operation handling procedure to a stand-alone , to make a cleaner separation between UI and logic, and also make it reusable.

command_handler.dart

Whenever you need to handle commands, just mix it in:

Mixin CommandHandler

In addition, with Dart SDK 2.7.0 or later, we can leverage to do things magical.

We can augment the Note model with FireStore related functionalities:

note_store.dart

Which makes persisting a note as easy as a method call:

Using note extensions

We can even add properties and methods to an enumeration, which is impossible in the declaration of enumerated types:

Extensions on an enum

That saves us a lot of repeated code!

Wrapping it up, we’ve delivered a working plain-text note editor in this iteration. We’ve even added features like reversible actions and Hero transitions. Please find the complete code example in this GitHub repo.

In the next part, I’d like to introduce how to query different subsets of notes from FireStore, and the issue of composite indexes.

Thank you for reading! 🙌

Flutter Community

Articles and Stories from the Flutter Community

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store