Fedi — Flutter open-source social network client. Part 2. Code.
Code-quality related things in Fedi: feature-based folder structure, naming, null-safety, lints, extensions, asserts, mixins, etc.
Fedi is an open-source client for Pleroma and Mastodon social networks written using Flutter available on iOS(Beta) and Android(Beta)
Series:
- Part 1. Architecture.
- Part 2. Code.
- Part 3. Build & Config.
- Part 4. Used packages.
- Part 5. Android Studio Plugins & Feature Plans.
In this part:
· Feature-based folder structure
· Naming
· One file = one class
· Null-safety
· Linters
· Named parameters
· Dart format & trailing comma
· Use all Dart features
∘ Spread operator & if in collections
∘ if null ??
∘ Cascade notation
· Extensions
∘ Extension for interfaces
∘ Extension for data classes
∘ Extension for enums
∘ Extension for Dart/Flutter/3rd party libraries classes
· Mixins
· Asserts
Feature-based folder structure
Fedi uses a features-based folder structure
At first, files are separated by features(like account or notification). And only inside the feature folder, they may be separated by layer (like model, bloc, or service).
Pros:
- Clean dependencies. You should clearly understand which feature depends on another and avoid two-ways and cycle dependencies. Low coupling helps maintain code quality and re-use code.
- In the future, you can transform project into Building a multi-package project with flutter. However, I don’t think it is required right now. Android Multi-module projects have better build time, but Flutter build time is OK as-is for me. The multi-module structure also helps make dependencies between features(error for cycle dependencies)
Cons:
- Sometimes it is hard to define features in app and make dependencies
Naming
- Prefer Effective Dart name and style code conventions.
- Prefer long file & classes names like account_follower_account_cached_list_bloc_impl.dart and AccountFollowerAccountCachedListBloc. It is class in account/follower/account_list/cached folder.
- Prefer object names similar to classes. Like object accountFollowerAccountCachedListBloc for class AccountFollowerAccountCachedListBloc. Sometimes it is shortened, for example accountListBloc instead of accountFollowerAccountCachedListBloc if other similar objects don’t exist in content.
Pros:
- It is easy to understand what classes do, which feature they belong to
- It is easy to navigate in IDE by typing the start letters of the name(Find a class and Find file Android Studio features).
Cons:
- Very long file names may look too big and hard to write/remember but Android Studio helps with this
One file = one class
In Dart, you can place as many classes in one file as you want.
However, in Fedi each file have only one class. Except for private classes which are used only in the current file(primarily used for widgets).
Pros:
- Easy to navigate
Cons:
- Nothing
Null-safety
Today (July 2021) almost all popular libraries from pub.dev support null-safety. So I think you should use this feature in any case.
Pros:
- it helps make your app more stable and bug-free;
- better code quality, you always know which var can be nullable or not.
Cons:
- you should spend time on it.
Linters
Flutter has several built-in static code analyzing tool and built-in lint rules.
However, Fedi uses additional lint rules to make code better:
- Official(maintained by Google) lints and flutter_lints. They have recommended and optional rules. It is a replacement for the deprecated pedantic and effective_dart packages
- dart_code_metrics. Additional rules, which do not exist in Google lints. Helps analyze the complexity of each line, function, class etc.
It will be good to enable almost all available options. Some rules conflict (absolute or relative imports), some look irrelevant. But ~90% rules are useful as for me and recommended set is must-have.
Fedi doesn’t use all rules, because we’ve integrated lints only several months ago, and enabling all rules showing 10000+ warnings. It will take some time to clean up the project and enable all features. You can find enabled rules in analysis_options.yaml
Most useful rules for me:
- empty_statements
- avoid_empty_else
- avoid_returning_null_for_future
- no_duplicate_case_values
- unnecessary_statements
- always_declare_return_types
- avoid_shadowing_type_parameters
- avoid_void_async
- await_only_futures
- empty_catches
- file_names
- lines_longer_than_80_chars
- unawaited_futures
- no-magic-number
- prefer-trailing-comma
You can find all rules with examples and descriptions on the next pages:
Pros:
- It helps make your app more stable and bug-free;
- Better code quality;
- Helps maintain code style when several people work on the project.
Cons:
- you should spend A LOT of time on it.
Named parameters
Fedi prefers to use named parameters in any case when the parameters count is 2 or more.
You can put required if the parameter is not optional.
Pros:
- better code quality and readability.
Cons:
- you should write names of the parameters each time. IDE helps with that.
Dart format & trailing comma
Dart format works well, however, without trailing comma, you will have a lot of braces on one level and it is hard to read such code.
Windows: Ctrl + Alt + L
Linux: Ctrl + Shift + Alt + L
macOS: Option + Command + L
Use all Dart features
Check Dart language tour for the cool language features
Spread operator & if in collections
if null ??
Cascade notation
Extensions
Fedi uses extensions in a lot of places.
One big problem with extensions is that Android Studio doesn’t recognize them well and sometimes you need to import file with extension to achieve autocomplete.
Extension for interfaces
It is an extension to the interface to move duplicated logic from implementation classes to one extension.
It may be replaced with mixins.
Extension for data classes
- Convert data types example
Extension for enums
- Add logic to enums
Extension for Dart/Flutter/3rd party libraries classes
- Improve code readability
Example from easy_dispose library written for Fedi
Mixins
Mixins is a cool feature to extend several classes in one class to add more features
In Fedi mixins is used to share common DAOs logic to generated DAO classes.
Asserts
Asserts are usually used in constructors and functions to check input parameters.
Final Words
Feel free to comment and fill issue if you don’t agree with something or have suggestions.
Next part —Part 3. Build & Config.
Start using Pleroma and Mastodon with Fedi if you still not in Fediverse: iOS(Beta) and Android(Beta). Any feedback is welcome.
If you are interested in Fedi and want to help to develop it you can start from Readme.