Flutter π¦
Project Structure
Generally speaking, a Flutter application can include multi-platforms codebase structure as a cross-platform framework. So itβs important to follow the corresponding project structure when it comes to a specific platform, for instances:
The following structure and directories must be followed for the core Flutter directories (lib/
):
project-flutter
ββ android/
ββ ios/
ββ web/
ββ assets/
ββ lib/
β ββ di/
β ββ api/
β ββ l10n/
β ββ helpers/
β ββ models/
β ββ presenters/
β ββ repositories/
β ββ preferences/
β ββ usecases/
β ββ views/
β ββ main.dart
β ββ app.dart
ββ integration_test/
ββ test/
ββ pubspec.yaml
ββ Makefile
ββ README.md
-
assets
: static assets (images, videos, fontsβ¦) included for the application to use at the compile time. -
android/
,ios/
,web/
: containing all the platform specific configurations corresponding to Android, iOS, Web compilation. -
lib
: the core domain logics and presentation for the whole Flutter application. Depending on the choice of architecture, there could be more or less of the presenter directories added. -
integration_test
: contains all the integration test suites. -
test
: contains all the unit test suites. -
pubspec.yaml
: specifies dependencies that the project requires, such as particular packages (and their versions), fonts, or assets files (images, videoβ¦). -
Makefile
: use aMakefile
to define executable CLI scripts. -
README.md
: contains all the prerequisites and setup guideline.
Exclusion of Generated Files
The team follows βWhat not to commitβ documentation to not include most of the files that the IDE, code editor, pub tool, and other tools generate. This also includes these various generated file extensions:
*.g.dart
*.gen.dart
*.config.dart
*.freezed.dart
*.mocks.dart
etc...
Excluding them benefits the team to:
- Avoid wrong statistics from LinearB with substantial code changes from gen code.
- Have fewer conflicts during the Pull Request merging process.
- Ease the code review process.
Hence, a code generator is necessary for local development or CI:
$ flutter packages pub run build_runner build --delete-conflicting-outputs
Development Environment
Code Editor
There is no specific editor is enforced at the team level. Flutter developers can pick what they are comfortable with but also what does not hinder their productivity.
The following options are the most widely used:
In additions, you will need:
- XCode for iOS code inspection and editing.
Version Managers
Due to the diversity of projects the team works on, each application usually requires different versions of Flutter. Versions managers allow to install and switch between different versions effortlessly.
The following version manager(s) is recommended:
- fvm is the preferred version manager for Flutter.
Formatting
Methods
- Use soft-tabs with a two-space indent. For more indentation and spacing rules, follow the Official Dart Formatting rule.
- Blank lines:
-
Separate each abstract method using a blank line for better readability.
abstract class Human { void speak(); void run(); }
-
- Line breaks:
- Use the line break before each parameter in a method when it exceeds the line length.
-
Always use a line break for the method with more than two parameters.
// Bad void createAccount(String firstName, String lastName, String email, String phoneNumber) { // Your code } // Good void createAccount( String firstName, String lastName, String email, String phoneNumber, ) { // Your code }
-
Commas: Prefer to add a trailing comma
,
when there are more than 2 parameters in a method for auto-formatting in VSCode or Android Studio. This applies to Widget also.// Bad void setReminder({String? label, DateTime? datetime, bool? enableSnooze}) { // Your code } // Good void setReminder({ String? label, DateTime? datetime, bool? enableSnooze, }) { // Your code }
- Keywords:
-
required
: should be at the same line of the parameters.// Bad void disableButton({ required Widget button, required Color color, }) // Good void disableButton({ required Widget button, required Color color, })
-
Comments
- Comment should follow the Effective Dart > Comments documentation.
Widgets
-
Add
const
before the widget whenever possible. By adding this keyword, the constant widget is NOT re-rendered when rebuilding the parent widget, improving the application performance. Read further in this article The Flutter const keyword demystified.Center( child: const Text('Hello World!'), );
-
Prefer to split the code into small widgets to avoid
large nested trees
. The debugging process will become easy for checking the UI issue. Besides, the widget tree is readability and clean. This could be flexible when creating a widget class or a function that returns a widget.// Bad Scaffold( body: Column( children: [ // The Header Container( child: Stack( children: [ Column( chilren: [ Row( children: [ Column( children: [ Container( // A child ), ], ), ], ), Container( // A child ), ], ), ], ), ), // The Body Expanded( child: Row( children: [ Column( children: [ Row( children: [ ListTile( leading: Column( children: [ Container( // A child ), ], ), ), ], ), Container( // A child ), ], ), ], ), ), // The Footer Expanded( child: Column( children: [ Row( children: [ Container( // A child ), Container( // A child ), ], ), ], ), ), ], ), ); // Good Scaffold( body: Column( children: [ _buildHeaderWidget(), BodyWidget(), FooterWidget(), ], ), ); Widget _buildHeaderWidget() { return Container( // The Header ); } class BodyWidget extends StatelessWidget { ... } class FooterWidget extends StatelessWidget { ... }
Naming
The team uses and respects the standardized naming conventions from Effective Dart style. The below guideline is the only slight variation we apply.
-
Capitalize acronyms and abbreviations no matter the length of letters.
// Bad class HTTPRequest {} class DBIOPort {} class TVVcr {} // Good class HttpRequest {} class DbIoPort {} class TvVcr {}
Localization
- Our team uses l10n as a main localization.
- The name of the strings inside
.arb
files should follow the pattern{feature}{optionalWidgetName}{stringType}
and usecamelCase
format. For example:-
loginTitle
: As we can see,login
is afeature
, andtitle
is astringType
. We donβt need to specifywidgetName
since there is only one title forlogin
. -
loginPhoneNumberTitle
: This string looks similar to the above string, but this string is for thePhoneNumber
field. In this case, we specifyPhoneNumber
as awidgetName
to distinguish between other titles. -
errorInvalidPassword
: In this case,error
is a special type offeature
, andInvalidPassword
is astringType
.
-
- More
feature
types can be named according to the application features list.
Package Id
- Package id is a unique id that identifies the application in the App Store (iOS) or Play Store (Android).
- Each platform has its terminology and convention for defining package id. While
application id
is used for an Android application, iOS applications call itbundle identifier
.- A valid
application id
allows alphanumeric characters, periods (.
), and underscores (_
). Read more about Application Id. - A valid
bundle identifier
allows alphanumeric characters, periods (.
), and hyphens (-
). Read more about CFBundleIdentifier.
- A valid