Lewis Cianci wrote down his journey about moving from Xamarin.Forms to Flutter and compared Flutter vs Xamarin.Forms.
Making the biggest change of my programming career
I remember where I was when I got the email that Flutter had finally reached a stable release. A friend of mine who was working on a Xamarin.Forms app with me had sent me an article on flutter.io (now flutter.dev) showing the release announcement. As a budding Xamarin.Forms developer and a .NET fanboy, my response was fairly diminutive.
“Great,” I thought. “Another mobile framework.”
This dismissive attitude was warranted. When Flutter was released, we already had Xamarin.Forms, Ionic, Cordova, React Native and NativeScript, not to mention abstractions that ran on these technologies (like Redux for React, Prism for Xamarin.Forms, etc.). And of course, each platform had its own native implementations for making apps. Previously, I had shipped apps on Ionic and dabbled with React Native, but neither felt like the true end of my search for a cross-platform development framework. Ionic didn’t lead to a great user experience, as it uses Cordova, and I didn’t enjoy having to declare user interfaces with React Native (through TSX or JSX files). That’s not me trash-talking those platforms — it’s just me explaining my choices.
Finding out about Flutter
When I found out about Flutter, I couldn’t help but wonder, “Why?” Why did the world need another cross-platform mobile framework? And to make matters worse, not only would you have to learn a completely new way of designing and describing your apps, but you would also have to learn a hitherto completely unknown language known as “Dart.” Dart code already looked vaguely familiar to me as a C# developer, but the little things still trip you up (like subtle syntax differences). So you have to invest more of your time into learning something new.
Learning a new framework
And when you’re learning how to use something new, you’re spending time doing something that isn’t productive. You can’t sell or ship the hours it takes for you to sit in front of a computer and learn how to use a new framework — that’s just not how it works. You can only do that after you have learned how to use the framework that you’re learning about, and there is a lot to learn before you hit that point.
And in my life as a developer, I have certainly started doing a lot of things and flamed out before finishing them. So there’s always a risk that you will find some bugbear or issue with the framework you are learning about that could be a roadblock in your development and subsequently cause you to bail out on trying to learn about that technology. (That’s what happened to me with NativeScript.) Every second you spend learning about a technology that you’re not going to use is a wasted second. I decided to take the leap and learn Flutter, and where did I wind up?
Time is a critical consideration of learning something new (Photo by Elena Koycheva on Unsplash)
Well, after about a year and a half, I’m a bona fide Flutter convert. Some of my coworkers are interested in what makes me so excited about Flutter, while others are just really puzzled. And currently, I’ve got a few Flutter apps that I’m moderately proud of. People read my articles about Flutter, and they watch the videos that I make about a technology that I’m genuinely excited about. (Some of them don’t understand my exuberance or have negative experiences with Flutter and let me know about them, and that’s okay. I appreciate all the engagement. 😊)
But today, I’ll be reflecting specifically on the beginning of my journey, why I changed from Xamarin.Forms to Flutter, some of my decisions that played into that and how happy I am with that choice today.
Image from James Montemagno
Leaving the monkey behind
I had used Xamarin.Forms for a couple of years and was having middling success. It used C#, and it was good enough for my needs. Writing UIs in XAML was a good enough fit, and I was able to reuse my C# knowledge to create apps. I had some experience in XAML from using WPF, so I figured it would be a good fit. I could also target the Universal Windows Platform (UWP), so my app could run pretty well anywhere, and I could stick with just C# (and XAML). I would say that I used the framework as a means to an end, as a way to have an app on a phone.
As time went on, though, I started experiencing more general weirdness from the framework. Apps would crash shortly after launching, and it would only happen when the app was built in release mode and not debug mode. Trying to get associated plugins to work with Xamarin.Forms wasn’t straightforward and required surfing many GitHub issue lists to try to find a way to solve my issues. Things that were supposed to be simple, like loading fonts (even Font Awesome), required a certain configuration of various NuGet packages, which weren’t well documented at the time. I remember implementing a whole new feature in my app, really refining and polishing it, and then doing a release build and seeing that it just crashed at launch.
Support was pretty hard to come by (Photo by Alexander Andrews on Unsplash)
I posted questions on Stack Overflow, but there were no obvious answers to the questions at hand. There was no community Gitter or Slack where I could ask my questions, so with no idea why my app was crashing in the release build, I just threw away my entire new feature and went back to my last known good build. I wish I could say this was an isolated experience, but unfortunately, it wasn’t.
Programming with Xamarin.Forms was frustrating
As a developer, I’m interested in putting in the minimum amount of effort required to produce a high-quality phone app for my customers. I don’t feel like that’s a particularly shocking statement to make — if someone can produce an app in a day, and someone else can produce the same app in five days, then I’d be inclined to go with the former if it looked and functioned the same. I like to spend less time on the basics of the app and more time on the really good-looking parts of my app. Logically, the basics of an app shouldn’t take very long, whereas the more complicated and nuanced functionality will take longer.
I’m also interested in a moderate level of design. Don’t get me wrong, I’m a developer, not a designer, and I certainly know my place. But without a designer on the books, it becomes my responsibility to make the apps pop and look great with gradients, animations, and other aspects of visual polish. One is inherently dependent on the other — the app has to be stable and functional before I can start worrying about how beautiful it is.
Unfortunately, implementing the core functionality of my app in a reliable way took so long and was so unpredictable that I never had time for the polish. Even accomplishing simple things, like making it so that users could navigate through the app, wasn’t something that came easily. Navigation is something of a sore point for apps made with Xamarin.Forms and something that was supposedly made better with Shell, but I developed my apps prior to Shell’s existence. Spending so long on the basics of my app meant I didn’t have time to improve the look and feel of my app, so the overall appearance suffered.
All my apps looked bland (Photo by Benjamin Bousquet on Unsplash)
These kinds of issues existed elsewhere in the framework as well, and I probably spent more time being frustrated than I did being pleased with it. There are probably quite a few areas that I could highlight in relation to this, but one that really stands out to me has to do with the
CarouselView implementations in Xamarin.Forms.
CarouselPage and CarouselView
Enter the CarouselPage. This is just a page that has pages nested within it, and it allows you to swipe from page to page. I use these quite a bit for startup wizards in apps, as I can show the user a page they can enter details on, and when they hit next, they will see the next page.
Fortunately, the CarouselPage in Xamarin.Forms was implemented and supported by the Xamarin.Forms team. Great! You can just grab that and use it in your Forms app then. That is until you actually do that and find out it’s missing critical features. (For example — it doesn’t give you an active index; instead, it just exposes a mysterious CurrentPage. How do you get an active index from that or trigger a page change from your code behind or ViewModel? It’s not easy.) Plus, when you Google this exact issue, posts crop up telling you that CarouselPage doesn’t support UI virtualization, so having a lot of pages in the CarouselPage will take up a lot of memory. Furthermore, other posts indicate that CarouselPage suffers from memory leaks anyway and should be avoided. Somewhere along this journey, I wound up finding the CarouselView package.
CarouselView — like CarouselPage, but better
CarouselView (as at https://github.com/alexrainman/CarouselView) was initially the solution to all of my carousel issues. It was as easy as installing the NuGet package into my project, and away I went. Then Xamarin.Forms 3.0 released, and it just stopped working. Not gracefully, mind you — navigating to a page with a CarouselView on it simply threw an exception.
At the time, there was a fix available. But subsequent releases of Xamarin.Forms broke the CarouselView again and again. The CarouselView is a community package that is maintained by the community, so it is in no way appropriate to make demands for updates or fixes from some guy who is maintaining it for free. The better question to ask is, “In all the years that Xamarin.Forms has been a thing, how did we not get a native, working, properly supported CarouselPage that did what it said it would do on the tin?” These days, there is apparently a
CollectionView that solves these issues, but for literally years, our only choice was a community-maintained
CarouselView or a broken Xamarin.Forms implementation via
CarouselPage. At the time when I wrote my apps,
CollectionView wasn’t available.
Perhaps it’s poor form to riff on the quality of a control like this. After all, not everyone uses a carousel, so perhaps it wasn’t a priority. But what actually did land in the framework were things like CSS support. Bringing CSS to Xamarin.Forms was always weird, as you could only ever express a subset of visual styling that you could express through XAML styles. So if you wanted to get the appropriate control over the look and feel of your app, you would wind up learning and using XAML styling anyway.
It’s nice that I can bring my CSS know-how to XAML, but I still have to learn XAML to make any use of Xamarin.Forms, and I can still completely express the look of my application through existing XAML styling. Including support for CSS means that the good people at Microsoft who maintain Xamarin.Forms have to implement the functionality, then they have to write the tests, and then the functionality has to be maintained for future releases. And while we got things like CSS support, other, more basic parts of the framework have largely been left untouched or have the same quality issues that they have had for years. When the framework is being widened with things like CSS support while existing issues aren’t getting the attention they need, it does paint a concerning picture of the future of the framework.
It was time to choose between Flutter vs Xamarin.Forms. (Photo by Ross Findon on Unsplash)
Deciding to change – from Xamarin.Forms to Flutter
Gradually, I started to dislike Xamarin.Forms more and more. Implementing simple features felt very difficult. Updates to the Xamarin.Forms package would break my app. Support channels were not clearly defined, and there was no easy way for me to get support or to solve the issues I was experiencing. Plus, everything I made looked truly, genuinely awful. Visually, it was very disinteresting, and the only way I could make things look nice was if I wrote custom renderers for each platform. This was a huge time sink and produced varying results.
One night, I got some random JNI error on my Android app that I simply couldn’t move past. It was right at the end of implementing a new feature, and no matter what I did, I couldn’t work out why the crash was occurring or how to solve it. This came after experiencing a lot of frustration with Xamarin.Forms in trying to implement new functionality.
At about this time, I had started to dabble with Flutter, but I was largely put off by the requirements to learn a new language (Dart) and the functional (instead of imperative) nature of the framework. But I closed my app and decided at that point that I wasn’t going to maintain any existing Xamarin.Forms apps and that I would rewrite the ones I had to in Flutter. I wouldn’t create any new Xamarin.Forms apps from that point forward, either.
Now, you could think that I’ve got a lot of gall to be here complaining about the quality of a free, open-source framework. Let me be clear — I am not here to insult one framework or say that one is necessarily better than another. I’m merely sharing my thoughts on why I decided to change and how that experience was for me. Many hardworking, dedicated engineers gave a lot of their time to get Xamarin.Forms to where it is today. I just think it wasn’t the best fit for what I was trying to do. So, what happened next?
Moving to Flutter
It’s not easy to sit down and commit to learning another language and framework (especially when you already know one that can make mobile apps). You make silly mistakes, you don’t know how constructors work, there’s some weird
super keyword that reminds you of Java, and so on and so forth. Additionally, because you are now responsible for the state of your app, you have to research state management solutions for your app and choose the “right one” despite probably not knowing enough to do so.
In the early, early days, I expected my apps to look truly bad. To my surprise, however, my Flutter apps almost immediately looked better than anything I had made with Xamarin.Forms. Material styles were available, and my app just rolled with those by default. While I hadn’t given any thought to how my apps would look (as they were essentially just “hello world” apps), Google’s Material Design team had done so, and the resulting look and feel was not bad at all.
Photo by HalGatewood.com on Unsplash
One of Flutter’s features I knew about at the time was “stateful hot reload.” This was of middling interest to me. I had already shelled out for the incredible LiveXAML for close to $200. Seeing my UI update when I saved it saved me a ton of time. So I kind of already had something like this. Plus, I had actually paid for it, and I obviously couldn’t use it in Flutter (meaning it would be tantamount to a waste of money if I switched to Flutter). I viewed “stateful hot reload” as what LiveXAML gave me, and in a way, I was kind of correct. If I made a change to my XAML and saved it, the view would update with whatever was in the view model. In this sense, the state was maintained.
However, I had thought about this the wrong way. In Flutter, you can edit anything. (I’m not sure about the technical implementation of this, but I certainly haven’t hit any limitations.) Your UI, your services, your business logic, anything. Then you save it, and the changes are synchronized to your device, and you can use them immediately. Even if you write brand-new code and set a breakpoint, when you save it, that breakpoint will be hit like it was always there.
Previously, to update a service in my app, I would have to stop the app, change the implementation, build the app and then run it again. This was about a one-to-two-minute process every single time. This stop-fix-run cycle was removed when I moved to Flutter, saving me hours in the first week.
There are obvious applications of this, like playing with the padding on the visual layer or changing colors, but there are also other uses. For example, I’ve had a need to play around with the device’s accelerometer and get pitch/yaw/roll from the device. I was testing out math equations to derive these values, and I had a print function in an infinite loop that was printing the device’s angles from 0–360. At first, my math was completely wrong, but as my app was running on a test phone, I could easily update the math, hit save, and see the new values printed to the console. Rapidly prototyping these changes without having to stop and start the app changed how I made apps.
Flutter’s implementation of hot reload is the best way to solve this particular problem — the best I have seen in the industry to date.
Better type checking for the UI
When delving into a new framework like Flutter, it’ll only be a matter of time before you write something that straight up doesn’t work. I experienced something similar when I was getting into Xamarin.Forms. With Xamarin.Forms, if you wrote bogus C#, then it would be type checked and undergo syntax validation, and then you would be told how you messed up.
But what if you messed up in the XAML for a Xamarin.Forms app? Well, in my experience, this wasn’t quite the same. It was easy to put the wrong type into a binding or bind it to a parameter that was wrong for what you were trying to do. If you got this just right, you would experience app crashes because the framework would not know how to bind your particular parameter to the receiving control. And because it seemed like this crash was occurring from within the framework, troubleshooting it was not easy.
If this happened, you could spend days trying to figure out what went wrong. In my experience, this could be anything from an extra ending ‘>’ in your XAML to just binding something incorrectly or to the wrong type. Sometimes when I had issues like this, I would just reset to my last known good codebase and then reimplement the same feature, hoping for a different outcome. The lack of effective type checking in XAML made for a particularly difficult time.
So, how is Flutter any better?
dynamic, which is the same as C#’s
object, but if you do that, you probably have bigger problems).
Everything — the UI, even your styles — is all written in Dart. These type errors are caught at development time, not at runtime, so you can catch issues before you deploy your app to the world. This is a huge advantage compared to Xamarin.Forms, as type checking in XAML, especially in styles, was sketchy at best.
Functional, reactive programming
In Xamarin.Forms, if I had a form that would let a user enter some details before hitting submit and then had some files download to the device, it could mean quite a few moving parts. There would be a
Label to show some text describing what was happening, something to show progress, and so on and so forth.
Using ViewModels, I would have something like
showLoadingIndicator as a boolean and something like
progressText as a string. The buttons would typically use something like an
ICommand, and all of the data that I wanted to show on the page would implement
INotifyPropertyChanged so that Xamarin.Forms would know when a property had changed so it could update the UI.
So, these controls would always be in the visual layer, but they would be hidden or shown based on the values in the view model. If the user had started the download process, for instance,
isDownloading would be toggled to
true (which would make the progress indicator come up and would disable the “Submit” button while the request was in flight). Every time this action finished, either successfully or with an error, I would have to toggle booleans back and forth to show or hide certain parts of the form, depending on what the user could do at that point in time.
Sounds great — until you forget to switch the loading indicator off or something fails and leaves your UI in an unexpected state. And that’s the key word here — state. In Xamarin.Forms (with Prism as my MVVM library), my state would be defined in the view model, and I was mutating that state by setting the properties to new values. Mutating the state is hard because you have a set of moving parts, and you are manually throwing the switch or changing the values on the fly to try to create a new UI state for your user. In my experience, this makes for a kind of average experience for the end-user.
With Flutter (with the BLoC pattern), the UI changes depending on what state your app is in. So, if your app is in the loading state, the widget tree with the loading indicator gets shown. If the user needs to log in, the widget tree with the login form gets shown. You wind up showing the UI to the user depending on what state your particular set of widgets is in. This means that no Flutter widget has a
visible property— if you don’t want it, you just don’t call for it to be rendered.
Essentially, all your apps become a collection of states, and then you define how the user moves through your apps and what events move from one state to the next state. Users put events into your state machine, and out pops a new state. They do this to move throughout your entire app. An event goes in; a new state comes out. If an error is encountered, then an error state comes out. You just define how to handle these states and what you would like to show to the user when these states are encountered. Going from modifying tens of properties on a view model to simply switching between states has made the act of app development a great deal simpler. Events and states fit better in my head than changing properties on a view model.
Photo by Andre Mouton on Unsplash
No more reflection
This is going to come across as a weird thing to talk about in this context, but bear with me. Basically, reflection is a way for an app to obtain type information about itself at runtime. You can also dynamically create objects by type or invoke parts of your application through reflection. This becomes a little dicey when Xamarin.Forms uses a “linker” to remove unused parts of your application. This process occurs through using static analysis to work out what parts of your app are used and what parts are unused. So, if you use reflection in your app or use a plugin that uses reflection (and a surprising number of plugins for Xamarin.Forms do use reflection) and then the linker removes that part of your app, what happens at runtime? The app crashes because the linker has removed that part of your app.
You may try to disable the linker, but then the entire Xamarin.Forms library gets included in your app, so your app is now massive. So that option is out. Your only choice is to try to configure the linker to leave parts of your app alone and not “link them out.” If you get this wrong or don’t thoroughly test your app, you’ll get a crash at runtime, or your users will.
Where does Flutter come into this? Well, Flutter doesn’t support reflection. This means that when you build your app, the compiler can reliably know what parts of your app are used and what parts aren’t. It can also use static analysis to know what to keep in and what to tree-shake out. And when this process is complete, you don’t run the risk of part of your app being partially shucked out due to the removal of code that the compiler thought was unused. You don’t configure the linker because the problem is avoided entirely. Not having to think about this, even on its own, is a huge relief and time saver.
It’s been worth the switch
All in all, I’m glad I moved to Flutter. Don’t get me wrong, it’s not a completely frictionless experience, but it is a framework that I enjoy using and like to learn more about. Plus, my apps look better and perform better, and they are smaller than they have ever been before. I’m getting into a little bit of design and other areas that I feel like I could not have accomplished with Xamarin.Forms. I feel like creating apps with Flutter is the beginning of something — of learning how to create beautiful, cross-platform experiences. And it doesn’t involve fighting with a framework to try to get it to do something, which is not a nice feeling.
In the past, when I have told people about how I changed from Xamarin.Forms to Flutter, they have asked why I didn’t change to Xamarin Native instead. Suffice to say, code reuse is a major reason for the existence of a cross-platform library, and I would like to reuse my UI code. I also don’t like how the binding libraries work in Xamarin Native, and trying to call native Android functions from C# is unwieldy.
Of course, your experience may be different. You may use Xamarin.Forms and love it, or you might have used Flutter and just absolutely loathe it. You may have sworn off all manner of cross-platform solutions and will only ever write native code for your platforms. And if that’s the case, that’s perfectly fine. Whatever you want to use to create something is completely up to you. What matters is that you are creating these apps — these experiences — that have never existed until you invented them. And I think that’s pretty cool. So keep creating, and use the best thing that works for you. For me, though, that’s definitely Flutter.
Lewis Cianci is a software developer in Brisbane, Australia. His first computer had a tape drive. He’s been developing software for at least ten years, and has used quite a few mobile development frameworks (like Ionic and Xamarin Forms) in his time. After converting to Flutter, though, he’s never going back. You can reach him at his blog, read about other non-fluttery things at Medium, or maybe catch a glimpse of him at your nearest and most fanciest coffee shop with him and his dear wife.
More articles by Lewis: