Skip to content

Commit d9fb581

Browse files
Merge pull request #955 from broxus/release/EWM-TECH_1.14.0
feat(EWM_TECH): 1.14.0
2 parents a622a4f + 7e9cf9f commit d9fb581

File tree

323 files changed

+6338
-4642
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

323 files changed

+6338
-4642
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
/dev/integration_tests/**/Pods
3737
/packages/flutter/coverage/
3838
version
39+
.fvmrc
3940

4041
# packages file containing multi-root paths
4142
.packages.generated

build.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,15 @@ targets:
3232
- lib/app/service/*.dart
3333
- lib/app/service/**/*.dart
3434
- lib/http/*.dart
35+
- lib/bootstrap/*.dart
3536
- lib/http/**/*.dart
37+
- lib/feature/**/*_wm.dart
38+
- lib/feature/**/*_model.dart
39+
- lib/app/**/*_wm.dart
40+
- lib/app/**/*_model.dart
41+
- lib/widgets/**/*_wm.dart
42+
- lib/widgets/**/*_model.dart
43+
- lib/feature/**/domain/*.dart
3644
- lib/feature/**/domain/*.dart
3745
- lib/feature/**/domain/**/*.dart
3846
- lib/feature/**/*route.dart
@@ -41,6 +49,7 @@ targets:
4149
- lib/app/router/guard.dart
4250
- lib/v1/**/*route.dart
4351
- lib/v1/**/*guard.dart
52+
- lib/utils/factories/error_handler/standard_error_handler.dart
4453
freezed:
4554
generate_for:
4655
- lib/app/service/*.dart

docs/architecture.md

Lines changed: 152 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -106,30 +106,151 @@ The following business object groups are stored in this layer:
106106

107107
- `*Widget` (View) - A Flutter widget responsible for displaying data to the user. The widget receives data from the `WidgetModel` through state notifiers and renders the UI accordingly. It should not contain business logic beyond what's needed for display. Widgets handle UI events and delegate them to the WidgetModel through method calls. Additionally, widgets process one-time events like navigation, dialogs, or snackbars through handling messages from the WidgetModel.
108108

109-
WidgetModel should provide (static method under class) factory method for creating instance, that based on injectable calls:
109+
### Elementary Widget Patterns
110+
111+
The project uses two patterns for implementing Elementary MVVM widgets, both providing automatic dependency injection:
112+
113+
#### 1. Non-Parametrized Pattern (CustomWidgetModel + InjectedElementaryWidget)
114+
115+
Use when widgets don't need parameters from parent widgets:
110116

111117
```dart
112-
WalletPageWidgetModel defaultWalletPageWidgetModelFactory(
113-
BuildContext context,
114-
) {
115-
return WalletPageWidgetModel(
116-
WalletPageModel(
117-
createPrimaryErrorHandler(context),
118-
inject(),
119-
inject(),
120-
inject(),
121-
inject(),
122-
inject(),
123-
),
124-
);
118+
// Widget - extends InjectedElementaryWidget
119+
class SplashScreen extends InjectedElementaryWidget<SplashScreenWidgetModel> {
120+
const SplashScreen({super.key});
121+
122+
@override
123+
Widget build(SplashScreenWidgetModel wm) {
124+
// UI implementation
125+
}
126+
}
127+
128+
// WidgetModel - extends CustomWidgetModel
129+
@injectable
130+
class SplashScreenWidgetModel extends CustomWidgetModel<SplashScreen, SplashScreenModel> {
131+
SplashScreenWidgetModel(super.model);
132+
// Business logic implementation
133+
}
134+
135+
// Model - extends ElementaryModel
136+
@injectable
137+
class SplashScreenModel extends ElementaryModel {
138+
SplashScreenModel(
139+
ErrorHandler errorHandler,
140+
this._service,
141+
) : super(errorHandler: errorHandler);
142+
143+
final YourService _service;
125144
}
126145
```
127146

147+
#### 2. Parametrized Pattern (CustomWidgetModelParametrized + InjectedElementaryParametrizedWidget)
148+
149+
Use when widgets need parameters from parent widgets:
150+
151+
```dart
152+
// Widget - extends InjectedElementaryParametrizedWidget
153+
class AccountCard extends InjectedElementaryParametrizedWidget<
154+
AccountCardWidgetModel, KeyAccount> {
155+
const AccountCard({
156+
required KeyAccount account,
157+
super.key,
158+
}) : super(wmFactoryParam: account);
159+
160+
@override
161+
Widget build(AccountCardWidgetModel wm) {
162+
// UI implementation
163+
}
164+
}
165+
166+
// WidgetModel - extends CustomWidgetModelParametrized
167+
@injectable
168+
class AccountCardWidgetModel extends CustomWidgetModelParametrized<
169+
AccountCard, AccountCardModel, KeyAccount> {
170+
AccountCardWidgetModel(super.model);
171+
172+
// Access parameter via reactive notifier
173+
late final ValueListenable<String> accountName =
174+
createWmParamsNotifier((account) => account.name);
175+
176+
// Or access current value directly
177+
KeyAccount get currentAccount => wmParams.value;
178+
}
179+
```
180+
181+
##### Complex Parameters
182+
183+
For multiple parameters, create a dedicated params class:
184+
185+
```dart
186+
// Params class for complex data
187+
class WalletActionsParams {
188+
const WalletActionsParams({
189+
required this.account,
190+
required this.allowStake,
191+
required this.sendSpecified,
192+
});
193+
194+
final KeyAccount account;
195+
final bool allowStake;
196+
final bool sendSpecified;
197+
}
198+
199+
// Widget usage
200+
class WalletActionsWidget extends InjectedElementaryParametrizedWidget<
201+
WalletActionsWidgetModel, WalletActionsParams> {
202+
WalletActionsWidget({
203+
required KeyAccount account,
204+
required bool allowStake,
205+
required bool sendSpecified,
206+
super.key,
207+
}) : super(
208+
wmFactoryParam: WalletActionsParams(
209+
account: account,
210+
allowStake: allowStake,
211+
sendSpecified: sendSpecified,
212+
),
213+
);
214+
}
215+
216+
// WidgetModel usage
217+
@injectable
218+
class WalletActionsWidgetModel extends CustomWidgetModelParametrized<
219+
WalletActionsWidget, WalletActionsModel, WalletActionsParams> {
220+
WalletActionsWidgetModel(super.model);
221+
222+
// React to parameter changes
223+
@override
224+
void initWidgetModel() {
225+
super.initWidgetModel();
226+
wmParams.listen((params) {
227+
// Handle parameter updates
228+
});
229+
}
230+
}
231+
```
232+
233+
#### When to Use Each Pattern
234+
235+
**Use Non-Parametrized Pattern when:**
236+
- Widget is self-contained
237+
- All data comes from injected services
238+
- No configuration from parent is needed
239+
- Examples: App root widget, feature entry points, modal sheets
240+
241+
**Use Parametrized Pattern when:**
242+
- Widget needs data from parent
243+
- Widget behavior changes based on parameters
244+
- Widget is reusable with different configurations
245+
- Examples: List items, reusable components, widgets displaying entity details
246+
247+
**Legacy Pattern**: For existing code that hasn't been migrated, you may still see the manual factory pattern with `defaultFactory` methods.
248+
128249
### MVVM add-ons
129250

130251
We use the [Elementary](https://pub.dev/packages/elementary) package as our implementation of the MVVM (Model-View-ViewModel) pattern. Elementary provides a clean separation of concerns and a structured approach to state management. More details can be found in the [Elementary documentation](https://pub.dev/packages/elementary).
131252

132-
- `CustomWidgetModel` - A specialized extension of the standard `WidgetModel` that includes additional functionality through mixins. It's the base class for most WidgetModels in our application and includes `NotifierSubscriptionsMixin`, `ContextWmMixin`.
253+
- `CustomWidgetModel` - A specialized extension of the standard `WidgetModel` that includes additional functionality through mixins. It's the base class for non-parametrized WidgetModels in our application and includes `NotifierSubscriptionsMixin`, `ContextWmMixin`.
133254

134255
```dart
135256
class CustomWidgetModel<W extends ElementaryWidget, M extends ElementaryModel>
@@ -138,6 +259,22 @@ We use the [Elementary](https://pub.dev/packages/elementary) package as our impl
138259
}
139260
```
140261

262+
- `CustomWidgetModelParametrized` - An extension of `CustomWidgetModel` designed for widgets that need parameters from parent widgets. It provides reactive parameter handling through a `wmParams` BehaviorSubject that automatically updates when widget rebuilds with new parameters.
263+
264+
```dart
265+
class CustomWidgetModelParametrized<
266+
W extends InjectedElementaryParametrizedWidget<WidgetModel, Param>,
267+
M extends ElementaryModel,
268+
Param> extends CustomWidgetModel<W, M> {
269+
270+
// Access current parameter value
271+
Param get currentParam => wmParams.value;
272+
273+
// Create reactive notifier from parameter
274+
ValueNotifier<T> createWmParamsNotifier<T>(T Function(Param param) convert);
275+
}
276+
```
277+
141278
- `NotifierSubscriptionsMixin` - A mixin that simplifies the creation and management of notifiers, controllers, and stream subscriptions. It ensures proper cleanup when the WidgetModel is disposed, preventing memory leaks. Key features include:
142279
- Creation of various notifiers (StateNotifier, EntityStateNotifier, ValueNotifier)
143280
- Creation of controllers (TextEditingController, FocusNode, ScrollController)

docs/llm/architecture_and_code_standards.instructions.md

Lines changed: 112 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -32,18 +32,124 @@ For each feature screen, create exactly these 3 files:
3232

3333
### Implementation Rules
3434

35-
- **Widget**: Pure UI, no business logic, extends `ElementaryWidget<WidgetModel>`
36-
- **Model**: Data handling, state management, extends `ElementaryModel`
37-
- **WidgetModel**: Business logic, user interactions, extends `WidgetModel<Widget, Model>`
35+
- **Widget**: Pure UI, no business logic
36+
- **Model**: Data handling, state management, extends `ElementaryModel`, MUST be `@injectable`
37+
- **WidgetModel**: Business logic, user interactions, MUST be `@injectable`
3838

39-
### CustomWidgetModel Base Class
39+
### Base Class Patterns
4040

41-
All WidgetModels MUST extend `CustomWidgetModel` which provides:
41+
The project uses two patterns for Elementary MVVM implementation:
42+
43+
#### 1. Non-Parametrized Pattern (CustomWidgetModel + InjectedElementaryWidget)
44+
45+
Use when widgets don't need parameters from parent widgets:
46+
47+
```dart
48+
// Widget - extends InjectedElementaryWidget
49+
class SplashScreen extends InjectedElementaryWidget<SplashScreenWidgetModel> {
50+
const SplashScreen({super.key});
51+
52+
@override
53+
Widget build(SplashScreenWidgetModel wm) {
54+
// UI implementation
55+
}
56+
}
57+
58+
// WidgetModel - extends CustomWidgetModel
59+
@injectable
60+
class SplashScreenWidgetModel extends CustomWidgetModel<SplashScreen, SplashScreenModel> {
61+
SplashScreenWidgetModel(super.model);
62+
// Business logic implementation
63+
}
64+
65+
// Model - extends ElementaryModel
66+
@injectable
67+
class SplashScreenModel extends ElementaryModel {
68+
SplashScreenModel(
69+
ErrorHandler errorHandler,
70+
this._service,
71+
) : super(errorHandler: errorHandler);
72+
73+
final YourService _service;
74+
}
75+
```
76+
77+
#### 2. Parametrized Pattern (CustomWidgetModelParametrized + InjectedElementaryParametrizedWidget)
78+
79+
Use when widgets need parameters from parent widgets:
80+
81+
```dart
82+
// Widget - extends InjectedElementaryParametrizedWidget
83+
class AccountCard extends InjectedElementaryParametrizedWidget<
84+
AccountCardWidgetModel, KeyAccount> {
85+
const AccountCard({
86+
required KeyAccount account,
87+
super.key,
88+
}) : super(wmFactoryParam: account);
89+
90+
@override
91+
Widget build(AccountCardWidgetModel wm) {
92+
// UI implementation
93+
}
94+
}
95+
96+
// WidgetModel - extends CustomWidgetModelParametrized
97+
@injectable
98+
class AccountCardWidgetModel extends CustomWidgetModelParametrized<
99+
AccountCard, AccountCardModel, KeyAccount> {
100+
AccountCardWidgetModel(super.model);
101+
102+
// Access parameter via reactive notifier
103+
late final ValueListenable<String> accountName =
104+
createWmParamsNotifier((account) => account.name);
105+
106+
// Or access current value directly
107+
KeyAccount get currentAccount => wmParams.value;
108+
}
109+
110+
// For multiple parameters, create a params class
111+
class MyWidgetParams {
112+
final String title;
113+
final VoidCallback onTap;
114+
const MyWidgetParams({required this.title, required this.onTap});
115+
}
116+
117+
// Widget with multiple parameters
118+
class MyWidget extends InjectedElementaryParametrizedWidget<
119+
MyWidgetModel, MyWidgetParams> {
120+
MyWidget({
121+
required String title,
122+
required VoidCallback onTap,
123+
super.key,
124+
}) : super(wmFactoryParam: MyWidgetParams(title: title, onTap: onTap));
125+
}
126+
```
127+
128+
### CustomWidgetModel Features
129+
130+
Both base WidgetModel classes provide:
42131

43132
- Error handling through `PrimaryErrorHandler`
44133
- Loading state management
45134
- Navigation helpers
46-
- Common mixins
135+
- Common mixins (`NotifierSubscriptionsMixin`, `ContextWmMixin`)
136+
- Automatic dependency injection via GetIt
137+
138+
### When to Use Each Pattern
139+
140+
**Use Non-Parametrized Pattern when:**
141+
142+
- Widget is self-contained
143+
- All data comes from injected services
144+
- No configuration from parent is needed
145+
- Examples: App root widget, feature entry points, modal sheets
146+
147+
**Use Parametrized Pattern when:**
148+
149+
- Widget needs data from parent
150+
- Widget behavior changes based on parameters
151+
- Widget is reusable with different configurations
152+
- Examples: List items, reusable components, widgets displaying entity details
47153

48154
## Dependency Injection
49155

@@ -57,25 +163,6 @@ class YourService {
57163
}
58164
```
59165

60-
### Service Access in WidgetModels
61-
62-
Services should injected into Model through constructor not in WidgetModel. Widget model will call model methods.
63-
64-
WidgetModel should provide factory method (static method under class) for creating instance, that creating Model by dependencies from injectable and WidgetModel.
65-
66-
```dart
67-
WalletPageWidgetModel defaultWalletPageWidgetModelFactory(
68-
BuildContext context,
69-
) {
70-
return WalletPageWidgetModel(
71-
WalletPageModel(
72-
createPrimaryErrorHandler(context),
73-
inject(),
74-
),
75-
);
76-
}
77-
```
78-
79166
## Business Logic Components
80167

81168
### Services (Stateful Business Logic)
@@ -309,4 +396,3 @@ void main() {
309396
- Implement proper boundaries between modules
310397
- Use dependency injection for inter-module communication
311398
- Design for modularity and reusability
312-

0 commit comments

Comments
 (0)