@@ -21,14 +21,11 @@ Listening to changes for a specific key allows you to react only when that parti
2121
2222``` dart
2323// Register a listener for a specific key
24- storage.addKeyListener
25- ('name
26- '
27- , onKeyChanged);
24+ storage.addKeyListener('name', onKeyChanged);
2825
2926void onKeyChanged() async {
30- final newValue = await storage.getString('name');
31- print('The value of "name" has changed to: $newValue');
27+ final newValue = await storage.getString('name');
28+ print('The value of "name" has changed to: $newValue');
3229}
3330
3431// unregister the listener
@@ -39,18 +36,14 @@ You can also listen to key changes in a named container:
3936
4037``` dart
4138
42- final container = await
43- storage.container
44- ('user
45- '
46- );
39+ final container = await storage.container('user');
4740
4841// Register a listener for a specific key in the container
4942container.addKeyListener('email', onEmailChanged);
5043
5144void onEmailChanged() async {
52- final newEmail = await container.getString('email');
53- print('The email has changed to: $newEmail');
45+ final newEmail = await container.getString('email');
46+ print('The email has changed to: $newEmail');
5447}
5548
5649// unregister the listener
@@ -66,22 +59,15 @@ Item holders itself only holds a single value, so you don't need to specify a ke
6659final itemHolder = storage.itemHolder<String>('status');
6760
6861// Register a listener for changes in the item holder
69- itemHolder.addListener
70- (
71- onStatusChanged);
62+ itemHolder.addListener(onStatusChanged);
7263
7364void onStatusChanged() async {
74- final newStatus = await itemHolder.get();
75- print('The status has changed to: $newStatus');
65+ final newStatus = await itemHolder.get();
66+ print('The status has changed to: $newStatus');
7667}
7768
7869// unregister the listener
79- itemHolder
80- .
81- removeListener
82- (
83- onStatusChanged
84- );
70+ itemHolder.removeListener(onStatusChanged);
8571```
8672
8773## Listening to All Changes
@@ -90,47 +76,31 @@ You can listen to all changes in the storage, regardless of which key was modifi
9076
9177``` dart
9278// Register a listener for all changes
93- storage.addListener
94- (
95- onStorageChanged);
79+ storage.addListener(onStorageChanged);
9680
9781void onStorageChanged() {
98- print('The storage has changed.');
82+ print('The storage has changed.');
9983}
10084
10185// unregister the listener
102- storage
103- .
104- removeListener
105- (
106- onStorageChanged
107- );
86+ storage.removeListener(onStorageChanged);
10887```
10988
11089You can also listen to all changes in a named container:
11190
11291``` dart
11392
114- final container = await
115- storage.container
116- ('settings
117- '
118- );
93+ final container = await storage.container('settings');
11994
12095// Register a listener for all changes in the container
12196container.addListener(onSettingsChanged);
12297
12398void onSettingsChanged() {
124- print('The settings container has changed.');
99+ print('The settings container has changed.');
125100}
126101
127102// unregister the listener
128- container
129- .
130- removeListener
131- (
132- onSettingsChanged
133- );
103+ container.removeListener(onSettingsChanged);
134104```
135105
136106# Streaming Item Holder Changes
@@ -150,8 +120,7 @@ final subscription = itemHolder.listen((newStatus) {
150120});
151121
152122// Don't forget to cancel the subscription when it's no longer needed
153- subscription.cancel
154- ();
123+ subscription.cancel();
155124```
156125
157126## Using with StreamBuilder in Flutter
@@ -251,8 +220,7 @@ StreamBuilder<String?>(
251220
252221This means calling ` storage.stream('key') ` directly in a build method is safe and will not create multiple streams.
253222
254- ** Recommended Pattern: Use ItemHolder Directly** : If you have an ` ItemHolder ` for the key, you can use it directly in
255- the ` StreamBuilder ` . This is the most efficient and clear approach.
223+ If you have an ` ItemHolder ` for the key, you can use it directly in the ` StreamBuilder ` . This is the most efficient and clear approach.
256224
257225``` dart
258226class _MyWidgetState extends State<MyWidget> {
@@ -281,85 +249,6 @@ class _MyWidgetState extends State<MyWidget> {
281249}
282250```
283251
284- ** Why ItemHolder is recommended:**
285-
286- - ** Clear intent** : Makes it obvious you're using a managed stream
287- - ** Value caching** : Uses ` BehaviorSubject ` - new listeners get the cached value immediately
288- - ** Efficient** : Single persistent stream with lazy activation
289- - ** Multiple listeners** : Supports many concurrent listeners without duplicating work
290- - ** No error emissions** : Transient failures don't get cached and replayed
291-
292- ### ✅ ** Also Acceptable: Cache the stream in a variable**
293-
294- If you prefer to use the ` stream() ` method, you can cache it in a ` late final ` variable.
295- Since ` stream() ` returns a cached ` ItemHolder ` , this is now equivalent to Pattern 1:
296-
297- ``` dart
298- class MyWidget extends StatefulWidget {
299- @override
300- State<MyWidget> createState() => _MyWidgetState();
301- }
302-
303- class _MyWidgetState extends State<MyWidget> {
304- // Cache the stream - actually returns the same ItemHolder as itemHolder<String>('name')
305- late final Stream<String?> nameStream = storage.stream<String>('name');
306-
307- @override
308- Widget build(BuildContext context) {
309- return StreamBuilder<String?>(
310- stream: nameStream, // ✅ SAFE: Same cached ItemHolder instance
311- builder: (context, snapshot) {
312- return Text(snapshot.data ?? 'Unknown');
313- },
314- );
315- }
316-
317- @override
318- void dispose() {
319- // If using stream(), you can cast to dispose if needed
320- if (nameStream is ItemHolder) {
321- (nameStream as ItemHolder).dispose();
322- }
323- super.dispose();
324- }
325- }
326- ```
327-
328- ### ✅ ** Create stream in ` initState() ` **
329-
330- You can also create the stream in ` initState() ` :
331-
332- ``` dart
333- class _MyWidgetState extends State<MyWidget> {
334- late Stream<String?> nameStream;
335-
336- @override
337- void initState() {
338- super.initState();
339- nameStream = storage.stream<String>('name'); // Returns cached ItemHolder
340- }
341-
342- @override
343- Widget build(BuildContext context) {
344- return StreamBuilder<String?>(
345- stream: nameStream, // ✅ SAFE: Same cached instance
346- builder: (context, snapshot) {
347- return Text(snapshot.data ?? 'Unknown');
348- },
349- );
350- }
351- }
352- ```
353-
354- ### Summary: Which pattern should you use?
355-
356- | Pattern | Recommended? | Notes |
357- | --------------------------------| -----------------------------------| ----------------------------------------------------------------------------|
358- | ** ItemHolder directly** | ✅ ** Best** | Most explicit. Clear intent. Direct access to ItemHolder API. |
359- | ** Cached stream (late final)** | ✅ Good | Same as above but less explicit. ` stream() ` returns ItemHolder internally. |
360- | ** initState stream** | ✅ Good | Useful for complex initialization. Still returns cached ItemHolder. |
361- | ** Direct stream() in build()** | ⚠️ ** Acceptable but discouraged** | Now safe (returns cached instance) but not recommended for code clarity. |
362-
363252## Streaming with Serializable Containers
364253
365254You can also stream changes in a ` SerializableContainer ` . This is useful when you want to listen to changes in a complex
0 commit comments