Flutter is a mobile application development framework designed for creating applications for both iOS and Android with a single codebase. It was created and is being developed by Google and the Flutter community. The first version of Flutter was released in May 2017 and the 1.0 version on December 4th, 2018. In the time of writing this article, the current stable version is 1.9.
Flutter applications are written in Dart programming language. For platform-specific code Flutter provides support for both Kotlin and Swift.
In this article, I’ll be covering the process of creating mobile applications with the Flutter framework, using a demo project as an example. The demo project is a simple, yet versatile application that demonstrates one way of building Flutter applications.
In the interest of brevity, we will skip over much of code-specific details. However, the entire source code for the demo project is available on Github for reference. The main purpose of this article is to provide the reader with an overview of application development using Flutter. Hence, we will also refrain from thorough comparison of cross-platform technologies (interested readers can find a good article on the topic here).
Like in every cross-platform mobile technology, having a single code base can save the company’s resources and time by not requiring developers to write and maintain two separate versions (iOS and Android) of an application. From a customer’s point of view, this correlates to reduced expenses in making the product.
Flutter could also be a fast way of demonstrating a proof of concept or developing MVP, even if the finalized production version is eventually built using more traditional, native technologies. In the future, Flutter will also support web-frontend development.
Finally, the user interface of Fuchsia OS, a new operating system currently being developed by Google is built with Flutter SDK, so it is likely that this will be one of the technologies for building applications for Fuchsia, although it is hard to find information about it because of the lack of documentation.
The demo project
The demo project used throughout the remainder of this article is an application that fetches weather data of Mars from NASA’s Insight Weather Service API. This is a simple, yet sophisticated enough application to demonstrate general architecture and common functionality of mobile applications written using Flutter.
Tools and technologies
In order to develop Flutter applications, you will need a development environment. The demo project was created using Android Studio as it offers the best support for Dart and Flutter. However, VS Code with the Flutter extension works fine as well.
The Architecture of Flutter Applications
The most popular way of building Flutter application is using the BloC (Business Logic Component) design pattern. For a more in-depth explanation into BloC, please see here.
Briefly, the BloC pattern allows the exposition of data from a data layer to the UI in a reactive way using data streams. Similarly, events from the UI layer are delegated back to the data layer. In our demo application, the design pattern looks like this:
Below we describe the individual components of the demo application. Each component corresponds to a code file on Github. Moving forward in the text, it is important to make the distinction between BloC (the design pattern) and bloc (the component).
weather_page – This is the view part of the application, responsible for displaying data the bloc provides through streams. Since the demo application has no user interactions implemented to it, we only have to worry about the incoming data streams. If we’d like to implement a feature where, for example, the user is able to refresh the weather data, we could send the refresh event to the data layer through the same bloc.
mars_bloc – The business logic component. Provides data as streams to the view and handles user input events accordingly. In this particular bloc the data gets fetched from the repository and added to the data stream in the constructor as soon as the bloc is initialized. Also, to prevent memory leaks, it is important to close the streams when the widget gets disposed. This can be done by creating a dispose method to the bloc class and calling it from the widget’s dispose method.
mars_repository – The single source of data. The repository is responsible for providing data that the upper layers of the application need. It gets a JSON response from the NasaApi andconverts the JSON string into a Dart object. The repository can also cache the current data to a local database so the user would get the most recent data if the network is not available or if the API doesn’t have valid sensor data to provide. However, the caching is not implemented in this demo project.
nasa_api – A simple class with a static method of fetching the report from the Nasa API.
Building the user interface
In Flutter, the user interface consists of widgets. Everything is a widget and the UI is built by nesting these widgets. Some of the most basic and commonly used widgets that the framework provides are for example, text, buttons, rows, columns and containers. Basic UI development in Flutter consists of creating custom widgets with the help of these provided widgets according to the desired design.
Widgets in Flutter can be either stateful or stateless. The main difference with these two types of widgets is that the stateless widget is immutable and is only built once when the instance of this widget is created. The stateful widget on the other hand is mutable and will be rebuilt every time its state changes.
As a top-level widget, Flutter provides convenience widgets for both Material Design and Cupertino Design. These widgets provide functionality and UI components common to these design systems.However, if neither of these design systems are used in the applications, it is also possible to use the WidgetsApp widget as a top-level widget, which just provides, according to the official documentation, commonly required widgets for an application.
Styling widgets in Flutter
Colors, shapes and font styles can be applied to individual widgets, but to provide consistency and reusability, it is recommended to use theme data throughout the whole application (see theme data in Github as the parameter in MaterialApp widget).
Running the app
The entry point of the application is the main.dart file. The main() method calls the runApp() method provided by the framework which gets the most upper level widget as parameter. In this case it is the WeatherApp widget.
Building a release version
Building a signed release version for Google Play can be done in two ways: from command line or using Android Studio. The process is the same as building a native Android application. Application packages can be uploaded to Google Play in either APK format or as an App Bundle, which is the recommended way.
If you want to do this process from the command line, you can follow this guide. If you want to create the release file as APK instead of the recommended App Bundle, follow the guide on the link above and make sure you use the –split-per-abi flag so your users won’t have to download a large APK that contains binaries for device architectures their device doesn’t use.
For guide on how to create a release for iOS, please see this document.
In conclusion, Flutter is a good framework to consider in mobile application development. With all the convenience widgets and support for both Material and Cupertino design it is easy to create a good-looking user interface fast.
One of the most helpful features of Flutter that speeds up the development process is the stateful hot reload of the application which allows (in most cases) the developer to see the changes in code immediately in the running application without actually restarting the app and losing its state.