
Best practices when building apps with Flutter and Dart

When building new apps or web pages with Flutter, I often think about possible implementation approaches. After more than five years of crafting, developing, and polishing mobile and web apps with Flutter, I decided to incorporate my thoughts, ideas, and best practices into that blog post.
By doing so, I'll make you sure you are getting acquainted with Bromelia philosophy about building new products.
So.. Let's get it started.
We'll discuss project structure, state management, theming, localization, and packages in this article. I'm pretty sure that by following these approaches, the development process may become more pleasant and efficient.
Project structure
It makes sense to start with project structure since it defines the overall perspective and feel of the app development process.
The most obvious approach and naive one is a layer-based project structure, it looks this way:

You can see that with this approach, all files related to one feature are placed inside the structure where one feature depends on multiple folders, so while developing a new feature (e.g. home functionality) you have to open and every time switch between different folders - pages, controllers, components, and tools in this very simple case displayed in the image above.
But imagine what happens when the app rapidly expands and you have more than one developer on the team... It can and would become messy.
When I first started developing with Flutter, it was in May 2019, this structure was very obvious to me and the only one that looked fine for me at the moment since I just didn't know better approaches. When the app was small the structure worked well, but then... Then, when the app (Fleengo) grew - it become a nightmare. To implement one functionality I had to open dozens of folders and constantly navigate between them. I felt less productive than I could be.
I wanted to mention this approach, so you know the difference and could pick the one that feels and suits better for you, your project, and your team. I want to share with you another project structure, that feels and works better for my scenarios and every project we at Bromelia work on.
Feature-based project structure

You can see that with a feature-based project structure, each of the features' functionality is placed inside one folder of the feature name. It makes the development experience much more pleasant in comparison to the layer-based approach since you don't have to open dozens of folders and nested folders, navigating between them while developing a feature.
Your observation domain is shrinking and it potentially could make your progress more efficient since you don't have to run through lots of files while you are actually and mostly focused on implementing one feature.
Architecture...
I really want to talk about project architecture, since this topic is extremely important and related to the structure of the project, but to keep things short and the article not bloated, it's better to write about architecture in another blog post.

State management
In this section, I want to talk about state management approaches since it has a very hot discussion among the Flutter community and it's reasonable, that state management impacts when it comes to a comfortable software development process.
Having more than 4 years of experience working with Bloc and Cubit, I noticed that it has one big disadvantage - boilerplate, which is not noticeable at the beginning but then becomes annoying. That's why it was decided to switch to Riverpod. And we are more than happy with this solution.
Riverpod is very robust and comfortable to work with since there are lots of things already implemented:
- AsyncValue (Data, Loading, Error states) allows you to provide data you expect to get from the controller, and you mostly don't need to create these states, as with the Bloc case.
- State switcher and mappers that are comfortable to use when building UI - state.when, state.whenOrNull, state.data ... etc.
- Hooks-based implementation, that saves a lot of development time - to get needed data from the controller in UI, you just use a watch or listen to hooks, instead of using widgets like BlocBuilder, BlocConsumer, and BlocListener like with Bloc case. The code becomes more cleaner, eliminating nesting widgets and making code more readable.
- Good tooling. It's convenient to create a new Riverpod controller since there is a package that provides code snippets for Riverpod. Simply type @riverpod annotation, click Tab, and it's done.
- Code generation. Not sure if that's a plus, cause you always depend on and wait for build_runner to generate. But when watching instead of building it's really fast and pleasant.
Theming
To customize your Flutter app you should use either ThemeData or CupertinoThemeData. Previously it was a problem cause it has a limited amount of color properties you can provide or use. As the app grows, it rapidly becomes not enough, that's actually a problem.
Recently Flutter team introduced Theme extensions that allow for extending theme properties and defining more colors and fonts for themes. The problem is that when defining these extensions it's also required to implement copyWith, hashcode, == operator, and lerp method.
It takes time and increases the boilerplate level of a project. theme_tailor is here to help. The package allows generating all required methods, operators, and properties while the developer is only focused on the initial package setup, picking colors, and running build_runner. Developing speed is rapidly changing. Just take a look at this...

Localization
For localization, I always use an officially supported package and plugin - Intl from Localizely. It's convenient to work with, once set up, you should create a .arb file where you put all key-value pairs of words or sentences. Setup and language switching are quite easy, just use the plugin. It's well-documented, so you'll find all the required setup and usage instructions on the plugin page.
Packages
Packages and libraries are something really important when it comes to development since they are mostly easy to use and supported by developers. Here is a list of some I can't work without and help a lot:
Conclusion
These best practices and packages are something that I was coming through the years of development and what makes me feel really happy and fast when working with them.
Hope that it will help you. Stay tuned for new updates!