-
Notifications
You must be signed in to change notification settings - Fork 10
Architecture
⚠️ This page is being written on the go along with the ticket #213
Component. | Library |
---|---|
State Management | controllable |
Dependency Injection | get_it + injectable |
Navigation | auto_route |
Localization | i18n_extension |
The code base is divided in several modules:
-
leafy_data
— all the data related stuff; -
leafy_domain
— domain related things: use cases, services interfaces etc; -
leafy_launcher
— launcher itself: the controllers, the app UI etc; -
leafy_localization
— all the translations and localizations; -
leafy_resources
— application colours, paddings, text styles etc.; -
leafy_ui_kit
— reusable widgets.
// TODO: Add links to the folders when the update is shipped // TODO: Put the detailed description about the packages to the corresponding readmes.
Every screen is a so-called «feature». Home
— is a parent feature while App Picker
and Settings
are sub-features of Home
.
The file structure for a feature is the following:
feature_name
controller
/* feature controller files */
widgets
/* feature sub widgets */
feature_page.dart
feature_body.dart
feature_listener.dart // if needed
Page
widgets contains the higher configuration of a page: Provider
/ Scaffold
/ SafeArea
/ Listeners
/ Inherited
— it all goes there. The UI of the page itself goes to the Body
widget.
So a page is usually something like this:
return XProvider<MyController>(
create: (_) => injector(),
child: const Scaffold(
body: SafeArea(
child: MyControllerListener(
child: MyBody(),
),
),
),
);
Reasons:
- The page configuration is easily found in one file;
- Imagine that
MyBody
in the example above will be replaced by the tree from the example below. That would create a terrible waterfall widget.
Body
widgets contain all the UI of the page.
❗️ One important requirement is to decompose widgets making them as small as possible.
So instead of:
build:
return Column(
children: [
Text(
someText,
style: AppStyle.first,
),
TextButton(
onPressed: () {
/* Do something */
},
child: Text(
pressMeToDoSomething,
style: AppStyle.first,
),
),
Column(
// More waterfalls ...
),
],
);
We do this:
build:
return Column(
children: const [
FeatureTitleText(),
FeatureDoSomethingButton(),
FeatureMoreWaterfalls(),
],
);
Reasons:
- The tree is more readable: in the latter example you can clearly see what the tree contains, e.g. the title, the button and some waterfall widgets. In the example above you would need to get into the details like reading the text to understand what's there in the tree;
- Moreover, we exclude possible waterfalls from the tree because all the nesting goes to other classes;
- The tree is less complex on every level;
- To avoid unnecessary rebuilds (notice: the whole list is
const
!); - To keep the possible widget logic (like animation controllers etc) separately from other stateless things.
🌱 Leafy Launcher
- Join the Telegram chat, @leafy_launcher;
- Send us an email to [email protected].