Skip to content

Commit 644000b

Browse files
authored
[shared_preferences] full api redesign with DataStore and cache-less interface (flutter#5210)
Full rework for shared_preferences, allows user to decide whether to use cache or be fully async and pull direct from the platform. fixes flutter#123078 fixes flutter#133098 fixes flutter#65145 fixes flutter#64739 fixes flutter#151036
1 parent 7c31459 commit 644000b

File tree

15 files changed

+2432
-461
lines changed

15 files changed

+2432
-461
lines changed

packages/shared_preferences/shared_preferences/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
## NEXT
1+
## 2.3.0
22

3+
* Adds `SharedPreferencesAsync` and `SharedPreferencesWithCache` APIs.
34
* Updates minimum supported SDK version to Flutter 3.16/Dart 3.2.
45

56
## 2.2.3

packages/shared_preferences/shared_preferences/README.md

Lines changed: 103 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,48 @@ Supported data types are `int`, `double`, `bool`, `String` and `List<String>`.
1717

1818
## Usage
1919

20-
### Examples
20+
## SharedPreferences vs SharedPreferencesAsync vs SharedPreferencesWithCache
21+
22+
Starting with version 2.3.0 there are three available APIs that can be used in this package.
23+
[SharedPreferences] is a legacy API that will be deprecated in the future. We highly encourage
24+
any new users of the plugin to use the newer [SharedPreferencesAsync] or [SharedPreferencesWithCache]
25+
APIs instead.
26+
27+
Consider migrating existing code to one of the new APIs. See [below](#migrating-from-sharedpreferences-to-sharedpreferencesasyncwithcache)
28+
for more information.
29+
30+
### Cache and async or sync getters
31+
32+
[SharedPreferences] and [SharedPreferencesWithCache] both use a local cache to store preferences.
33+
This allows for synchronous get calls after the initial setup call fetches the preferences from the platform.
34+
However, The cache can present issues as well:
35+
36+
- If you are using `shared_preferences` from multiple isolates, since each
37+
isolate has its own singleton and cache.
38+
- If you are using `shared_preferences` in multiple engine instances (including
39+
those created by plugins that create background contexts on mobile devices,
40+
such as `firebase_messaging`).
41+
- If you are modifying the underlying system preference store through something
42+
other than the `shared_preferences` plugin, such as native code.
43+
44+
This can be remedied by calling the `reload` method before using a getter as needed.
45+
If most get calls need a reload, consider using [SharedPreferencesAsync] instead.
46+
47+
[SharedPreferencesAsync] does not utilize a local cache which causes all calls to be asynchronous
48+
calls to the host platforms storage solution. This can be less performant, but should always provide the
49+
latest data stored on the native platform regardless of what process was used to store it.
50+
51+
### Android platform storage
52+
53+
The [SharedPreferences] API uses the native [Android Shared Preferences](https://developer.android.com/reference/android/content/SharedPreferences) tool to store data.
54+
55+
The [SharedPreferencesAsync] and [SharedPreferencesWithCache] APIs use [DataStore Preferences](https://developer.android.com/topic/libraries/architecture/datastore) to store data.
56+
57+
## Examples
2158
Here are small examples that show you how to use the API.
2259

60+
### SharedPreferences
61+
2362
#### Write data
2463
<?code-excerpt "readme_excerpts.dart (Write)"?>
2564
```dart
@@ -60,32 +99,67 @@ final List<String>? items = prefs.getStringList('items');
6099
await prefs.remove('counter');
61100
```
62101

63-
### Multiple instances
102+
### SharedPreferencesAsync
103+
<?code-excerpt "readme_excerpts.dart (Async)"?>
104+
```dart
105+
final SharedPreferencesAsync asyncPrefs = SharedPreferencesAsync();
64106
65-
In order to make preference lookup via the `get*` methods synchronous,
66-
`shared_preferences` uses a cache on the Dart side, which is normally only
67-
updated by the `set*` methods. Usually this is an implementation detail that
68-
does not affect callers, but it can cause issues in a few cases:
69-
- If you are using `shared_preferences` from multiple isolates, since each
70-
isolate has its own `SharedPreferences` singleton and cache.
71-
- If you are using `shared_preferences` in multiple engine instances (including
72-
those created by plugins that create background contexts on mobile devices,
73-
such as `firebase_messaging`).
74-
- If you are modifying the underlying system preference store through something
75-
other than the `shared_preferences` plugin, such as native code.
107+
await asyncPrefs.setBool('repeat', true);
108+
await asyncPrefs.setString('action', 'Start');
109+
110+
final bool? repeat = await asyncPrefs.getBool('repeat');
111+
final String? action = await asyncPrefs.getString('action');
112+
113+
await asyncPrefs.remove('repeat');
114+
115+
// Any time a filter option is included as a method parameter, strongly consider
116+
// using it to avoid potentially unwanted side effects.
117+
await asyncPrefs.clear(allowList: <String>{'action', 'repeat'});
118+
```
119+
120+
### SharedPreferencesWithCache
121+
<?code-excerpt "readme_excerpts.dart (WithCache)"?>
122+
```dart
123+
final SharedPreferencesWithCache prefsWithCache =
124+
await SharedPreferencesWithCache.create(
125+
cacheOptions: const SharedPreferencesWithCacheOptions(
126+
// When an allowlist is included, any keys that aren't included cannot be used.
127+
allowList: <String>{'repeat', 'action'},
128+
),
129+
);
76130
77-
If you need to read a preference value that may have been changed by anything
78-
other than the `SharedPreferences` instance you are reading it from, you should
79-
call `reload()` on the instance before reading from it to update its cache with
80-
any external changes.
131+
await prefsWithCache.setBool('repeat', true);
132+
await prefsWithCache.setString('action', 'Start');
133+
134+
final bool? repeat = prefsWithCache.getBool('repeat');
135+
final String? action = prefsWithCache.getString('action');
136+
137+
await prefsWithCache.remove('repeat');
138+
139+
// Since the filter options are set at creation, they aren't needed during clear.
140+
await prefsWithCache.clear();
141+
```
81142

82-
If this is problematic for your use case, you can thumbs up
83-
[this issue](https://github.com/flutter/flutter/issues/123078) to express
84-
interest in APIs that provide direct (asynchronous) access to the underlying
85-
preference store, and/or subscribe to it for updates.
86143

87144
### Migration and Prefixes
88145

146+
#### Migrating from SharedPreferences to SharedPreferencesAsync/WithCache
147+
148+
Currently, migration from the older [SharedPreferences] API to the newer
149+
[SharedPreferencesAsync] or [SharedPreferencesWithCache] will need to be done manually.
150+
151+
A simple form of this could be fetching all preferences with [SharedPreferences] and adding
152+
them back using [SharedPreferencesAsync], then storing a preference indicating that the
153+
migration has been done so that future runs don't repeat the migration.
154+
155+
If a migration is not performed before moving to [SharedPreferencesAsync] or [SharedPreferencesWithCache],
156+
most (if not all) data will be lost. Android preferences are stored in a new system, and all platforms
157+
are likely to have some form of enforced prefix (see below) that would not transfer automatically.
158+
159+
A tool to make this process easier can be tracked here: https://github.com/flutter/flutter/issues/150732
160+
161+
#### Adding, Removing, or changing prefixes on SharedPreferences
162+
89163
By default, the `SharedPreferences` plugin will only read (and write) preferences
90164
that begin with the prefix `flutter.`. This is all handled internally by the plugin
91165
and does not require manually adding this prefix.
@@ -123,11 +197,11 @@ SharedPreferences.setMockInitialValues(values);
123197

124198
### Storage location by platform
125199

126-
| Platform | Location |
127-
| :--- | :--- |
128-
| Android | SharedPreferences |
129-
| iOS | NSUserDefaults |
130-
| Linux | In the XDG_DATA_HOME directory |
131-
| macOS | NSUserDefaults |
132-
| Web | LocalStorage |
133-
| Windows | In the roaming AppData directory |
200+
| Platform | SharedPreferences | SharedPreferencesAsync/WithCache |
201+
| :--- | :--- | :--- |
202+
| Android | SharedPreferences | DataStore Preferences |
203+
| iOS | NSUserDefaults | NSUserDefaults |
204+
| Linux | In the XDG_DATA_HOME directory | In the XDG_DATA_HOME directory |
205+
| macOS | NSUserDefaults | NSUserDefaults |
206+
| Web | LocalStorage | LocalStorage |
207+
| Windows | In the roaming AppData directory | In the roaming AppData directory |

0 commit comments

Comments
 (0)