prefnotifiers 1.0.8+1
prefnotifiers: ^1.0.8+1 copied to clipboard
Represents shared preferences as ValueNotifier objects. Reads and writes occur asynchronously in background.
prefnotifiers #
This library represents shared_preferences as ValueNotifier objects.
It fits in well with the paradigm of data models. Models make data readily available to widgets.
Reads and writes occur asynchronously in background.
Why use PrefNotifier? #
Suppose, we have parameter, that can be read with shared_preferences like that:
final prefs = await SharedPreferences.getInstance();
int myParamValue = await prefs.getInt("MyParameter");
There are two lines of problem:
- This code is asynchronous. We cannot use such code directly when building a widget
- The
paramValuedoes not reflect the parameter changes
Instead, we suggest using the new PrefNotifier class for accessing the parameter:
final myParam = PrefNotifier<int>("MyParameter");
myParamobject can be used as the only representation of"MyParameter"in the whole programmyParam.valueallows indirectly read and write the shared preference value without getting out of syncWidget build(_)methods can access value without relying onFutureBuildermyParam.addListenermakes it possible to track changes of the value
What is PrefNotifier? #
PrefNotifier.value provides the best value we have for the moment. The actual read/write operations happen asynchronously in background.
PrefNotifier serves as a model for an individual parameter stored in shared preferences.
Types #
| Type | Аlternative to SharedPreferences' |
|---|---|
PrefNotifier<bool> |
.setBool .getBool .remove |
PrefNotifier<int> |
.setInt .getInt .remove |
PrefNotifier<double> |
.setDouble .getDouble .remove |
PrefNotifier<String> |
.setString .getString .remove |
PrefNotifier<List<String>> |
.setStringList .getStringList .remove |
Basic operations #
| PrefNotifier | SharedPreferences |
|---|---|
myParam = PrefNotifier<int>('MyParameter') |
prefs = await SharedPreferences.getInstance() |
myParam.value = 42 |
await prefs.setInt('MyParameter', 42) |
int? x = myParam.value |
int? x = await prefs.getInt('MyParameter') |
myParam.value = null |
await prefs.remove('MyParameter') |
But the most great is:
myParam.addListener(() => print('Value changed! New value: ${myParam.value}');
How to use PrefNotifier? #
Create #
final myParam = PrefNotifier<int>("MyParameter");
Before 1.0.0 and sound null safety it's PrefItem
final myParam = PrefItem<int>(SharedPrefsStorage(), "MyParameter");
In newer version of the library PrefItem works as well. PrefNotifier is an easier to use alias.
Read #
Reading is is not finished yet. But we already can access myParam.value. By default, it returns null.
We can use it in synchronous code:
Widget build(BuildContext context) {
if (myParam.value==null)
return Text("Not initialized yet");
else
return Text("Value is ${myParam.value}");
}
Since PrefNotifier inherits from the ValueListenable, we can automatically
rebuild the widget when the new value of myParam will be available:
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: myParam,
builder: (BuildContext context, int? value, Widget child) {
if (value==null)
return Text("Not initialized yet");
else
return Text("Value is $value");
});
}
Write #
The code above will also rebuild the widget when value is changed. Let's change the value in a button callback:
onTap: () {
// myParam.value is 3, shared preferences value is 3
myParam.value += 1;
myParam.value += 1;
// myParam.value is already changed to 5
//
// The widget will rebuild momentarily (i.e. on the next frame)
//
// Shared preferences still contain value 3. But asynchronous writing
// already started. It will rewrite value in a few milliseconds
}
Load #
For a newly created PrefNotifier the value returns null until the object
reads the actual data from the storage. Asynchronous loading starts
automatically when the object is created.
But what if we want to get the loaded data before doing anything else?
final myParam = PrefNotifier<int>("TheParameter");
await myParam.initialized;
// we waited while the object was reading the data.
// Now myParam.value returns the value from the storage, not default NULL.
// Even if it is NULL, it is a NULL from the storage :)
Keep in sync #
Create a single instance of PrefNotifier for a particular parameter. Only access this parameter with this PrefNotifier instance.
final myParam = PrefNotifier<int>("MyParameter");
myParam.value = 5;
await (await SharedPreferences.getInstance())
.setInt("MyParameter", 10); // DON'T DO THIS
var otherNotifier = PrefNotifier<int>("MyParameter"); // DON'T DO THIS
otherNotifier = 20;
// now the myParam.value is still 5.
// And the myParam has no idea it is changed
Test #
To test a program that uses PrefNotifiers, you can populate initial values the same way, as in the case of using SharedPreferences directly:
TestWidgetsFlutterBinding.ensureInitialized();
SharedPreferences.setMockInitialValues(Map<String, dynamic> values);