Skip to content

Commit a90af1b

Browse files
committed
📝 update docs and examples
1 parent 054321e commit a90af1b

File tree

11 files changed

+488
-139
lines changed

11 files changed

+488
-139
lines changed

packages/hyper_storage/README.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,3 +281,36 @@ final storage = await HyperStorage.initMocked(initialData: {
281281
## Contributing
282282

283283
Contributions are welcome! Please feel free to open an issue or submit a pull request.
284+
285+
## License
286+
287+
```
288+
BSD 3-Clause License
289+
290+
Copyright (c) 2025, Hyperdesigned
291+
292+
Redistribution and use in source and binary forms, with or without
293+
modification, are permitted provided that the following conditions are met:
294+
295+
1. Redistributions of source code must retain the above copyright notice, this
296+
list of conditions and the following disclaimer.
297+
298+
2. Redistributions in binary form must reproduce the above copyright notice,
299+
this list of conditions and the following disclaimer in the documentation
300+
and/or other materials provided with the distribution.
301+
302+
3. Neither the name of the copyright holder nor the names of its
303+
contributors may be used to endorse or promote products derived from
304+
this software without specific prior written permission.
305+
306+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
307+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
308+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
309+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
310+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
311+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
312+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
313+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
314+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
315+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
316+
```

packages/hyper_storage/docs/reactivity.md

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ void onStorageChanged() {
8686
storage.removeListener(onStorageChanged);
8787
```
8888

89-
You can also listen to all changes in a named container:
89+
You can also listen to all changes in a container:
9090

9191
```dart
9292
@@ -142,13 +142,22 @@ StreamBuilder<String?>(
142142
```
143143

144144
**Note**: Unlike traditional streams, ItemHolder does **not** emit errors during value retrieval. This prevents
145-
transient failures (like network issues) from being cached and replayed to future listeners. The stream simply
146-
retains its last valid value and retries on the next update.
145+
transient failures from being cached and replayed to future listeners. The stream simply retains its last valid
146+
value and retries on the next update.
147147

148148
## Converting Item Holder to a ValueNotifier
149149

150150
You can convert an `ItemHolder` to a `ValueNotifier` for easier integration with Flutter's state management.
151151

152+
For this, you need to import the `hyper_storage_flutter` package:
153+
154+
```yaml
155+
dependencies:
156+
hyper_storage_flutter: ^<latest_version>
157+
```
158+
159+
Then you can use the `asValueNotifier` extension method:
160+
152161
```dart
153162
final itemHolder = storage.itemHolder<String>('status');
154163
@@ -218,9 +227,9 @@ StreamBuilder<String?>(
218227
);
219228
```
220229

221-
This means calling `storage.stream('key')` directly in a build method is safe and will not create multiple streams.
230+
> This means calling `storage.stream('key')` directly in a build method is safe and will not create multiple streams.
222231

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.
232+
> If you have an `ItemHolder` for the key, you can use it directly in the `StreamBuilder`. This is the most efficient and clear approach.
224233

225234
```dart
226235
class _MyWidgetState extends State<MyWidget> {
@@ -270,6 +279,10 @@ final allTodosSubscription = todos.streamAll().listen((items) {
270279
allTodosSubscription.cancel();
271280
```
272281

282+
> Note: Unlike `stream<T>(key)`, the `streamAll()` method does create a new stream instance each time it's called.
283+
> It is **NOT SAFE** to call `streamAll()` directly in a build method or frequently. Instead, create the stream once
284+
> and reuse it.
285+
273286
You can also stream changes for a specific key in a `SerializableStorageContainer`:
274287

275288
```dart

packages/hyper_storage_flutter/README.md

Lines changed: 168 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -26,76 +26,209 @@ Then, run `flutter pub get`.
2626

2727
## Usage
2828

29-
This package extends `ItemHolder` with an `asValueNotifier` helper so you can bridge Hyper Storage with Flutter's
30-
`ValueListenable` APIs.
29+
# Hyper Storage Flutter Examples
30+
31+
This file provides examples of how to use the `hyper_storage_flutter` package to build reactive user interfaces in
32+
Flutter.
33+
34+
## Listening to a Single Key
35+
36+
There are multiple ways to listen to a single key in your storage.
37+
38+
- Using `ItemHolder`: ItemHolder is listenable and can be streamed too.
39+
- Using `ItemHolder.asValueNotifier`: This is a convenient way to get a `ValueNotifier` for a specific key.
40+
- Using `storage.stream<E>(key)`: This provides a stream of changes for a specific key.
41+
42+
You can use `asValueNotifier` on an `ItemHolder` to obtain a `ValueNotifier` for a specific key. This allows you to
43+
rebuild your UI automatically whenever the value of that key changes.
44+
45+
### Using `ItemHolder` as `ValueNotifier`.
46+
47+
> Note: `ItemHolder.asValueNotifier` must be called outside of the `build` method, typically in `initState` as it will
48+
> create a new `ValueNotifier` each time it is called.
3149

3250
```dart
3351
import 'package:flutter/material.dart';
3452
import 'package:hyper_storage/hyper_storage.dart';
3553
import 'package:hyper_storage_flutter/hyper_storage_flutter.dart';
36-
import 'package:hyper_storage_shared_preferences/shared_preferences_backend.dart';
37-
38-
void main() async {
39-
WidgetsFlutterBinding.ensureInitialized();
40-
final storage = await HyperStorage.init(backend: SharedPreferencesBackend());
41-
runApp(MyApp(storage: storage));
42-
}
4354
44-
class MyApp extends StatefulWidget {
55+
class CounterScreen extends StatefulWidget {
4556
final HyperStorage storage;
4657
47-
const MyApp({super.key, required this.storage});
58+
const CounterScreen({super.key, required this.storage});
4859
4960
@override
50-
State<MyApp> createState() => _MyAppState();
61+
State<CounterScreen> createState() => _CounterScreenState();
5162
}
5263
53-
class _MyAppState extends State<MyApp> {
54-
late final ItemHolder<String> _messageHolder;
55-
late final ValueNotifier<String?> _messageNotifier;
64+
class _CounterScreenState extends State<CounterScreen> {
65+
late final ItemHolder<int> _counterHolder;
66+
late final ValueNotifier<int?> _counterNotifier;
5667
5768
@override
5869
void initState() {
5970
super.initState();
60-
_messageHolder = widget.storage.itemHolder<String>('message');
61-
_messageNotifier = _messageHolder.asValueNotifier();
71+
_counterHolder = widget.storage.itemHolder<int>('counter');
72+
_counterNotifier = _counterHolder.asValueNotifier();
6273
}
6374
6475
@override
6576
void dispose() {
66-
_messageNotifier.dispose();
67-
_messageHolder.dispose();
77+
_counterNotifier.dispose();
78+
_counterHolder.dispose();
6879
super.dispose();
6980
}
7081
7182
@override
7283
Widget build(BuildContext context) {
73-
return MaterialApp(
74-
home: Scaffold(
75-
appBar: AppBar(title: const Text('Hyper Storage Flutter')),
76-
body: Center(
77-
child: ValueListenableBuilder<String?>(
78-
valueListenable: _messageNotifier,
79-
builder: (context, message, child) {
80-
return Text(message ?? 'No message yet');
81-
},
82-
),
84+
return Scaffold(
85+
body: Center(
86+
child: ValueListenableBuilder<int?>(
87+
valueListenable: _counterNotifier,
88+
builder: (context, counter, child) {
89+
return Text('Counter: ${counter ?? 0}');
90+
},
8391
),
84-
floatingActionButton: FloatingActionButton(
85-
onPressed: () async {
86-
final current = await _messageHolder.get() ?? 'Hello';
87-
await _messageHolder.set('$current!');
92+
),
93+
floatingActionButton: FloatingActionButton(
94+
onPressed: () async {
95+
final currentCounter = await _counterHolder.get() ?? 0;
96+
await _counterHolder.set(currentCounter + 1);
97+
},
98+
child: const Icon(Icons.add),
99+
),
100+
);
101+
}
102+
}
103+
```
104+
105+
### Using `ItemHolder` with `StreamBuilder`.
106+
107+
> Note: It is safe to reuse the same `ItemHolder` instance multiple times without disposing it, as it manages its own
108+
> resources. Calling `itemHolder` multiple times with the same key will return the same instance.
109+
110+
```dart
111+
import 'package:flutter/material.dart';
112+
import 'package:hyper_storage/hyper_storage.dart';
113+
import 'package:hyper_storage_flutter/hyper_storage_flutter.dart';
114+
115+
class CounterScreen extends StatefulWidget {
116+
final HyperStorage storage;
117+
118+
const CounterScreen({super.key, required this.storage});
119+
120+
@override
121+
State<CounterScreen> createState() => _CounterScreenState();
122+
}
123+
124+
class _CounterScreenState extends State<CounterScreen> {
125+
late final ItemHolder<int> _counterHolder = widget.storage.itemHolder<int>('counter');
126+
127+
@override
128+
void dispose() {
129+
_counterHolder.dispose();
130+
super.dispose();
131+
}
132+
133+
@override
134+
Widget build(BuildContext context) {
135+
return Scaffold(
136+
body: Center(
137+
child: StreamBuilder<int?>(
138+
stream: _counterHolder,
139+
builder: (context, snapshot) {
140+
return Text('Counter: ${snapshot.data ?? 0}');
88141
},
89-
child: const Icon(Icons.add),
90142
),
91143
),
144+
floatingActionButton: FloatingActionButton(
145+
onPressed: () async {
146+
final currentCounter = await _counterHolder.get() ?? 0;
147+
await _counterHolder.set(currentCounter + 1);
148+
},
149+
child: const Icon(Icons.add),
150+
),
92151
);
93152
}
94153
}
95154
```
96155

97-
For more detailed examples, please see the [example.md](example.md) file.
156+
### Using streams with `StreamBuilder`.
157+
158+
> Note: `storage.stream<E>(key)` is safe to call inside the `build` method as it manages the stream internally.
159+
160+
```dart
161+
import 'package:flutter/material.dart';
162+
import 'package:hyper_storage/hyper_storage.dart';
163+
import 'package:hyper_storage_flutter/hyper_storage_flutter.dart';
164+
165+
class CounterScreen extends StatefulWidget {
166+
final HyperStorage storage;
167+
168+
const CounterScreen({super.key, required this.storage});
169+
170+
@override
171+
State<CounterScreen> createState() => _CounterScreenState();
172+
}
173+
174+
class _CounterScreenState extends State<CounterScreen> {
175+
176+
@override
177+
Widget build(BuildContext context) {
178+
return Scaffold(
179+
body: Center(
180+
child: StreamBuilder<int?>(
181+
stream: widget.storage.stream<int>('counter'),
182+
builder: (context, snapshot) {
183+
return Text('Counter: ${snapshot.data ?? 0}');
184+
},
185+
),
186+
),
187+
floatingActionButton: FloatingActionButton(
188+
onPressed: () async {
189+
final currentCounter = await widget.storage.getInt('counter') ?? 0;
190+
await widget.storage.setInt('counter', currentCounter + 1);
191+
},
192+
child: const Icon(Icons.add),
193+
),
194+
);
195+
}
196+
}
197+
```
98198

99199
## Contributing
100200

101201
Contributions are welcome! Please feel free to open an issue or submit a pull request.
202+
203+
## License
204+
205+
```
206+
BSD 3-Clause License
207+
208+
Copyright (c) 2025, Hyperdesigned
209+
210+
Redistribution and use in source and binary forms, with or without
211+
modification, are permitted provided that the following conditions are met:
212+
213+
1. Redistributions of source code must retain the above copyright notice, this
214+
list of conditions and the following disclaimer.
215+
216+
2. Redistributions in binary form must reproduce the above copyright notice,
217+
this list of conditions and the following disclaimer in the documentation
218+
and/or other materials provided with the distribution.
219+
220+
3. Neither the name of the copyright holder nor the names of its
221+
contributors may be used to endorse or promote products derived from
222+
this software without specific prior written permission.
223+
224+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
225+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
226+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
227+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
228+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
229+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
230+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
231+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
232+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
233+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
234+
```

0 commit comments

Comments
 (0)