Author: Petrus Nguyễn Thái Học
- Shared preference with
rxdart
Stream observation. - Reactive shared preferences for
Flutter
. - Reactive stream wrapper around SharedPreferences.
- This package provides reactive shared preferences interaction with very little code. It is designed specifically to be used with Flutter and Dart.
Liked some of my work? Buy me a coffee (or more likely a beer)
Since version 1.3.4
, this package is an extension of rx_storage package.
-
It's a single-subscription
Stream
(i.e. it can only be listened once). -
Stream
will emit the value (nullable) or aTypeError
as its first event when it is listened to. -
It will automatically emit the new value when the value associated with key was changed successfully (it will also emit
null
when value associated with key wasremoved
or set tonull
). -
When value read from Storage has a type other than expected type:
- If value is
null
, theStream
will emitnull
(this occurred becausenull
can be cast to any nullable type). - Otherwise, the
Stream
will emit aTypeError
.
- If value is
-
Can emit two consecutive data events that are equal. You should use Rx operator like
distinct
(more commonly known asdistinctUntilChanged
in other Rx implementations) to create aStream
where data events are skipped if they are equal to the previous data event.
Key changed |----------K1---K2------K1----K1-----K2---------> time
|
Value stream |-----@----@------------@-----@-----------------> time
| ^
| |
| Listen(key=K1)
|
| @: nullable value or TypeError
In your flutter project, add the dependency to your pubspec.yaml
dependencies:
[...]
rx_shared_preferences: <latest_version>
- Import
rx_shared_preferences
.
import 'package:rx_shared_preferences/rx_shared_preferences.dart';
- Wrap your
SharedPreferences
in aRxSharedPreferences
.
// via constructor.
final rxPrefs = RxSharedPreferences(await SharedPreferences.getInstance()); // use await
final rxPrefs = RxSharedPreferences(SharedPreferences.getInstance()); // await is optional
final rxPrefs = RxSharedPreferences.getInstance(); // default singleton instance
// via extension.
final rxPrefs = (await SharedPreferences.getInstance()).rx; // await is required
NOTE: When using
RxSharedPreferences.getInstance()
and extension(await SharedPreferences.getInstance()).rx
, to config the logger, you can useRxSharedPreferencesConfigs.logger
setter.
You can pass a logger to the optional parameter of RxSharedPreferences
constructor.
The logger will log messages about operations (such as read, write, ...) and stream events.
This package provides two RxSharedPreferencesLogger
s:
RxSharedPreferencesDefaultLogger
.RxSharedPreferencesEmptyLogger
.
final rxPrefs = RxSharedPreferences(
SharedPreferences.getInstance(),
kReleaseMode ? null : RxSharedPreferencesDefaultLogger(),
// disable logging when running in release mode.
);
NOTE: To disable logging when running in release mode, you can pass
null
orconst RxSharedPreferencesEmptyLogger()
toRxSharedPreferences
constructor, or useRxSharedPreferencesConfigs.logger
setter.
NOTE: To prevent printing
↓ Disposed successfully → DisposeBag#...
.import 'package:disposebag/disposebag.dart' show DisposeBagConfigs; void main() { DisposeBagConfigs.logger = null; }
- Then, just listen
Stream
s, transformStream
s through operators such asmap
,flatMap
, etc... - If you need to listen to the
Stream
many times, you can use broadcast operators such asshare
,shareValue
,publish
,publishValue
, etc...
// Listen
rxPrefs.getStringListStream('KEY_LIST').listen(print); // [*]
// Broadcast stream
rxPrefs.getStringListStream('KEY_LIST').share();
rxPrefs.getStringListStream('KEY_LIST').shareValue();
rxPrefs.getStringListStream('KEY_LIST').asBroadcastStream();
// Transform stream
rxPrefs.getIntStream('KEY_INT')
.map((i) => /* Do something cool */)
.where((i) => /* Filtering */)
...
// must **use same rxPrefs** instance when set value and select stream
await rxPrefs.setStringList('KEY_LIST', ['Cool']); // [*] will print ['Cool']
-
In the previous example, we re-used the
RxSharedPreferences
objectrxPrefs
for all operations. All operations must go through this object in order to correctly notify subscribers. Basically, you must use the sameRxSharedPreferences
instance when set value and select stream. -
In a Flutter app, you can:
-
Create a global
RxSharedPreferences
instance. -
Use the default singleton instance via
RxSharedPreferences.getInstance()
. -
Use
InheritedWidget
/Provider
to provide aRxSharedPreferences
instance (create it inmain
function) for all widgets (recommended). See example/main.
-
// An example for wrong usage.
rxPrefs1.getStringListStream('KEY_LIST').listen(print); // [*]
rxPrefs2.setStringList('KEY_LIST', ['Cool']); // [*] will not print anything,
// because rxPrefs1 and rxPrefs2 are different instances.
- All
Stream
s APIs (via extension methods).
Stream<Object?> getObjectStream(String key, [Decoder<Object?>? decoder]);
Stream<bool?> getBoolStream(String key);
Stream<double?> getDoubleStream(String key);
Stream<int?> getIntStream(String key);
Stream<String?> getStringStream(String key);
Stream<List<String>?> getStringListStream(String key);
Stream<Set<String>> getKeysStream();
Future<void> updateBool(String key, Transformer<bool?> transformer);
Future<void> updateDouble(String key, Transformer<double?> transformer);
Future<void> updateInt(String key, Transformer<int?> transformer);
Future<void> updateString(String key, Transformer<String?> transformer);
Future<void> updateStringList(String key, Transformer<List<String>?> transformer);
- All methods from RxStorage
(because
RxSharedPreferences
implementsRxStorage
).
Future<void> update<T extends Object>(String key, Decoder<T?> decoder, Transformer<T?> transformer, Encoder<T?> encoder);
Stream<T?> observe<T extends Object>(String key, Decoder<T?> decoder);
Stream<Map<String, Object?>> observeAll();
Future<void> dispose();
RxSharedPreferences
is like toSharedPreferences
, it providesread
,write
functions (via extension methods).
Future<Object?> getObject(String key, [Decoder<Object?>? decoder]);
Future<bool?> getBool(String key);
Future<double?> getDouble(String key);
Future<int?> getInt(String key);
Future<Set<String>> getKeys();
Future<String?> getString(String key);
Future<List<String>?> getStringList(String key);
Future<Map<String, Object?>> reload();
Future<void> setBool(String key, bool? value);
Future<void> setDouble(String key, double? value);
Future<void> setInt(String key, int? value);
Future<void> setString(String key, String? value);
Future<void> setStringList(String key, List<String>? value);
- All methods from Storage
(because
RxSharedPreferences
implementsStorage
).
Future<bool> containsKey(String key);
Future<T?> read<T extends Object>(String key, Decoder<T?> decoder);
Future<Map<String, Object?>> readAll();
Future<void> clear();
Future<void> remove(String key);
Future<void> write<T extends Object>(String key, T? value, Encoder<T?> encoder);
You can dispose the RxSharedPreferences
when it is no longer needed.
Just call rxPrefs.dispose()
.
Usually, you call this method on dispose
method of a Flutter State
.
Simple authentication app with BLoC rxdart pattern |
Build ListView from Stream using RxSharedPreferences |
Change theme and locale (language) runtime |
---|---|---|
Please file feature requests and bugs at the issue tracker.
MIT License
Copyright (c) 2019-2024 Petrus Nguyễn Thái Học
Thanks goes to these wonderful people (emoji key):
Petrus Nguyễn Thái Học 💻 📖 🚧 |
This project follows the all-contributors specification. Contributions of any kind welcome!