What I wish I knew before building a Flutter App
For the last week I've been building an app with Flutter. I opted for Flutter because I liked the promises it makes. Namely the crossplatform nature and the fast reload during development. Here are my first impressions:
Key insights
- Flutter is not a replacement for native app development
- The Dart language is very strictly typed
- Learning Flutter feels like learning HTML again
- It's easy to shoot yourself in the foot in regards to state
- Flutter is an incomplete framework
- Check your dependencies carefully
- The hot reloading is amazing
- Going beyond Material Design
Flutter is not a replacement for native app development
The first surprise Flutter had in store for me was the fact that the documentation recommends the use of Android Studio or Visual Studio Code. Those are the very same tools you might already know from your previous experiences with app development. I know I did.
A different way to describe Flutter is that it enables Dart to be used as a scripting language with an interpreter for different platforms. That means you're still talking to the same APIs as with any native App. In fact if you want to interface with any device capabilities, beyond showing UI and making HTTP requests, you will need to write the native code for those features.
The advantage to using Flutter is the fact that pub.dev provides a repository of packages that enable those native implementations for you, by the community.
The Dart language is very strictly typed
I love strictly typed languages, so for me Dart feels like home. There is a range of strictness though and Dart leans towards being very strict. Much like my experience with Java. Here is my favorite screenshot I made during development:
The solution to the above error (displayed by Android Studio) was to add the dynamic keyword to the first parameter of the function in question.
After the first few hours you should have encountered most of these quirks a few times and they are, once you know what to look for, easy to fix. If you're like me, you might feel a tiny bit inferior until that point.
Learning Flutter feels like learning HTML again
The documentation I followed focused on using different UI elements. That makes sense, most of what an App does is in the UI and its elements. For the most part those elements are sorted in a tree.
MaterialApp
└── StartScreen
└── Column
├── Padding
│ └── DropdownButtonFormField
├── Padding
│ └── Flexible
│ └── Text
└── ElevatedButton
Intuitively this kind of structure looks a lot like HTML (or XML) to me, just with Dart Classes instead of being plain text.
That's where the similarities end though. Each of these elements has its own quirks you're going to have to learn the hard way. For example:
Say you want to disable input on a Text Input field during loading. The Text Input has a property to achieve just that. Doing the same for a dropdown list is completely different. That element does not have the property in question. Instead you need to pass null to the onChanged property in place of your callback.
On their own these design choices aren't bad. Just be aware that you have to learn all of these details, like learning the vocabulary for a new language.
It's easy to shoot yourself in the foot in regards to state
The solution offered for state management seems easy enough at first glance. Any Widget (roughly equal to a component) can have it's own state, or be stateless. It's easy to forget the lessons learned from Redux or Vuex. Having each component remember it's own state can easily devolve into a complex web of interconnected state changes, making the application hard to understand and debug.
Instead you should try and keep a master record — a source of truth — somewhere near the top of the structure. From there you can pass values down the chain, just like you would in one of the aforementioned solutions.
Flutter is an incomplete framework
At first I hesitated to call Flutter a framework. It's main purpose is as a development tool for interoperability and programmer convenience. During the course of building my app though, I encountered many features that come out of the box and which I would consider part of a framework:
- Routing (definition and navigation)
- Template language and components
- Debugging tools
- Theming
- Internationalization (i18n) support
- State management
That's a nice start, but probably not all you need. If you plan on building a decent sized app, I suggest you think about building or searching for solutions in some areas, as the out of the box solution doesn't exist or is lacking.
Forms
Of course you can build forms with Flutter. How to access the data from each component varies though, as well as configurating the current state. Each row also requires some boilerplate code. For this reason I would recommend researching some kind of formbuilder or controller.
JSON Serialisation / Deserialization
In contrast to what you might be used to, handling JSON requires some setup for converting JSON into native Objects. If you don't need that, it's easy to parse a JSON string into String Maps. That might not be suitable if you have a large project.
Brand Styles
I listed theming as one of the builtin components. That's only half true. Some Widgets can be styled globally, without additional parameters. For others (Text, for example) their style needs to be set as a property every time. Consider setting up subclasses of some Widgets, or maybe a factory of some kind to avoid setting the attribute styles on every object by hand.
Check your dependencies carefully
As mentioned above, the packages you install are going to be responsible for the native implementation of their features. For my app I was testing on Android and everything was working fine. Out of curiosity I checked the source repository and found out that the iOS implementation is basically a no-op. Sure, the version number 0.0.1 also could have given me a hint, but let that be a lesson to all of us. Make sure your dependencies work well on all your target platforms, before you go all in on them.
The hot reloading is amazing
This is, in my opinion, the best feature of the bunch. Saving your changes and seeing your changes in under a second rivals the feedback loop for JavaScript applications. I expected this feature to only work in some situations, but in reality there are only a handful of situations I know of that require a full reload of the application. Those situations center around state management, specifically state initialisation.
Going beyond Material Design
Every example I found was based on Googles Material design. What happens if you don't want your app to be in the Material Design style? I did some research and the general consensus seems to be as follows:
- All the Widgets can be fully customized, even so far that they no longer have the Material design look.
- It doesn't make sense to reinvent the wheel for vital components like text inputs, navigation or similar common design element.
Make of that what you will. In this case I can't speak from experience.
Conclusion
After one week my first application is done. It's just a tool for my own use, so it doesn't look fancy. It works really well though and for the most part almost everything was intuitive to use. A bigger application would require a lot more thought along the lines of my points above. Those are by no means showstoppers, so I can only recommend you give Flutter a try.
Thank you for reading
I hope you enjoyed the article and maybe even learned something. If you would like to stay in contact I have a mailing list or you can reach out to me via social media.