I find any technology a tool to create wonderful things. That was one of the reasons why I gave Flutter a chance. To be honest it shook my world in a very pleasant way. One of the key benefits that I, as an Android engineer first noticed is the immediate feedback loop. If every talented and curious developer out there develops the things at the moment a creative energy flows, how many wonderful features, products or concepts can happen in a span of one Android clean-build?
However, the least of my intentions with this article is to give Android a bad name. Circumstances under which our codebase is built and developed should not represent reality. If Android fits the use case and you manage to make it work, then it works. The same applies to Flutter.
N26 Cards feature walkthrough
I have been working at N26 for more than 2 years now and spent most of the time working with the Cards team. It had been a wonderful ride, both as a learning process and giving the users true value. So I kinda know the use-case I feel confident to implement it with Flutter.
N26 Cards Settings consists of a list of Cards in a horizontal list and actions that can be performed over each of these cards. The actions can be entry points to other flows or switches representing enablement of a feature.
My account’s Cards Settings
When the user goes to this screen they are presented with a loading indicator under the toolbar, until the cards and the actions are properly shown. Swiping through cards items changes the views in the lower half of the screen. These views are the actions that can be performed over the currently selected card. Lock action would perform an operation that will lock the selected card. The ATM Withdrawals toggle will enable or disable the possibility to withdraw money from ATMs for the selected card, etc.
The actions not only enable users and money flow but also provide a specific level of security for the users.
Cards View States
The cards list, as a single element is the core of the use case. Without it, nothing else can exist. If we describe this view in separate states, we will have:
- Loading: presented under the toolbar in a form of an indeterminate progress indicator, applicable to all operations that the user performs (loading of cards, loading of settings, change of a setting, etc.)
- Success: presented as a list of cards, a list of actions, and a list of toggles.
- Fatal: a fatal error that shows a view with information that something terrible đ± happened trying to render the use-case.
- NonFatal: a non-blocking error that shows in the form of a snack-bar and does not block the rest of the use-case. It usually is an atomic operation that failed to execute (like toggling some of the Cards’ preferences)
Small side note on the view states:
What the View State is?
It is one particular moment of the life of the view. More in-depth, the View state is a singular point in time, representing data over something that we can perceive and experience, like for example the screen. So if you observe all of the states when you spread the view from 0 to infinity, in each of those moments it will represent only one of the defined states. In our case that would be 4 (Loading, Success, Fatal, NonFatal).
What the View State is not?
A carrier of data. The view states do not transfer data from one tier to the other with the help of the View State objects. The View states are built, using that data. The state is not data bridge. Think of the View State more abstractly, not just in the form of a Class. It is represented by code because that is the language that we and the platform both know and can understand each other with.
Back to Cards states
For the purpose of the example, let’s first establish the UI so that we can easily go to more advanced topics after.
Let’s create the project
Make sure to first enable the web support for Flutter that makes things even funnier. You will have to be on top of master or beta channels for Flutter to be able to do that:
Then finally, we create a project scaffold.
# flutter create cards
Technical Notes
A bit about the libraries and concepts that will be used in this example.
- for the architectural design, I will use Flutter Bloc
- to support the logic, I will use RxDart
I personally am against 3rd party architectural frameworks, but that is in a perfect world. Each architecture should fit the requirements of the technical use-case and the more complex the project becomes it’ll be more difficult to depend on something that needs to be continuously maintained. However, in reality, each architectural approach in use is a framework that somebody else came up with. So for this article, we will use the Bloc pattern with Flutter Bloc, a library by Felix Angelov.
What is Bloc?
It is a concept that helps separate the presentational and business logic. My attempt to describe it is:
event driven domain abstraction whose outcome are all the states for a given View, based on all the events that can happen on that view.
Or, simpler:
everything that the user can do on one view, will cause a change under the rules defined in this Bloc.
Think of Bloc as a State Machine if you will. Events are propagated from the view, they are intercepted in an event handler, an operation is performed, the result is passed to the view.
In the third part of this series we’ll see an actual code as example, and explain it easier.
RxDart
For mapping the business and presentational logic in reactive and convenient way, I will be using RxDart.
RxDart is one amazing library and not just because it’s ReactiveX, but also because it operates over an existing API from Dart, the Stream API, mainly by extending its functionalities and operators set with extension functions. Also, it goes very well with the stateful design of Fluter. It flows.
You can read more about implementing a reactive architecture, based on Bloc in this article. It is my personal favorite and material from which I originally learned a lot. Also, read everything else written by Didier Boelens on that blog. It’s golden.
Immutable Types and Dart
Bloc helps us keep a reference to the current state so that we can copy and change some of its properties. So how can we copy properties of objects in Dart? Technically, we cannot.
In the beginning, I found the lack of Algebraic Data Types and Data classes support in Dart a bit difficult. You are dealing with statefulness on so many levels, yet you do not have an easy way to create immutable data. How can I have a data object I can deep copy? What about sealed classes? This might be a deal-breaker for many that are hesitant whether they should start learning Flutter and Dart or not, but one thing I’d like to say here is: there is a way to make this work. It can be hacked or you can use third-party libraries. Here are few I want to mention:
Dart is an ongoing and alive initiative and only recently we had the null safety introduced in tech review. Seeing Dart outside the box will bring more benefits and knowledge.
There are few workarounds that come either as library or hack, but we’ll keep it basic. We can kickoff this project using one very cool feature of Dart, factory constructors. Even though a small feature, it is one of my favorite in Dart as it’s contributing a lot in the cleanness and readability of the code. Also, it’s very useful.
Factory Constructors
Factory constructors allow us to create and initialize objects with control on how these objects are created (new instances, objects from memory or cache, etc). In our case we will use them for parametrizing the construction of objects for the state we want.
For example, for our Loading state we can have something like this:
It is not the use case for which the factory constructors are meant, but it solves our problem of sub-typing. We will see later on how these constructors will be used in the Bloc and how the actual state will be checked in the View.
Looks like we have pretty much what we need. We defined a set of states that we want to implement for our feature, we selected a basic technical stack and described some potential pitfalls on the way through.
In the next article, we will see actual code that will take us from those handwritten drawings, to something that is very close from UI perspective to N26's Cards Settings. My ultimate goal with these series would be to come to something as close as possible to the live version of Cards Settings, all written in Flutter for 3 platforms, Android, iOS and Web.
Here is the repository that will hold the final code by the end of the series.
Thanks for reading so far.
đ Feel free to reach out in the comments or on Twitter !
Thanks to Mustafa Berkay Mutlu and Fábio Carballo.