Why native app developers should take a serious look at Flutter

34,405 reads

Before I start, in case you haven’t heard about it, Flutter is an app SDK (backed by Google) to build ‘modern mobile apps’. It is still in alpha, but comes with great documentation and tooling, with some production apps already out.

I am a hobbyist developer, and I recently launched an app using Flutter myself. It was such a pleasurable experience, that I had to share why I liked it so much.

A little backstory…

The app I made (called Chips of Fury) is a virtual poker chips app (now live on the Play store and App store). It is for those times when you want to play poker (or any other card game) with friends, but don’t have a poker chip set available. So it is a non-trivial multi-player app, which requires real time sync between all devices as players play turns, with plenty of custom UI elements.

It took me about 1.5 months to code ‘Chips of Fury’ end-to-end, including my time spent learning Flutter. In my previous attempts to launch this app, I tried writing native apps in Android/Java and iOS/Swift, but left them both at varying degrees of completion. I just couldn’t get them to a ‘good enough to launch’ stage. The reason was mostly boredom, of doing the grunt work one needs to do to finish a project. So Flutter not only helped me build fast, more importantly, it helped me actually finish. It was that much fun! Needless to say, I’m a Flutter fan now.

So is it like React Native?

I bumped across Flutter 5–6 months back, and at that time I had bookmarked it for a future weekend of hacking. I did not delve into much detail then, and mentally bucketed it into the same category as React Native.

On a cursory look, the React Native comparison is pretty obvious. But on an ideological level, the similarities with RN start and end with the reactive programming model. IMHO, Flutter takes a generational leap forward in terms of the ideas it has implemented upon. Just to be clear, I’ve tried using React only a couple of times, for learning purposes mostly. So I am not going to compare Flutter and React. In fact, I have a feeling that most developers are _not_ looking at Flutter seriously enough because of this mental bucketing with RN.

My goal is to try and convince native app developers (iOS/Swift/ObjC or Android/Java/Kotlin) to give Flutter a serious look. The fact that the same code can run on Android and iOS is arguably not the biggest reason why Flutter will impress you the most. Without further ado, here are the reasons why I feel Flutter is what the future of app development will look like. Listen up yo!

1. It is all Dart

Dart what? 😕. This is a common reaction, when I talk about Flutter to other fellow developers. And to be honest, Dart does not offer much of a syntactical advantage compared to Swift or Kotlin.

However, I just wanted to stress on this point, because I think Google under-estimates how many developers don’t try out a new tool when they think they’ll have to learn a new language. So much so, that it wouldn’t matter even if it is technically superior.

So in Dart’s defence, it is a simple language to learn, and is unlikely to be a deterrent for trying Flutter out. Have a look at this sample class in Dart.

class Vehicle {

final int numberOfWheels;

final double mileage;

final double horsePower;



int speed = 0;



Vehicle(this.numberOfWheels, this.mileage, this.horsePower);



void accelerate() {

speed = speed + 1;

}



void decelerate() {

speed = speed - 1;

}



void brake() {

speed = 0;

}

}

As you can see, it is pretty straightforward to read with syntax similar to most popular object oriented languages. Of course, there is some language specific syntactical sugar, and you can start writing code that is more concise once you learn the language, but my point is that the learning curve is rather small.

In fact, if you have not tried Swift/Kotlin, then Dart might be a good alternative to play around with.

In Flutter’s defence: Google/Flutter had good reason to choose Dart. The way Flutter works, it needs to create and destroy a large number of short lived objects very fast. Dart’s ‘garbage collection’ is very well suited for such a use case. And it shows in the performance of the apps that can be built using Flutter.

So assuming that you’ll not be writing off Flutter just because of Dart, let me proceed further.

2. It is all Dart — Part II

If you develop apps for Android or iOS, following IDE workflows are common-

Android Studio / XCode development flow when building screen layouts

And you’re also probably familiar with markup such as the following-

Layout XML’s / JSX (React native)

In Flutter, layouts are defined using Dart code only. There is no XML / templating language. There’s no visual designer/storyboarding tool either.

My hunch is, upon hearing this, a number of you might even cringe a little. Prima facie, that was my reaction too. Isn’t it easier to do layouts using a visual tool. Wouldn’t writing all kinds of constraint logic in code make things overly complicated?

The answer for me turned out to be no. And boy! what an eye opener it has been. Let me try and make that case.

First of all.. meet Mr. Hot Reload.

Here is a sample with app code and the emulator side by side. This is a totally custom layout with a top and bottom bar and a middle container for the content. The code you see is the _whole_ code needed to run the app.

There is no separate layout xml, no xib file etc. The changes you make in code can be hot reloaded instantly. Hot reloads even preserve your application state, so you don’t even have to click and reach the screen you were on to proceed. I can’t stress enough how this is light years ahead of Android’s Instant Run, or any similar solution. It just works, even on large non-trivial apps. And it is crazy fast. That’s the power of Dart for you.

In practice, that makes a visual editor interface redundant. I did not miss XCode’s rather nice auto layout at all.

And as a result, I have been way more productive writing layouts in Flutter (Dart) than either Android/XCode. Once you get the hang of it (for me that meant a couple of weeks), there is a substantial overhead reduction because of very little context switching that is happening. One does not have to switch to a design mode, and pick a mouse and start clicking around. And then wondering if something has to be done programmatically, how to achieve that etc. Everything is programmatic. And the APIs are very well designed. It becomes intuitive soon and is much more powerful than the constructs offered by auto layout / layout XMLs.

3. Flutter is reactive

If you’ve never used a Reactive framework, then this might be a learning curve. The API prevents coding in an imperative style. To me this took a little bit of getting used to, but it has been a blessing (in disguise?).

For those of you who are aware of the reactive paradigm, you can skip the rest of this point. For the rest, I wanted to illustrate the reactive style using a small example. Have a look at this simple app. It shows ‘Hello <name>’ with the name that the user inputs, and lets the user select a color for the message as well.

Lets imagine how you would go about architecting such an app in native Android (or iOS). Since this is a simple one page app, lets just say we’ll code it all in a single Activity (or ViewController). In an imperative style, the structure would look something like this…

Imperative style of code

How it works: There will be a listener to track changes in the TextField and the Dropdown values. On change we would be obtaining an instance of the Text label and calling the relevant methods to update its values. (It is likely that we will be keeping these values in some private instance variables to maintain state as well)

This means, that in a typical public API for a View in Android or iOS, you would find a number of getters and setters for various properties. For example, look at the public API for the TextView class in Android. I can’t reproduce it in full here as it is too long. The number of methods it has is in the hundreds — 259 to be exact, plus 4 constructors.

Some methods in the Android TextView.java class

getText

setText

setTextSize

length

getLineHeight

getLayout

setCompoundDrawables

setCompoundDrawablesWithIntrinsicBounds

setPadding

getTextSize

setTextSize

getLetterSpacing

setLetterSpacing

setFontFeatureSettings

setTextColor

getTextColors

getCurrentTextColor

.

.

.

.

200+ more

So each View has some specific properties which are exposed via these methods. It is not uncommon to find tens or hundreds of methods exposed by any given widget type, which let you customize its look & feel and behavioural aspects to your heart’s content. Cool right? 😱

Now lets look at how the structure might look like in a reactive style of code…

Reactive style of code

How it works: There will be a listener to track changes in the TextField and the Dropdown values. On change we would be updating these values in the ‘global state’ and tell Flutter to repaint.

Flutter will automatically take care of figuring out what the Text label should look like.

This style of coding comes with a lot of advantages. For example, suppose the next version of this app has another greeting text widget added to it, that says “Bye”.

Hello <name>.

Bye <name>.

Now in the imperative style, the listeners will need to be updated to call the relevant setter methods on the new “Bye” text widget (or View) as well.

Whereas, in the reactive app, the listeners stay the same. They will continue to update the global state (as earlier). The new widget will simply repaint itself as per that state.

In complex apps, this style of coding really starts to shine. It is much simpler to maintain and reason about.

Another advantage is that the public API is hugely simplified. Although this is also due to the Widget architecture of Flutter (which is explained in the next point). To illustrate, have a look at the code for the Text widget in Flutter.

class Text extends StatelessWidget {

const Text(this.data, {

Key key,

this.style,

this.textAlign,

this.textDirection,

this.softWrap,

this.overflow,

this.textScaleFactor,

this.maxLines,

}) : assert(data != null),

super(key: key);

/// The text to display.

final String data;

final TextStyle style;

final TextAlign textAlign;

final TextDirection textDirection;

final bool softWrap;

final TextOverflow overflow;

final double textScaleFactor;

final int maxLines;

@override

void debugFillProperties(DiagnosticPropertiesBuilder description {...}

}

It has a single constructor. And that’s about it. The other two methods are to be called by the framework. This is the whole code with the comments removed and a few lines removed from the overridden methods. Isn’t it elegant? It is possible in part because there are no setters and getters. The Widgets prefer to rebuild themselves using their constructor arguments, rather than mutating themselves using setters.

Reactive vs imperative, the mindset change:

I feel if you’ve always been coding in an imperative style, the appreciation for a reactive style comes once you actually try it out and it clicks💡. In the beginning, I spent a lot of time trying to figure out how I’ll call a method on a child widget to update its internal state (like the greeting label in our example). I was stuck in an imperative mindset of doing things.

I remember what lit a bulb in my head after struggling for a few days. It was a conversation I was having with a friendly member on the Flutter gitter channel. Here was his advice (emphasis is mine).

@rrousselGit

A widget shouldn't visit it's children. It becomes messy. But you can access the state of your parents.

@animeshjain

hmm ok. perhaps i'm still stuck with an imperative style of programming.

@rrousselGit

Think in immutable. Don't update the child. Update the parent, and create a new batch of child. Once you get used to it, you realize it has a lot of advantages.

The architecture in Reactive tends to be more around figuring out how to manage State. This deserves proper discussion in a separate series of articles, as there are multiple ways to doing it. But needless to say, once I got the hang of this style of coding, it was pretty awesome. Just speaking from the experience of having built a fully functional app now, I realise I have spent almost zero time on bugs caused by side effects!

4. Everything is a widget

I briefly touched upon how elegant and concise the Flutter widget APIs are in the previous point. This point will explain it further.

What is a widget?

For most developers, a widget typically invokes a mental picture of an element that renders on a screen and encapsulates some behaviour. The Android and iOS counterparts would be the various View classes.

Flutter has taken a different/broader view of this concept, and in the process has extended it beyond just structural elements. The widget architecture heavily favours composition over inheritance, thus making widgets much more powerful and composable (duh!). Quoting from Flutter’s documentation -

A widget can define

- a structural element (like a button or menu)

- a stylistic element (like a font or color scheme)

- an aspect of layout (like padding)

and so on…

Even a behaviour is a widget (like GestureDetector). There is a type of widget that helps with state management (InheritedWidget), and one that helps build animations (AnimatedWidget). Quoting again from the documentation-

You can also control the layout of a widget by composing it with other widgets. For example, to center a widget, you wrap it in a Center widget. There are widgets for padding, alignment, row, columns, and grids. These layout widgets do not have a visual representation of their own. Instead, their sole purpose is to control some aspect of another widget’s layout. To understand why a widget renders in a certain way, it’s often helpful to inspect the neighboring widgets.

By following “Composition > Inheritance”, the widgets start from primitive ones, to some really complex ones. For eg. the Container widget provided by Flutter is composed of a number of primitives

Container is made of upto 7 nested widgets!

The ingenuity of this design strikes once you start building. The framework make it extremely easy to customise the look and feel of any stock widgets, and build your own if needed. And this has the effect of making the API footprint very small. For eg. every Text widget does not need a property called Padding. You simply wrap the Text widget with a Padding widget.

To repeat, they key thing to remember is… Composition over Inheritance. It is powerfully applied throughout Flutter’s API and makes it really elegant and simple.

5. Say goodbye to Activity lifecycle management

I’m not sure I’ve met anyone who has liked managing the lifecycle of an Activity (or Fragment or ViewController). As someone who does not develop apps on a day to day basis, I found them annoying to say the least, and a huge time suck in general.

Getting a Fragment to work inside an Activity, with asynchronous data loading and local state management might as well be some kind of black magic to me. So I did what an average engineer would do.. I copy pasted code, toiled through documentation, watched cat videos, tried to make it all work and life stuttered by.

Flutter takes away the fun in all that.

6. Consistent 60 FPS (frames per second) performance

Flutter apps compile to Native code, so the performance is as good as it gets. In fact, I found it to be much more suitable for building a game like app than using Java/Swift. Because of its reactive nature, writing UI code is much cleaner. And that combined with the performance characteristics, probably makes it a good contender for building games as well.

This article by @wmleler1 (developer advocate @ Google) does a great job of explaining why Flutter’s rendering is crazy fast - https://hackernoon.com/whats-revolutionary-about-flutter-946915b09514. I’ll just borrow from this article and post a few diagrams and quotes that present a TLDR version of it, comparing the three architectures - Native vs React Native vs Flutter.

Native apps (Java/Swift)

How native Android/iOS code interacts with the platform

Your app talks to the platform to create widgets, or access services like the camera. The widgets are rendered to a screen canvas, and events are passed back to the widgets. This is a simple architecture, but you pretty much have to create separate apps for each platform because the widgets are different, not to mention the native languages.

React Native apps (Javascript)

How React Native interacts with the platform

React Native is very popular (and deserves to be), but because the JavaScript realm accesses the OEM widgets in the native realm, it has to go through the bridge for those as well. Widgets are typically accessed quite frequently (up to 60 times a second during animations, transitions, or when the user “swipes” something on the screen with their finger) so this can cause performance problems. As one article about React Native puts it:

Here lies one of the main keys to understanding React Native performance. Each realm by itself is blazingly fast. The performance bottleneck often occurs when we move from one realm to the other. In order to architect performant React Native apps, we must keep passes over the bridge to a minimum.

Flutter apps (Dart)

How Flutter interacts with the platform

Flutter takes a different approach to avoiding performance problems caused by the need for a JavaScript bridge by using a compiled programming language, namely Dart. Dart is compiled “ahead of time” (AOT) into native code for multiple platforms. This allows Flutter to communicate with the platform without going through a JavaScript bridge that does a context switch.

Flutter has a new architecture that includes widgets that look and feel good, are fast, and are customizable and extensible. That’s right, Flutter does not use the OEM widgets (or DOM WebViews), it provides its own widgets.

Flutter moves the widgets and the renderer from the platform into the app, which allows them to be customizable and extensible. All that Flutter requires of the platform is a canvas in which to render the widgets so they can appear on the device screen, and access to events (touches, timers, etc.) and services (location, camera, etc.).

Do give that article a full read in case you want to understand Flutter’s performance better.

If you’re on Android, you can try the Flutter gallery app, it has some very cool animations showcased - https://play.google.com/store/apps/details?id=io.flutter.gallery. Here’s a gif from that app

(iOS app store does not list demo apps, so on iOS you’ll have to build it for yourself - https://github.com/flutter/flutter/tree/master/examples/flutter_gallery)

This brings us to our penultimate, but one of the biggest wins of Flutter…

7. Release on both android and iOS like a boss.

Enough said!

And last but not the least is…

8. The Flutter Community

This is a very important point because Flutter is new (and alpha). And from that perspective, the community support is very good. The Flutter gitter channel has constant chatter and a good mix of newbies and experienced devs hanging out. Most of my questions got answered within a few hours there. For longer form questions, stackoverflow is also answered pretty fast.

The Flutter dev team that hangs out on chat is very helpful. They’re very welcoming of new people and answering the simple questions that we tend to get stuck at. And in general I found the tone of the conversation to be humble and authentic. I think it bodes well for the future of the framework.

That’s all folks. I hope I’ve been able to convince you to play around with Flutter. The article is mostly talking about the positive aspects of Flutter, because in my development experience that is what my overwhelming experience was. That doesn’t mean there are no shortcomings.

The caveat is that Flutter is new, so there are known issues/limitations, and of course unknown ones that may come along. It is not within the scope of this article to address the limitations. Also, many limitations are a moving target and the Flutter dev team is closing stuff rapidly.

Listing some of the limitations here, that seem to be commonly brought up in the community -

Inline maps are not supported yet - https://github.com/flutter/flutter/issues/73

Lack of support for inline video. But a patch is in it seems and that should hit the master branch soon 🎉 🍻 - https://github.com/flutter/flutter/pull/12525

Instance state (Android) is not saved. So if the app gets killed in the background, you will lose state. There’s no simple way to handle this yet — https://github.com/flutter/flutter/issues/6827

Acknowledgements: Thanks for proof reading and the suggestions - Brian Egan, Ajeet Kushwaha, Sajat Jain, Gaurav Karwa, Arpit

If you enjoyed this article, please share within your network. And if you play poker, try giving Chips of Fury (the app I made using Flutter) a spin.

Thanks!

Tags