I have been working with Flutter on and off for over a year and learning new things about the programming language Dart. As always with new frameworks and languages I thrive to achieve the “best” way to do things.
In this post I want to share what I learned and how (and why) I do things the way I do.
I assume you are already familiar with Flutter. Flutter is a framework that was built to code apps for cross-compatibility between devices and OSes. So a Flutter application should work both on Android, iOS, on the web and on desktop (since Flutter 2). Many tips here are inspired by other people, so I will reference them accordingly.
When first creating a Flutter project with
flutter create, the following structure is created:
/amazing_project ├── amazing_project.iml ├── analysis_options.yaml ├── android ├── ios ├── lib ├── pubspec.lock ├── pubspec.yaml ├── README.md ├── test └── windows
Most of the magic of Flutter is hidden inside its build system and its integrated library. More on that is revealed after building an application, but if you have worked with Flutter you know that your code resides in the
In a fully developed application you have screens (the UI), database models (local or remote) and some application logic. I often struggle to place everything where it makes sense, so I opted for the following structure:
lib/ ├── components │ ├── chat_widgets.dart │ ├── confirmation_dialog.dart │ ├── custom_appbar.dart │ ├── profile_picture.dart │ ├── rounded_button.dart │ ├── rounded_inputfield.dart │ ├── rounded_passwordfield.dart │ └── text_field_container.dart ├── constants.dart ├── main.dart ├── models ├── providers ├── screens └── theme.dart
From top to bottom: The
components/ directory contains UI components that are used all over the application. That means that multiple screens may use these. These are more general elements in order to provide a common theme to your app (and not rebuild the same widgets over and over again).
models/ directory contains the data models that the app uses, consisting of mostly data classes (e.g., what is a user). The logic (e.g., what to do with a user) is defined separately from the data models. This folder also contains appropriate subfolders, such as
firebase/ for Firebase-related models, and a
workflows/ folder where the logic is defined.
providers/ is the directory for classes that use the provider package (and what is required to notify different parts of the UI). While this is part of the UI, Providers are used and called all over the application. You can find a tutorial in the Flutter docs.
screens/ is the folder that contains the majority of my apps, namely the UI elements. This directory is subdivided into multiple folders:
lib/screens/ ├── account │ ├── components │ │ ├── login_ui.dart │ │ └── signup_ui.dart │ ├── login_screen.dart │ └── signup_screen.dart ├── conversations │ ├── chat_screen.dart │ └── conversations_screen.dart ├── navigation │ ├── components │ │ ├── drawer_animation_controller.dart │ │ ├── drawer_list_bottom.dart │ │ ├── drawer_list_menu.dart │ │ ├── drawer_profile_picture.dart │ │ └── home_drawer.dart │ └── navigation_screen.dart └── settings ├── components ├── my_profile_screen.dart └── settings_screen.dart
Here we see the return of the
components/ folder. So, every folder in screens contains one part of the application. This is a pattern that I picked up using the Django framework, where every subfolder is an “app”. This also means that every Widget in my application is neatly split up in multiple sub-widgets. The main widgets are found in the top-level folders (
conversations/, etc.) and every widget part is found below that. What this looks like on the code level is found in the next sections.
Another comment here: This setup works great if you want to properly structure a big project and if you want to make an effort to keep your main widgets as small as possible (extracting sub-widgets into their own files and classes). I find this cleaner than making one big widget, even if I only use the widget once in the code.
Widgets are a big part of Flutter, because every UI element is a “widget”, and there are different types of widgets. The Widget tree can grow significantly, for example a simple Settings screen with a header and 3 buttons:
As you can see from the diagram, I already split up the top branches into different UI classes:
SettingsMenu. Below that, we split up even more, with
SettingsMenuButton etc. All of these components are located in the (surprise)
settings/components/ subfolder. This is way cleaner than just putting the above code in one single Dart file (and makes refactoring and fixing bugs a bit easier).
Most of the tips above are based on months of reading other people’s code, trying and refactoring stuff until it works or just pure try-and-error. But I want to make a few mentions here on great resources.
The Flutter Way is a Youtube Channel that shows how to build really nice UIs in Flutter. You can support the founder Abu Anwar via Patreon and get exclusive access to resources. The public GitHub repos are also a good source of inspiration for your own coding style. You can also just watch the longer videos, since they always contain really relaxing instrumental music.
Awesome Flutter is a collection of articles, videos and other resources around Flutter. I generally do not like these repos as links are often outdated or 404, but this collection has served me well in the past. It’s especially helpful here since Flutter has not such a big community yet compared to other frameworks out there.
While the Flutter Cookbook is not exactly a hidden gem, it contains a lot of examples on how to do common stuff. Very helpful when you want to include some more graphical candy to your app.
I also looked at some books, but I did not like the writing or the project structure, so I ended up not using most of them. Still, the Flutter Apprentice book has some interesting book material on Github with a lot of example apps.
That’s it for today! Hope you get something out of my notes. I really enjoy Flutter now that I set myself some rules on how to use it, and I hope you do too.