Flutter plugin for reading and writing key-value pairs.
notified_preferences is a wrapper around shared_preferences. It provides you with an easy way of listening to changes in your preference values.
If you're already using shared_preferences
, you can replace it.
A helper mixin class, NotifiedPreferences
is provided with which you can create your own Settings object:
class Settings with NotifiedPreferences {
late final PreferenceNotifier<bool> hasSeenTutorial =
createSetting(key: 'hasSeenTutorial', initial: false);
late final PreferenceNotifier<bool> clicked =
createSetting(key: 'buttonClicks', initial: 0);
}
NotifiedPreferences
has to be initialized once, when you create your Settings object:
Future<void> main() async {
Settings settings = Settings();
await settings.initialize();
runApp(
// inject your settings here, with e.g. Provider.
Provider.value(
value: settings,
child: MyApp(),
)
);
}
This has the benefit that all other operations are completely synchronous.
You can listen to your preferences by using ValueListenableBuilder
just like with normal ValueNotifier
:
ValueListenableBuilder<int>(
valueListenable: settings.clicked,
builder: (context, value, child) => Text('You have clicked the button $value times!'),
)
And when you change your value, listeners will be notified / rebuilt:
FloatingActionButton(
onPressed: () => settings.clicked.value++,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
You can also listen to your preferences with addListener
:
void _onClicked() {
print('The user has clicked ${settings.clicked.value} times!');
}
settings.clicked.addListener(_onClicked);
Remember to remove the listener again, to avoid memory leaks:
@override
void dispose() {
settings.clicked.removeListener(_onClicked);
super.dispose();
}
! Using anonymous functions with addListener
results in them being unable to be removed !
If you want to store preferences which aren't contained in the base types,
String, int, double, bool, List<String>
and their nullable counterparts,
you can store them as json:
late final PreferenceNotifier<ComplexObject> complexObject = createJsonSetting(
key: 'complexObject',
initialValue: ComplexObject(
someInt: 0,
someString: 'a',
),
fromJson: (json) => ComplexObject.fromJson(json),
);
If you want to store enums, a convenience method is provided:
late final PreferenceNotifier<SomeEnum> someEnum = createEnumSetting(
key: 'someEnum',
initialValue: SomeEnum.a,
values: SomeEnum.values,
);
If you are already using a different SharedPreferences
wrapper like encrypted_shared_preferences
,
or if you want to mock the implementation for testing, you can pass it during initialisation:
await settings.initialize(otherSharedPrefs);
If you do not want to use NotifiedPreferences
, you can instantiate your PreferenceNotifier
s manually:
final myNotifier = PreferenceNotifier<T>(
preferences: preferences,
key: key,
initialValue: initialValue,
read: read,
write: write,
);
Note that you lose functionality to clear all your settings.
If you somehow want to implement custom logic inside read or write of your Preferences, you can do so:
late final PreferenceNotifier<ComplexObject> complexObject = createSetting(
key: 'complexObject',
initialValue: ComplexObject(
someInt: 0,
someString: 'a',
),
read: (prefs, key) {
String? value = prefs.getString(key);
ComplexObject? result;
if (value != null) {
result = ComplexObject.fromJson(jsonDecode(value));
}
return result;
},
write: (prefs, key, value) => prefs.setStringOrNull(
key,
json.encode(value.toJson()),
),
);
Note that read cannot be async.
If you would like to store your Preferences on multiple classes instead of a single one,
you can use NotifiedSettings
, which is not abstract:
NotifiedSettings settings = await NotifiedSettings.getInstance();
class SomeController {
final ValueNotifier<String?> someValue = settings.createSettings(
key: 'someString',
initialValue: null,
);
}
This way you can retain your ability to clear all settings.