streaming_shared_preferences 1.0.0-dev-6 copy "streaming_shared_preferences: ^1.0.0-dev-6" to clipboard
streaming_shared_preferences: ^1.0.0-dev-6 copied to clipboard

outdated

A reactive key-value store for Flutter projects, built on top of shared_preferences.

streaming_shared_preferences - (dev preview) #

(almost ready, going through final testing + documentation. there might be minor API changes - for now, use at your own risk)

pub package Build Status Coverage Status

A reactive key-value store for Flutter projects.

It wraps shared_preferences with a reactive Stream based layer, allowing you to listen and react to changes in the underlying values.

Simple usage example #

To get a hold of StreamingSharedPreferences, await on instance:

import 'package:streaming_shared_preferences/streaming_shared_preferences.dart';

...
final preferences = await StreamingSharedPreferences.instance;

The public API follows the same naming convention as shared_preferences does, but with a little twist - every getter returns a Preference object, which is a special type of Stream.

Here's a plain Dart example how you would listen to changes in an integer and print the value to console every time it changes:

// Provide a default value of 0 in case "counter" is null.
final counter = preferences.getInt('counter', defaultValue: 0);

// "counter" is a Stream - it can do anything a Stream can.
counter.listen((value) {
  print(value);
});

// Somewhere else in your code, update the value.
//
// You can also call preferences.setInt('counter', <value>) but this
// is a little more convenient as there's no need to specify the key.
counter.setValue(1);
counter.setValue(2);
counter.setValue(3);

Assuming that there's no previously stored value for counter, the above example will print 0, 1, 2 and 3 to the console.

If you need to get the value synchronously, you can call final currentValue = counter.getValue().

Go simple if you don't have a lot of preferences #

If you have only one Preference in your app, it might make sense to create and listen to a Preference inline:

class MyCounterWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    /// PreferenceBuilder is like StreamBuilder, but we don't have to
    /// provide `initialData` - that can be fetched synchronously from
    /// the provided Preference.
    ///
    /// If you want, you could use a StreamBuilder too.
    return PreferenceBuilder<int>(
      preferences.getInt('counter', defaultValue: 0),
      builder: (BuildContext context, AsyncSnapshot<int> snapshot) {
        return Text('Button pressed ${snapshot.data} times!');
      }
    );
  }
}

Use a wrapper class when having multiple preferences #

If you have multiple preferences, the recommended approach is to create a class that holds all your Preference objects in a single place:

/// A class that holds [Preference] objects for the common values that you want
/// to store in your app. This is *not* necessarily needed, but it makes your
/// code more neat and fool-proof.
class MyAppSettings {
  MyAppSettings(StreamingSharedPreferences preferences)
      : counter = preferences.getInt('counter', defaultValue: 0),
        nickname = preferences.getString('nickname', defaultValue: '');

  final Preference<int> counter;
  final Preference<String> nickname;
}

In our app entrypoint, we obtain an instance to StreamingSharedPreferences once and pass that to our settings class. Now we can pass MyAppSettings down to the widgets that use it:

Future<void> main() async {
  /// Obtain instance to streaming shared preferences, create MyAppSettings, and
  /// once that's done, run the app.
  final preferences = await StreamingSharedPreferences.instance;
  final settings = MyAppSettings(preferences);
  
  runApp(MyApp(settings));
}

This makes the calling code become quite neat:

class MyCounterWidget extends StatelessWidget {
  MyCounterWidget(this.settings);
  final MyAppSettings settings;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        PreferenceBuilder<String>(
          settings.nickname,
          builder: (context, snapshot) => Text('Hey ${snapshot.data}!'),
        ),
        PreferenceBuilder<int>(
          settings.counter,
          builder: (context, snapshot) => Text('You have pushed the button ${snapshot.data} times!'),
        ),
        FloatingActionButton(
          onPressed: () {
            /// To obtain the current value synchronously, we can call ".getValue()".
            final currentValue = settings.counter.getValue();

            /// To update the value, we can call ".setValue()" - no need to provide a key!
            /// Alternatively, we could just call "preferences.setInt('counter', currentValue + 1)".
            settings.counter.setValue(currentValue + 1);
          },
          child: Icon(Icons.add),
        ),
      ],
    );
  }
}

When your widget hierarchy becomes deep enough, you would want to pass MyAppSettings around with an InheritedWidget instead.

"But muh abstraction!" #

If you're all about the clean architecture and don't want to pollute your domain layer with Preference objects from a third-party library by some random internet stranger, all the power to you.

Here's one way to make Uncle Bob proud.

/// The contract for persistent values in your app that can be shared
/// to your pure business logic classes
abstract class MyAppSettings {
  Stream<int> getCounter();
  void setCounter(int value);
}

class MyHomePageBloc {
  MyHomePageBloc(this.settings);
  final MyAppSettings settings;

  // Do something with "getCounter()" and "setCounter()" along with 
  // whatever your business use case needs
}

No StreamingSharedPreferences specifics went in there. If for some reason you want to switch into some other library (or get rid of this library altogether), you can do so without modifying your business logic.

Here's how the implementation based on StreamingSharedPreferences would look like:

/// One implementation of MyAppSettings backed by StreamingSharedPreferences
class MyStreamingSharedPreferencesSettings implements MyAppSettings {
  MyStreamingSharedPreferences(StreamingSharedPreferences preferences)
      : counter = preferences.getInt('counter', defaultValue: 0);

  final Preference<int> _counter;

  // Preference<int> is a Stream<int>, so we can just return it
  @override
  Stream<int> getCounter() => _counter;

  // Preference exposes a handy "setValue()" method to update the value
  @override
  void setCounter(int value) => _counter.setValue(value);
}

Not too bad, is it? It's a good thing Preference<int> is also a Stream<int> and that there's a handy setter function for every preference.

122
likes
0
pub points
97%
popularity

Publisher

verified publisheriiro.dev

A reactive key-value store for Flutter projects, built on top of shared_preferences.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, meta, shared_preferences

More

Packages that depend on streaming_shared_preferences