Introduction to Flutter

For anyone who hasn’t yet heard about Flutter, Flutter is an open source framework created by Google that allows you to build native, cross platform applications targeting every major platform, Android, iOS, Web, Windows, Linux and macOS with a single codebase in the Dart programming language.

flutter.dev

Flutter provides an extensive library of UI widgets and gives you the freedom to customize the UI to the pixel level. The framework creates lightweight and performant apps, rendered at 60 fps that look and feel great. Flutter is actively developed by Google and there is a big community of developers maintaining packages for pretty much anything you might want to do with your apps. There are great tools and extensions for all major IDEs. Personally, I’ve been using Flutter for more than 2 years and I find it very easy to work with and saw myself going from a POC to a full-fledged app without breaking a sweat.

Hello World

There are tons of good guides out there to get you started with Flutter so I am not going to overanalyze the basics here. Instead, I will focus on how we can architect our application to achieve a clean design by applying the MVVM pattern and using Provider, one of the most popular state management packages for Flutter. So let’s start with a basic Hello World application to showcase some of the framework’s mechanics and we can expand from there. The Flutter 101 example in most of the Getting Started guides is the Counter application. I am going to use the same example for the purposes of this blog post.

A few takeaways from this example:

  • Widgets are the building blocks of Flutter. The classes that we created in the above example inherit from the framework’s built-in widget classes
  • We declare the user interface as a hierarchy of nested widgets - Our widgets override the build method which creates the widget composition tree to be rendered
  • The user interface is updated in response to events and we use the setState() method to trigger a refresh on the UI

If you develop something simple like the above and you only need to manage the state of one screen, chances are, you don’t need to care much about the architecture. But in most real world scenarios, you will probably be dealing with multiple screens, transitions between them along with some complicated business logic. If you don’t cater for a clean architecture, you will probably end up having headaches maintaining a messy codebase in the longterm.

MVVM

MVVM (Model-View-ViewModel) is a well established architectural pattern by Microsoft for front end applications and can be easily applied on Flutter projects. By implementing this pattern, we separate our application code in 3 layers. The View which is the UI of the application, the Model which represents the domain objects and the ViewModel which is a mediator between the two and responsible of handling user input on the UI to update the data in the Model layer. The purpose of implementing this pattern is to remove any logic from the UI layer and leverage data binding to propagate changes between the UI and the model objects. This design produces a clean architecture, separating the code in these 3 well distinguished layers resulting in an easy to maintain and cover with tests codebase. It plays particularly well with one popular state management library for Flutter, the Provider package. Let’s see how we can structure our codebase to implement state management with Provider and apply the MVVM pattern.

This is the app scaffolding code. In the above code we use the Provider package to declare one Provider object for our ViewModel. Provider is a wrapper around the InheritedWidget base class. InheritedWidget can be used to propagate information down the widget tree and can be particularly useful to share dependencies between components of our application. Provider package removes a lot of the boilerplate code and simplifies the process of using InheritedWidget. It offers a range of different Provider types that fit well in different scenarios. In our example we use the basic ChangeNotifierProvider. This Provider type supports creating objects that derive from Flutter’s ChangeNotifier base class and thus can send change notifications to their listeners. This is the perfect candidate for our ViewModel classes which are responsible of sending notifications to the UI widgets to trigger the rebuilds when the underlying model objects are updated.

On the UI side, the main point to observe is the usage of Provider package to access our dependencies. Using the provider resembles of the Service Locator pattern. We call the Provider.of() method to get an instance of a dependency. The call is inexpensive, O(1), hence can be invoked with peace of mind in many parts of the code when we are dealing with complex user interfaces. In this example we use it to get an instance of our ViewModel class and assign the incrementCounter() method to the onPressed handler in the floatingActionButton widget. We also bind the counter property of the ViewModel to the Text widget to display the number of times we hit the button. All this is happening inside the build method of our widget. So when the ViewModel updates the underlying Model object, it sends a notification which the widget will receive and it will force a rebuild.

The ViewModel in our example is responsible of handling user input which in this case is clicking/tapping the button to increase the counter. This takes place in the incrementCounter() method. It first updates the Model object’s value and then invokes notifyListeners() to send the notification to the UI and trigger a rebuild.

The Model object is a just a simple object storing an int value. Coming from a .NET background I feel very comfortable applying this pattern on Flutter projects. I imagine it might not be everyone’s cup of tea but I believe by adopting this design you end up with a clean, easily extendable and testable application and it fits very well with the tools provided by the Provider package. Or even the built-in mechanisms of Flutter should you choose to go more low-level. I hope you enjoyed the read and if you still haven’t given Flutter a look I encourage you to give it a try.

Akis Papaditsas (Monex Insight - Developer)