My Road Test With Flutter (Version 3)
Charles Tatum II
Software developer with experience in web and mobile, and skills in QA & technical writing
Ever since native mobile platforms have been available, many in the industry have had an interest in cross-platform development, where writing one code base would allow deployment to multiple platforms. The dominant platforms in mobile are Android and iOS. Windows itself is not exclusively mobile, but given so many devices for Windows are portable – such as laptops and tablets – Windows, in my opinion shouldn’t be ignored as another target platform.
I have long considered myself a staunch supporter of Microsoft’s Xamarin platform. Xamarin bowed on the national stage about a decade ago, and was purchased by Microsoft in the 2015-16 time frame. They have since funded it and taken over ongoing development of it. I’ve been a fan of Xamarin not just for the “write once, run anywhere” promise, but also because Xamarin was built on Microsoft’s own .NET platform, using the rich and powerful C# language and their arguably best-of-breed editor, Visual Studio.
Xamarin Forms Failed Me This One Time, Opening The Door
I wanted to use Xamarin Forms on a project I did earlier this year. FuelAdvisor, a gas cost calculator I wrote and released as my very first Android project in 2009, struck me as apropos for a refresh in the light of hiked gas prices this year. I wrote the original code base with Android using Java (Kotlin was not available yet), but wanted to release it for iOS (Apple) and Windows also. Xamarin Forms was a great choice...at first.
I managed to get the Android and Windows versions released successfully, within days of each other. But a peculiar thing happened with the iOS release – when downloaded from Apple's App Store, the buttons on the home screen were non-responsive, frozen, stuck. It was a huge disappointment and I wasn’t sure whether to lay blame with Microsoft, Apple, or both. There was no such issue running the iOS app via the debugger on my physical iPhone device, so what happened was a mystery.
I would spend another three to four weeks re-developing FuelAdvisor using a technology I hadn’t used in years, Apache Cordova, to get the iOS version done. Apache Cordova uses, effectively, web design and web views with HTML5 (and JavaScript and CSS) to control the UI, and generates a native project “wrapper” in which assets and settings are placed (in this last case, an Xcode project).
Apache Cordova worked well enough, but I was annoyed that Xamarin Forms fell short. I had heard something about Flutter in my reading of various blogs and online articles. Flutter was another Google product. Maybe that would be worth a look?
Not The Best Reputation
In researching Flutter, my observation was that while it got positive reviews for its efficacy, the widespread concern was that Google might abandon it: the Mountain View company has developed a reputation for yanking support from platforms that seemed to be going well. There’s even a mildly amusing web site devoted to all Google’s orphans (https://killedbygoogle.com/), complete with headstone icons for each dead project.
A previous time I ventured out to try something big and new was Ionic. If you don’t remember Ionic, I’m not surprised. It had a hot moment – for a while I saw many solicitations from organizations looking for it. Ionic was similar to Angular, and was another JavaScript-based development platform for mobile and the web (I actually thought Ionic to be easier than Angular). By the time I got proficient in it, even going so far as to release an app to the Google Play Store using it, demand for it had completely evaporated.
So I approached Flutter with some sense of hope, but also caution. No one likes getting on a ship, only to discover after investing time and resources, that it’s sinking.
How I Road Tested Flutter
Resolved to give it a shot, I downloaded the Flutter bundle – the product and the SDK. Flutter’s online documentation is crisp, neat and attractive – always reassuring when you’re venturing onto a new path.
The main editor for Flutter, interestingly enough, is not a Google offering – it’s one of Microsoft’s. Visual Studio Code, their lightweight, be-all, do-all code editor has an extension for Flutter that one just plugs in like any of the other extensions available.
I wanted to make it a fun pursuit, so my goal was to produce an app for the casino game Blackjack, and to do it for all the platforms I would normally use with Xamarin Forms – Android, iOS and Windows. It was not intended to be a fancy effort in terms of the game or its architecture – just a simplified version of the game with the basic elements: wagering, the deal, hitting or standing, and payouts.
And my effort was to road-test the basics of putting an app together – adding controls to a UI, wiring them up, adding logic, debugging, and deploying a release package. (This was not intended to be a major excursion involving design patterns, optimization, or architectural patterns – the goal was to see how long it would take to piece together something functional. The answer, to steal my own thunder, is not long.)
Installation
The process involves all of the following:
Your machine must also have any packages for native development already installed (for Android, that means Android Studio; for Windows that means Visual Studio, and for iOS that means Xcode). I installed Flutter on three Windows machines (a tablet, a desktop, and a laptop) and one Mac (a MacBook Air).
Getting Rolling
Overall, I had a better impression of Flutter than I did of the original Xamarin Forms in 2012-13, and again in 2015. In the first case, Xamarin’s 2 MB ceiling was too low to be able to do anything without paying for it, and in 2015 when I did work for a client as a Dell consultant, it wasn’t ready for prime time yet – a simple “Hello, World” type application did not run, straight out of the box.
So when I generated the starter Flutter project (a simple counter for the number of times a single button is tapped), plugged in my Android phone and ran it, I was pleased. Ditto on both the iOS simulator and even (later) Windows. The look is wonderfully consistent across all the devices, which is something that Xamarin doesn’t quite accomplish as well. As Flutter is Android-centric first, what appears is a Material Design-looking app, but they have a library (named, accordingly, “Cupertino”) which mimics iOS UI visual markers.
Flutter’s programming environment is interesting. Even though it’s a Google product, it complements Microsoft’s Visual Studio Code very nicely. It is certainly much smaller in terms of disk size, utilizing only about 1 GB. (When I deleted an old installation of Visual Studio from my Surface tablet, it freed up 20 GB.)
When an app is run, the upper right corner features a red ribbon reading “DEBUG”, which can be hidden by setting a flag in the main Dart source file.
Flutter and Dart: It's All About Widgets
I did some research on the Dart language and the architecture of Flutter in general. I was mildly unnerved when I saw the instruction setState, because that was reminiscent of another platform I don’t particularly care for, React/React Native. I have lightly tinkered with React in the past, but dislike it for its peculiar concept of state management, and its abandonment of pure JS for its own variant, JSX. The feel of React just wasn’t intuitive enough for me.
Interestingly enough, Flutter does indeed bear some parallels to React (maybe attempting to woo React developers away?). There is a concept of state management, but the way Flutter documentation explained it, it made a little more sense to me. Similar with React considering everything a “component”, Flutter considers everything a “widget” in its own single-concept world. But there are two kinds of widgets, the big umbrellas:
The concept of everything being a widget is a little odd to me. In some cases, it fits – if we’re talking about UI controls, it’s fine.
Another thing that separates Flutter from Xamarin Forms is that Flutter requires the developer to think of everything in a widget hierarchy, a tree – even more so than .NET’s XAML or Android’s own XML. In the grand scheme of things, this isn’t necessarily that much different – XAML groups controls by levels and can be traversed using Visual Studio, and Android, which has its own XML to structure controls. Even with Apache Cordova, HTML itself is a big tree, nothing other than hierarchical tags from <html> all the way through </html>.
With the Dart source, the UI and logic are all mixed there together, again. I say “again” because early programming languages didn’t separate “code-behind” from UI tags. One interesting thing about that, though, is that there is ONE language for BOTH - Dart. With .NET, code-behind is in C#; UI is in XAML. With Android, code-behind is in Kotlin or Java, UI is in XML. It wouldn’t surprise me if some devs are already at work seeing ways to restore that separation.
With a Widget, a declaration has only one method to override – and that’s build. Other code instructions can be included in a Widget method for whatever purpose, but on a return instruction, a Widget is passed back with oodles of other Widgets nested inside like the peelable layers of an onion.
There is also a kind of recursion of which one must be aware, regarding UI definition, perhaps in a way one doesn’t quite have to with other platforms like .NET/C#. Since Widgets are concentrically defined, yanking one “level” of a Widget could have unexpected results. Thankfully, the Flutter extension includes macros (a yellow light bulb appears when a Widget name is tapped and focused) and offers options for deleting a containing Widget, or inserting a new, enclosing one.
Coding Observations
Method calls are interesting with Dart. The arguments for any method can be specified as they typically would be in most languages, as data-type, symbol-name. For example:
void convertTemp(int ftemp)
This would be called like:
convertTemp(100);
But I noticed some calls had a syntax where the parameter name was also specified with the value like:
changeName(pName: "Charles");
That's because the method call is supplying a property, like so:
领英推荐
void changeName(this.pName) { ... }
And this property is used as an argument, for which there must be a corresponding property declaration elsewhere in the Widget class, e.g.:
final String? pName;
Dart provides for nullable objects (the “?” mark denotes this) – something that Kotlin has, how nice. Another Kotlin marker in the language is that the new keyword is not required to create an instance of any object.
This was nifty, too: with a Text object, if you have a long line broken into multiple lines, concatenation is accomplished simply by stacking the strings – no use of the “+” operator needed. And the same escape sequences (marked with a “\”) seem to work the same way in Dart as they do in Kotlin and Java both.
Behavioral Observations
The biggest thing I noticed when running a Flutter app is when a child object is larger than its parent and no scroll view is present, Flutter does an interesting thing by presenting a sort of “yellowjacket”/yellow and black hazard bar indicating an overrun took place, and by how many pixels. I’ve not seen anything like this in any other of the languages I’ve worked with.
Aside from that, runtime performance is slick, and consistent across all platforms.
Testing and Debugging
As with other modern languages, breakpoints can be set in the code, and I made use of the print instruction to send messages to the console.
Hot Reload reduces the time to debug considerably. When I tested my Xamarin Forms creations, especially for iOS – which required two machines on the same network – it usually took several minutes for the first time to crank into motion, and its Hot Reload feature wasn’t quite as snappy.
Creating The Release Executable
Generating the executable isn’t complicated. You open your machine’s terminal or console command window, navigate to the root folder of your Flutter project, and type one of the following:
flutter build android
flutter build ios
flutter build windows
Flutter then calls the native platform’s builder. In the case of Android, it fires up an appropriate Gradle script. For Windows, it runs the Windows packager. And for iOS, it goes through Xcode, and that can only be (as usual) on a Mac machine.
iOS Considerations
One gotcha I experienced with generating the Xcode executable: you have to remember to alter the project’s Product Scheme setting which, when the Flutter project is initially built, defaults to Debug. Editing that Scheme and changing the value to Release allows for a release version. When I generated a debug version of the app and then tried running it from outside the Flutter environment (as I've been able to do with any other platform) I got a black-screen message from iOS reporting that since iOS 14, the debug flag must be turned off, or that a full release version of the executable must be generated, in order to run it as a free-standing application.
Also, as a reminder, the only way you can generate an iOS executable is on a Mac, and you must be a member of one of Apple’s Development Programs (which start at $99 annually). However, it’s nice that there’s no cross-machine, over-the-network requirement as Xamarin Forms expects.
Likes
Hot Reload is pretty amazing. Xamarin Forms has the same feature (with the same name, even) but the feature just seems...crisper...with Flutter. Developers also have the option of tapping a button on an action bar at the top of Visual Studio Code to refresh the entire app – a good feature because if a UI control is based on a variable for which you changed the value, that code change alone will not update the UI.
Consistency of look and feel across platforms. A resulting app with Flutter is nearly identical visually across every machine. Android, iOS, Windows – all looked and behaved consistently. For grins, I even tried running a macOS version of the app, which I’d never done with Xamarin Forms. It looked great!
Dart is a good language. I had some concerns about Dart before using it, recalling my experiences learning Objective C. I’ve already observed some features borrowed from another of Google’s languages, Kotlin, but it uses lots of conventions many other powerful languages use, like the ternary operator ( condition ? true-action : false-action ) and the null test ( object ?? value-if-null ).
Flutter has a rich library of objects, methods, and functions. A casual perusal of Flutter’s online documentation shows it’s a well thought-out language. It kind of has to be, though – it’s something that’s being used to generate for other platforms.
Excellent online documentation, lots of other online blogs and support. As I was learning the ropes with Flutter and Dart, I found plenty of help. Since I joined up with Flutter on their recent version, 3.0.1, it seems I came along at a good time.
The very same code base can run on Windows or Mac machines, unaltered. I set up a GitHub repository for this first project, and used both Windows and Mac machines to push and pull commits to it. It was gratifying to be able to shut down Visual Studio Code with a Flutter project on one machine, start up the other machine, bring in the Flutter project, unchanged, press F5, and see it run. With Xamarin Forms I always had to use the Device object or the DependencyService to determine native-level code per platform.
Could Be Better/Easier
Slow first run. One thing I saw on all my machines is that the very first run (F5) of an app is very slow. It took several minutes minutes for Flutter to get all its gears in motion and compile a running executable. Once up and running, though, the Hot Reload works very well and is very snappy.
Widget-centric paradigm is odd. During my first project, I noticed concepts that Flutter implements that, in other languages, would be handled with arguments, attributes, or parameters. Take, for example, the Container widget. This object is a multi-purpose box for any child controls one wishes to place in it. If one wants the space to expand to fill a parent, though, the Container widget needs to be wrapped inside of another widget – in this case, Expanded().
With Xamarin Forms, by comparison, the StackLayout (essentially the same concept) is expanded to fill available space by specifying either of the properties HorizontalOptions or VerticalOptions followed by an enum constant listing various options (StartAndExpand, CenterAndExpand, EndAndExpand, etc.). It’s easier to modify a layout by toggling or changing a property than to insert a new concentric Widget object into an already complex layout hierarchy.
The way Visual Studio Code facilitates this for Flutter, though, is by providing macros to either wrap an existing Widget in a new Widget, or to remove an existing Widget, allowing its child to become the new parent.
A hierarchy of UI Widgets is logical to me. But designating everything as a Widget – even properties – is carrying the paradigm a bit far. I would like to see Flutter parameterize more options – like arguments in method signatures – rather than having to play a sort of coding game of Jenga with an ever-growing stack of Widget objects.
No specific handles/names for any Widget objects. In just about any other language I have used, there’s always been some kind of ID tag for an object. With JavaScript, it’s the “id” tag used with findViewById() to locate a particular object in the DOM. With XAML, it’s the x:Name property. Flutter has something called "key", but it's not the same thing; the documentation describes it as "Controls how one widget replaces another widget in the tree".
Was the name “Route” really needed? Flutter’s documentation calls a page or a screen within an app a “Route”. I’m not sure what was gained changing the name of a concept most developers are already familiar with. When I think of a “route” I think of a way, a path, an algorithm, for getting from Point A to Point B, not the target destination itself. Maybe I’ll warm to the term if I can understand what drove this word choice.
Conclusion
As the title of this essay reads, this is a “first look” at Flutter, but it is an informed one from years of industry and hobby experience. Having worked with C#, Java, Kotlin, Objective C, Swift, JavaScript, XAML, XML, JSON, SQL, and numerous other languages and development platforms, I’ve come to know commonalities among many of the languages, and some of the differences that distinguish each. Flutter with Dart is a decent environment, so far, that has its own quirks, but generally has impressed me a lot. I haven’t yet dug into image processing, network connections, file IO, screen transitions, animations, a deep dive on styling, handling user-granted privileges, or a bunch of other topics the industry is always wanting, but that will be coming.
I like what I’ve seen in Flutter (version 3.0.1 as of this writing) thus far, in all of just a week’s time. Outputs have been prettier beyond my expectations, and have been accomplished with seemingly less overhead to accomplish my ideas, although I’ve had to shift my thinking about how UI is coded, which is not an explicit deal-breaker. I just hope that Google lets Flutter fly (“flit”? “flap”?) as long as it has done for the Android platform because if it does, Flutter may just become as successful as Android has been, and Dart will take its place among other powerful languages in use today.
Demo - The Finished Product
Have a look at what I created with Flutter 3 in just a few days' learning time!