@@ -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
3351import 'package:flutter/material.dart';
3452import 'package:hyper_storage/hyper_storage.dart';
3553import '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
101201Contributions 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