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
You have probably come across ListView and GridView in tutorials. But I found Row, Column, and Stack (in particular with Positioned) equally important to master.
I’ve written a code tutorial on mastering Row and Column. As for Stack and Positioned, have a look at the second example in my How to overlay text and icon on an image tutorial.
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.
Mobile app developer with 12 years experience. Started with the Android SDK in 2010 and switched to Flutter in 2017. Past apps range from start ups to large tech (Google) and non tech (British Airways) companies, in various sectors (transport, commercial operations, e-commerce).
4 thoughts on “5 lessons learned while working on a complex Flutter application”
Even better is:
print(“Step 1 $expectedStringInstance”);
…or, even better still:
debugPrint(“Step 1 $expectedStringInstance”);
Very helpful article. I was particularly interested in your experience with improving the performance of your app by splitting your widgets. Perhaps an idea for a future post could be some code example of how you did this? Perhaps showing the non-performant code against the improved code? Just and idea, but I would find that really useful. Thanks.
Thanks for your feedback. I do have a draft post in progress for this. Well, I still have to write most of it but I will hopefully be able to publish it this month.
Hello there It was really a nice article. I need a help from you. i’m trying to make a image gallery application which get images from an API.
I was using Image.network(“Url_of_my_image_on_server”) and its loading the image. But when i load all images of this user around 200-300 images the app is getting crashed after scrolling up and down about 3-4 scrolls. This is may be because of out of memory or something like that, i dont know exactly. Can you please suggest a good or may be the best practice of loading images from network ?
I’ve tried libraries like cached_network_image, extended_image, etc.. which can be found on Pub.dev.
It would be really helpful if you suggest me a way of doing it. Thank you 😀