rx_shared_preferences alt text

Author: Petrus Nguyễn Thái Học

Codacy Badge Pub Pub codecov Build Status Build example Flutter CI License: MIT Style Hits

All Contributors

  • 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.


Since version 1.3.4, this package is an extension of rx_storage package.

More detail about returned Stream

  • It's a single-subscription Stream (ie. it can only be listened once).

  • Stream will emit the value (nullable) or a TypeError as its first event when it is listen to.

  • It will automatically emit value when value associated with key was changed successfully (emit null when value associated with key was removed or set to null).

  • When value read from Storage has a type other than expected type:

    • If value is null, the Stream will emit null (this occurred because null can be cast to any nullable type).
    • Otherwise, the Stream will emit a TypeError.
  • Can emit two consecutive data events that are equal. You should use Rx operator like distinct (More commonly known as distinctUntilChanged in other Rx implementations) to create an Stream 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

Getting Started

In your flutter project, add the dependency to your pubspec.yaml

  rx_shared_preferences: ^2.2.0


1. Import and instatiance

  • Import rx_shared_preferences.
import 'package:rx_shared_preferences/rx_shared_preferences.dart';
  • Wrap your SharedPreferences in a RxSharedPreferences.
// via constructor.
final rxPrefs = RxSharedPreferences(await SharedPreferences.getInstance());
final rxPrefs = RxSharedPreferences(SharedPreferences.getInstance()); // await is optional
final rxPrefs = RxSharedPreferences.getInstance(); // default singleton instance

// via extension.
final rxPrefs = (await SharedPreferences.getInstance()).rx;

NOTE: When using RxSharedPreferences.getInstance() and extension (await SharedPreferences.getInstance()).rx, to config the logger, you can use RxSharedPreferencesConfigs.logger setter.

2. Add a logger (optional)

You can add logger optional parameter to RxSharedPreferences constructor. The logger will log messages about operations (such as read, write) and stream events. This package provides two RxSharedPreferencesLoggers:

  • RxSharedPreferencesDefaultLogger.
  • RxSharedPreferencesEmptyLogger.
final rxPrefs = RxSharedPreferences(
  kReleaseMode ? null : RxSharedPreferencesDefaultLogger(),
  // disable logging when running in release mode.

NOTE: To disable logging when running in release mode, you can pass null or const RxSharedPreferencesEmptyLogger() to RxSharedPreferences constructor or use RxSharedPreferencesConfigs.logger setter.

NOTE: To prevent printing ↓ Disposed successfully → DisposeBag#....

import 'package:disposebag/disposebag.dart' show DisposeBagConfigs;
void main() {
  DisposeBagConfigs.logger = null;

3. Select stream and observe

  • Then, just listen Stream, transform Stream through operators such as (map, flatMap, etc...).
  • If you need listen to this Stream many times, you can use broadcast operators such as share, shareValue, publish, publishValue, etc...
// Listen
rxPrefs.getStringListStream('KEY_LIST').listen(print); // [*]

// Broadcast stream

// Transform stream
  .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 object rxPrefs for all writing operations. All writing operations must go through this object in order to correctly notify subscribers.

  • In Flutter, you:

    • Can create global RxSharedPreferences instance.

    • Can use default singleton instance RxSharedPreferences.getInstance()

    • Can use InheritedWidget/Provider to provide a RxSharedPreferences instance (create it in main 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
  • Streams 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>                 executeUpdateBool(String key, Transformer<bool?> transformer);
  Future<void>                 executeUpdateDouble(String key, Transformer<double?> transformer);
  Future<void>                 executeUpdateInt(String key, Transformer<int?> transformer);
  Future<void>                 executeUpdateString(String key, Transformer<String?> transformer);
  Future<void>                 executeUpdateStringList(String key, Transformer<List<String>?> transformer);
  • All methods from RxStorage (RxSharedPreferences implements RxStorage).
  Future<void>                 executeUpdate<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();

4. Get and set methods like to SharedPreferences

  • RxSharedPreferences is like to SharedPreferences, it provides read, 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 (RxSharedPreferences implements Storage).
  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);

5. Dispose

You can dispose RxSharedPreferences when is no longer needed. Just call rxPrefs.dispose(). Usually you call this method on dispose of a State

Example demo

Simple authentication app with BLoC rxdart patternBuild ListView from Stream using RxSharedPreferencesChange theme and locale (language) runtime


    Copyright (c) 2019-2021 Petrus Nguyễn Thái Học

Contributors ✨

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!


Author: Petrus Nguyễn Thái Học.