My Impressions from Building Flutter App

TL:DR;

  • Flutter (flutter.io) is a mobile app dev framework from Google. You can build Android and iOS apps with it.
  • I built a public transport app in Flutter, over a very short period (about 60 man-hours, server-side gRPC endpoints existed)
  • Flutter's plugin ecosystem has a long way to go, but it's only from experimenting that we'll get it there.
  • If you're building a "simple" app, try Flutter out.

About The App

I built a train information app, called "MyTrain Gauteng", for Metrorail commuters in Gauteng, South Africa.
I'm a developer in Moving Gauteng, a platform that provides public transit info to commuters in said Gauteng, South Africa.
Our target market is train commuters with Android devices (lowest living standard measure, most use Android, some low-end).

The app:

  • Provides static and crowd-sourced train information (delays, stations, etc.).
  • (Will) allow commuters to also contribute info (bus spotting and the like).
  • Provides alerts about cancellations from Metrorail.
  • Is built on our own quasi-GTFS-RealTime concoction.
  • Relies a lot on gRPC streaming (both directions).
Image Description

How the MyTrain Gauteng app looks like


I have an Android app called MyTrain Gauteng, that I had been working on, on and off (mostly off). The main cause of the on-off part is because I've been developing it in my "spare time". I had planned on completing its development in December 2017, but I saw myself soon in January 2018, and being back at work full-time.

An important Android "problem" is that getting an app to perform well is a lot of work, even with the new-age Architecture Component stuff (my opinion). So you'll start developing an app, and end up battling with the framework when the app doesn't perform well.

A few weeks ago, Flutter beta 1 was released, and as a Hacker News frequenter, I saw the announcement. I decided to give it a try, though there is a bit of back-story.

I (or the royal "we") have been using gRPC since late 2016; mostly experimenting, patching together some services at my consulting gigs, and the like. I've on the other hand known about Flutter (or "looked into it") since November 2017, when Matias Duarte from Google tweeted a blog post about Flutter.
At the time, gRPC support on Dart was still experimental.

gRPC-Dart has since been released, and as of the Flutter beta 1, I decided to have a look at it again.

This blog post highlights some of my experiences developing an Android app with Flutter.

Dart; The Language

I'm a self-taught developer, comfortable with JS (~8 years; NodeJS since 0.x days), Java + Kotlin (intermediate), Python (~3 years), Scala (Apache Spark when cleints don't support Python), and some experimenting with Go, Pony, Rust, etc.

Leearning Dart was easy enough, in fact, very pleasant. The Dart language tour itself was quick enough to explore. Dart feels like a hybrid of Java and JavaScript to me.

I don't think I'll be cross-platforming all the things with Dart, but I would add it to my programming arsenal.

There were small nuances about the language, like how it encourages one to trail commas. I'm not very opinionated, so that quickly went out the way (to the extent that I'm also trailing commas).

Type System

Not much to add, being comfortable with TypeScript and type-inferency langs like Kotlin, I couldn't tell the difference between Dart 1 and 2. I did however use types explicitly, because that's the right thing to do if they are available (unless you use Kotlin, exceptional tooling from JetBrains).

Streams; Futures

If you've dabbled with Rx (in any language), and if you async await, a good read of the documentation/guide is all you need. Streams seem mature enough, although if you use the vanilla language, you'll find some things missing.

I tried throttling a location plugin that sends me updates every 2.5 seconds, I wanted to drop stream updates and get them to a certain interval. The solutions that I tried didn't work. I didn't want to use RxDart (worried about package bloat), but I might have to use it.

Flutter

I won't talk about Flutter tooling much, except that it's good enough. I started developing in Android Studio, but found it frustrating, and switched to VS Code after about a few man-hours.

The Flutter dev-environment is pleasant enough once you get in the flow of things.

Hot Reload

One of the challenges when building UI is the iteration cycle. Flutter's hot reload is really that amazing. If you've built UI in Android Studio's XML GUI, you'll find Flutter to be a delightful change.

Ecosystem

This is the most important for me, and what you should really consider before digging in deep into Flutter.

I mainly need(ed) the following functionality:

  1. Location support
  2. Permissions
  3. Geofencing
  4. Google Maps
  5. gRPC support
  6. Push notifications
  7. Google Signin, with offline server auth
  8. Chrome custom tabs, or a concoction that allows me to pass auth headers with the url

Location support

There was a library that is no longer actively maintained, it handle(d/s) the basics, like once-off location updates, and location streams.
It was throwing some errors with Dart 2, but someone else forked it and fixed the issue. I forked it and used my own copy.

About a week ago, another library was released (Geolocation), which I've started using. It helps following issues on GH, and it shows that gaps will get filled over time.

Geolocation currently sends location updates at a rapid frequency, so it's something I have to spend a bit of time working on.

Permissions

There's Permit, which has since been archived by its owner. I needed permissions mainly for location access. The Geolocation plugin above handles it for me, so I'm sorted.

Geofencing

I have to digress a bit. If the core Android framework supports something, it seems not-hard to get Flutter to do it (core includes Google Play Services).
The difficulty is however in creating a plugin that works for both Android and iOS.

Geolocation above, plans on supporting Geofencing. I'm also going to take a bash at it after my exams (went back to school so I can learn Maths and Stats, and become a "Data Scientist").

Google Maps

If you "desperately" need maps in your app, there's a few options. The "official" Google plugin doesn't seem ready as yet.

I need maps to display route maps, trains moving on the map, and station locations. I've decided not to implement that for now, as most train commuters are already familiar with the train network.

A nice-to-have once maps are ready, is to showcase gRPC's server-streaming. I've implemented a nifty solution which would be great to release into the wild.

gRPC

The Android app that I was building ran atop gRPC, so I couldn't negotiate here. I've had a bad taste in my mouth dealing with JSON in the past.

The gRPC-Dart plugin supports everything that one would typically use:

  • Streaming in all directions (the main interesting point)
  • Metadata (though binary metadata could be improved, see my issue (here: github::grpc:grpc-dart/issue/72)[https://github.com/grpc/grpc-dart/issues/72])
  • Shared connection across multiple services

Other things like auto-retry aren't yet universally (across various grpc impls) supported yet, so one has to roll that out on their own.

I am so far finding bidirectional-streaming to be a bit flaky, but I haven't gathered enough details yet. The client and server play a ping-pong game of terminating the stream.

Push Notifications

The Firebase integration (for FCM and others) is good enough for most use-cases at this point. Notifications work, the configuration isn't a lot of work.

The only thing that's important is that you need to add a certain key:value pair to your FCM payload for your app to handle it better.

As an anecdote, it took me about 40 minutes to get push notification working on the app, it previously took me longer on Android (declare an IntentService, register it, etc.).
The downside though is that one can't customise how their notification looks like, so you end up not taking advantage of all Android APIs).

Google Sign-in

I'm one of those people who cautiously implemented Firebase (after the huge Google I/O reveal a few years ago). The downside's that I still rely on server-side authentication of users. The flow is as below:

  • User opens and authenticates with app
  • App requests a server token from Google Signin
  • App sends token to server
  • Server checks if user already exists, creating one if not
  • Firebase user is linked to existing auth

The Google plugin currently doesn't support this, though I've been looking at how to implement it myself (for Android only, no iOS experience as mentioned). I opened an issue over the weekend github::flutter:flutter/issues/16613.

Custom Tabs with Headers

There's flutter_web_browser which works, but doesn't yet support headers. I'm planning on working on it, as I use headers to authenticate users when sending them to our website (for features that aren't yet implemented in the app).

Ecosystem Summary

As "earlyish" adopters, looks like we have to get comfortable with being able to write our own Flutter plugins. It's not nice to pester people and expect them to take on the burden of maintaining software for free.

With that said, I'm going to play around more when I get a chane, and implement the features I'd like.

Plugin Development

From the entitlement issue that we've been going through within the Open Source community; once you release a Flutter plugin that only works for one platform, you might get complaints from developers from the other platform. I have no iOS experience, so my contributions will be limited.

If you choose to develop an Android + iOS app with Flutter, please do a thorough investigation of plugins first.

App Size

One of my main and initial concerns with Flutter was around the app size. There's unfortunately not much "we" the end-users, as well as Google Devs, can do about it.

The Flutter FAQ says that an empty release APK is about 7MB. For my market, this is an issue. Mobile data is costly in South Africa, mainly because it's cheaper if you afford to buy large data bundles, so the poorest of the poor end up paying more per MB in aggregate.

One thing that is confusing around app size, is that the debug variant is often 3x the profile or release variant. So don't let a 30MB app scare you just yet.

I also noticed that after switching on Dart 2 support on VS Code, my app size grew. I built a release version and compared to Dart 1, and found no size difference (phew).

The MyTrain app is currently 11.8MB, though I'd have loved to have it at around 7-8MB. There's a lot of Google Play Services libs (from Firebase) that could be proguarded away, but they won't easily get me back to 8MB.

Capture

Look and Feel, Dev Experience

If you've built a Material-based Android app (at least before the new material-components-android, which I waa unaware of), you understand the difficulty in building a Material app on Android. We often end up with a concoction of third-party libs that do small things, like handle the FAB, create "easier" cards, etc.

With Flutter, I've been impressed by the "everything is a widget" mantra. Once you can separate stateless vs stateful in widget-land, you'll find your development velocity to increase.

Creating custom widgets is a bit tricky at first, so I recommend one to invest a lot of time in the various layout widgets. If you're an Android developer who knows HTML+CSS, I'd recommend you put on the front-end hat when thinking about Flutter widgets.

Is it Native?

One of Flutter's performance selling points is that you'll be able to build fluent apps that render at 60FPS. Don't judge that while in debug-mode (which was called SLOW MODE until recently).

The app is fluent enough, I still see jank where I am doing a lot of work in the background. If you're familiar with the event-loop programming model, it becomes easier to reason about improving code performance.

So, yes, it quacks and walks native, I think it's "native" enough.

Custom Fonts

I didn't try custom emojis, but custom fonts work well. I must admit that the app was looking boring before I added a custom font and painted it with colour. Now it's less boring.

Custom Icons

I got lazy, and tried to use the built-in stuff as much as I could. That's why there's a clock on the bottom navigation. I couldn't figure out how to do this properly as Flutter currently doesn't support SVG.

Image Description

Screenshot of app

Image Description

Screenshot of app

Image Description

Screenshot of app

Release Process & Updates

If you've released an app to the Play Store before, you'll be fine. Nothing is different.

The only rant that I have at this point is that Google Play doesn't seem to diff the apk that gets sent to users. If the shared Flutter linked libraries remain the same, it'd be great if users wouldn't have to download them again.

Conclusion

I've enjoyed building the MyTrain app, key being Flutter and gRPC. This is a good combination, and I'll be building more apps with Flutter over the months.