Flutter’s Key Difference: Owning Every Pixel
There’s been plenty of comparing Flutter with other cross-platform mobile frameworks. But the key difference is often overlooked or downplayed.
Flutter owns every pixel on the screen.
Cross-platform frameworks such as React Native and Xamarin.Forms which use native Android and iOS interface elements sound like a great idea, on paper:
“ Write once and run anywhere! Fully native interfaces! Apps feel like they fit right in! Screens magically adapt to platform paradigms!”
But these frameworks aren’t totally in control, they’re having to rely on the OS to draw the interface. And those native interface components come with baggage, like a partner who’s not totally over their ex.
And it can be a surprising amount of baggage. Development on Android started 16 years ago. In most of the world, it would nearly be able to drink. Sometimes I think it’s started early.
APIs have been changed, deprecated and removed. UI elements have evolved. With iOS, it’s all closed-source. Trying to tie the platform worlds together— when they’re already incredibly complex — can have some serious difficulties.
In my experience, designers don’t especially care about apps “fitting right in”. They want to bring their own twist, to use a company’s branding throughout an app, to let their creative juices flow like a waterfall.
I’m not passing judgement on if this is a good or a bad thing, merely stating that for many developers it’s their daily reality. I’ll always be advocate of following platform design guidelines as much as possible.
But trying to contort native UI components to placate our designer overlords can be really hard. Sometimes it requires hacking through view native hierarchies, coming up with fragile work-arounds. Often we’ve just ended up saying no, that a design is too costly or time-consuming to implement. Sorry.
When we do bite the bullet for custom layouts, it inevitably leads to lots of platform-specific UI code and often fighting the framework or layout engine.
Especially on Android, navigating the world of different versions, vendor-specific customisations and Support Libraries/AndroidX can be a nightmare. For instance I’ve had fun in the past with date pickers looking wildly different depending on Android versions, with users complaining about the harder-to-use versions.
It can end up feeling like it would be quicker to just do two native apps.
Flutter changes this.
Flutter, like a power-hungry dictator, takes charge of every pixel on the screen, and controls each and every one of them. It wipes the slate clean, leaving all the baggage of iOS and Android behind.
It uses Skia to draw everything, and that means everything looks identical on every platform, if you want it to. There are some default platform-specific tweaks, such as scroll views “bouncing” on iOS but coming to a hard stop on Android.
If you want your app to fit in with the OS, it offers platform-specific controls such as the Cupertino widgets for iOS. These are, in the nicest possible meaning of the word, forgeries of the actual components.
Widgets don’t automatically change style to match the platform they’re running on; you have to add some platform-specific code to make this work. Ways to make this easier are being discussed, though.
As with any apparently magic solution in software development, there are tradeoffs.
Reproducing native widgets is a big job, and Flutter’s aren’t perfect. Some of the native components feel a tiny bit off. Purist geeks will spot the difference, but 99.9% of users won’t. Text editing behavior is especially tricky to reproduce; I wouldn’t use Flutter to build a word processor, for instance.
New OS versions will bring design changes, and Flutter widgets will have to be updated. But with iOS you get 3 months between WWDC announcements and a public release, which should be enough time to make everything look new and sparkly.
And on last check with Android you get approximately 3 millennia between initial preview versions and users actually updating (sorry, Android fans, I love you really).
There are also sometimes when you need to use native widgets, such as embedding web views, maps or existing packages. Flutter does support this, but it’s still in preview, with a number of issues still to be sorted.
Can Anything Else Own Every Pixel?
Are there any alternatives that draw everything themselves? Well…
Progressive Web Apps and frameworks such as Ionic come close, but you’re still at the mercy of the platform’s web renderers, which will still have some differences and bugs. But they do give you all the flexibility of CSS, but at the cost of ever-evolving standards with differing support.
Canvas elements (such as SkiaSharp for Xamarin and GCanvas for React Native) let you draw anything you want in a cross-platform way, but manually. And manually is a big deal: it’s not practical to make your own set of UI widgets for each app. But they’re great for building individual components such as charts.
Xamarin.Forms 4 — currently in preview — is adding support for “Material Visuals” to create apps that look the same cross-platform, but it still uses platform-specific drawing (including Google’s Material Components for iOS).
Some companies have experimented with using games engines such as Unity to do cross-platform GUIs, but the results have been sub-par. They’re not the right tool for the job, and the interfaces just feel wrong.
Cross-platform tools that use native components can be absolutely fine for simple designs. But when apps get all fancy-pants with custom designs, they can just get in the way.
Flutter makes building interfaces simpler. It stops trying to glue complicated frameworks together. It gives you the freedom to choose between native-looking interfaces or to go wild with bespoke everything. It’s not perfect, but it is simple and effective.