Skip to content

Commit 0ed9337

Browse files
committed
+Minor bugfixes
1 parent 5f9f4e5 commit 0ed9337

6 files changed

Lines changed: 1417 additions & 75 deletions

File tree

README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,70 @@ You can also remove all dependencies all at once, i.e. when you log the user out
193193
DI.session.unregisterAll();
194194
```
195195

196+
## Step 9: Service Lifecycle Management
197+
198+
`df_di` includes base service classes with well-defined lifecycle states (init, pause, resume, dispose). These integrate seamlessly with the DI system.
199+
200+
| Class | Purpose |
201+
|-------|---------|
202+
| `Service` | Base service with init/pause/resume/dispose lifecycle |
203+
| `StreamService<TData>` | Service that manages a data stream |
204+
| `PollingStreamService<TData>` | StreamService that polls at regular intervals |
205+
206+
```dart
207+
import 'package:df_di/df_di.dart';
208+
209+
/// A simple counter service with lifecycle management.
210+
final class CounterService extends Service {
211+
int _count = 0;
212+
int get count => _count;
213+
214+
void increment() => _count++;
215+
216+
@override
217+
TServiceResolvables<Unit> provideInitListeners(void _) => [
218+
(_) {
219+
_count = 0;
220+
print('CounterService initialized');
221+
return syncUnit();
222+
},
223+
];
224+
225+
@override
226+
TServiceResolvables<Unit> providePauseListeners(void _) => [];
227+
228+
@override
229+
TServiceResolvables<Unit> provideResumeListeners(void _) => [];
230+
231+
@override
232+
TServiceResolvables<Unit> provideDisposeListeners(void _) => [
233+
(_) {
234+
print('CounterService disposed with count: $_count');
235+
return syncUnit();
236+
},
237+
];
238+
}
239+
240+
// Register with DI and use lifecycle callbacks
241+
DI.global.register<CounterService>(
242+
CounterService(),
243+
onRegister: (service) => service.init(),
244+
onUnregister: ServiceMixin.unregister, // Calls dispose() automatically
245+
);
246+
247+
// Access the service
248+
final counter = DI.global<CounterService>();
249+
counter.increment();
250+
```
251+
252+
For Flutter apps that need to respond to app lifecycle events (pause when backgrounded, resume when foregrounded), use the `ObservedService` variants from [df_flutter_services](https://pub.dev/packages/df_flutter_services).
253+
254+
## Related Packages
255+
256+
- [df_flutter_services](https://pub.dev/packages/df_flutter_services) - Flutter-specific service classes with app lifecycle integration
257+
- [df_pod](https://pub.dev/packages/df_pod) - Reactive state containers
258+
- [df_safer_dart](https://pub.dev/packages/df_safer_dart) - Option, Result, Resolvable types
259+
196260
<!-- END _README_CONTENT -->
197261

198262
---

_README_CONTENT.md

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,4 +176,68 @@ You can also remove all dependencies all at once, i.e. when you log the user out
176176
```dart
177177
// This will unregister all dependencies in the reverse order by which they were registered.
178178
DI.session.unregisterAll();
179-
```
179+
```
180+
181+
## Step 9: Service Lifecycle Management
182+
183+
`df_di` includes base service classes with well-defined lifecycle states (init, pause, resume, dispose). These integrate seamlessly with the DI system.
184+
185+
| Class | Purpose |
186+
|-------|---------|
187+
| `Service` | Base service with init/pause/resume/dispose lifecycle |
188+
| `StreamService<TData>` | Service that manages a data stream |
189+
| `PollingStreamService<TData>` | StreamService that polls at regular intervals |
190+
191+
```dart
192+
import 'package:df_di/df_di.dart';
193+
194+
/// A simple counter service with lifecycle management.
195+
final class CounterService extends Service {
196+
int _count = 0;
197+
int get count => _count;
198+
199+
void increment() => _count++;
200+
201+
@override
202+
TServiceResolvables<Unit> provideInitListeners(void _) => [
203+
(_) {
204+
_count = 0;
205+
print('CounterService initialized');
206+
return syncUnit();
207+
},
208+
];
209+
210+
@override
211+
TServiceResolvables<Unit> providePauseListeners(void _) => [];
212+
213+
@override
214+
TServiceResolvables<Unit> provideResumeListeners(void _) => [];
215+
216+
@override
217+
TServiceResolvables<Unit> provideDisposeListeners(void _) => [
218+
(_) {
219+
print('CounterService disposed with count: $_count');
220+
return syncUnit();
221+
},
222+
];
223+
}
224+
225+
// Register with DI and use lifecycle callbacks
226+
DI.global.register<CounterService>(
227+
CounterService(),
228+
onRegister: (service) => service.init(),
229+
onUnregister: ServiceMixin.unregister, // Calls dispose() automatically
230+
);
231+
232+
// Access the service
233+
final counter = DI.global<CounterService>();
234+
counter.increment();
235+
```
236+
237+
For Flutter apps that need to respond to app lifecycle events (pause when backgrounded, resume when foregrounded), use the `ObservedService` variants from [df_flutter_services](https://pub.dev/packages/df_flutter_services).
238+
239+
## Related Packages
240+
241+
- [df_flutter_services](https://pub.dev/packages/df_flutter_services) - Flutter-specific service classes with app lifecycle integration
242+
- [df_pod](https://pub.dev/packages/df_pod) - Reactive state containers
243+
- [df_safer_dart](https://pub.dev/packages/df_safer_dart) - Option, Result, Resolvable types

example/example.dart

Lines changed: 117 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,18 @@
1-
//.title
2-
// ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
3-
//
4-
// Copyright © dev-cetera.com & contributors.
5-
//
6-
// The use of this source code is governed by an MIT-style license described in
7-
// the LICENSE file located in this project's root directory.
8-
//
9-
// See: https://opensource.org/license/mit
10-
//
11-
// ▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓
12-
//.title~
13-
14-
import 'dart:async' show unawaited;
1+
// This example demonstrates dependency injection with df_di.
2+
// ignore_for_file: avoid_print
153

164
import 'package:df_di/df_di.dart';
175

186
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
197

20-
/// A simple service to manage user data.
8+
/// Example 1: A simple service class to demonstrate DI registration.
219
class UserService {
22-
//
23-
//
24-
//
25-
2610
final int id;
2711
bool _isDisposed = false;
2812

29-
//
30-
//
31-
//
32-
3313
UserService(this.id);
3414

35-
//
36-
//
37-
//
38-
39-
/// Gets the user data for the user identified by [id].
4015
Async<Map<String, dynamic>> getUserData() {
41-
// Any errors thrown witin Async will be passed to the Async instance
42-
// returned.
4316
return Async(() async {
4417
if (_isDisposed) {
4518
throw Err('UserService has already been disposed!');
@@ -49,40 +22,91 @@ class UserService {
4922
});
5023
}
5124

52-
//
53-
//
54-
//
55-
56-
/// Cleans up resources used by the service.
5725
Async<Unit> dispose() {
5826
return Async(() async {
5927
if (_isDisposed) {
6028
throw Err('UserService has already been disposed!');
6129
}
6230
_isDisposed = true;
31+
print('UserService disposed');
6332
return Unit();
6433
});
6534
}
6635
}
6736

6837
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
6938

39+
/// Example 2: A Service with lifecycle management.
40+
final class CounterService extends Service {
41+
int _count = 0;
42+
43+
int get count => _count;
44+
45+
void increment() {
46+
_count++;
47+
print('Count incremented to: $_count');
48+
}
49+
50+
@override
51+
TServiceResolvables<Unit> provideInitListeners(void _) {
52+
return [
53+
(_) {
54+
_count = 0;
55+
print('CounterService initialized');
56+
return syncUnit();
57+
},
58+
];
59+
}
60+
61+
@override
62+
TServiceResolvables<Unit> providePauseListeners(void _) {
63+
return [
64+
(_) {
65+
print('CounterService paused');
66+
return syncUnit();
67+
},
68+
];
69+
}
70+
71+
@override
72+
TServiceResolvables<Unit> provideResumeListeners(void _) {
73+
return [
74+
(_) {
75+
print('CounterService resumed');
76+
return syncUnit();
77+
},
78+
];
79+
}
80+
81+
@override
82+
TServiceResolvables<Unit> provideDisposeListeners(void _) {
83+
return [
84+
(_) {
85+
print('CounterService disposed with final count: $_count');
86+
return syncUnit();
87+
},
88+
];
89+
}
90+
}
91+
92+
// ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
93+
7094
Future<void> main() async {
95+
print('=== df_di Example ===\n');
96+
97+
// --- Part 1: Basic DI with UserService ---
98+
print('--- Part 1: Basic DI Registration ---');
99+
71100
UNSAFE:
72101
{
73-
// Create some future to a resource that may not yet exist in DI.global that
74-
// we can await later.
102+
// Create a future that waits for UserService to be registered
75103
final userServiceFuture = DI.global.untilSuper<UserService>().unwrap();
76104

77-
// Register the service after a delay.
78-
79-
// This simulates a part of your application that initializes and provides
80-
// the service, for example, after a user logs in.
81-
Future.delayed(const Duration(seconds: 2), () {
105+
// Register the service after a delay (simulating async initialization)
106+
Future.delayed(const Duration(seconds: 1), () {
82107
DI.global
83108
.register<UserService>(
84109
UserService(123),
85-
// Handle what happens when we unregister the dependency.
86110
onUnregister: (result) {
87111
if (result.isOk()) {
88112
final userService = result.unwrap();
@@ -93,46 +117,66 @@ Future<void> main() async {
93117
},
94118
)
95119
.end;
120+
print('UserService registered');
96121
});
97122

98-
// Await the service and use it.
99-
100-
// Execution will pause here until the `Future.delayed` block above registers
101-
// the service, which in turn completes the `userServiceFuture`.
123+
// Wait for the service to be registered
102124
final userService = await userServiceFuture;
103125

126+
// Use the service
104127
final userDataResult = await userService.getUserData().value;
105128
if (userDataResult.isOk()) {
106-
final userData = userDataResult.unwrap();
107-
print(userData);
108-
} else {
109-
print('Error: ${userDataResult.err()}');
129+
print('User data: ${userDataResult.unwrap()}');
110130
}
111131

112-
// Unregister the service to trigger cleanup.
113-
114-
// This is useful when a user logs out or a feature is no longer needed.
115-
// Calling `unregister` will trigger the `onUnregister` callback we defined.
132+
// Unregister the service (triggers dispose via onUnregister)
116133
final _ = await DI.global.unregister<UserService>().value;
134+
print('UserService unregistered\n');
135+
}
117136

118-
final isRegistered = DI.global.isRegistered<UserService>();
119-
print('Is UserService still registered? $isRegistered');
137+
// --- Part 2: Service Lifecycle with CounterService ---
138+
print('--- Part 2: Service Lifecycle ---');
120139

121-
// Let's see what happens if we get try and a dependency that is no longer
122-
// egistered.
123-
final userDataOpt = DI.global.get<UserService>();
124-
if (userDataOpt.isSome()) {
125-
print('This should never print!');
126-
} else {
127-
print('No UserService is registeted here! This is expected!');
128-
}
140+
// Register CounterService with lifecycle callbacks
141+
DI.global.register<CounterService>(
142+
CounterService(),
143+
onRegister: (service) => service.init(),
144+
onUnregister: ServiceMixin.unregister,
145+
);
129146

130-
// Let's see what happens if we get try and dispose the service again!
131-
unawaited(
132-
userService.dispose().unwrap().catchError((Object e) {
133-
print(e);
134-
return Unit();
135-
}),
136-
);
137-
}
147+
// Wait for initialization to complete
148+
final counterService = await DI.global.untilSuper<CounterService>().unwrap();
149+
150+
// Use the service
151+
counterService.increment();
152+
counterService.increment();
153+
counterService.increment();
154+
print('Current count: ${counterService.count}');
155+
156+
// Demonstrate pause/resume
157+
await counterService.pause().value;
158+
await counterService.resume().value;
159+
160+
// Unregister (triggers dispose via ServiceMixin.unregister)
161+
await DI.global.unregister<CounterService>().value;
162+
print('CounterService unregistered\n');
163+
164+
// --- Part 3: Hierarchical Containers ---
165+
print('--- Part 3: Hierarchical Containers ---');
166+
167+
// Register in session container
168+
DI.session.register<String>('session_token_123');
169+
170+
// Access from user container (child of session)
171+
final token = DI.user<String>();
172+
print('Token from DI.user: $token');
173+
174+
// Check registration
175+
print('Is String registered in DI.session? ${DI.session.isRegistered<String>()}');
176+
print('Is String registered in DI.user? ${DI.user.isRegistered<String>()}');
177+
178+
// Clean up
179+
DI.session.unregister<String>();
180+
181+
print('\n=== Example Complete ===');
138182
}

0 commit comments

Comments
 (0)