Write once, run everywhere, with Flutter (finally)

Cross platform app development - being able to write something once and then have it run on different platforms is a dream that everyone from developers to large companies have been chasing for a long time. Ever since smartphones became the mainstay of computing though, this quest shifted more towards unifying development across Android and iOS.

Recently I set out to build a cross platform pomodoro timer app, something that can work on all the different devices and platforms we have today (let’s list it, Android, iOS, Linux, MacOS, Windows), with a consistent and aesthetic UI and be able to sync to cloud, with some nice gamification enhancements. My goal was to build this from a single codebase, rather than to build it seperately for each platform since that would mean writing the same thing 5 times! Now the main question is …

So how do you build a cross platform app that you write once and then run everywhere from mobile platforms to the major desktop operating systems, in 2020 ?

Facebook made a huge development towards cross platform mobile apps development with release of React Native in 2015 and since then building mobile apps have really gotten way more simpler as well as accesible to a broader set of developers.

And with release of Flutter in 2017 by Google, building high fidelity mobile apps has been made incredibly simple, with developers and companies crafting amazing experiences and UIs using it.

On the desktop side though, things were quite slow for a while till GitHub released Electron in 2013, allowing web technologies and javascript to be used for writing desktop apps which greatly increased the speed and ease of development of desktop apps, and allowed the bigger pool of web developers to start building native desktop apps. These apps, while great, package the entire chromium engine to render the UI, which causes a major performance and battery life hit, annoying a lot of users. Mentioning of any new desktop app built using Electron almost always leads to a debate on merits and demerits of the framework.

For apps which are more on the heavier side in terms of functionality, features and scope, such as IDEs, social platforms etc, Electron can be a really good choice since the additional heft from Electron won’t be that apparent given the scope of the app. 120 or 200 MB is acceptable for a feature packed IDE but not for a small To Do app. Visual Studio Code is a great example of this, arguably the Electron based app yet.

For rest of the more single purpose apps, the fact that Electron based apps typically start at 120 MB atleast and consume massive amounts of RAM is a major issue and often we see users avoiding an app purely because of the unjustifiable performance impact of Electron.

While we are on the subject of cross platform apps, Electron still doesn’t allow for building apps for the mobile platforms,and probably never will given its focus exclusively on desktop platforms.

There are some projects that allow React Native to extend from mobile side of things to desktop, such as react-native-windows and react-native-macos. But most of these projects still require considerable work before going mainstream . Also, with their inherent platform differences and the way React Native works, these cannot exactly be used for a “write once and run everywhere” experience, most often the UI for each platform will have to be written differently.

Now out of all the technologies we discussed above, let’s figure out which one to use.

We could have used Electron but then for the mobile platforms, we would have to build, or atleast package the web app code separately with something else like PhoneGap or Cordova, which is not the best experience anyways on mobile devices, web apps often don’t match the fidelity of native UIs and that gets especially more apparent on touch based mobile devices.

We could have used React Native but then we would be building the UI part seperately for the desktop platforms using react-native-windows AND react-native-macos, that means writing the UI three times! Once for mobile platforms with react-native and then once for Windows using react-native-windows and then again for MacOS with react-native-macos, and we are still forgetting Linux.

While React Native is not there yet to build desktop apps, Flutter on the other hand has recently seen significant developments, both official as well as from community to bring its support to desktop platforms.

The official Flutter team recently released support for desktop platforms in alpha, currently that only supports the MacOS platform though and is not considered completely stable yet anyways.

The architecture of Flutter is considerably more easier to bring to new platforms than React Native, thanks to the fact that it doesn’t rely on the native widgets of the platform and draws all its widget on its own (for web developers, it draws the UI more like a its drawing inside a canvas element and less like DOM elements). Plus another benefit of this approach is that the same extremely huge set of widgets available on Flutter is also available on any new platform, developers are good to go from the beginning to develop incredibly complex UIs from the very start. No need to wait for components to catch up.

Although another really interesting project I came across was go-flutter desktop, which uses Go and GLFW to bring Flutter to desktop, basically the platform side of things for Flutter is handled by Go in a cross platform (Windows, Linux and MacOS) way for all the desktop platforms.

Using it to bring a Flutter app to desktop is as simple as running a single command in the project and that’s it!

Now talking about the experience of using Flutter as a developer to build an app, its one of the smoothest flow that I have experienced in a longwhile.

Screenshot of DartPad, building the Flutter app UI

My workflow for building the app was pretty simple:

  1. To try and test different UI possibilities, I started building the prototype of the UI online! No need to spend time setting up Flutter toolchain, Android studio, Xcode or anything else at all! With the support for Flutter added to DartPad, we can start building Flutter apps directly from browser and quickly iterate on ideas.
  2. Once the UI was ready to go, I switched to setting up Flutter on the system itself . Then I wrote the rest of the functional and native integration dependent parts of the app, things like splite db, key-value store etc.
  3. Once the app was completely ready, the Android and iOS builds can be created using the flutter toolchain itself:
flutter build apk
flutter build ios

For the desktop apps including the installers, Its as simple as running:

hover build windows-msi
hover build darwin-dmg
hover build linux-deb

Hover, is part of the go-flutter-desktop project itself and handles building the releases for the desktop platforms.

And that’s it!, you now have setup package for Windows, dmg installer for MacOS and deb package for Debian based Linux distros (It can also build rpm, appimage and snap packages for other Linux distros), all ready to be distributed, along with the usual Android and iOS binaries ready to push to their respective stores.

And with that you have your app ready to go for any major desktop or mobile platform!

But that’s not all though…

Flutter actually also supports web in beta! That means the same code can actually also be used to create a web app version.

Although I didn’t need that for my project and plus I ran into some small issues with the web support that’s still being worked on by Flutter team.

Also, any plugin that is used in the app like sqlite (which was used in my app), key value storage etc will require their platform side of code to be written in Go to bring it to desktop.

While the process is not that difficult, its still an extra step in a different environment, since your Flutter app is gonna be written in dart completely otherwise.

Although the go-flutter-desktop and the community around it do provide the desktop port of several of the most common plugins used in the Flutter ecosystem which covers most of the stuff that a standard app would need.

Another consideration when evaluating this is that, since Flutter currently only has the widgets for Material UI and iOS interface, if you want your app to look like any other truly native app on your desktop, you will have to create the UI components from scratch on your own which might be a significant effort.

This was not a problem with my app since the UI was designed to look and behave the same on all platforms and I think just using Material UI widgets with some small changes to fit your product’s design language would actually suffice for most apps.

So there you go, although there is still a lot of development yet to happen to bring this to maturity for most production level apps, I think that the toolchain is in a very good stage and progressing towards even better future where we can finally build apps for all the different platforms with an incredible UX and that too with just one codebase!

Hope this was helpful, if it was then do follow me on twitter for more such articles.

Also, do check out the app that I built and described the process for above at https://tomatoro.app


Published 23 Apr 2020

Engineering, Product. Maker of mostly inanimate things.
Swapnil Devesh on Twitter