Here are some of the tips and tricks I discovered over my last two years of making Flutter application that has reached more than 2+ million users, we also got the Google Play Best App 2021 for Indonesia region. This article will be about the technical tips on developing a flutter app that will be more robust, and easier to maintain by a team.
1. Prepare a well-defined architecture
Before jumping into coding, make sure you already have a well-defined architecture. Discuss the architecture with your team and don’t forget to store it in one document for future reference. In my experience, by not having a well-defined and written architecture, every developer can put their code anywhere with any style into the project. They can mix up logic code and UI code, and vice versa. So when a bug happens, we need to take extra time to fix it, because every feature has its style.
The other advantage of having a well-written architecture document is when there are disputes between teammates, we can check the document as a reference and resolve it. The document itself is also not going to be set in stone, we can add or modify the document when necessary.
Every project has its niche and uniqueness, so you need to choose your architecture carefully. Here is one of my favorite architecture for Flutter project. I’m going to write an article about this architecture shortly. It is a simpler version of Clean Architecture.
2. Use Linter
TL;DR: Linter is saving your time in the long run. Using lint tools can help you accelerate development and reduce costs by finding errors earlier. It also reduces the time of the unnecessary debate on code style with your teammate when reviewing a pull request. So, instead of debating with your teammate in the pull request comment about repeating the same style issues, you can trust your linter machine to prevent that happen in the first place. There are a lot of different lint tools out there for flutter, my recommendation is to use the lint package and dart_code_metrics package.
Install lint tools as soon as possible, because it will more and more costly if you postpone the installation. Installing lint tools for a fresh project is going to be way easier than installing them in the middle of a complex project because you need to also change the legacy code to follow the lint rules.
3. Make the widget smaller as possible
TL;DR: Smaller widget, more maintainable app. One of my regrets when building my previous flutter app is to have a very large widget. My widgets are too long, it can take up more than 500 lines of code (LOC) per widget file. At first, I think it was okay. But, after I have more and more features, having a single widget that has a lot of code and does a lot of stuff makes you less productive, because:
- It is harder to read. When your widget class is too big and complex, your brain will take too much information at one time and it will be harder for you to understand. If you have a smaller class, you can understand the code easier because the context that needs to enter your brain is smaller.
- It is harder to find bugs. If you want to change the things that produce the bug, you need to read a lot of code and do a lot of scrolling instead of just jumping into the file that you suspect the bug exists. It can take your precious time in the long run.
- It is easier to create duplication. The developers will tend to add new stuff without thinking very much because the widget is already too big and chaotic. If the widget is small, then I also can easy to find similarities between UI components and reduce duplication in my code.
There are two ways of making the widget smaller, by extracting it to a class or method. TL;DR: You should prefer using classes over functions to make a reusable widget tree. Check this for an elaborated answer on why a class is better than a method. For me, choosing a class over a method is better performance and also for standardization reasons.
4. Use state management
TL;DR: Don’t use setState if you already use a state management solution. After two years of using flutter extensively, separating between the logic and UI parts is a must. my first project is not using any state management at all, and when I encounter a bug in the logic part, I fixed the bug in the same file with the UI part. It will be fine when you deal with a simple app, but when you have a larger app, it will be a headache for you and your team. It will eventually slow down your development process. My recommendation is to use cubit as your state management because it is less boilerplate than BloC and provides a nice way of handling state in the app (and also scalable).
If you have already implemented any state management, my advice is to not use setState method in any of your code because it means that you need to add business logic in your UI code, except if you can not achieve it otherwise (I rarely encounter such a problem).
5. Don’t leave a broken window
TL;DR: Don’t leave bad code, fixed it as soon as possible. Don’t leave “broken windows” (bad designs, wrong decisions, poor code) un-repaired. Fix each one as soon as it is discovered. If you don’t have the time, one of the tricks is to create a TODO comment here and there, but in my experience, TODO comments for enhancement rarely get picked up by developers because we always have other exciting tasks to do. So, clearing up all TODO as soon as possible is a better idea, especially if the TODO task only takes less than 15 minutes of your time. Remember, discipline equals freedom.
6. Use a lot of packages
I believe that we need to less and less do redundant work. If there is someone out there that has a proven solution to our problem, why we are not using that? It will make our development faster, and we can bring value to our company faster. Of course, you need to make your due diligence before using some packages. Make sure that the package is tested, the author is trusted, and also is acceptable (discuss it with your team before adding any package). In my experience, if you need to have your custom solution, forking a package is also a good idea rather than implementing your solution from scratch, because it will also save your time.
7. Use flutter_gen
By using flutter_gen, I increase my development speed, because my project is an assets-intensive app, flutter_gen helps me when I want to import my assets to the app. It will generate your assets to a class by a single command, so you can get rid of all your string-based assets APIs.
8. A smaller class is always better
In a lot of cases, a smaller class is always better, but don’t forget to use your common sense. The benefits of having smaller classes are easier bug fixes, easier to understand and maintain, and also increase readability.
For example, if you have one page that has two different complex features in it. Instead of using one controller, you can use two controllers instead. By having two different controllers, it will be easier to maintain because each class is much simpler. Of course, you need to do your due diligence and analysis before doing this because every case is different.
9. Have a proper CI/CD
By having proper CI/CD, you know if your code is breaking or not. You also do not waste time on repetitive tasks such as deploying your app to the store.
The main benefits that I got after installing CI/CD:
- Fast feedback loop. In my case, I installed CI on every push to Github, it will run
flutter analyzeto do statical analysis on your dart code,
flutter testto run the tests, and build the app to Firebase App Distribution (that QA team and developers can access). When I run that process every time I commit my code, the feedback is very fast. As a developer, I can know if I break something on each commit. The QA team also be able to check the app faster because they will instantly get the app on every push.
- Freeing my time. I’d like to spend my time doing something fun and not doing the same boring and repetitive tasks. By having a CI/CD to publish my app to the Play Store/App Store/3rd party app I will save my time and eventually thank myself in the future.
10. Read more books
This is not a practical tip, but I think knowing what is a “good code” look like is a very good fundamental skill that every developer must know. I have three book recommendations that are Clean Code, The Pragmatic Programmer, and Head First Design Patterns. I improve a lot as a software engineer by having only just read these 3 books.
Full Article: Zharfan Akbar @ Medium
Is it okay to use runBlocking?
In this video I’ll talk about when it’s fine to use the runBlocking function from Kotlin coroutines and when you...
Mobile App Development Best Practices – 07.12
KSP2 Preview, Mastering in SwiftUI, How to implement gamification and more!
Gemini is the new foundation for artificial intelligence in Android
Foundation models are trained on a variety of data sources to create artificial intelligence systems that can adapt to a...
Google has released AlphaCode 2 based on Gemini
Google today, along with its Gemini artificial intelligence model, unveiled AlphaCode 2, an improved version of the AlphaCode code generator...
ColorfulX – Metal for crafting multi-colored gradients
ColorfulX is an implementation using Metal for crafting multi-colored gradients. ColorfulX Platform UIKit and AppKit platforms are generally supported. Due to MTKView not...
Mobile App Development Best Practices – 06.12
Power of enums, A New Foundation for AI on Android, developer dogmas and more!