Last weekend I released to the open public my first relatively stable version of the rework of my app What You Eat – this time, made with the Flutter Framework. My target was first – the iOS platform, since I stopped working on the old PhoneGap version years ago, and on second place – Android аnd Web – currently available from beta URLs / Places as – you cannot re-implement absolutely everything with a fresh new “cool” framework out of the box. Currently I have no plans to package my application as Desktop App but if I wanted to – it would be not so hard thanks the upcoming features of the Flutter Framework to convert your code to fast native code that is used in Chromium.
The benefit of using any technology or stack of technologies is to write less of your own code. When you write code – it is not tested. Most developers go through the workflow of the logic in the best positive way (called happy path). That’s why it is always good idea to have someone else to test it. And the code of the frameworks is tested by many so if there is any bugs in there – the community will find them and open them to the public so they could be fixed faster than any single in-house development.
One way to write less code is to use Programming Design Patterns – Object Orientated, Functional, or whatever. In my experience I have seen by colleagues and not few times even me – Design Patterns be used without perfect understanding how they minimize code and then it actually increases its amount as one does not understand the pattern – it will increase the bugs. Only a personal experience and a realization how – could decrease the code and minimize the bugs. Flutter comes with a lot of options and if you pick every possibility is a potential for going nuts. I have used MVVM in Android, so I tried to keep it the same in my Dart code. I’ve updated my GeneratorApp to Generate Kotlin Android and Flutter MVVM code.
First thing you must remember before diving into Flutter is, it is a User Interface Framework. I am very impressed with it on all platforms – user interface components, animations, speed of coding, very close to 1:1 behavior everywhere – when there is a problem with the business logic related to the UI, it is problem on all platforms, when it’s fixed in one place – it fixed in others. But for everything else – there are plugins and when these plugins are not up-to-date, they will bring you trouble in unexpected moments.
One thing to watch out and the framework actually force you to thing about is the size. area of the widgets where/how much space – they should be placed/painted to:
When you have images and especially from the Internet it is good idea to restrict them – give them fixed size (width or height) or percentage of the view ( MediaQuery.of(context).size.width / 4 ). Otherwise they may get small or too big, depending on the source file.
When you have Text components that are coming from Internationalized places, user generated fields, or even running the app on different size than the development environment – the text tend to go nuts. Several different techniques are used – If the text is inside of Row or Column – wrap it in Expanded – to fill the remaining area, Flex/Flexible – to fill a percentage of the are, FittedBox with fit: BoxFit.scaleDown – to scale the text down.
Flutter molds your thinking in terms of Widgets/Components. In general the more you wrap the repeating UI elements in your own components or utility wrappers or methods, the less code you’ll need to write and the more easy it will be to change something if needed in one place instead of ten. Also in case of encapsulation in widgets, makes the code more extendable via standard Object Oriented Extension. And there is also a bad thing about it. The Flutter team is not endless and cannot have the time and energy and the knowledge to create all the possible components. So, many popular user interface elements are currently available via community plugins. And plugins bring bad developer experience many times.
Minimize the places in your code where plugin code is referenced and expose it via basic parameters (integers, strings, etc) or your own interfaces /models/ components. I had this personal experience. Some plugins may not work on the other mobile platform, or especially the Web, as it is still in technical preview. About the web – Luckily, I have some Web Experience and I am able to replicate the more popular things since you could access the DOM API/JavaScript from dart:html.
SharedPreference interface is not accessible on the Web, but the web has localStorage API. Wrapping up your calls to this key value storage in get/set utils passing key(-value) pair or even a Map<String, String> and making this plugin be referenced in only one place is a good idea.
Displaying HTML is not a part of the Flutter Core and there is a plugin for the mobile platforms – flutter_html. It is not available on the web but, for the web HTML is “native code”. I have workaround it with util method like this:
var div = DivElement();
div.innerHtml = code;
return div.text;
I’ve used the connectivity plugin. In my experience – the listening of onConnectivityChanged does not work on my Amazon Tablet. So I replaced it with a go to network settings and a retry button bellow the “No Internet Connection” label. On the web there are events for that: window.addEventListener('online',
fn);window.addEventListener('offline',
fn);
But, for consistency, I’am not using them – leaving the same behavior. For the “isConnected” check – there is navigator.isOnline on the web and a Connectivity.checkConnectivity() API that worked on all my mobile devices and simulators/emulators.
I made some improvements in my TranslateApp while developing my app, so it generates Internationalization code for the Flutter Platform. On Web, the I18n Package is currently not working 100%, but this didn’t stopped me from implementing it in the browser changing some small fractions of the code in it’s initialization – commenting the flutter only stuff, changing the source of current lang, etc . Besides some problems in my own code and configuration, It actually worked pretty well. There is an issue with text selection on smaller devices: https://github.com/flutter/flutter/issues/35826 the Cut/Copy/Paste native component goes out of the view and shows the yellow/black ui error. I’ve left the field without it and I’m watching the issue on GitHub.
Testing locally for the web – When you don’t have a chrome started execute open -a Google\ Chrome –args –disable-web-security –user-data-dir=”” (on Mac) or equivalent on the other OSes – from the command line and you’ll be able to develop and test the UI on your local environment without a need of local copy of the back end server that must also serve the content so all the sandboxing and security are in place as in production. Actually, next time I have an app to develop with flutter, I’ll probably start with the Web, as handling different screen sizes is as fast as resizing the browser window. And also – the web has less plugins and will force me to write better encapsulated code that will be replaced when migrating to mobile.
On Android there are very simple to implement, but very cool transition animations, that are also very easy in Flutter – where they are called Hero Animations. One problem with them that I haven’t encountered before on native is having two or more elements tagged with the same transition ids in one screen – especially in tablet mode. I workaround-ed them by having less “Heroes” in tablet mode and replacing pushAndReplace for more complex views.
In flutter there are several ways to create builds – by the command line interface, the command line of the platforms, or using the IDEs – opening the projects with Android Studio and xCode (as the framework generates all the necessary files) and build executable files from the UI.
Building Android install-able from Flutter CLI didn’t work for me (flutter build apk/appbundle. Maybe it was because of some of the plugins I’ve included. I changed them all to target/compile the same sdk version, placed the AndroidX properties and parameters, matched all the kotlin and gradle versions of tools and dependencies, and it didn’t showed me good errors that I could understand. I didn’t try building from command line with gradle, but with the Android Studio UI – it build install-able files successfully.
Building an iOS was also tricky. Flutter CLI currently cannot configure the Apple provisioning profile/account. It is done through xCode. In my experience in some moment running/executing the app from xCode stopped working – some Flutter engine initialization error. I even sent a package from the official UI and got rejected (with good reason). But “flutter run ios”, and “flutter build ios” worked. But, The output of the build currently is Runner.app – not IPA – that is required by the app store. So I found some links on the Internet and the result was accepted and it is working OK for know:
https://github.com/flutter/flutter/issues/13065
https://shashikantjagtap.net/full-stack-ios-continuous-delivery-with-xcodebuild-and-exportoptions-plist/
https://github.com/flutter/flutter/issues/22306
The Web CLI for Flutter (flutter webdev) actually worked from the first time without major issues that needed Googling. I had issues but the error messages from the build were nice and pointed to exactly what I need to change.
To summarize – I have mixed feelings about it. When fast prototyping for a single platform – The progress and development could be flashing fast, but when targeting all the possible environments, more thinking and caution is required by the framework consumer/developer. There are pros and cons – like every other technology. Having in mind that it is still relatively new, it could get better in the future in the weak points. We’ll see.
Top comments (3)
This is a really good point at the end of an overall unbiased article making really good observations. Thank you for writing it!
One problem with Flutter, which is the same as React Native, is that people think that if they just learn the framework, they will have an easy time making multiplatform apps. But as Airbnb pointed out about React Native, it's really a third platform sitting on top of the other two (Android and iOS...even worse if adding web and iPadOS and Windows and Mac and WatchOS, and tvOS, and Android TV and Android Wear, etc).
Kotlin Multiplatform, on the other hand, is upfront that common code should expect some actual platform specific implementation. And it accepts that UIs will/should be different on different platforms because the user experience guidelines, expectations, and requirements are different. At least, once you get past the early stages, that's when the "all in one" solutions become a hindrance and you start wishing for better native interior and switch back to native.
Yes, this is important, thank you for calling it out.
Suffer the same pain, trying to make a file opener function. its hard to separate the web and android code when compiling, dart:html doesn't play nice with android