I’ve spent the last 3 months being the sole developer for a fairly complex Flutter app for a client. It’s been very fun and Flutter is, generally, a pleasure to code with.
It’s also been a learning experience, so I’ve compiled a list of the 5 most important lessons (for me).
Split your stateful widgets
Often, we refactor classes when a file gets big, and that’s fine. But there is another reason to split a stateful widget in Flutter. Remember that the whole widget rebuilds when your code calls setState()? So split your widgets between their static and dynamic content, separate widgets if they contain 2 visual parts that obtain their data separately etc
Example: I had a stack of images, inside a list, and some of the images contained animations. With a simple logic, all the stack code fitted in under 100 LOC in one widget. However, scrolling was a bit jerky. I improved it by creating a widget for each image, and animating those as and when required, instead of rebuilding the whole stack.
Update 29/06/18: I’ve simplified and turned the example above into a code tutorial Improve your Flutter app performance: split your widgets
Handle errors properly in your Futures
I wrote a code tutorial on writing async code in Flutter, so I won’t repeat myself here, except to say one more time “Don’t forget to handle errors!”.
Example: the app uses Firebase plugins for the backend. Sometimes, the whole app froze on start up. It turned out that I was trying to get a value for a key that didn’t exist in a document in the database. A silly mistake which triggered an app freeze instead of an app crash, and took longer than it should have to track down.
Don’t let your print statements crash your app
While working on new code, I often litter it with print statements, so if it doesn’t work as expected when I run it, I can find out from the logs why. Well, sometimes, the print statements were the reason for the problem!
It turns out that print("Step 1" + expectedStringInstance.toString()) will run fine if expectedStringInstance is null but print("Step 1" + expectedStringInstance) will throw an ArgumentError exception.
As a Java developer, it’s one of the few counter intuitive things I have found in Dart and I keep being caught out! (I’m guessing it is because null is an instance of the class Null)
Choose one architecture and apply it everywhere
I chose MVP but it doesn’t really matter what you choose. What is important is to understand it and apply it everywhere, even on very simple features. Why? Because no doubt those simple features will become more complex and if you don’t have the architecture in place, you will risk writing spaghetti code.
This is true for all frameworks, but especially important when part of your cognitive energy goes into figuring out how to do more basic things with the framework.
Know your multiple children layout widgets
One of the best ways to learn Flutter is to write a large app using it. You can check jobs out on my Flutter job board, or create your own app project. Need inspiration? Have a look at this list of public APIs.