Skip to content

Commit dc267d5

Browse files
committed
🎨 improve code formatting and readability in reactivity documentation
1 parent 14c5342 commit dc267d5

File tree

1 file changed

+89
-63
lines changed

1 file changed

+89
-63
lines changed

packages/hyper_storage/docs/reactivity.md

Lines changed: 89 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
- [Streaming Item Holder Changes](#streaming-item-holder-changes)
77
- [Converting Item Holder to a ValueNotifier](#converting-item-holder-to-a-valuenotifier)
88
- [Streaming key changes](#streaming-key-changes)
9-
- [⚠️ Important: Using Streams with Flutter's StreamBuilder](#️-important-using-streams-with-flutters-streambuilder)
109
- [Streaming with Serializable Containers](#streaming-with-serializable-containers)
1110

1211
## Reactivity
@@ -21,11 +20,14 @@ Listening to changes for a specific key allows you to react only when that parti
2120

2221
```dart
2322
// Register a listener for a specific key
24-
storage.addKeyListener('name', onKeyChanged);
23+
storage.addKeyListener
24+
('name
25+
'
26+
, onKeyChanged);
2527
2628
void onKeyChanged() async {
27-
final newValue = await storage.getString('name');
28-
print('The value of "name" has changed to: $newValue');
29+
final newValue = await storage.getString('name');
30+
print('The value of "name" has changed to: $newValue');
2931
}
3032
3133
// unregister the listener
@@ -35,14 +37,19 @@ storage.removeKeyListener('name', onKeyChanged);
3537
You can also listen to key changes in a named container:
3638

3739
```dart
38-
final container = await storage.container('user');
40+
41+
final container = await
42+
storage.container
43+
('user
44+
'
45+
);
3946
4047
// Register a listener for a specific key in the container
4148
container.addKeyListener('email', onEmailChanged);
4249
4350
void onEmailChanged() async {
44-
final newEmail = await container.getString('email');
45-
print('The email has changed to: $newEmail');
51+
final newEmail = await container.getString('email');
52+
print('The email has changed to: $newEmail');
4653
}
4754
4855
// unregister the listener
@@ -54,49 +61,75 @@ Item Holder also supports listeners:
5461
Item holders itself only holds a single value, so you don't need to specify a key when adding a listener.
5562

5663
```dart
64+
5765
final itemHolder = storage.itemHolder<String>('status');
5866
5967
// Register a listener for changes in the item holder
60-
itemHolder.addListener(onStatusChanged);
68+
itemHolder.addListener
69+
(
70+
onStatusChanged);
6171
6272
void onStatusChanged() async {
63-
final newStatus = await itemHolder.get();
64-
print('The status has changed to: $newStatus');
73+
final newStatus = await itemHolder.get();
74+
print('The status has changed to: $newStatus');
6575
}
6676
6777
// unregister the listener
68-
itemHolder.removeListener(onStatusChanged);
78+
itemHolder
79+
.
80+
removeListener
81+
(
82+
onStatusChanged
83+
);
6984
```
7085

7186
## Listening to All Changes
87+
7288
You can listen to all changes in the storage, regardless of which key was modified.
7389

7490
```dart
7591
// Register a listener for all changes
76-
storage.addListener(onStorageChanged);
92+
storage.addListener
93+
(
94+
onStorageChanged);
7795
7896
void onStorageChanged() {
79-
print('The storage has changed.');
97+
print('The storage has changed.');
8098
}
8199
82100
// unregister the listener
83-
storage.removeListener(onStorageChanged);
101+
storage
102+
.
103+
removeListener
104+
(
105+
onStorageChanged
106+
);
84107
```
85108

86109
You can also listen to all changes in a named container:
87110

88111
```dart
89-
final container = await storage.container('settings');
112+
113+
final container = await
114+
storage.container
115+
('settings
116+
'
117+
);
90118
91119
// Register a listener for all changes in the container
92120
container.addListener(onSettingsChanged);
93121
94122
void onSettingsChanged() {
95-
print('The settings container has changed.');
123+
print('The settings container has changed.');
96124
}
97125
98126
// unregister the listener
99-
container.removeListener(onSettingsChanged);
127+
container
128+
.
129+
removeListener
130+
(
131+
onSettingsChanged
132+
);
100133
```
101134

102135
# Streaming Item Holder Changes
@@ -106,23 +139,18 @@ flexible way, such as using `StreamBuilder` in Flutter.
106139

107140
`ItemHolder<E>` extends `ManagedStream<E?>` and implements `Stream<E?>`, so you can listen to it directly.
108141

109-
## Implementation Details
110-
111-
ItemHolder uses a `BehaviorSubject` (from rxdart) internally, which provides several benefits:
112-
- **Automatic value caching**: The latest value is cached and automatically emitted to new listeners
113-
- **Multiple concurrent listeners**: Efficiently supports many listeners without duplicating work
114-
- **Lazy activation**: Only starts fetching values when someone is actually listening
115-
116142
```dart
143+
117144
final itemHolder = storage.itemHolder<String>('status');
118145
119146
// Listen to changes using Stream API
120147
final subscription = itemHolder.listen((newStatus) {
121-
print('The status has changed to: $newStatus');
148+
print('The status has changed to: $newStatus');
122149
});
123150
124151
// Don't forget to cancel the subscription when it's no longer needed
125-
subscription.cancel();
152+
subscription.cancel
153+
();
126154
```
127155

128156
## Using with StreamBuilder in Flutter
@@ -160,15 +188,16 @@ final valueNotifier = itemHolder.asValueNotifier();
160188
// Use with ValueListenableBuilder in Flutter
161189
ValueListenableBuilder<String?>(
162190
valueListenable: valueNotifier,
163-
builder: (context, status, child) {
191+
builder: (context, status, child) {
164192
return Text('Status: ${status ?? 'Unknown'}');
165193
},
166194
);
167195
```
168-
> Whenever you call `asValueNotifier`, it creates a new `ValueNotifier` instance. If you want to avoid creating
196+
197+
> Whenever you call `asValueNotifier`, it creates a new `ValueNotifier` instance. If you want to avoid creating
169198
> multiple instances, consider storing the `ValueNotifier` in a variable and reusing it. Remember to dispose the
170-
> `ValueNotifier` once you no longer need it.
171-
>
199+
> `ValueNotifier` once you no longer need it.
200+
>
172201
> Make sure to add `hyper_storage_flutter` package to your dependencies to use `asValueNotifier` method.
173202
174203
## Streaming key changes
@@ -179,7 +208,7 @@ You can also stream changes for a specific key using the `stream` method.
179208
// Listen to changes for a specific key
180209
final nameStream = storage.stream<String>('name');
181210
final subscription = nameStream.listen((newName) {
182-
print('The value of "name" has changed to: $newName');
211+
print('The value of "name" has changed to: $newName');
183212
});
184213
// Don't forget to cancel the subscription when it's no longer needed
185214
subscription.cancel();
@@ -188,54 +217,51 @@ subscription.cancel();
188217
You can also stream key changes in a named container:
189218

190219
```dart
220+
191221
final container = await storage.container('user');
192222
193223
// Listen to changes for a specific key in the container
194224
final emailStream = container.stream<String>('email');
195225
196226
final subscription = emailStream.listen((newEmail) {
197-
print('The email has changed to: $newEmail');
227+
print('The email has changed to: $newEmail');
198228
});
199229
200230
// Don't forget to cancel the subscription when it's no longer needed
201231
subscription.cancel();
202232
```
203233

204-
## ⚠️ Important: Using Streams with Flutter's StreamBuilder
205-
206-
When using streams with Flutter's `StreamBuilder`, it's important to understand the best patterns and practices.
207-
208-
### **The `stream()` Method is Now Safe**
234+
## Using Streams with Flutter's StreamBuilder
209235

210-
As of the latest version, the `stream()` method returns a **cached `ItemHolder` instance**, making it safe to call repeatedly:
236+
When using streams with Flutter's `StreamBuilder`.
211237

212238
```dart
213-
// These both return the exact same ItemHolder object:
214-
final stream1 = storage.stream<String>('name');
215-
final stream2 = storage.stream<String>('name');
216-
print(identical(stream1, stream2)); // true
239+
StreamBuilder<String?>(
240+
stream: storage.stream<String>('name'), // Use directly from storage.
241+
builder: (context, snapshot) {
242+
if (snapshot.connectionState == ConnectionState.waiting) {
243+
return CircularProgressIndicator();
244+
}
245+
final name = snapshot.data ?? 'Unknown';
246+
return Text('Name: $name');
247+
},
248+
);
217249
```
218250

219-
This means calling `storage.stream('key')` directly in a build method is now technically safe, as it returns the same cached instance each time. However, for code clarity and best practices, we still recommend the patterns below.
220-
221-
### **Recommended Pattern: Use ItemHolder Directly**
251+
This means calling `storage.stream('key')` directly in a build method is safe and will not create multiple streams.
222252

223-
The clearest and most explicit approach is to use `ItemHolder` directly:
253+
**Recommended Pattern: Use ItemHolder Directly**: If you have an `ItemHolder` for the key, you can use it directly in
254+
the `StreamBuilder`. This is the most efficient and clear approach.
224255

225256
```dart
226-
class MyWidget extends StatefulWidget {
227-
@override
228-
State<MyWidget> createState() => _MyWidgetState();
229-
}
230-
231257
class _MyWidgetState extends State<MyWidget> {
232258
// Create ItemHolder once - it's a persistent stream with value caching
233259
late final itemHolder = storage.itemHolder<String>('name');
234260
235261
@override
236262
Widget build(BuildContext context) {
237263
return StreamBuilder<String?>(
238-
stream: itemHolder, // ✅ BEST: Clear intent, uses ItemHolder directly
264+
stream: itemHolder, // uses ItemHolder directly
239265
builder: (context, snapshot) {
240266
if (snapshot.connectionState == ConnectionState.waiting) {
241267
return CircularProgressIndicator();
@@ -309,7 +335,7 @@ class _MyWidgetState extends State<MyWidget> {
309335
@override
310336
void initState() {
311337
super.initState();
312-
nameStream = storage.stream<String>('name'); // Returns cached ItemHolder
338+
nameStream = storage.stream<String>('name'); // Returns cached ItemHolder
313339
}
314340
315341
@override
@@ -326,21 +352,21 @@ class _MyWidgetState extends State<MyWidget> {
326352

327353
### Summary: Which pattern should you use?
328354

329-
| Pattern | Recommended? | Notes |
330-
|---------|-------------|-------|
331-
| **ItemHolder directly** |**Best** | Most explicit. Clear intent. Direct access to ItemHolder API. |
332-
| **Cached stream (late final)** | ✅ Good | Same as above but less explicit. `stream()` returns ItemHolder internally. |
333-
| **initState stream** | ✅ Good | Useful for complex initialization. Still returns cached ItemHolder. |
334-
| **Direct stream() in build()** | ⚠️ **Acceptable but discouraged** | Now safe (returns cached instance) but not recommended for code clarity. |
355+
| Pattern | Recommended? | Notes |
356+
|--------------------------------|-----------------------------------|----------------------------------------------------------------------------|
357+
| **ItemHolder directly** |**Best** | Most explicit. Clear intent. Direct access to ItemHolder API. |
358+
| **Cached stream (late final)** | ✅ Good | Same as above but less explicit. `stream()` returns ItemHolder internally. |
359+
| **initState stream** | ✅ Good | Useful for complex initialization. Still returns cached ItemHolder. |
360+
| **Direct stream() in build()** | ⚠️ **Acceptable but discouraged** | Now safe (returns cached instance) but not recommended for code clarity. |
335361

336362
## Streaming with Serializable Containers
337363

338364
You can also stream changes in a `SerializableContainer`. This is useful when you want to listen to changes in a complex
339365
object.
340366

341367
```dart
342-
final todos = await storage.jsonSerializableContainer<Todo>(
343-
'todos',
368+
369+
final todos = await storage.jsonSerializableContainer<Todo>('todos',
344370
fromJson: Todo.fromJson,
345371
toJson: (todo) => todo.toJson(),
346372
);
@@ -357,8 +383,8 @@ allTodosSubscription.cancel();
357383
You can also stream changes for a specific key in a `SerializableStorageContainer`:
358384

359385
```dart
360-
final todos = await storage.jsonSerializableContainer<Todo>(
361-
'todos',
386+
387+
final todos = await storage.jsonSerializableContainer<Todo>('todos',
362388
fromJson: Todo.fromJson,
363389
toJson: (todo) => todo.toJson(),
364390
);

0 commit comments

Comments
 (0)