Flutter 2
Flutter 2
Supervisors Candidate
Prof. Giovanni MALNATI
Daniele PALUMBO
Ing. Fabio FERRERO
July 2021
Abstract
In the mobile field there are mainly two operating systems, Android from Google
and iOS from Apple. To release applications on both it is necessary to develop
separate codes in the respective languages of the two operating systems. Over the
years, many companies created frameworks for cross-platform development of mobile
applications that allow the same code to be used on multiple operating systems.
Unfortunately, the advantages of having a single codebase are often balanced by
the limitations that a framework introduces mainly regarding performance and
integration with the mobile operating system. For this reason it is necessary to
analyze a new framework when it is released to evaluate its effectiveness and in
this thesis I analyze the Flutter framework.
Flutter is a framework released by Google in 2018 that experienced a rapid
growth in popularity in recent years. Flutter proposes a declarative approach to UI
programming and promises both a performance equivalent to the native execution,
and graphics which are indistinguishable from native ones. Flutter is written in
Dart and is a cross-platform framework based on a compiled approach. It uses
a custom engine to render each element on the screen within a 2D Skia canvas.
Flutter’s entire rendering pipeline is implemented directly by the framework and
is independent of the native components of the underlying operating system. It
therefore differs from other cross-platform frameworks such as React Native or Ionic.
Dart is a procedural language with functional aspects released in 2011. Furthermore,
Dart is a language that supports both compiled and interpreted execution. This
allows Flutter to have a fast and dynamic development environment, offering
features such as the Hot reload. Subsequently, the code can be compiled into
machine code and executed without the overhead of interpretation.
In this work I study Flutter focusing on the needs of an enterprise work envi-
ronment, where it is necessary to rely on tested and effective systems. My work
begins by analyzing what is the state of the art in cross-platform development and
what approaches exist to create a single codebase. I also include in the analysis
the other best known frameworks on the market. Next, I focus my thesis on the
technical aspect of Flutter, analyzing the techniques for building the UI and the
mechanisms for managing data and the internal state. In addition, I expand my
work analyzing Dart and the whole development environment.
One of the main objectives of this thesis is to analyze the execution performance
of Dart and the Flutter framework in different contexts. The results obtained show
that Flutter performance values are two to three times lower than native results.
The deterioration in performance remains constant with increasing complexity.
However, in very simple cases the Flutter results are identical to native ones. On
the other hand, compared to React Native, Flutter achieves better results both
in resource usage and average FPS (Frames Per Second), making applications
written in Flutter effectively indistinguishable from native applications. Moreover,
the documentation of Dart and Flutter is complete and well detailed and the
developer community has grown rapidly thanks to the open-source approach. From
my analysis I obtained positive results on the effectiveness of Flutter as a valid
alternative to native development.
ii
i
Acknowledgements
Scrivo questi ringraziamenti l’ultima sera prima di consegnare questa tesi. Mi
sento che ho completato la prima parte del percorso che ho iniziato tanti anni fa.
Questi 3 anni a Torino sono stati incredibili e per questo voglio ringraziare tutte le
persone che mi sono state vicino. Sono davvero contento di aver preso la decisione
di partire. Tante cose sono cambiate nella mia vita e ho avuto la libertà di fare le
scelte che sentivo più giuste.
Voglio però con tutto il cuore dedicare questi ringraziamenti alla mia mamma
Cristina. Quando sono partito per Torino mamma aveva già scoperto di avere il
cancro. Nonostante questo, dal primo momento ha appoggiato la mia scelta di
partire. Mi ha sempre incentivato a dare il meglio e non vedeva l’ora di vedermi
laureato. Mi diceva che era orgogliosa di me per come ero cresciuto, riferendosi al
me stesso del primo anno di ingegneria a Roma che l’università l’aveva mollata.
Fino a quando hai potuto sei stata accanto a me e mi hai dato forza e voglia di
fare. Sento di non aver disatteso le aspettative e ho fatto tesoro di tutto quello che
mi hai detto negli anni mamma. Oggi concludo questo percorso di studi e vorrei tu
fossi qui per vedermi e festeggiare con me.
Ringrazio il mio papà Biagio, Gino per gli amici, che insieme a mamma non
hanno fatto mancare mai nulla a me e mio fratello. Ci avete dato la possibilità di
studiare e di diventare gli adulti che vogliamo. Mi avete dato tanta fiducia quando
ne ho avuto bisogno. Ti voglio bene papà.
Ringrazio mio fratello Davide che è la persona più spensierata che conosco, ti
voglio bene perché sei sempre pronto a ridere e scherzare, o a parlare dei razzi per
Marte, o per cantare Shinzo So Sasayego. Ti voglio bene fratellino e sono sicuro
che arriverà anche per te il momento di festeggiare la laurea.
Ringrazio la mia Giulia che è stata con me nella parte più difficile di questo
percorso. Sei entrata nella mia vita come una compagna di corso e ora sei la
scoperta più bella che ho fatto qui a Torino. Devo ringraziarti sicuramente per
avermi supportato nei momenti peggiori, quando inizio a lamentarmi di tutto e
borbotto. Ma ti ringrazio anche per le possibilità che ci stiamo costruendo insieme
e che mi danno fiducia per il futuro.
Voglio ringraziare i miei amici di Roma che anche vivendo lontano mi hanno
ii
fatto sentire a casa ogni volta che stavo con loro, come se in realtà non fossi mai
partito. E’ anche grazie a voi che Ostia per me rimane un posto speciale.
Infine lascio un ringraziamento e un saluto a tutte le persone che hanno condiviso
con me questi anni e che, ognuno a modo suo, hanno contribuito a rendere speciale
la mia magistrale.
iii
Table of Contents
1 Introduction 1
6 Results 96
6.1 Flutter advantages and disadvantages . . . . . . . . . . . . . . . . . 96
6.2 Development experience . . . . . . . . . . . . . . . . . . . . . . . . 101
Bibliography 105
vi
List of Tables
4.1 Performance for the execution of the same Android application made
of a list of a thousand elements in Kotlin, Flutter and React Native. 64
4.2 Performance for the execution of the same iOS application made of
a list of a thousand elements in Swift, Flutter and React Native. . . 64
4.3 Performance for the execution of the same Android application made
of a grid of 200 animated images in Kotlin, Flutter and React Native. 66
4.4 Performance for the execution of the same iOS application made of
a grid of 200 animated images in Swift, Flutter and React Native. . 66
4.5 Table with CPU usage comparison obtained with several iterations
on Matilda Olsson’s test application made of a simple list page, a
Home page and a navigation bar. . . . . . . . . . . . . . . . . . . . 68
4.6 The table shows the distribution of programming languages in the
Ebay Motors team project. The code in Dart is shared between
Android and iOS applications. . . . . . . . . . . . . . . . . . . . . . 72
4.7 Performance comparison for the same development tasks between a
Flutter app and a React native app made by Gabriel Basilio Brito[19] 73
4.8 Performance comparison for the same development tasks between a
Flutter app and a React native app performed by the channel Smart
Code App[20] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
vii
List of Figures
2.1 The figure shows some noticeable graphical differences between the
most common components of the graphical interface in iOS and
Android. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.2 The figure shows the same version of Google’s Gmail application
on iOS - left - and on Android - right. The graphical differences
between the two apps are minimal with a trend towards Material
Design on both. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.3 The figure shows the same version of Facebook’s Instagram applica-
tion on iOS - left - and on Android - right. The graphical differences
between the two apps are minimal with a trend towards Apple
Human Interface on both. . . . . . . . . . . . . . . . . . . . . . . . 10
2.4 The graph represents the trend for last 5 years of world research
regarding the four main frameworks for the development of cross-
platform applications. . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.1 the graph shows the FPS values of the same app in Flutter and
React Native. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.2 CPU consumption comparison between the same application made
by a list of thousand elements written in Native, Flutter and React
Native . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
viii
4.3 Max memory usage comparison between the same application made
by a list of thousand elements written in Native, Flutter and React
Native . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.4 Average FPS comparison between the same Android and iOS ap-
plication made by a grid of 200 animated images written in Native,
Flutter and React Native . . . . . . . . . . . . . . . . . . . . . . . . 67
4.5 CPU consumption comparison between the same Android and iOS
application made by a grid of 200 animated images written in Native,
Flutter and React Native . . . . . . . . . . . . . . . . . . . . . . . . 67
4.6 Max memory usage comparison between the same Android and iOS
application made by a grid of 200 animated images written in Native,
Flutter and React Native . . . . . . . . . . . . . . . . . . . . . . . . 68
4.7 Graph with CPU usage comparison obtained with several iterations
on Matilda Olsson’s test application made of a simple list page, a
Home page and a navigation bar . . . . . . . . . . . . . . . . . . . . 69
4.8 Widget tree evolution of the stopwatch application through 3 steps.
The red boxes indicate stateful widgets, the white ones mean stateless
widgets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
4.9 Performance comparison between the iOS stopwatch and the Bizzotto
Flutter stopwatch application with each of the evolution steps of the
widget tree[16] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
4.10 Performance for the execution of the Borwein algorithm in Android
between Java, Kotlin, Flutter and React Native implementations. . 77
4.11 Performance for the execution of the Gauss-Legendre algorithm in
Android between Java, Kotlin, Flutter and React Native implemen-
tations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
4.12 Performance for the execution of the Borwein algorithm in iOS
between Objective-C, Swift, Flutter and React Native implementations. 78
4.13 Performance for the execution of the Gauss-Legendre algorithm in
iOS between Objective-C, Swift, Flutter and React Native imple-
mentations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
4.14 The graph shows the distribution of programming languages in the
Ebay Motors team project. The code in Dart is shared between
Android and iOS applications. . . . . . . . . . . . . . . . . . . . . . 79
4.15 The graph shows the results of the Ebay Motors internal team
survey on how fast programming was in Flutter compared to Native
programming. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
ix
5.1 The figure represents the mockup of the user page created by the
product team. This screen contains all the personal information that
can be used by the user. The central card represents the experience
that the user has achieved using the application. . . . . . . . . . . . 82
5.2 The figure represents the interface created by me following the design
of Figure 5.1. All the features present in the mockup have been
integrated, including internal navigation. . . . . . . . . . . . . . . 86
5.3 The figure shows the donut chart realized by the IrisCube Reply
development team. This is the vertical version of this chart. In
this version, the labels no not include the amount related to the
represented category. . . . . . . . . . . . . . . . . . . . . . . . . . 88
5.4 This is the horizontal version of the chart presented in Figure 5.3. In
this version, the labels include the amount related to the represented
category. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
5.5 The figure shows the interface of the account balance management.
Here is possible to see the graphic representation of the nack account.
The graph is scrollable and the user can select the date interval. . 89
5.6 The figure shows the graphical result of creating a donut chart. . . 91
5.7 The figure shows the graphical result of creating a donut chart within
label data in the horizontal view. . . . . . . . . . . . . . . . . . . . 92
5.8 The figure shows the graphical result of creating a donut chart within
label data in the horizontal view. . . . . . . . . . . . . . . . . . . . 94
x
Chapter 1
Introduction
In today’s highly disruptive and competitive mobile app development world, busi-
nesses would not risk missing their presence on both platform stores, Google Play
Store and the Apple App Store. However, going for native apps usually means a
budgeting problem, as native development involves multiple teams. This is why
cross-platform app development has emerged as a valid choice for mobile app devel-
opment: it aims to reduce costs without giving up the presence of an application
both on Android and iOS. Cross-platform framework are also commonly used in
mobile development, as they offer a wide range of advantages and simplifications
during development. Cross-Platform development - also known as multi-platform
software or platform-independent software - is based on the idea of writing the
code once and running it everywhere.
Although cross-platform development exists since a long time in desktop environ-
ments, in this project we are interested mainly in mobile platforms. In particular,
I take into consideration only two platforms, Android and iOS, which are by far
the most used mobile OS in the world, since all other alternatives failed to reach a
minimum market share. Indeed, in the development of a mobile app, the developers
have to write the code for two different platforms in order to actually cover 99% of
all active mobile devices worldwide.
There are significant differences among the various cross-platform frameworks
for mobile development both for the architecture and internal functioning and for
the programming languages and methodologies used. One of these cross-platform
frameworks is Flutter1 . Flutter is the solution proposed by Google in 2018 to
develop cross-platform applications. It supports the mobile worlds, therefore
Android and iOS, as well as the desktop and the web world. It is a very versatile
and powerful framework which places itself in an advantageous position compared
1
https://flutter.dev/
1
Introduction
programming style and its workflow. Furthermore, this chapter also analyzes
Flutter performance and scalability in depth. The last part presents a particular
Flutter feature that allows its integration into existing native applications, aiming
at optimizing the development of cross-platform features without having to sacrifice
what has already been developed. These feature is tested to understand if this
solution is really achievable and which are its consequences.
In chapter 5 I describe and implement two different real case scenarios in Flutter.
These implementations are compared to analogous interface natively developed
by IrisCube Reply S.r.l.2 in one of their mobile applications realized for a bank
company. The first scenario is a simple example and aims to emphasize the
versatility of Flutter graphics components. This example concerns the creation
of the private area for a user. This user area includes an interface for showing
personal data and another interface for editing them. The reference model comes
from the user area of the mobile application of one of the most important Italian
banks. The second case scenario is more complex and it aims to create an interface
for personal financial management. This implementation uses an articulated layout
which also includes animated graphs. Also in this case the starting model comes
from the same application. In this last case, it is important to consider that in the
native case a public library available on the network was used: this made necessary
the comparison between the availability of third-party libraries for Flutter against
native environments.
Finally, in order to offer a correct point of view on the effectiveness of Flutter, in
the chapter 6 I show the results achieved by combining the considerations obtained
from the analysis of the framework from chapters 3 and 4 and the results of its
usage in the development of real use cases. In the results of this thesis I highlight
the limitations and capabilities of Flutter and understand if it can be suitable for
enterprise environments, such as companies with a large development team. I also
evaluate Flutter performance both against native development, and against less
recent frameworks. This helps to understand if it offers a better solution than
the previous frameworks and if Flutter can really be a winning choice over native
development. At the end, I draw my conclusions and I suggest some enhancements
and future work for this project.
2
https://dart.dev/
3
Chapter 2
development for the two operating systems uses different languages and programs:
for Android the default language is Kotlin and the reference IDE is Android Studio,
for iOS instead there are Swift and XCode. Google and Apple fully support these
two environments, because in both cases they are in charge of developing and
maintaining both the programming language and the reference IDE. This is indeed
the main advantage of the native approach: Google and Apple guarantee developers
the most stable language and IDE for development, a complete and reliable SDK
for the mobile operating system and a large series of third-party libraries ready
for the development. use. For native development, there are two disadvantages in
particular: cost and consistency. To achieve the goal of distributing applications of
a high quality level, it is necessary for a company to have talented and capable
programmers available. The development team must be able both to create new
features and to maintain applications over time - OS version updates, development
SDK updates, and so on. Furthermore, even a design team is often needed to have
a functional and aesthetically pleasing interface.
Android and iOS are conceptually similar - both were mobile operating systems
born at the same time and with the same needs - but they differ in several key
aspects. These platforms differ not only in terms of what native applications
look like; they also differ in terms of the structure and flow. We need to keep
these differences in mind to provide the best user experience through the native
application design. Google and Apple over the years have developed their own
philosophy on how apps should be developed. For Google this resulted in the
creation of the Material Design1 , while for Apple the philosophy has become the
Human Interface2 . In practice, there are long lists of guidelines that Google and
Apple encourage to use. The primary objective is to obtain a sort of uniformity in
key operations for the applications on the store, such as internal navigation. For
end users it is a great advantage, because even between very different applications,
recurring patterns and behaviours can be found in common. In fact, continuing the
example of the internal app navigation, guidelines by Apple and Google recommend
to use platform-standard navigation controls whenever possible: page controls, tab
bars, segmented controls, table views, collection views, and split views. Users are
familiar with how these controls typically work on each platform, so if a developer
uses the standard controls, its users will intuitively know how to get around the
app.
Therefore the problem with the cost of the native development is what I have
just explained: a company needs deep experience in both operating systems. This
turns out into creating very distinct development teams that work according to the
1
https://material.io/design/
2
https://developer.apple.com/design/human-interface-guidelines/
5
State of the art in cross-platform frameworks
design and colors - for example, in Android an active switch has the main color
of the app, while in iOS it is always green by default. The icons, although
similar, differ in the thickness of the strokes and in the filling. The date
picker and the time picker are modals in both OS - screen above the current
screen - but they differ profoundly in structure and design. The differences
are omnipresent and not always just graphic: for example, the material design
makes great use of the fab button as an always accessible element for the
creation of new contents; iOS lacks a counterpart. Some examples of those
differences can be seen in Figure 2.1.
Figure 2.1: The figure shows some noticeable graphical differences between the
most common components of the graphical interface in iOS and Android.
It is clear that there are many differences between Android and iOS. Each
platform has its unique interactions. Good design is design that respects users
habits in each operating system. It is really important to keep in mind the differences
between platforms when designing a mobile application for both iOS and Android,
so that the final result will meet the expectations of users. If a company wants
each element in native applications to look the same across platforms, additional
development effort is required to develop custom view implementations - as iOS-like
controls on Android or viceversa.
However, consistency is not always an important goal for a company and pursuing
consistency across different platforms is not always an unsolvable problem. There
are examples that clearly show how consistency can be obtained also through native
development. In particular, the Gmail app, developed by Google - Figure 2.2, and
the Instagram app - Figure 2.3, developed by Facebook, are interesting. The first
follows the rules of Material design, while the second follows the Human interface.
In both cases, differences between Android and iOS versions are minimal.
The cross-platform approach aims to mitigate the flaws of native development.
Costs can be reduced by specializing the development team towards a single tech-
nology, and consistency can be easily achieved by using unique graphics components
for both platforms. Furthermore, a single development pipeline also has the effect of
speeding up development and decreasing time to market. However, if the benefits of
8
State of the art in cross-platform frameworks
Figure 2.2: The figure shows the same version of Google’s Gmail application on
iOS - left - and on Android - right. The graphical differences between the two apps
are minimal with a trend towards Material Design on both.
cross-platform were really that obvious and overwhelming ones, native development
would be less used than cross-platform frameworks. The reality instead shows the
opposite, with native development beating the cross-platform approach in all usage
statistics.
The goal of developing a single codebase for different OSs is not trivial and this
has held back cross-platform approach adoption and diffusion. In particular, the
task of developing a single codebase can be initially divided into three aspects: user
interface development, hardware feature implementation and maintainability over
time.
These three aspects are by far the most important because they represent the
common part in the development of any application. In particular, the first point
can be further divided into:
• In-app navigation;
• layout definition;
• UI construction;
• animations and transitions.
Hardware feature implementation instead concerns exclusively the framework
itself and the APIs it offers. Similarly, maintainability involves the framework
9
State of the art in cross-platform frameworks
Figure 2.3: The figure shows the same version of Facebook’s Instagram application
on iOS - left - and on Android - right. The graphical differences between the two
apps are minimal with a trend towards Apple Human Interface on both.
ability to stay stable and updated over time. The objective of the thesis becomes
to understand how Flutter manages the key aspects of the development of an
application and if it is able to do better than previous competitors. The analysis
begins in the next section with the introduction of the various approaches to cross-
platform development and the main frameworks that derive from them. After that,
the work will be focused mainly on Flutter itself and related to native development.
Over the past few years we have seen increasing standardization of browsers -
for example Chromium by Google is the base for many browsers - and increasing
support for accessing device functionalities. This made it possible for fully web
applications to both behave and appear as native applications installed via the
platform store. For example, a web app has now the possibilities to access features
such as localization and internal storage not needing a bridging system but only
using the features offered by the browser. Standardization has made it necessary
to compensate for the raw look and feel of a pure website to make the interface to
look more like a native application. This reason led Google in 2015 to coin the term
“Progressive Web App (PWA)” to describe web apps that take advantage of the
new features offered by modern browsers. PWAs improve traditional web apps with
so-called service workers (to allow for running code in a background thread), a web
app manifest (to provide metadata), off-line capabilities, and an installation-like
user experience. Google also provides several interface creation tools that are based
on the material theme. These tools allow to enhance the graphic aspect of a PWA
as well as provide it with more advanced behaviours than a normal website.
A PWA however remains a web app. It needs the browser to work and depends
on the functionalities that the browser provides. If a certain functionality is not
available through the browser or on a specific platform, a PWA remains limited
with no way around the problem. Also, the average user looks for an application
available in the platform’s official store, and getting a web app through the browser
is counterintuitive. Finally, the PWA concept is young and in some ways still
immature. For these reasons, the web app approach is often considered more of an
additional tool to increase a company’s presence in the mobile market rather than
a true cross-platform approach.
platforms has the consequence that the compiled approach is typically focused on a
few key aspects - for example the implementation of the business logic, leaving out
the graphic part to be built with classic tools. Flutter in this context is the most
recent and flexible solution because it provides a complete application development
tool, from business logic and user interface development, to the integration of all
native physical features. Flutter has as its primary objective to mitigate all the
typical limitations of cross-platform approaches.
The main difference between Flutter - or in general a framework based on the
compiled approach - and the interpreted approach is that Flutter does not render
any native components offered by the OS. Instead, internally all Flutter renderings
are done on a the Skia Graphics Engine. This works through painting on a 2D
Skia Canvas. The Flutter rendering engine is able to re-create the look and feel
of native user interfaces. An application created with Flutter must be compiled
in bytecode before being executed and this would greatly extend the development
time in favour of a faster execution time. However, Flutter introduces a great
novelty because it integrates a Dart VM. This virtual machine allows advanced
features in the development phase such as the hot reload. The final application
is always compiled AOT - ahead of time compilation - which eliminates the need
for any intermediate layer of interpretation and guarantees optimal performances
comparable to native ones.
The main disadvantage of using a compiled cross-platform framework is that
any code changes must be published through the official stores. The hybrid and
interpreted approaches offer the ability to push code changes using tools such as
Microsoft CodePush. However, even native apps suffer from this flaw, and both
Apple and Google offer solutions to quickly deploy hotfixes by streamlining the
approval process.
1. Ionic. This is an open-source SDK for hybrid mobile app development - based
on the Hybrid approach. The original version was released in 2013 by Drifty
and was built on top of AngularJS and Apache Cordova. However, the latest
release was re-built as a set of Web Components, allowing the user to choose
any user interface framework, as Angular, React or Vue. It also allows the use
14
State of the art in cross-platform frameworks
100
75
50
25
0
2017-01-01 2018-01-01 2019-01-01 2020-01-01 2021-01-01
Figure 2.4: The graph represents the trend for last 5 years of world research
regarding the four main frameworks for the development of cross-platform applica-
tions.
(a) Too many releases. The developers criticized the Drifty choice of
releasing too many versions of Ionic and announcing the release of Ionic
4 when Ionic 3 was still in its infancy. As a result, many developers felt
that Drifty developers intended to reinvent Ionic from scratch without
careful planning.
(b) Lack of documentation. Ionic’s documentation has always been deemed
poor and incomplete. The situation became unmanageable for developers
due to the many versions of Ionic supported at the same time which made
the documentation fragmented.
(c) Performance issue. The Cordova bridge necessary for the operation of
Ionic has much lower performance than the native code. In case of heavy
operations the limit of the bridge becomes even more evident.
(a) Confusion about Xamarin. There are several frameworks for Xamarin.
Previously there were Xamarin.iOS and Xamarin.Android, now wrapped
under Xamarin.Native. Then Xamarin.Forms was released in 2020 which
promoted an innovative approach to reusing UI code, but which was in-
compatible with Xamarin.Native and instead relied on Xamarin.Essential.
At the end of 2020, a new framework for cross-platform development by
16
State of the art in cross-platform frameworks
3. React Native.
This is an open-source cross-platform framework created by Facebook to
develop mobile applications. React Native was born thanks to the experience
gained with the development of ReactJS. React Native supports application
development for Android, iOS, MacOS, Windows, Web, UWP, Android TV and
TvOS and it combines the language and skills of ReactJS with the capabilities
of the native.
React Native works virtually identical to the React framework, except for how
React Native manipulates the DOM which is not via the Virtual DOM. React
Native DOM runs in a background process, which interprets the JavaScript
code directly on the end-device. Communication with the native platform are
via serialized data over an asynchronous bridge. This bridge is th foundation
of the React Native features and capabilities.
Native components are wrapped by React components in a way in which they
interact with native APIs using the declarative UI paradigm and JavaScript.
This allows Javascript developers to develop native app and can let existing
native teams work much faster. React Native styling has a similar syntax to
CSS, but it does not use HTML or CSS. Instead, JavaScript messages are used
to manipulate native views. Furthermore, React Native allows developers to
write native code inside React Native projects, which makes them even more
flexible.
As can be seen in Figure 2.4, the popularity of React Native has been grow-
ing in recent years. ReactJS is one of the most popular web development
frameworks and this has greatly influenced the popularity of React Native.
Many developers like React Native and Javascript. Additionally, Javascript is
establishing itself as a new industry standard globally, and React Native has
access to the world’s largest community and millions of libraries. However,
the growth of React Native has not been as great as Facebook hoped. Com-
pared to native development, React Native has lower usage rates and there
17
State of the art in cross-platform frameworks
are criticisms regarding its heaviness, long technical times to build code and
general lower performance than native.
On an industrial level, React Native is Flutter’s biggest competitor. Right
now, it is the only large cross-platform framework that is healthy and with
great growth opportunities. For this reason I will quote React Native many
times in this thesis as an example or comparison.
4. Flutter
Flutter is an open-source UI cross-platform framework developed by Google
released in 2018. Flutter allows to develop applications for Android, iOS, Web,
Windows, MacOS and Linux from a single codebase.
Flutter main focus during release presentation was to provide a way of being
able to render consistently at 120 frames per second. In 2021, version 2.0
was released, which was the first version to add official support for web-based
and desktop applications for Windows, macOS, and Linux and an improved
Add-to-App feature.
During development, Flutter apps run in a VM that offers stateful hot reload
of changes without needing a full recompile. For release, Flutter apps are
compiled directly to machine code, whether Intel x64 or ARM instructions,
or to JavaScript if targeting the web. The framework is open source, with a
permissive BSD license, and has a thriving ecosystem of third-party packages
that supplement the core library functionality.
Flutter’s success was immediate as also demonstrated in Figure 2.4. The
developer community has welcomed flutter for its potential. In the following
chapters of this thesis I will try to analyze all the main aspects of Flutter and
objectively evaluate its capabilities.
Besides the four frameworks I’ve shown above, there are many other frameworks
for cross-platform development. If a framework is not on this list it is for one of
this two reasons: either the framework is not specifically designed for creating cross-
platform applications, or the framework is old or not very widespread. Furthermore,
I think that not including further frameworks as comparisons does not affect the
quality of Flutter’s analysis. This is because I will be comparing Flutter primarily
to native development and React Native - currently the industry standard for
cross-platform mobile applications.
18
Chapter 3
configure a tree of objects. In a hierarchical way, some widgets are used to manage
a specialized tree of objects for layout, which becomes the container where manage
another tree of objects dedicated to composition - for example a color property of a
text or aspect ratio of a video source. Basically, Flutter is built around mechanisms
to efficiently walk complex trees, convert trees of objects into lower-level objects,
and propagate changes across these trees. In this context, a widget practically is
only a lightweight “blueprint”. The widget, to declare its user interface, needs to
overrides its the build() method, which is a function used to convert its own state
in UI code - this code is later used to actually draw the user interface on the Skia
canvas. To make a change in the UI, a widget triggers the build method on itself -
through the setState() for StatefulWidgets - and it constructs a new widget subtree
where the UI is now affected by the last change.
Listing 3.1: Simple widget declaration in Flutter. Available property for a widget
are defined in its constructor. More on that later.
1 r e t u r n WidgetA (
2 c o l o r : red ,
3 propertyX : valueX ,
4 c h i l d : WidgetB ( . . . )
5 );
The build() method is designed to have a fast execution and it should be free of
side effects, all heavy computational work should be done asynchronously and stored
as part of the state - more on state management later. This allow Flutter to call it
whenever needed, potentially once for each frame rendered - similarly to how React
handles the DOM and its updates. In conclusion, updating is done by telling the
framework to replace a widget in the tree with a new one updated. Consequently,
new and old widgets are compared by the framework to efficiently updates the user
interface. Flutter automated comparison process is quite effective, and this enables
high-performance and promotes interactivity. This approach creates some memory
side complications caused by numerous fast objects instantiation and deletion,
but this is handled by how the framework language manages these complications.
Fortunately, Dart is particularly well suited for this task.
As explained, in Flutter widgets are mandatory to pursue a declarative paradigm
approach: in a nutshell, declarative means that we simply declare how an element
should be rendered given the current state without never actually touching it. To
make it more clear in practice, it’s good not to think of how to accomplish a certain
result - intended as flow of statements that change a program’s state - but instead
how a component should render itself in it’s new state [7].
The application structure is the most important consideration to embrace a
declarative approach and Flutter declarative UI is entirely built around widgets as
a unit of composition. As mentioned, a widget is a lightweight “blueprint”, a basic
building block heavily customizable, that represents an immutable declaration of
23
Flutter framework analysis
Listing 3.2: Simple Material app in Flutter. This is the entire code needed to
make a hello world application for Android and iOs. As already suggested by the
previous explanation, all classes instantiated in the following example are widgets.
1 // F l u t t e r M a t e r i a l l i b r a r y
2 import ’ package : f l u t t e r / m a t e r i a l . dart ’ ;
3
4 // Dart d e f a u l t e n t r y p o i n t . Can be o v e r r i d e i n c o n f i g u r a t i o n .
5 v o i d main ( ) => runApp ( HelloWorldApp ( ) ) ;
6
7 // I extend S t a t e l e s s w i d g e t b e c a u s e t h i s p a r t o f t h e UI d o e s not
depend on a n y t h i n g o t h e r than t h e c o n f i g u r a t i o n i n f o r m a t i o n i n t h e
object i t s e l f .
8 c l a s s HelloWorldApp e x t e n d s S t a t e l e s s W i d g e t {
9
10 // As mentioned , my w i d g e t o v e r r i d e s t h e d e f a u l t b u i l d method .
B u i l d r e t u r n s always a Widget o b j e c t . New keyword can be o m i t t e d
as i t i s s u p e r f l u o u s .
11 @override
12 Widget b u i l d ( Bu i l d Co n t e xt c o n t e x t ) {
13 r e t u r n MaterialApp (
14 home : S c a f f o l d (
15 appBar : AppBar ( t i t l e : Text ( ’ H e l l o World Example ’ ) ) ,
16 body : Center (
17 c h i l d : Text ( ’ H e l l o World ’ ) ,
18 )
19 ),
20 );
21 }
22 }
The result of the previous code is represented in Figure 3.2. The resulting screen
is really simple, but it is interesting to note that with very few lines of code it is
possible to launch complex applications.
24
Flutter framework analysis
1
https://developer.apple.com/xcode/swiftui/
2
https://developer.android.com/jetpack/compose
26
Flutter framework analysis
this way a developer can compose a custom container from core and basic widgets
in novel ways, instead of subclassing the default Container Widget to introduce
a customized effect. Anyway, the most important thing for a widget is its visual
representation inside the build method, because it represents the widget part of
the user interface in a concrete way. Flutter recursively asks each widget to build
itself. This process continues in depth until a fully described tree made of concrete
renderable objects is obtained. Finally, objects of this tree are stitched together
into a renderable object tree. Efficiently lay out a hierarchy of widgets, defining
their size and position, before they are rendered on the screen is one of the Flutter
main tasks.
In order to explain the process of defining a layout in more detail, it is useful
to first investigate how Flutter handles the rendering process: during the build
phase, each node in the render tree is a RenderObject, which is an extremely
general abstract model for layout and painting. RenderObjects have slots for
memorizing a parent and parentData - where the parent RenderObject can store
child-specific data. However, a RenderObject does not define a child model nor a
coordinate system or a specific layout protocol. Knowing the minimum necessary
of the child object and not binding to any reference system, make RenderObject
a flexible component which has sufficient abstraction to handle all variety of use
cases. Basically, a RenderObject contains primitives that are easy to specialize: for
example, RenderParagraph is in charge of rendering text, RenderImage renders
images and RenderTransform manages transformation before painting. Most of
the widgets in Flutter are rendered by an object that inherits from the RenderBox
subclass of RenderObject. This uses Cartesian coordinates as layout system and
represents a RenderObject of fixed size in a 2D Cartesian space and provides the
basis for a box constraint model: for each widget to be rendered, a minimum and
maximum width and height are established. The RenderView is the root of all
RenderObjects ant it represents the total output of the render tree. When a new
frame has to be rendered for any reason - the underlying platform demands when to
render - a call is made to RenderView compositeFrame() method. Consequentely,
a SceneBuilder is built to trigger an update of the scene. RenderView waits for the
scene to be completed and then passes the composited scene to the Window.render()
method in dart:ui. Finally, the composited scene arrives to the GPU that controls
how to render it. This is in a nutshell how Flutter render mechanism works. Hence
to actually perform layout, Flutter has to traverse in depth-first the render tree,
passing down from parent to its children information about size constraints. Flutter
layout can’t really be understood without knowing the main rule that defines how
widgets decide how much space they can use:
In more detail:
27
Flutter framework analysis
• A widget receives its own constraints from the parent. Constraints are a set of
4 doubles: a minimum and maximum width, and a minimum and maximum
height. By default, minimum values are 0 and maximum values depends on
platform screen.
• This widget has to say to its parent the size he wants to be within the
constraints. To discover it, the widget passes to its list of children their
constraints. Many widgets have just one child, but others - as Column, Row
or ListView widgets - have more children. Each children can receive different
constraints based on parent widget internally behaviour. One by one, children
are asked what size they want to be.
• Then, using children size, the widget positions them one by one - horizontally
in the x axis and vertically in the y axis.
• Finally, the widget computes its own final size and tells it to its parent.
As I said, a widget deciding its size must respect the constraints given by its
parent and each widget passes up its own size. This process is implemented as
single walk down in the widget tree. At the end, the result is that each object has a
defined size and is ready to be painted with the paint() method. This Flutter layout
engine based on box constraints approach is very effective and it is able to layout
the widgets tree in O(n) time. However, this process also has some limitations:
• Depending on constraints means that a widget can’t have any size it wants.
This situation could results in a typical error that the space needed for a
widget is out of bounds. Hence, the widget overflows.
• The parent always decides children position on the screen, so a widget does
not decide - and cannot know - its own position. This usually does not happen
in a UI built with an imperative approach.
• Is not possible to precisely define position and size of a widget without taking
into consideration all the widgets tree. A parent size and position depends on
its parent size and position, and so on up to the root.
28
Flutter framework analysis
• If a parent does not have enough information in its constraints on how to align
a child and this child wants to have a different size, the parent might ignore
child size. To avoid this, is necessary to be specific in alignment declarations.
1 Row(
2 children : [
3 Expanded (
4 c h i l d : Center (
5 c h i l d : Container (
6 c o l o r : C o l o r s . red ,
7 c h i l d : Text (
8 ’ This i s a v e r y l o n g and b i g t e x t , which w i l l not f i t i n
one l i n e . ’ ,
9 s t y l e : TextStyle ( f o n t S i z e : 20) ,
10 ),
11 ),
12 ),
13 ),
14 C o n t a i n e r ( c o l o r : C o l o r s . green , c h i l d : Text ( ’ S h o r t t e x t ! ’ ) ) ,
15 ],
16 )
This code produces as result the screen in Figure 3.3. I analyze in detail the
interactions between the various widgets:
• The first Text widget - line 7 - wants to be long as all the space necessary to
the text to fit in one line. A Row widget does not impose any restriction on
29
Flutter framework analysis
width, so by default the first text widget overflows the physical screen on the
right.
• The Container - line 5 - does not define any additional constraints on its text
widget child. In this example is useful only to visually show the text widget
dimension by setting the background color to red. Same regarding the center
widget - line 4.
• Instead, the Expanded widget - line 3 - tries to get all the available space on
the row main axis. Therefore, it does not impose any limit on its child width
and asks to its parent Row to have an infinite size.
• When one of Row children is wrapped in an Expanded widget, the Row does
not let this child define its own width anymore. Instead, the Row widget
computes Expanded child width according to width of others children. In this
example, the second Text widget - line 14 - just needs to occupy the space
dedicated to the short text, so the Row widget forces the Expanded child
widget to have a width equal to the remaining space. In other words, the
original child’s width becomes irrelevant, and is ignored.
I said that a parent widget might ignore child decided size, but it is also common
to have a parent that dictate precisely the size of a child widget by setting maximum
and minimum constraints to the same value. Let’s think to the topmost render
object which constrains its child to be the size of the underlying physical screen. A
more flexible situation is when a parent dictates to its child only one dimension -
width for example - leaving to its child to decide the desired height. For a simple
example, see the FlowText widget, which is built with respect to a given width but
it can be tall as needed by the text to not overflow.
Finally, also a child can decides the best use of the space the parent made
available. In this case, a child object decides how to render its content basing
on parent constraints. For example, in a LayoutBuilder3 widget the builder child
uses the passed-down constraints to determine which widget to return. Check the
following code:
1 Widget b u i l d ( B u i l dC o n t e xt c o n t e x t ) {
2 return LayoutBuilder (
3 b u i l d e r : ( context , c o n s t r a i n t s ) {
4 // As example , c o n s i d e r t h e f i r s t c o n d i t i o n f o r a v e r t i c a l
l a y o u t and t h e s e c o n d c o n d i t i o n f o r t h e same l a y o u t i n p o r t r a i t
mode
3
https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html
30
Flutter framework analysis
5 i f ( c o n s t r a i n t s . maxWidth < 6 0 0 ) {
6 r e t u r n OneColumnLayout ( ) ;
7 } else {
8 r e t u r n TwoColumnLayout ( ) ;
9 }
10 },
11 );
12 }
Flutter provides developers with a rich catalog of widgets, which are classified in
visual, structural, platform, and interactive widgets. There are about two hundred
widgets available and they are deeply different from one another: each one is built
around a specific responsibility and it is easy to find always the most suitable
widget for the current problem. Flutter documentation is incredibly comprehensive
and exhaustive: each widget has its own web page where it is possible to find
an introduction to the widget, documentation about constructor, parameters and
options, examples of use and often also a summary video. In addition, Flutter
provides numerous tutorials and usage examples that immediately help developers
to learn the most common and used widgets.
In conclusion, the layout mechanism offered by Flutter is powerful, flexible and
incredibly fast. Developers are required to fully understand the main rule that
defines the entire layout process: “Constraints go down. Sizes go up. Parent sets
position”. With this rule in mind is possible to build actually complex interfaces
with minimum effort using what Flutter offers as building components. To avoid
errors or unpredictable behaviours in the various nested layers of the UI, it is
always important to recognize which widgets define constraints, which ones adapt
to available space, which ones require infinite space and which ones use only the
space strictly needed.
4
https://pub.dev/packages/flutter_svg
32
Flutter framework analysis
Flutter assets can be accessed also in platform specific code. On Android the
assets are available using the AssetManager API provided by “android.content.res”.
The logical key needed for retrieving the asset is obtained from lookupKeyForAsset
on PluginRegistry.Registrar; this process available when developing a plugin for
Flutter in native code, and allows Java or Kotlin code to rely on Flutter assets
configuration.
The process is similar for the iOS counterpart. Instead of the AssetManager,
in iOS Flutter assets are available using the mainBundle. The process to retrieve
the key is the same, and this allows to use Flutter assets configuration with
Objective-C/Swift code.
In other occasions is not possible to rely on Flutter for managing assets, but it
is necessary to work with assets in the platform projects directly. Two common
uses are the icon app and the launch screen.
Firstly, updating a Flutter app icon works the same way as updating icons in
native Android or iOS environment. For Android the new icon goes in the Android
folder inside the Flutter project root directory, under .../android/app/src/main/res.
Developers have to replace default placeholder images named ic_launcher.png with
the desired assets and after they have to update the AndroidManifest.xml file.
For what concerning iOS, the process is similar but it is done inside iOS folder
from the Flutter root directory - .../ios/Runner. In AppIcon.appiconset directory
there are always several placeholder images to be replaced with the desired new
icon and names as dictated by the Apple Human Interface Guidelines.
Secondly, also for drawing transitional launch screen Flutter uses native platform
mechanisms. The launch screen starts on app initialization while the Flutter
framework loads. The launch screen persists until Flutter renders the first frame of
the application. The Launch screen, also called splash screen in Android, usually is
a simple image, but it could also include animated content or special transitions. It
is absolutely necessary to consult the native documentation of the platform because
Flutter does not include any of these features.
the end of 2020, Flutter provided a basic widget for routing, called Navigator. The
Navigator manages a stack of Route objects and provides two methods implemented
with an imperative approach: those API are Navigator.push and Navigator.pop
methods. This two method were used to navigate between two routes: push()
navigates to one route from another route, while pop() navigates back to the
previous route.
As were implemented, those imperative APIs allows targeted modifications to
the routing stack, without offering flexibility: developers asked extensions to the
imperative API to obtain full control over the Navigator stack. Furthermore, the
imperative approach on which Navigator is based feels outdated and not very
Flutter style. In a generic widget, to change its children is enough just rebuild the
widget with a new set of children, but this concept is not valid for the Navigator:
to change the set of routes with a new set, it is not enough just to rebuild the
navigator, but must be used a mix of imperative APIs to achieve the goal.
The other known limitation for developers with the old routing system is
nested Navigators. These are commonly used in tabbed interfaces where each tab
comes with its own Navigator, which is nested with respect to the root Navigator.
Nested Navigators are not necessary to keep track of the route as a whole, but
they are needed to know the single tab in which the user is. Using the default
Navigator meant that developers had to manually implement nested routing to
avoid unexpected behaviour for the back button. If a user hits the back button to
go back from the current tab to the previous one, the button will not get him back.
Instead, it will pop the route from the Navigator, bringing the user to the previous
view in the stack instead of the expected tab.
For these reasons, at the end of 2020, the Flutter team released a big update
for the internal navigation and routing system, called Navigator 2.0. Now Flutter
provides developers with a complex and fully customizable system for implementing
and managing in-app navigation. New Navigator APIs allows to set the navigator’s
history stack in a declarative way, by providing a list of Pages objects.
Before explaining the various components in detail, it is useful to show the
innovations introduced with the new Navigator and what the several components
are in charge of:
Basically, the transition delegate has to make two decisions. The first one is to
decide the behaviour of a transition between two routes: the new route can animate
in/out or just appear/disappear. Secondly, the transition delegate decides how to
order Routes which are added and removed at the same location in the history stack.
Developers can both configure the transition delegate behaviour of the Navigator
or provide a different transition delegate. It is also possible to choose a specific
transition delegate for each update of the Navigator list of Pages. This is useful to
implement different transition styles; if no custom delegate is provided, the default
one will be used and it will implement transitions in a Material push-like effect.
The Navigator also support a new onPopPage() callback in addition to the
Pages list. The callback is usually invoked by the Navigator in response to a
Navigator.pop() call. This asks to the receiver of the callback if the given Route
- corresponding to a Page - should be popped or not. If the receiver agrees, the
Navigator changes the state and triggers the update of the Navigator with a new
list of Pages without the popped one. Otherwise, in case of error or false response,
the popped Page is not removed from the list and it will be treated as a new Page
to show changes in the state according to the error.
Despite the effectiveness of the new routing introduced by Flutter in solving the
criticalities of the previous approach, the acceptance by developers has not been
great so far. Principal criticisms regard the API complexity and the numerous
components needed to implement in a custom context. Developers expected the
presence of pre-made solutions for common scenarios offered by an extra abstraction
layer which is absent by now. In fact, even in the main article about Navigator
2.0, the author refers to the possibility of using these new Navigator APIs to build
an higher-level API package. This is certainly an aspect than could be further
explored in the future [9][8].
Most widgets do not need a mutable state, because they do not have any
properties that change over time. Icon, IconButton or Text are examples of
stateless widgets. To implement this immutable condition, a widget subclasses the
StatelessWidget5 .
A Stateless widget is used when the part of the user interface described in its
build method do not depend on dynamic factors: anything the widget needs is the
configuration in the widget itself and the BuildContext in which the it is inflated.
For this reason, the build method of a stateless widget can be called only once for
each widget instance when the app is running. This call happens when the widget
is responsible for drawing itself. If the framework needs to redraw a Stateless
widget, then it will need to create a new instance of this.
On the other hand, a Stateful widget is dynamic. If a widget can change in
response to a user interaction - for example changing its appearance - it needs to
subclass the Stateful superclass. Checkbox, Radio, Slider or TextField are examples
of Stateful widgets6 .
Implementation is slightly different for stateful widgets with respect to stateless
ones. It requires creating two classes, one subclass of StatefulWidget, which defines
the widget, and another one subclass of State, which contains the widget state and
defines the widget build() method. This separation is in place because a Widget is
always an immutable object and Flutter wants to not treat stateless and stateful
widgets in a separate way. For this reason, a widget state is stored in a State object,
well distinct from the widget appearance.
The state consists of values that can change. Whenever this happens, the State
object calls its setState() method, which tells the framework to redraw the widget.
So, let’s analyze the code example below. Consider this simple widget with
a counter and a button in its view. The counter increments whenever the user
taps the button, and then the new value is showed. In this example, the counter
is the state for that widget, while other components are just stateless widgets -
ElevatedButton, Text and Padding widgets. After the value changed, the widget
needs to be rebuilt to show the updated value in the UI. However, the build method
is the one of the object extending the State class.
1 // This c l a s s i s t h e c o n f i g u r a t i o n f o r t h e s t a t e and i t h o l d s v a l u e s
p r o v i d e d by t h e p a r e n t
2 c l a s s MyCounter e x t e n d s S t a t e f u l W i d g e t {
3 @override
4 _CounterState c r e a t e S t a t e ( ) => _CounterState ( ) ;
5
https://api.flutter.dev/flutter/widgets/StatelessWidget-class.html
6
https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html
37
Flutter framework analysis
5 }
6
7 c l a s s _MyCounterState e x t e n d s S t a t e <MyCounter> {
8 // l o c a l v a r i a b l e c o u n t e r
9 i n t _counter = 0 ;
10
11 v o i d _increment ( ) {
12 // This method c a l l s s e t S t a t e which t e l l s F l u t t e r t h a t something
has changed i n t h i s S t a t e . As conse quenc e , F l u t t e r r e r u n t h e b u i l d
method below .
13 // I f _counter i s changed w i t h o u t c a l l i n g s e t S t a t e ( ) n o t h i n g w i l l
change UI s i d e .
14 setState (() {
15 _counter++;
16 }) ;
17 }
18
19 @override
20 Widget b u i l d ( Bu i l d Co n t e xt c o n t e x t ) {
21 r e t u r n Column (
22 mainAxisAlignment : MainAxisAlignment . c e n t e r ,
23 children : [
24 Padding (
25 padding : E d g e I n s e t s . a l l ( 1 6 ) ,
26 c h i l d : Text ( ’ Counter v a l u e : $_counter ’ )
27 ),
28 ElevatedButton (
29 o n P r e s s e d : _increment ,
30 c h i l d : Text ( ’ Increment ’ ) ,
31 ),
32 ],
33 );
34 }
35 }
As I said before, having the state separate from the widget permits to treat both
stateless and stateful widgets exactly in the same way, without concerning about
losing state. Is not necessary to hold an object to preserve its state, because the
parent can create a new child instance at any time, without losing the persistent
state. Flutter is in charge of finding and reusing existing state when appropriate.
Going deeper into the aspect of state management, it is evident that stateful
widgets are suitable for managing a specific type of state, usually called "Ephemeral
state". A useful definition of state in an application context is "all data the app
needs to build the UI at any given time". Following this sentence, the app state
can be separated in two conceptual types; the first one is the ephemeral state and
the second one is the shared state.
As I explained in this section, the ephemeral state usually represents the state
38
Flutter framework analysis
in the context of a Widget. This contains the local data actually needed to the
widget to build its interface. The counter example perfectly describes this type of
local data, where the counter value is useful only to the widget containing it. Other
example could be the current page index in a PageView or the current selected tab
in a BottomNavigationBar. This state is generally simpler than the shared one, it
do not require to use state management techniques, as serialization, and it do not
change in complex ways during app execution. For local state, all you need is a
StatefulWidget.
On the other hand, the shared state represent the application state, which
consists in all the information to share across many parts of the application.
Furthermore, shared state also includes data developers want to keep between user
sessions. Example of shared state are the user preferences or the login info, but
also data about items in a shopping cart and read/unread state of notifications.
For this type of state there is not a single rule on how to manage it. The right
implementation of share state management depends on the complexity and nature
of the application, the team experience and the current development phase in which
the app is. Flutter does not pose any constraint on the approach the developers
prefer. They can freely use State and setState() of Stateful Widgets to manage all
of the application state. The Flutter team takes this approach in many simple app.
It is also possible that, as the application grows in features, a local state might
need to be moved to app state and vice-versa.
For that reason, the diagram in Figure 3.5 can help in simple way to summarize
which is the right choice between ephemeral and shared state for a given piece of
data.
Data
For managing the shared state - or application state - Flutter suggests to use the
39
Flutter framework analysis
Provider package7 . If there are no strong reasons for using an alternative approach
- as Redux - then the Provider package is probably the right choice, because it is
easy to understand and use.
In Flutter, thanks to its declarative approach, it makes sense to manage the
state above the widgets that access and use it. The main reason for this, for how
Flutter is structured, it is hard to imperatively change a widget from outside, for
example by calling a method on it. The Provider package is based on three main
components:
1 v o i d main ( ) {
2 runApp (
3 // t h i s o b j e c t p r o v i d e s t h e d e f i n i t i o n o f a b u i l d e r t h a t
c r e a t e s a new i n s t a n c e o f MyModel . C h a n g e N o t i f i e r P r o v i d e r i s a
smart component and i t w i l l not r e b u i l d MyModel u n l e s s
n e c e s s a r y . Furthermore , i t a u t o m a t i c a l l y c a l l s d i s p o s e ( ) on
t h e model when t h e i n s t a n c e i s no l o n g e r n e c e s a r y .
4
7
https://pub.dev/packages/provider
40
Flutter framework analysis
5 ChangeNotifierProvider (
6 c r e a t e : ( c o n t e x t ) => MyModel ( ) ,
7 c h i l d : MyApp( ) ,
8 ),
9 );
10 }
• Consumer. This is a Widget from the Provider library that offers methods
to interact with models provided by the ChangeNotifierProvider. The exposed
instance of such a model can be used to interact with the model state. Con-
sumer needs a type - Consumer<MyModel> - because without it the Provider
package, which is based on types, does not know what the developer wants.
The only other required parameter along with the type for a Consumer widget
is the builder. This is a function that is called whenever the notifyListeners()
method of the ChangeNotifier is called.
Compared to a classic Flutter builder, the Consumer builder requires three
parameter: the context, which is also present in every build method, an
instance of ChangeNotifier, provided from the above, and a child, which is in
place for optimization. It is a best practice to put the Consumer widgets as
deep in the tree as possible, to avoid to rebuild large portions of the UI for
minor changes in the application state.
There is a last component, called Provider.of, that is useful to access data in
the shared model, but these data are not needed to show the UI. For example,
consider again the cart example and now add a "clear cart" button or a "add item
to cart" button. These buttons do not need to display data from the provided
model, but they only have to trigger a change in the model. It is just possible to
use a Consumer, but that would result in a waste of resources.
By combining all these components together, any data in the application state can
be easily managed. Furthermore, as I explain in the next section, this management
mechanism is also necessary to manage live data coming from the internal storage
or the network.
by the app with a key-value structure and this data could also be shared among
different applications. Instead, iOS provides the UserDefaults component to achieve
the same objective, with the only difference that this is a simple synchronous
database with cache features.
By default, Flutter does not have a built-in mechanism to access this platform
specific classes, but there is available an official package made by Flutter team which
resolves this problem, called shared_preferences 8 . This is a simple library which
allows developers to use SharedPreferences and UserDefaults without worrying
about the underlying platform. The package shared_preferences provides methods
to asynchronously read and write data using Flutter Future mechanism. These
methods are different for different type of data. Methods to write are set[type],
so there are setString, setInt, setBool, setDouble and also setStringList and all of
them return a boolean value to confirm the writing operation. Instead, for reading
the methods are similar but they are composed with get plus the type to read.
These methods are available only through an instance of shared_preferences library.
Hence, to implement a simple string memorization, the code in following example
is enough.
1
2 Future<bool > s a v e S t r i n g T o D e v i c e ( t e x t ) async {
3 S h a r e d P r e f e r e n c e s sp = a w a i t S h a r e d P r e f e r e n c e s . g e t I n s t a n c e ( ) ;
4 r e t u r n sp . s e t S t r i n g ( ’ s t r i n g _ k e y ’ , t e x t ) ;
5 }
6
7 Future<S t r i n g > g e t S t r i n g F r o m D e v i c e ( ) async {
8 S h a r e d P r e f e r e n c e s sp = a w a i t S h a r e d P r e f e r e n c e s . g e t I n s t a n c e ( ) ;
9 r e t u r n sp . g e t S t r i n g ( ’ s t r i n g _ k e y ’ ) ;
10 }
8
https://pub.dev/packages/shared_preferences
42
Flutter framework analysis
card information - and cryptographic keys and certificates the app needs to work
with. On the other hand, Android has a similar system called KeyStore, which
from the outside works similar to the Apple counterpart, but supports different
encryption algorithms. Furthermore, Android KeyStore create a system wide
credential mechanism.
As for shared_preferences, Flutter does not have built-in support for secure
storage, but this support can be achieved using a third party library called flut-
ter_keychain9 . This library provides developers with a singleton to write and
read asynchronously secure data. It uses secure Keychain in iOS and Keystore
in Android - in particular, for Android the library uses the AES encryption. The
following code helps to understand how to use it.
1
2 Future<bool > s a v e S e n s i t i v e V a l u e ( s t r i n g ) async {
3 r e s u l t = a w a i t F l u t t e r K e y c h a i n . put ( key : " s t r i n g _ k e y " , v a l u e : s t r i n g
);
4 return r e s u l t ;
5 }
6
The previous solutions are good to store simple data in an easy way, but they are
not a real database. To actually persist data available to many users in necessary
to link the mobile application to a real database. Firestore is a common used
solution to achieve both data persistence and real-time data updates. Firestore is
a NoSQL database built by Google on top of Google Firebase database. A piece of
data linked to its remote version in the database is called a LiveData in Android.
It can auto update its value when the remote value changes.
Flutter does not exactly have LiveData, but the same result can be achieved
through an official package, called cloud_firestore 10 , and through a stream widget
called StreamBuilder11 . This is widget is based on a sequence of snapshots repre-
senting changes in its state. The build() method of the StreamBuilder is called at
the discretion of the Flutter pipeline. This will receive a timing-dependent sequence
of snapshots that represent the interaction with the stream.
9
https://pub.dev/packages/flutter_keychain
10
https://pub.dev/packages/cloud_firestore
11
https://api.flutter.dev/flutter/widgets/StreamBuilder-class.html
43
Flutter framework analysis
3.4.3 Internationalization
In a business context, it is essential to provide customers with localized applications
in the users’ language. In this case, it is therefore necessary to rely on the
internationalization of the application. With this, it is possible to substitute the
application simple static strings with a key-value mechanism to retrieve saved
translations from file, based on the language chosen by the user.
Flutter provides both a built-in mechanism to internazionalize the application
and a good documentation on how to set up it for each locale supported by the
mobile app.
Flutter has a localization package, called flutter_localizations, for translating
its own components. This package has to be specified in the pubspec.yaml file as
a dependency. Developers must add also every file used to store the translations.
For example, if developers decide to use json files, they will create a file for each
locale with .json extension and they will import them under the assets entry in the
pubspec.yaml.
Flutter two main widget for building applications both support localization; the
first one is the MaterialApp widget and the second one is the CupertinoApp widget.
In particular, Flutter localization uses delegates for initialization localization files,
so developers has to implement a class in charge of loading the translated strings
from file to the memory during execution. The follwing code snippet shows how to
add localization to a MaterialApp.
1 c l a s s LocalizedApp e x t e n d s S t a t e l e s s W i d g e t {
2 @override
3 Widget b u i l d ( Bu i l d Co n t e xt c o n t e x t ) {
4 r e t u r n MaterialApp (
5 t i t l e : " Simple l o c a l i z e d app " ,
6 localizationsDelegates : [
7 GlobalMaterialLocalizations . delegate ,
8 GlobalWidgetsLocalizations . delegate ,
9 GlobalCupertinoLocalizations . delegate
10 ],
44
Flutter framework analysis
11 supportedLocales : [
12 L o c a l e ( ’ en ’ , ’US ’ ) , // e n g l i s h + c o u n t r y code
13 L o c a l e ( ’ i t ’ , ’ IT ’ ) , // i t a l i a n + c o u n t r y code
14 ],
15 home : MyApp( ) ,
16 );
17 }}
For the sake of brevity, I do not show in this thesis all the steps necessary for
the implementation of localization. My main interest in this case is to show that
Flutter has no shortcomings from this point of view. In practice, Flutter offers
similar mechanisms to other cross-platform frameworks, such as ReactNative, and
native solutions. Furthermore, third party libraries, as easy_localization12 , allow
developers to minimize the amount of code they have to write and offer pre-made
complete solutions.
12
https://pub.dev/packages/easy_localization
13
https://pub.dev/packages/url_launcher
45
Flutter framework analysis
1
2 phone_launch ( ) async {
3 i f ( Platform . isAndroid ) {
4 c o n s t scheme = " t e l : + 3 9 3 3 3 1 2 3 1 2 3 " ;
5 a w a i t l a u n c h ( scheme ) ;
6 } e l s e i f ( Platform . isIOS ) {
7 c o n s t scheme = " t e l : 0 0 3 9 − 3 3 3 1 2 3 1 2 3 " ;
8 a w a i t l a u n c h ( scheme ) ;
9 }
10 }
11
12 sms_launch ( ) async {
13 i f ( Platform . isAndroid ) {
14 c o n s t scheme = " sms :+39333123123? body=H e l l o %20World " ;
15 a w a i t l a u n c h ( scheme ) ;
16 } e l s e i f ( Platform . isIOS ) {
17 c o n s t scheme = " sms :0039 −333123123& body=H e l l o%World " ;
18 a w a i t l a u n c h ( scheme ) ;
19 }
20 }
It may be seen in the above code example how the launch method is implemented
as an asynchronous call to the underlying platform. Furthermore, via code it is
possible to know on which operating system the app is installed and then implement
different behaviours if needed.
To send an email via the default app of the device the code to implement is the
same as for tel and sms, but the scheme is mailto. Anyway, creating and sending
an email is a more complex action than a call or a text message. This is because,
in addition to the email address, in the mailto scheme it is possible to specify the
subject and body of the message, even in html format. For the sake of brevity,
I’m not showing the related code snippet. The official documentation recommends
using the URI object constructor for more code clarity.
Finally, the url_laucher package allows developers to open web pages with the
http or https scheme either by opening the default browser or with an in-app
webview. Javascript can be enabled if needed. All these options are parameters
supported by the launch method and do not require special attention. The only
46
Flutter framework analysis
useful thing about implementing a url scheme is knowing the default behaviour of
the operating system. For example, Android opens a url in the browser, while iOS
uses a webview by default.
Of all the native features, two in particular are not included by default in Flutter
and are not available through official packages. In particular, url_launcher does
not allow access to the device geolocationing and contact list features. However,
both of these aspects are available through third-party developer libraries.
To access the geolocation services there are mainly two libraries: the first is
geolocator14 and the second is location15 . The two libraries are very similar and both
offer high-level functionality. It is important to underline that it is always necessary
to implement the right controls to obtain access permissions to geolocation. By
default, on both Android and iOS, access is restricted to user permissions. Without
going into the specifics of the implementation via code, it is useful to know that in
both libraries it is possible to specify the accuracy of the geolocation - this affects
energy consumption - and to implement a callback when the user’s location changes
- very useful for showing changes to the UI in real time.
Instead, to use the device contact list devs can use the contacts_service16 library.
This allows to easily access the functions related to the contact list: view the list
of contacts - with or without the relative contact image - insert, delete or modify a
contact and also open the default phone book of the device if necessary.
The last of the absolutely necessary native features in a real app are the access
to both media files from the photo gallery and from the camera. Both can be easily
achieved by adding a library created by the Flutter team called image_picker17 .
The code to be written to actually implement these features in an application is
very little. In particular, it is sufficient to define what the input source will be and
then store the return path to the source. As mentioned above for geolocation, it is
essential to correctly manage user permissions for accessing the device memory. I
am not showing the code necessary for the implementation here, because in chapter
5 I will deal more specifically with this aspect.
In conclusion to this section it is fair to mention that Flutter offers a flexible
system that allows developers to call platform-specific APIs for both Android and
iOS. This system is available for all native languages: Kotlin or Java code on
Android, Swift or Objective-C code on iOS. The Flutter solution built-in to support
platform API does not rely on code translation from Dart to native language, but
rather on a flexible message passing style. This style relies over platform channel
14
https://pub.dev/packages/geolocator
15
https://pub.dev/packages/location
16
https://pub.dev/packages/contacts_service
17
https://pub.dev/packages/image_picker
47
Flutter framework analysis
concept: the Flutter portion of the app constantly sends messages to its host, the
iOS or Android portion of the app, over this platform channel.
The host listens on this channel and receives any message from the Flutter app.
The message has to be strictly defined in a codec because the platform channel
support only binary communications. The host platform, after a new message
on the channel, can call any number of platform-specific APIs using the native
programming language. When the computation is done, the host sends a response
back to the Flutter portion of the app. This system works in both directions and
in practice does not limit Flutter’s flexibility towards the underlying platform.
With platform channel, Flutter can therefore interact with native platform
APIs, but actually not directly. This approach is different from Xamarin and
NativeScript cross-platform framework, where developers can call native APIs
directly using the same language of the framework. That is because of language
bindings. For common and simple cases this overhead due to platform channel is
irrelevant. However, in more custom scenarios, developers have to write native
Swift or Java/Kotlin code to interact with native APIs, using different IDEs, and
communicate via platform channels. It is important to understand that developers
enter in a low level context and that they will need to write not trivial native code.
Anyway this flexibility is a long-term guarantee that Flutter’s cross-platform
approach is not limiting, although the actual implementation could be very tricky.
This article written by Mikkel Ravn addresses in detail how to use the Flutter
platform channels [10].
It may seem like a disadvantage to have always to import an external library
to implement native functionalities, and the question arises why Flutter does not
provide these capabilities out-of-the-box. Despite this, as I said previously, these
libraries offer high-level methods that allow devs to obtain complex functionalities
with a few lines of code. It is also common in the native context to import libraries
that wrap the functionalities of the underlying platform to simplify implementation
and speed up development. For this reason, the Flutter team’s choice to keep
the framework lean is a winning choice. Developers can clearly decide what to
import and what not, relieving the framework of all those features that are certainly
useful but not always necessary. In conclusion, the purpose of this section is to
demonstrate that Flutter has no evident limits on the use of native features.
18
https://flutter.dev/docs/development/tools/sdk/release-notes/supported-
platforms
49
Flutter framework analysis
of stand-alone applications for wearOS. Since the release 2.0 of WearOS, Google
wanted devs to think of the watch as a separate device instead of just an extension
of the phone.
Instead in the context of Apple, which has its WatchOS operating system for
wearables, the situation is more balanced between the two possibilities. In general,
the choice between one solution or the other always falls on the question of whether
or not the application for the Apple Watch will need to communicate with the
phone. If so, Apple recommends creating a companion app, otherwise a stand-alone
app.
Unfortunately, Flutter does not offer much in either cases. In particular, with
some not trivial changes to the native Android code, it is possible to create a
stand-alone application for WearOS using a third-party library called wear 19 . This
library offers several possibilities, including support for both rectangular and round
screen clocks. However, it is fair to point out that this library wear is not yet
finished and not very popular. In the article written by Souvik Biswas[11], he shows
a workable solution to create a WearOS application without too much difficulty.
From my point of view, the solution is quite cumbersome and not actually ready
for a business context.
The situation is even worse for Apple wearable. In fact, Flutter does not officially
support WatchOS in any way. In case a developer wants to create a WatchOS
extension there is a solution elegantly shown in this article written by Rafael Ferraz
[12]. From my point of view, as a Flutter analyzer, it is useful to point out how
constantly it is necessary to use the swift code and to use the platform channels
that I introduced in the previous section. As in the case of WearOS, I therefore
consider this solution not ready for a business context.
It is clear that the Flutter team has not yet invested enough to support the
world of wearables. Furthermore, at the time of writing this thesis, there does
not seem to be any intention from the Flutter team to work on wearable support
anytime soon. This is actually a lack of Flutter, although personally I do not
consider it particularly problematic. Indeed, applications for wearables are not
often a priority business and many companies have not yet invested to create their
application for wearables. This is also because in general users use the smartwatch
mainly to access notifications, to quickly manage calls and to use the features
related to fitness.
In conclusion, I add in this section that the limitations for wearables are present
and accentuated also for other devices that are part of the mobile ecosystem. With
Flutter it is not possible to create applications neither for televisions - AndroidTV
and TvOS - nor for cars - Android Auto and CarPlay. Again, the situation will not
19
https://pub.dev/packages/wear
50
Flutter framework analysis
51
Chapter 4
runtime context which includes the garbage collector and various native methods
for dart as libraries to function, runtime type information and dynamic method
lookup. The AOT mode is used to package and distribute the completed app.
During the development phase, the dart code is interpreted by the DVM using
the JIT mode - Just In Time compilation. The DVM is able to compile code to low
level very fast and it can dynamically load Dart source, parsing it and compiling
it to native machine code on the fly to execute it. In JIT mode, Dart VM offers
advanced features, as hot-reload which simplify and speed up development and
debugging.
The hot-reload allows developers to immediately run the latest changes made
to the source code without having to restart the application or lose the state of
the application. Dart VM reloads all the libraries with updated code, updates
classes with the new versions of fields and functions and the Flutter framework
automatically rebuild, repaint and re-layout the widget tree.
Considering the background common to many developers, Dart has a short
learning curve. The typical background of a mobile developer includes languages
such as Java, C # and C ++ or similar languages commonly used in software
development. This background is enough to start programming immediately in Dart
because it requires concepts already present in many other languages. In particular,
to program in Dart developers need to have knowledge about variables, built-in
types, flow control structures - for loops, while loops, if-else, switch - functions -
lexical Scope and Lexical closures - classes and concepts about OOP programming,
async programming and Dart keywords.
For a Javascript programmer starting to develop with React Native is probably
more intuitive due to the possibility of really reusing all his knowledge. Despite this,
Dart does not introduce complicated novelties; indeed, the common impression
using Dart is that one already seems to know Dart. This is due to the ability of
the language to re-propose concepts already present in many other languages in a
simple and intuitive key. The Flutter framework, on the other hand, requires the
programmer to master its mechanisms especially for what concerns the functioning
of the UI components. In chapter 3 I analyzed Flutter’s mechanism for managing
2D space and it is evident how Flutter’s approach is different from other solutions
such as React Native. But for Dart itself these complications do not exist: to
program efficiently in Dart a developer just needs to study the complete and
exhaustive documentation which is well explained and full of examples, including
interactive ones. Personally, since I had to learn Dart to work on this thesis, I
can subjectively confirm the simplicity of Dart. My programming background also
includes very high-level easy-to-write languages like Ruby, or modern functional
languages like Elixir, and Dart did not represent a stumbling block for me, rather
it made learning Flutter the main focus of my work.
In conclusion, for a company investing in Dart is a low risk operation. The
54
Flutter for Enterprise mobile app development
language is robust and open source, the concepts behind Dart are common to
many other languages so they easily become reusable knowledge. The ability to
compile and run Dart code on virtually any platform - desktop, browser and mobile
- can potentially help a company to keep development costs down by reducing the
number of people specializing in different programming languages. On the other
hand, from a programmer’s point of view Dart has great support for all the tools
needed to work, it is a language with an intuitive syntax, easy to learn and that
promotes productivity. These aspects are sufficient to promote Dart as a good
business investment, like other more known languages in the enterprise context.
1
https://developer.android.com/studio
55
Flutter for Enterprise mobile app development
use Android Studio on a Mac computer. Android Studio natively interfaces with
Apple’s Xcode and no further configuration is required.
As JetBrains is actively involved in the development of Android Studio and the
Flutter plugin, this plugin can also be used on other IDEs distributed by Jetbrains.
It is therefore possible to develop Flutter applications for example using IntelliJ,
which in fact is the second recommended IDE on the official Flutter page.
Similarly, for Visual Studio Code is enough simply to add the officially available
Flutter extension to start developing Flutter applications. A Mac computer is
always needed to build and emulate iOS. Visual Studio Code is for many developers
the main IDE to work, thanks to the quality of the extensions and the general
lightness and speed of the program when compared to Android Studio, well known
for its heaviness of execution. Are good news to be able to use it without having
to configure anything manually.
Finally, among the programs recommended by the official Flutter team, there
is also an online editor called DartPad2 . It is used to develop code on the fly
via browser or to share code snippets online. The Flutter documentation itself
makes extensive use of interactive examples built on this editor. Being online, no
configuration is required and one just need to access the web page to start writing
Dart code. Since late 2019, DartPad also supports creating Flutter applications
directly via the browser. Obviously DartPad does not completely replace all the
features of an IDE like Android Studio, but it is certainly a particularly fast and
simple add-on tool that can be handy in many situations.
The official Flutter page also reports the existence of plugins developed by the
Dart community for the Emacs and Vim editors and for the Eclipse IDE. It is
also possible to use Xcode as an IDE even if you need at least Flutter version
1.15 and Xcode 11.4 version or higher to have an automatic integration of the
framework. If not, some manual procedures are needed, well explained in the official
documentation3 .
For this thesis I developed all the practical examples of chapter 5 through
Android Studio and I was positively impressed by the completeness of the IDE. My
previous experience with Android Studio was based on development with Kotlin
and I had the initial perception that I would have a penalized experience of using
another language. In practice, however, I found that all the main features offered
by Flutter are well implemented and easy to use.
In conclusion, it is evident that every developer can find the most suitable
program for its work. Also, as all major and well-known IDEs are well supported,
each company can decide which editor or IDE is most convenient for its business
2
https://dartpad.dev
3
https://flutter.dev/docs/development/ios-project-migration
56
Flutter for Enterprise mobile app development
• End-to-end or integration testing verify the flow and the behaviour of two or
more components by combining them. An integration test generally runs in a
real context, which could be on a real device or an OS emulator for a mobile
app, or on a browser for a web app, and consists mainly of two pieces: the
source code - or app - itself, and the test code that puts the app through its
paces. In addition to testing the correctness of the execution, an integration
test often measures also performance and app behaviours on different devices
and OS.
Dart introduces two libraries to implement these 3 types of tests. The first book
is test4 , which provides a standard way of writing tests. This package can be used
to write groups of test using the test annotation. It offers features common to many
other language testing library as the @TestOn annotation which is used to restrict
tests to run on a specific environment or the @Tag annotation to group tests. A
tag also allows to create a custom configuration for some tests. Dart allows to write
asynchronous tests in the same way developers would write synchronous tests and
this helps testing commonly used Dart asynchronous features. Test configuration
in Dart is done through yaml file, creating a dart_test.yaml file. There could be
more than one configuration file and only the nearest one is used to configure the
current test.
The second most important test library of Dart is the mockito5 package. This
provides a simple way to create and configure mock objects, to use them in fixed
scenarios. Mocks are usually used to verify that the system under test interacts
with the mock object in expected ways.
Flutter introduces its features by building them on top of what Dart offers
and this is also visible in testing. Flutter adds two major libraries to simplify
cross-platform application testing. The first one is the flutter_test6 package which
adds several utility methods to test that a widget’s behaviour is as expected. It
allows to easily invoke the construction of a widget with the necessary parameters
and check that the result contains text, images or even whole widgets. For example,
take the case of a developer who wants to test a product listing page. The developer
4
https://pub.dev/packages/test
5
https://pub.dev/packages/mockito
6
https://api.flutter.dev/flutter/flutter_test/flutter_test-library.html
58
Flutter for Enterprise mobile app development
mock 3 products and can test with this library that the resulting list contains 3
item product list instances.
The second main package is the flutter_driver7 , which provides complex APIs
and methods to test and check Flutter applications that run on real devices or
emulators. This library runs the application in a separate process from testing and
allows to accurately simulate complex behaviours that an average user would do
with the application. In particular, flutter_driver allows developers to run complex
flows along the application and simulate both the short or long taps and the user’s
swipes.
By combining these various libraries it is possible to thoroughly test an app
written with Flutter. However, considering that a Flutter application can poten-
tially run on very different systems with equally different input mechanisms, test
management is still immature in my opinion. Compared to other cross-platform
frameworks however, Flutter has the advantage of relying little on the underly-
ing operating system because it implements everything by itself and this reduces
problems of incompatibility and complexity of the test suite. In conclusion, in a
business context, testing Flutter code is not trivial, but testing a single code base
can be a great incentive to test more and with more quality. The difficulties could
arise especially if a TDD - Test Driven Development - policy is applied.
7
https://api.flutter.dev/flutter/flutter_driver/flutter_driver-library.html
59
Flutter for Enterprise mobile app development
deal with defining a set of rules that the code must respect and highlight the invalid
portions of code to be corrected.
Linting primarily promotes two ways to improve code quality and efficiency.
The first is through the use of a common syntax. Many high-level languages allow
a developer to achieve the same result with multiple code implementations. For
example in Dart it is possible to declare a string with single quotes but also with
double quotes. The result is the same in both cases but it can be confusing to have
some strings with double quotes and some not. This suggests to an inexperienced
person that there is a difference between the two cases, when in reality the two
approaches are equal. The case of quotes is however simple: the advantage of
uniforming the syntax is more evident when applied to the whole set of analogous
diversities that can create ambiguity and differences. For example, a developer
might have a habit of initializing variables to null, or using camel-case or snake-case
indiscriminately, or always declaring the type of the variable even when not needed.
All these cases fall under the Style Rules heading. Defining at project level
which style rules you want to have allows the linter to advise developers on how to
change the code. It also allows to automate the correction of these errors with a
single command since usually the style rules are only aesthetic and not functional.
In the long run, the most important achievement for developers is being able to
read and change non-their code without running into style differences that increase
the chance of errors or misunderstood.
The second approach of linters is to improve code quality through advanced static
analysis. These are more complex techniques that include checks on compliance
with programming patterns, analysis of complexity metrics, support for multiple
safety and security-focused coding standards.
Dart’s main linting library is called lint 8 and was created by an independent
developer. Although this library has more than 200 rules that can be defined in
the configuration file, these rules are all about style rules. There are also other
linters but overall they are inferior in quality and capacity to the lint library.
In conclusion, compared to other languages, Dart linters are less mature and
less ready for a business context. Other languages, such as Ruby or Javascript, do
better by providing deeper and more intelligent static code analysis tools.
8
https://pub.dev/packages/lint
60
Flutter for Enterprise mobile app development
chapter 3, I start from the hypothesis that the actual performances of Flutter are
analogous to the native execution. The Flutter architectural model does not present
obvious weaknesses on the performance side, but rather the whole framework has
been designed and developed to maximize efficiency and avoid slowdowns common
to other cross-platform frameworks.
In some ways Flutter is much more like Unity than other cross-platform frame-
works like React Native. It does not rely on the high-level APIs that the underlying
operating system exposes to the outside, but works at a lower level, implementing
everything necessary from the ground up. Flutter implements its execution model
avoiding costly interactions from a performance point of view. The similarities with
Unity are bidirectional: some developers have long used Unity, which is a game
engine, to develop non-game applications with excellent results. In fact, there is a
library for Unity that simulates Flutter’s widget model to build UI elements9 .
Using Unity instead of a framework specifically designed for mobile applications
of course has its drawbacks. It’s hard to find ready-made solutions online and
help from other developers on channels like StackOverflow. There are dozens of
utility libraries for Flutter that reduce development time which do not exist for
Unity. Furthermore, as a non-gaming project in Unity grows, it is difficult to find
other staff already trained on the subject. Unity is a game engine and is great
for developing mobile games, not developing mobile applications. However, the
fact that some developers have used it confirms that so far the cross-platform
performance has not been up to the native.
However, the advantage of using Unity is to rely on a really fast framework on
any platform because it is built specifically to maintain high performance even in
stressful contexts using a single codebase. Flutter has the same goal and that is
why I am assuming that Flutter’s performance are good enough to match native
ones.
To evaluate performance, it is first necessary to identify what the analysis
consists of. There are different types of performance and each type relates to a
specific area of interest. In particular, these are sufficient to provide a satisfactory
picture of Flutter’s overall performance:
• Rendering speed - animation smoothness, frames per second while UI is
changed or some UI effects that take place in time;
• Computation speed - the speed of mathematical calculations and memory
manipulations for business logic.
To confirm my initial hypothesis on Flutter I will analyze the behaviour of the
framework with respect to the native execution in these 2 contexts. However, it
9
https://github.com/UnityTech/UIWidgets
61
Flutter for Enterprise mobile app development
Figure 4.1: the graph shows the FPS values of the same app in Flutter and React
Native.
Table 4.1: Performance for the execution of the same Android application made
of a list of a thousand elements in Kotlin, Flutter and React Native.
Table 4.2: Performance for the execution of the same iOS application made of a
list of a thousand elements in Swift, Flutter and React Native.
From the Table 4.1 it can be seen that the average FPS is excellent for all three
platforms and remains around 60 FPS. However, native execution is more efficient.
The Flutter application uses twice the CPU on average and takes up twice as
much memory as the native application. The performance of React Native is worse
and shows further consumption of hardware resources. For iOS, the situation is
slightly different[14]. From the Table 4.2 it is visible that even in this case the
FPS average is great for all three applications, but in this test Flutter is closer
to native execution. The GPU consumption column is necessary as Swift renders
the UI using the GPU. In fact, Swift and Flutter’s CPU and GPU usage rates
are similar but opposite. However, memory consumption is similar. React native
also in this case shows again a greater consumption of resources. The CPU and
memory consumption data are the most interesting of the test and are detailed in
the graph of Figure 4.2 and in graph of Figure 4.3.
Before drawing conclusions, I also analyze the second test case. This test consists
of a screen with two hundred images arranged in a grid. The performance test
applies graphic effects to the images at the same time: the two hundred images are
treated in groups of three images. In each group the first image is faded, the second
scaled and the last rotated. The execution platform is similar to the previous test
and also in this case the results are the average of several executions.
The results of the second test for Android are summarized in Table 4.3, while
for iOS are in Table 4.4
The results of Table 4.3 clearly show the advantages of native execution in
the Android context. The application in Kotlin is the only one that manages
to maintain an excellent FPS average, while both the Flutter and React Native
applications struggle to reach an acceptable FPS value, remaining below an average
64
Flutter for Enterprise mobile app development
Figure 4.2: CPU consumption comparison between the same application made
by a list of thousand elements written in Native, Flutter and React Native
Figure 4.3: Max memory usage comparison between the same application made
by a list of thousand elements written in Native, Flutter and React Native
of 20 FPS. Is also visible in this test that Flutter uses on average about double the
CPU and double the memory than a native application in Kotlin, without however
achieving an acceptable result. The situation is even worse for React Native[14].
In iOS the situation is different and more balanced. As can be seen from
65
Flutter for Enterprise mobile app development
Table 4.3: Performance for the execution of the same Android application made
of a grid of 200 animated images in Kotlin, Flutter and React Native.
Table 4.4: Performance for the execution of the same iOS application made of a
grid of 200 animated images in Swift, Flutter and React Native.
Table 4.4, the three applications - native, Flutter and React Native - manage all to
reach 60 FPS. However, the consumption of hardware resources remains in favor of
the native solution. Swift’s advantage over Flutter is less pronounced than Kotlin’s
advantage, and especially on the memory side there is a substantial draw. As with
the previous test, Flutter’s performance for the CPU has to be compared to the
GPU performance for Swift due to the different execution model.
It is important to underline that the performance of React Native appears to
be excessively negative compared to the native execution. The code of the React
Native applications has been implemented using only javascript code which is not
suitable for excessively expensive computational tasks. More on this topic in the
subsection 4.2.2. React Native allows you to easily integrate native code or use
natively implemented libraries for specific tasks. In this thesis, however, the interest
is not to analyze the performances obtainable by optimizing React Native. The
compiled approach chosen by Flutter easily allows to improve the performances
obtainable without using native code, external libraries or complex optimizations
compared to the React Native approach.
The data of this test are further analyzed in the following graphs: in Figure 4.4
for FPS average comparison, in Figure 4.5 for CPU usage comparison and in
Figure 4.6 for memory comparison.
Another interesting analysis is that made by Matilda Olsson in her thesis "A Com-
parison of Performance and Looks Between Flutter and Native Applications"[15].
To collect the data, Olsson created two very simple applications for Android and
iOS using native programming and the Flutter framework. The applications include
a twenty-item list and a bottom navigation bar. The test is performed manually
and consists in scrolling the list of 20 elements and navigating from the list screen
66
Flutter for Enterprise mobile app development
40
Average FPS
20
0
Android iOS
Figure 4.4: Average FPS comparison between the same Android and iOS applica-
tion made by a grid of 200 animated images written in Native, Flutter and React
Native
100
Percentage usage %
75
50
25
0
Android - CPU iOS - CPU iOS - GPU
Figure 4.5: CPU consumption comparison between the same Android and iOS
application made by a grid of 200 animated images written in Native, Flutter and
React Native
200
Max memory usage MB
150
100
50
0
Android iOS
Figure 4.6: Max memory usage comparison between the same Android and iOS
application made by a grid of 200 animated images written in Native, Flutter and
React Native
Figure 4.7.
Table 4.5: Table with CPU usage comparison obtained with several iterations on
Matilda Olsson’s test application made of a simple list page, a Home page and a
navigation bar.
The application developed by Olsson is very basic. There are no long lists,
changing widgets or complex animations. I integrated her work because the
data clearly shows that in this simple context Flutter’s performance is absolutely
analogous to native performance on both platforms[15]. This is in contrast to the
results obtained from previous articles and shows that Flutter’s scalability is not
yet at the level of native scalability.
Going deeper into Flutter’s scalability discourse, the next article I include in
this thesis analyzes this aspect. Andrea Bizzotto has made an article in which
he analyzes the creation of a stopwatch app in Flutter[16]. In particular, the
application consists of a text, which represents the current time elapsed from the
68
Flutter for Enterprise mobile app development
CPU usage % 30
20
10
0
CPU max usage % CPU min usage % CPU average usage
%
Figure 4.7: Graph with CPU usage comparison obtained with several iterations
on Matilda Olsson’s test application made of a simple list page, a Home page and
a navigation bar
start of the count, and two buttons: one to start the stopwatch and one to restart
the count.
The problem with scalability I expressed concern the application state manage-
ment which Bizzotto analyses. Flutter’s declarative approach dictates that the text
might change in response to the change of the internal state of the application,
which in this case represents the time passed so far. The stopwatch text is made up
of minutes, seconds and tenths of a second. The latter are managed as hundredths
of a second via software to improve accuracy. This implies that the internal state
of the application changes every 30ms and the interface is refreshed at each change.
Proper state management in this case is crucial. Every 30ms, the widget in
charge of state management will be redesigned, along with all its children. In the
first version of the application, Bizzotto enclosed the state management in the
outermost widget within the Scaffold widget. In the second iteration he moved the
management of the state to a custom widget that contains within it the text of
the stopwatch. Finally, in the third version, the application state has been further
divided between a new custom widget for the exclusive management of hundredths
of a second and the previous custom widget, now only responsible for managing
seconds and minutes.
In Figure 4.8 the evolution of state management is shown.
In his article, Bizzotto analyzed the CPU and memory consumption of the
stopwatch application written in Flutter in each of the 3 steps, and then compared
69
Flutter for Enterprise mobile app development
the data with the Apple stopwatch application inside iOS. The results are shown
in the graphs of Figure 4.9.
The results of Bizzotto analysis show that without a thoughtful management
of the state Flutter efficiency suffers. The first version of the Flutter application
consumes up to four times more CPU and memory than the native iOS application.
The situation improves by optimizing the number of widgets that need to be
redrawn at each state change. In the intermediate version the application, Flutter
application consumes about three times the resources of the native application,
while in the most optimized case it consumes only twice as much. The difference
in consumed resources is also evident in the number of allocated threads. The
Apple stopwatch application allocates a single thread while the Flutter application
allocates 3 threads in total: one for the event loop, one for I/O operations and
another one for UI refresh[16].
From the Bizzotto tests it emerges that the declarative approach consumes
more resources in the presence of frequent and continuous refreshes of the UI.
The comparison with the stopwatch application is deliberately disadvantageous
for Flutter, because the imperative approach allows to change the text of the
stopwatch with a simple assignment. The results also highlights the importance
of code quality in Flutter state management. It is important to note which UI
components must be updated at each frame and limit the interface update to as
few widgets as possible. This is a disadvantage of Flutter declarative approach and
needs attention from developers.
also considered that both Android and iOS native programming have been around
for more time and they have been incredibly optimized over the years to better
cooperate with the hardware. The Flutter framework starts well and can only
improve from this point on.
Dart Code (∼220 000 lines) Android - Kotlin iOS - Swift Other
98.30% 0.35% 0.25% 1%
Table 4.6: The table shows the distribution of programming languages in the
Ebay Motors team project. The code in Dart is shared between Android and iOS
applications.
72
Flutter for Enterprise mobile app development
The results of the work of the Ebay team show an incredible value of code shared
between the two platforms. Overall, the Dart code makes up 98.3% of the total
code. The native code represents just 0.6% of the total code and has been used only
for platform specific configuration and for icon and splash screen definition[18].
The second metric collected by the Ebay team concerns a survey carried out
among developers regarding the programming speed in Flutter. The results are
shown in the Figure 4.15 graph.
The survey results show that the vast majority of developers think that devel-
oping in Flutter has increased their productivity. The 70% of developers say that
development in Flutter is more than twice as fast as native programming. The
other interesting fact is that no developer in this sample has expressed the opinion
that developing in Flutter is slower.
In addition to the article by the Ebay team, I decided to integrate the data of
two videos published on Youtube respectively by the user Gabriel Basilio Brito[19]
and by the Smart Code App channel[20]. These two videos are conceptually similar
but were made 3 years apart from each other. The first was released in December
2018 and the second in January 2021. The videos show some time metrics in
common actions in software development by comparing the time needed by Flutter
and React Native to perform these action. The results of the Basilio Brito video are
shown in Table 4.7, while the results of the Smart Code App video are in Table 4.8
Table 4.7: Performance comparison for the same development tasks between a
Flutter app and a React native app made by Gabriel Basilio Brito[19]
The data from Table 4.7 and Table 4.8 show that on average Flutter takes less
time to perform common software development tasks. On average, to create a new
application, React Native needs ten times less it takes for Flutter. On the other
hand, in the case of the first app build, in Android Flutter takes about half the
time, while in iOS it takes about five times less. The time required in Flutter for
hot reload is also two to four times less than that required in React Native. Finally,
it is interesting to note that Flutter’s debug build weight is higher than its React
Native counterpart, but the final APK weight is lower for Flutter[19][20].
In conclusion, the data in this section show Flutter efficiency in optimizing
development times. This is definitely an advantage for developers who can be more
productive using Flutter than other technologies.
73
Flutter for Enterprise mobile app development
Table 4.8: Performance comparison for the same development tasks between a
Flutter app and a React native app performed by the channel Smart Code App[20]
other parts of the application will continue to work as before, while the Flutter
code can retrieve the data from the native backend and thus focus solely on UI
development. If the migration process goes well, a company can then decide to
go so far as to completely eliminate native code and build a stand-alone Flutter
app. Otherwise, if Flutter’s approach is not suitable for business purposes, you can
delete the Flutter library and related screens added to the native application.
The migration period can easily be more or less long depending on the knowledge
and size of the development team and the complexity of the native application. As
I showed in chapter 3, developing in Flutter generally requires fewer and simpler
lines of code. During the migration, the development team can get in touch with
the features and mechanisms of Flutter without immediately having to develop all
the knowledge necessary to build an application from scratch.
75
Flutter for Enterprise mobile app development
Column Column
Center Center
Button Button Button Button
Text
Redraw on:
Version 3 TimerPage - button press
- timer callback
(every 30ms)
Column
Container Row
Center
Button Button
TimerText
Figure 4.8: Widget tree evolution of the stopwatch application through 3 steps.
The red boxes indicate stateful widgets, the white ones mean stateless widgets.
76
Flutter for Enterprise mobile app development
30
20
10
0
CPU average % Memory max MB Memory min MB
Figure 4.9: Performance comparison between the iOS stopwatch and the Bizzotto
Flutter stopwatch application with each of the evolution steps of the widget tree[16]
1000
750
500
ms
250
0
Java Kotlin Flutter React native
Figure 4.10: Performance for the execution of the Borwein algorithm in Android
between Java, Kotlin, Flutter and React Native implementations.
77
Flutter for Enterprise mobile app development
4000
3000
2000
ms
1000
0
Java Kotlin Flutter React native
600
400
ms
200
0
Objective-C Swift Flutter React native
Figure 4.12: Performance for the execution of the Borwein algorithm in iOS
between Objective-C, Swift, Flutter and React Native implementations.
78
Flutter for Enterprise mobile app development
3000
2000
ms
1000
0
Objective-C Swift Flutter React native
Android - Kotlin
iOS - Swift
Other
Figure 4.14: The graph shows the distribution of programming languages in the
Ebay Motors team project. The code in Dart is shared between Android and iOS
applications.
79
Flutter for Enterprise mobile app development
80%
60%
Percentage %
40%
20%
0%
Slower About the same speed A little bit faster More than twice as fast
Figure 4.15: The graph shows the results of the Ebay Motors internal team
survey on how fast programming was in Flutter compared to Native programming.
80
Chapter 5
Implementation of a real
case scenario
In this chapter I analyze two real use cases to practice some of the key aspects of
Flutter that I showed in chapter 3. The first case concerns the construction of a
section of an application in charge to show the user’s personal information. The
second case instead consists of a screen for personal financial management. In both
cases the focus is on programming, but with different objectives. In the first case -
section 5.1 - the goal is to build a custom layout using the widgets provided by
Flutter, while in the second use case - section 5.2 - the goal is to generate some
graphs to show financial data using native Flutter or a library available for Flutter.
are all BottomNavigationBar widgets. The order and style of the buttons depends
on the customization you apply and I used it to recreate the bottom navigation
bar of the mockup. Finally, the onTap field wants a function, which in my case
only takes care of updating the index of the widget list of the body field so that
another page is shown.
Figure 5.1: The figure represents the mockup of the user page created by the
product team. This screen contains all the personal information that can be used
by the user. The central card represents the experience that the user has achieved
using the application.
The profile page consists of an header and a tab section below. The header
contains the user’s profile photo together with the name and tax code. Immediately
below there are info about user experience with the digital products. The tab
section it is divided into three pages that include personal data, activated products
and user documents.
The header is built with a Column Widget which has as children three widgets.
82
Implementation of a real case scenario
The first is the round profile photo with the photo change button which is built
with a Stack widget. This widget allows to graphically overlay multiple widgets.
In my case there is a CircleAvatar widget and a RawButton widget. The first is
a native Flutter widget that allows in a very simple way to have a round profile
photo. On the other hand, I had to create by hand the button to equal the mockup
because the pre-made buttons were not customizable enough. Anyway, it was still
easy to realize the button because the documentation is clear and exhaustive. The
code for these two widgets is shown in Listing 5.1. At the company level, it is
advisable to define a custom class that allows developers to use the customized
button in several parts of the code without redefining it.
Listing 5.1: Header section - circle avatar and modal button to trigger the avatar
photo change.
1 // C i r c l e A v a t a r w i d g e t wants o n l y t h e background image − AssetImage
w i d g e t w i t h i n t h e r e s o u r c e path − and t h e image r a d i u s
2 CircleAvatar (
3 radius : 50 ,
4 backgroundImage : AssetImage ( _image . path ) ,
5 )
6 // The raw button w i d g e t must be c o n f i g u r e d i n both g r a p h i c a l a s p e c t
and b e h a v i o u r . I t i s t o t a l l y g r a p h i c a l l y c u s t o m i z a b l e but many
f i e l d s have t o be d e f i n e d .
7 c h i l d : RawMaterialButton (
8 o n P r e s s e d : ( ) async {
9 await _dialogCall ( context ) ;
10 },
11 elevation : 2.0 ,
12 f i l l C o l o r : C o l o r s . white ,
13 c h i l d : Icon (
14 I c o n s . camera_alt ,
15 size : 20.0 ,
16 c o l o r : C o l o r s . green ,
17 ),
18 padding : E d g e I n s e t s . a l l ( 8 ) ,
19 shape : C i r c l e B o r d e r ( s i d e : B o r d e r S i d e ( width : 2 , c o l o r : C o l o r s .
green ) ) ,
20 m a t e r i a l T a p T a r g e t S i z e : M a t e r i a l T a p T a r g e t S i z e . shrinkWrap ,
21 c o n s t r a i n t s : B o x C o n s t r a i n t s ( minWidth : 0 )
22 )
In the header there is also a custom card that represents the user’s digital
experience. The product team inserted it to entice the customer to explore the
products and security features of the application. The biggest technical difficulty
of this widget is only the experience bar. I made it with a list of transparent but
edged containers on a bar made with a FractionallySizedBox widget. The length
and colors of the bar depend on the user experience. Colors are selected from four
83
Implementation of a real case scenario
The tab section is built with native Flutter components, but has been deeply
customized by me. In Flutter it is not trivial to insert a TabView inside other
widgets using the DefaultTabController. To do it, I need to decompose the
TabView into the TabBar and the related views. The whole page is therefore
structured as a Column widget composed of the ProfileHeader, the TabBar and the
views of the TabBar. The TabBar requires a custom TabController which, however,
can be easily created from the basic TabController. The most important thing is
to define the method to update the body of the TabBar. This method updates the
index after the user click or swipe on the interface. The views of the TabBar are
very simple and are all defined as a list of items, where each item is composed of a
flat card with icon, name of the field and the respective value. Each section can be
modified with the appropriate button.
To complete the layout and have all the necessary space inside each view of
the external BottomNavigationBar, the layout of each page is contained entirely
within a CustomScrollView. This widget supports the Sliver field which requires
a SliverAppBar and a SliverList. The SliverList wants as input a list of widgets
as children. This system allows me to simultaneously manage the behaviour of
the screen scroll and the behaviour of the AppBar as I prefer. I automatically
determine the height of the list by delegating the decision to the children of the
list.
Overall, the most difficult aspect in creating the layout was automatically
defining the height of the screen. Being made up of columns and lists, there is in
principle no widget that sets a maximum height. The initial solution was only one:
84
Implementation of a real case scenario
manually set the overall height. But I wanted to deduce the height automatically
from the number of components on the screen. To solve this problem it was enough
to use the properties of the Container widget, which is built by default to have the
minimum size equal to the space needed by its child. Each of the nested elements
of the TabBar was then enclosed in a Container and in this way the List widget is
able to deduce the necessary space from its children, to uniquely determine the
overall height and finally to communicate it to the external CustomScrollView.
Using a CustomScrollView allowed me to keep the bottom navigation bar always
visible by scrolling within the view. Instead the AppBar is fixed at the top of the
internal view and disappears by scrolling. The code of the layout can be consulted
in Listing 5.3. The result of this work is visible in Figure 5.2. As you can see from
this image, the result is very close to the mockup. Although I had no previous
Flutter experience, it was easy to manipulate the framework’s native widgets. The
UI as code concept is well expressed. It is intuitive to program the UI thanks to
the expressiveness of the native widgets. These widgets allow even with minimal
knowledge to build advanced interfaces.
Listing 5.3: The code in the figure represents the skeleton of the profile screen
within one of the views of the BottomNavigationBar.
1 c l a s s ProfileContainer extends StatelessWidget {
2
3 @override
4 Widget b u i l d ( Bu i l d Co n t e xt c o n t e x t ) {
5 return Scaffold (
6 body : CustomScrollView (
7 s l i v e r s : <Widget >[
8 SliverAppBar (
9 // b e h a v i o u r and UI f o r t h e AppBar
10 f l e x i b l e S p a c e : Row ( . . . ) ,
11 ),
12 // L i s t with a s i n g l e c h i l d which d e c i d e s t h e s i z e f o r t h e
whole s c r e e n
13 SliverList (
14 d e l e g a t e : S l i v e r C h i l d B u i l d e r D e l e g a t e ( (_, i ) {
15 return P r o f i l e () ;
16 },
17 childCount : 1
18 ),
19 ),
20 ],
21 ),
22 );
23 }
24 }
Finally, I have included the state management for the user. I modeled the
85
Implementation of a real case scenario
Figure 5.2: The figure represents the interface created by me following the design
of Figure 5.1. All the features present in the mockup have been integrated, including
internal navigation.
code to work with a user model which I have defined and which contains all the
necessary fields. The user model is visible in Listing 5.4. The user is shared with
the Flutter Provider pattern. I have defined a class UserProvider which keeps
a User variable as an internal state and extends the ChangeNotifier Class. The
UserProvider class and its methods are shown in Listing 5.5. The provider can
notify any widget that subscribes to the state of the object. If there is a change in
the state of the user object, this change will be propagated to all dependent views.
A widget to retrieve the need status of the Provider class and takes the status of
the user with the code shown in Listing 5.6. This code retrieves a top widget in
the widget tree that has a Provider available. In my simple case I used the root
widget to define the Provider. The code is shown in Listing 5.7.
86
Implementation of a real case scenario
Listing 5.5: UserProvider class which includes the getUser() and setUser() meth-
ods.
1 c l a s s UserProvider extends ChangeNotifier {
2 var _user = User ( ) ;
3
4 User g e t U s e r ( ) {
5 r e t u r n _user ;
6 }
7
8 v o i d s e t U s e r ( User newUser ) {
9 _user = newUser ;
10 notifyListeners () ;
11 }
12
13 }
Listing 5.6: Provider method to retrieve from a parent widget an instance of the
shared state.
1 var u s e r = P r o v i d e r . of <U s e r P r o v i d e r >( c o n t e x t , l i s t e n : f a l s e ) . g e t U s e r
() ;
2 }
Listing 5.7: MultiProvider widget is in charge to initialize the Provider. It’s child
subtree will have access to this Provider.
1 return MultiProvider (
2 providers : [ ChangeNotifierProvider . value ( value : UserProvider () )
],
3 c h i l d : MaterialApp (
4 t i t l e : ’ P r o f i l e Demo ’ ,
5 theme : . . . ,
87
Implementation of a real case scenario
6 home : HomeContainer ( ) ,
7 ),
8 )
Figure 5.3: The figure shows the donut chart realized by the IrisCube Reply
development team. This is the vertical version of this chart. In this version, the
labels no not include the amount related to the represented category.
Figure 5.4: This is the horizontal version of the chart presented in Figure 5.3. In
this version, the labels include the amount related to the represented category.
Figure 5.5: The figure shows the interface of the account balance management.
Here is possible to see the graphic representation of the nack account. The graph
is scrollable and the user can select the date interval.
options that Flutter provides, I focused my work on the Flutter native library and
a third parties library.
To make these two graphs I explored the possibilities offered by native Flutter
and some libraries. Flutter’s main chart library is charts\_flutter1 . This library
has not yet reached version 1.0 and does not have null_safety support - required
only from version 2.7 of the Flutter framework. This library offers bar, time, pie and
line charts. Furthermore, it allows to customize the axes, the labels and the graphic
aspects. However the library is still a work in progress. I found it difficult to create
1
https://pub.dev/packages/charts_flutter
89
Implementation of a real case scenario
even just the pie chart respecting the specifications of the mockup. The library is
not versatile enough and is only good for very simple and not very technical graphs.
So I tried a different library. Among the Flutter packages I found the third party
pie_chart library. This library offers a simple and very versatile way to customize
pie or donut charts. The result is exceptional and allows me to create the first of
the two graphics of the application.
The results of my programming show that it is really easy to make a donut
chart. The chart can be dynamically animated by updating the data source. An
animation time determines the speed of change. Since it is possible to customize
the legend, the labels, the colors, the general dimensions and the animation time,
this library makes it possible to fully respect the graphic defined by the product
team. I have included the chart structure code in Listing 5.8, while the final chart
result is available in Figure 5.6. In the Figure 5.7, on the other hand, the version of
the horizontal chart is visible. To have different widgets based on the orientation of
the phone, it simply enough to encapsulate the screen in a OrientationBuilder
widget. In Listing 5.9 I have included a very simple code snippet that uses the
OrientationBuilder input parameter orientation to determine which widget to
use.
Listing 5.8: Code structure for a donut chart to represent expenses for a user
account
1 Scaffold (
2 appBar : . . .
3 ),
4 body :
5 ...
6 c h i l d : PieChart (
7 dataMap : expensesData ,
8 a n i m a t i o n D u r a t i o n : Duration ( m i l l i s e c o n d s : 8 0 0 ) ,
9 c h a r t R a d i u s : MediaQuery . o f ( c o n t e x t ) . s i z e . width / 3 ,
10 c e n t e r T e x t : TotalExpensesWidget ( expensesData ) ,
11 l e g e n d O p t i o n s : LegendOptions ( . . . ) ,
12 c o l o r L i s t : C o l o r L i s t ( expensesData ) ,
13 c h a r t V a l u e s O p t i o n s : ChartValuesOptions ( . . . ) ,
14 ),
15 ...
16 )
90
Implementation of a real case scenario
Figure 5.6: The figure shows the graphical result of creating a donut chart.
Listing 5.9: Code structure for an OrientationBuilder widget inside the pie chart
screen
1 c l a s s PieChartScreen extends StatelessWidget {
2 c o n s t P i e C h a r t S c r e e n ( { Key? key } ) : s u p e r ( key : key ) ;
3
4 @override
5 Widget b u i l d ( Bu i l d Co n t e xt c o n t e x t ) {
6 return OrientationBuilder (
7 b u i l d e r : ( context , o r i e n t a t i o n ) {
8 r e t u r n o r i e n t a t i o n == O r i e n t a t i o n . p o r t r a i t
9 ? PieChartVertical ()
10 : PieChartHorizontal () ;
11 },
12 );
13 }
14 }
Subsequently, I made the chart for personal finance management. This chart is
technically an interactive way to show the user account data. In the case of the
native application, this graph was created specifically by the development team. In
my analysis I was interested in understanding if it was possible to make it with
Flutter pre-made components or with some third parties library, in order to avoid
to program myself from scratch. I have explored several alternatives, and I found
a package in particular which have returned positive results. I considered the
library syncfusion_flutter_charts2 created by SyncFusion3 . This company has
2
https://pub.dev/packages/syncfusion_flutter_charts
3
https://www.syncfusion.com/
91
Implementation of a real case scenario
Figure 5.7: The figure shows the graphical result of creating a donut chart within
label data in the horizontal view.
released its own charting library that is incredibly comprehensive. This library
allows to create all types of graphs available through the native Flutter library
and also adds pyramids and radial gauge graphs. The library also provides other
components but these are not related to the discussion of graphs.
Using this library I tried to make a solution similar to the mockup. The graph
in Figure 5.5 is a scrollable list. On the x axis there are the days. On the vertical
axis there is the value range for the bank account. The max value of the range is
defined by the greatest element of the data array. The chart is an area chart; the
color of the area is a white to blue gradient. The color of the top line is the same
as the area but with 100% opacity. The day selector is fixed in the center of the
page and the intersection of the selector with the chart area value is a circle. I
started by defining an array of data that I pass as a status to the graph’s container
widget. I defined it as a simple map with the key on the day and the value of the
current account as the value.
For the realization I started from the library code for the creation of a AreaChart
widget. Inside this widget I have inserted a scrolling graph. The x-axis takes the
keys of the data list that I made with the data of each day from January 15th to
March 15th. In Listing 5.10 I show the structure of the Cartesian graph. It can be
seen how the structure is strongly generic. In my case, the _getDateTimeSeries()
field returns a AreaSeries widget, which will have the task of drawing the graph on
the screen. Each type of series has a different behaviour. The library documentation
is complete and this helped me understand what to do to define the graph and
the behaviour of the center pointer. This component is a Trackball widget in the
syncfusion library.
The code for the trackball structure can be seen in Listing 5.11. I got the
scrolling functionality by encapsulating the time list in a InfiniteScrolling
92
Implementation of a real case scenario
graph. In my case, this graph is responsible for showing only part of the list. The
data loading is asynchronous, so it would be possible to integrate this graph directly
with the http API to make the data request. While waiting for data, a circular
loading bar is shown in the graph. In my simple case, however, the data is all local
and there is no waiting.
Listing 5.10: Code for the definition of a Cartesian graph. This structure is
common to all types of Cartesian graphs in the library. By customizing the various
fields of the graph it is possible to obtain any combination of behaviours provided
by the library.
1 S f C a r t e s i a n C h a r t _buildDateTimeAxisChart ( ) {
2 return SfCartesianChart (
3 plotAreaBorderWidth : 0 ,
4 t i t l e : ChartTitle (
5 t e x t : ’ Conto $ {_accountNumber } ’ ) ,
6 primaryXAxis :
7 DateTimeAxis ( m a j o r G r i d L i n e s : c o n s t MajorGridLines ( width :
0) ) ,
8 primaryYAxis : NumericAxis (
9 minimum : 0 ,
10 maximum : _maxAccountValue + 2 0 0 0 0 ,
11 i n t e r v a l : 20000 ,
12 a x i s L i n e : c o n s t A x i s L i n e ( width : 0 ) ,
13 majorTickLines : const MajorTickLines ( s i z e : 0) ,
14
15 ),
16 s e r i e s : _getDateTimeSeries ( ) ,
17 t r a c k b a l l b e h a v i o u r : _myTrackballbehaviour ) ;
18 }
93
Implementation of a real case scenario
14 t o o l t i p A l i g n m e n t : ChartAlignment . c e n t e r ,
15 t o o l t i p D i s p l a y M o d e : T ra ck ba ll Di sp la yMo de . f l o a t A l l P o i n t s ,
16 t o o l t i p S e t t i n g s : I n t e r a c t i v e T o o l t i p ( format : ’ p o i n t . y ’ ) ,
17 shouldAlwaysShow : t r u e ,
18 );
Figure 5.8: The figure shows the graphical result of creating a donut chart within
label data in the horizontal view.
The result of this programming is shown in Figure 5.8. Overall, the result
obtained for the creation of the mockup - Figure 5.5 - is a satisfactory outcome.
94
Implementation of a real case scenario
95
Chapter 6
Results
In this chapter I show the results obtained from the Flutter analysis carried out in
the previous chapters. The results shown here summarize the partial conclusions
reached by talking about the various topics of this thesis.
graphically helps in my opinion to keep the code cohesive. In most cases, you
should never exit the same file for all the code concerning a part of the UI. I
therefore consider the Flutter solution to be flexible and suitable both in simple
cases and in more complex cases with local and remote information. Flutter state
management requires design by developers to be implemented correctly, but it pays
off in the long run by simplicity.
In chapter 4 I analyzed the Flutter framework in a business context to answer
the main question of this thesis: whether or not Flutter is suitable for a company
active in the field of mobile applications development. The conclusions show lights
and shadows, I go in order to summarize them.
In the subsection 4.1.1 section, I analyzed Dart, the programming language
behind Flutter. Dart is an excellent language that takes the best from other more
popular languages, while maintaining a simple, clean and expressive syntax. It is
no coincidence that from the surveys carried out annually by StackOverflow, Dart
appears in sixth place in the ranking of the most popular programming languages
of 2020 [21]. Dart is positioned behind Kotlin, who occupies the fourth place, but
ahead of Swift, eighth place, and Javascript, ninth place. However, Dart is far
ahead in this ranking compared to Java and Objective-C, considered by many
experts languages that will disappear over time in favor of more modern solutions.
In addition, Dart provides a set of development tools, in addition to Flutter’s
tools, which create a complete development environment. In the sections 4.1.2, 4.1.3
and 4.1.4 I have analyzed in detail some of the essential tools for developing code
in an enterprise environment. I started by analyzing the test suite I described as
comprehensive to cover the main types of testing - unit tests, component tests, and
e2e tests. The result of the analysis is positive because Flutter introduces high-level
methods to test the UI and, in addition, having a single codebase, incentives to
better test the code. Testing is evidently one of the advantages of Flutter. Even
from the analysis on the editors that support the features of Dart and Flutter, no
criticalities appear.
Instead, for the quality of the code, Dart does not yet have tools as advanced
as other languages. I pointed out in subsection 4.1.4 that there are libraries to
guarantee style rules and syntax uniformity, but there are no libraries for advanced
static code analysis. Furthermore, in the enterprise environment it is common to
rely on advanced tools for automatic documentation, performance analysis and
continuos integration. Dart is still a marginal language on the world scene and
has received less attention than more famous languages such as Javascript. This is
certainly a disadvantage right now, but the situation is likely to improve quickly in
the future.
In section 4.2 I analyzed the performances of the Flutter framework by dividing
the work into two main parts: in the first section, subsection 4.2.1, the analysis
concerns the graphic performance of the framework, while in the second section,
98
Results
1
https://killedbygoogle.com/
100
Results
there are discussions on the web about why these two systems promoted by Google
are so similar 2 . Investing in Flutter could therefore pose a risk.
However, the results on Flutter’s analysis can only be positive and the prospects
for the future are many. In chapter 7 I offer some ideas for reflections on possible
developments.
2
https://discuss.kotlinlang.org/t/google-flutter-what-could-be-their-reason-
for-choosing-dart-over-kotlin/21179
101
Results
Overall, it took me just over a week to complete the first of the mockups I
presented in the section 5.1 in Flutter. This was enough time to introduce the
internal app navigation, the data list and the custom card-shaped widget within
the dynamic user experience bar. My experience has therefore been positive and I
confirm that I have encountered few obstacles. I confirm this experience also with
the second project presented in section 5.2. This was of course more difficult in
complexity than in the first case, but the increase in difficulty was proportional to
the problem and not to the code. After understanding the Flutter mechanisms,
it is easy to increase the complexity of the code and keep up to date with the
innovations introduced.
I personally also enjoyed Flutter’s integration with Android Studio. There are
two shortcuts for generating a new widget template, which are stless and stful.
In addition, a context menu that can be called up with alt + enter allows you
to add or remove a widget from the tree by generating or adapting its keywords.
Productivity is high when writing in Flutter on Android Studio. Finally, it’s easy
to set up the debugger and run code on a physical device or emulator. Building
an application in Flutter means seeing it evolve on the phone display. It is very
convenient with the hot-reload to instantly reload minor changes and evaluate
their effectiveness. In general, Flutter applications seem to be more aesthetically
pleasing thanks to this ease of developing them in real time.
102
Chapter 7
It is a good choice for both small and large companies. Flutter allows companies
to reach both mobile operating systems - Android and iOS - without the need for
dedicated teams. The development environment is complete with the main tools
needed by developers and the very comprehensive documentation reduces learning
time. Sure, Flutter can capitalize on the benefits of cross-platform development
- speed up software development and keep costs down - without introducing
disadvantages over native programming.
However, Flutter is still a young framework with several aspects that can be
improved upon. One of the possible future works is to follow the development of
the framework trying to include more of the mobile ecosystem among the supported
devices. Right now the main lack is the impossibility of easily implementing
applications for wearables, or the integration with TV systems and with Android
and iOS home widgets. In addition, the development team has already planned
some updates to further improve the performance of the framework and simplify
the use of the most common structures of each mobile application. So another
future work is to delve further into the performance analysis. It would be necessary
to create a more complex application in parallel between native and Flutter to
collect some metrics: how long the development took, how much space the final
application occupies, what the execution performance is and what users think of
it as user experience. Unfortunately, these metrics were impossible to collect in
this thesis alone and therefore a new experiment would be interesting to further
explore the performance aspect.
The future developments I’ve described may help to better understand Flutter
and cross-platform development in general. In the desktop and web environment it
is common to develop a single codebase to cover multiple devices, while in the mobile
environment this trend is less widespread. Flutter seems to be the beginning of a
change in trend and it is necessary to follow its developments to better understand
the phenomenon. Of course, it will also be Google’s job to continue to support and
promote Flutter so that it can establish itself as an industry standard.
104
Bibliography
[1] Andreas Biørn-Hansen, Christoph Rieger, Tor-Morten Grønli, Tim A. Ma-
jchrzak, and Gheorghita Ghinea. «An empirical investigation of performance
overhead in cross-platform mobile development frameworks». In: Empirical
Software Engineering (July 2020). doi: 10 . 1007 / s10664 - 020 - 09827 - 6
(cit. on p. 11).
[2] Dave Francis. Is Drifty’s Ionic dead? https://foresightmobile.com/blog/
ionic. Accessed: 2021-06-29. Dec. 2020 (cit. on p. 15).
[3] Dave Francis. Everything that is wrong with Xamarin and why it is bad for
you. https://foresightmobile.com/blog/2020/09/15/isxamarindead.
Accessed: 2021-06-29. Sept. 2020 (cit. on p. 16).
[4] Jan Rabe. Everything that is wrong with Xamarin and why it is bad for
you. https://medium.com/@kibotu/everything-that-is-wrong-with-
xamarin-and-why-it-is-bad-for-you-a5399075e50a. Accessed: 2021-06-
29. Oct. 2018 (cit. on p. 17).
[5] Flutter developers. How big is the Flutter engine? https://flutter.dev/
docs/resources/faq#how-big-is-the-flutter-engine. Accessed: 2021-
06-29. 2021 (cit. on pp. 20, 39).
[6] Dan Abramov. Basic Reducer Structure and State Shape. https://redux.
js . org / recipes / structuring - reducers / basic - reducer - structure.
Accessed: 2021-06-29. 2015 (cit. on p. 22).
[7] Ian Mundy. Declarative vs Imperative Programming. https://codeburst.
io/declarative-vs-imperative-programming-a8a7c93d9ad2. Accessed:
2021-06-29. Feb. 2017 (cit. on p. 23).
[8] Michael Goderbauer. Navigator 2.0 and Router. https : / / docs . google
. com / document / d / 1Q0jx0l4 - xymph9O6zLaOY4d _ f7YFpNWX _ eGbzYxr9wY.
Accessed: 2021-06-29. Oct. 2019 (cit. on pp. 35, 36).
105
BIBLIOGRAPHY
[9] John Ryan. Learning Flutter’s new navigation and routing system. https:
/ / medium . com / flutter / learning - flutters - new - navigation - and -
routing-system-7c9068155ade. Accessed: 2021-06-29. Sept. 2020 (cit. on
p. 36).
[10] Mikkel Rawn. Flutter Platform Channels. https://medium.com/flutter/
flutter- platform- channels- ce7f540a104e. Accessed: 2021-06-29. Aug.
2018 (cit. on p. 48).
[11] Souvik Biswas. Flutter: Building Wear OS app. https://medium.com/flut
ter-community/flutter-building-wearos-app-fedf0f06d1b4. Accessed:
2021-06-29. June 2019 (cit. on p. 50).
[12] Rafael Ferraz. Flutter + Apple Watch — Swift. https : / / medium . com /
@ferrazrx/flutter-apple-watch-swift-b43110dc4545. Accessed: 2021-
06-29. Dec. 2019 (cit. on p. 50).
[13] Jozef Petro. Flutter and React Native performance overview. https://su
dolabs.io/blog/flutter- and- react- native- performance- overview.
Accessed: 2021-06-29. 2020 (cit. on p. 62).
[14] InVerita. Flutter vs React Native vs Native: Deep Performance Comparison.
https://medium.com/swlh/flutter-vs-react-native-vs-native-deep-
performance-comparison-990b90c11433. Accessed: 2021-06-29. June 2020
(cit. on pp. 62, 64, 65).
[15] Matilda Olsson. «A Comparison of Performance and Looks Between Flutter
and Native Applications: When to prefer Flutter over native in mobile appli-
cation development». MA thesis. Blekinge Institute of Technology, June 2020
(cit. on pp. 66, 68).
[16] Andrea Bizzotto. How fast is Flutter? I built a stopwatch app to find out.
https://www.freecodecamp.org/news/how-fast-is-flutter-i-built-
a- stopwatch- app- to- find- out- 9956fa0e40bd/. Accessed: 2021-06-29.
Mar. 2018 (cit. on pp. 68, 70, 77).
[17] Ihor Demedyuk and Nazar Tsybulskyi. Flutter vs Native vs React-Native:
Examining performance. https://medium.com/swlh/flutter- vs- nativ
e- vs- react- native- examining- performance- 31338f081980. Accessed:
2021-06-29. Mar. 2020 (cit. on pp. 70, 71).
[18] Corey Sprague and Larry McKenzie. eBay Motors: Accelerating With Flutter™.
https://tech.ebayinc.com/product/ebay-motors-accelerating-with-
fluttertm/. Accessed: 2021-06-29. Sept. 2020 (cit. on pp. 72, 73).
[19] Gabriel Basilio Brito. React Native vs Flutter: Android Benchmark. https:
//www.youtube.com/watch?v=brXMXcscrg0. Dec. 2018 (cit. on p. 73).
106
BIBLIOGRAPHY
[20] Smart Code App. React Native vs Flutter: Android Benchmark. FlutterVsRe
actNative|PerformanceandBuildsizeBenchmark. Jan. 2021 (cit. on pp. 73,
74).
[21] Stackoverflow developers. Most Loved, Dreaded, and Wanted. https://insigh
ts.stackoverflow.com/survey/2020#technology-most-loved-dreaded-
and-wanted-languages-loved. Accessed: 2021-06-29. 2020 (cit. on p. 98).
[22] Nix developers. React Native vs. Flutter: What is Better for App Development
in 2021? https://nix- united.com/blog/flutter- vs- react- native/.
Accessed: 2021-06-29. 2020 (cit. on p. 100).
107