[shared_preferences] allow custom key prefixes platform only changes (#3497)
[shared_preferences] allow custom key prefixes platform only changes
diff --git a/packages/shared_preferences/shared_preferences_platform_interface/CHANGELOG.md b/packages/shared_preferences/shared_preferences_platform_interface/CHANGELOG.md
index 43b0166..5f29cb7 100644
--- a/packages/shared_preferences/shared_preferences_platform_interface/CHANGELOG.md
+++ b/packages/shared_preferences/shared_preferences_platform_interface/CHANGELOG.md
@@ -1,5 +1,6 @@
-## NEXT
+## 2.2.0
+* Adds `getAllWithPrefix` and `clearWithPrefix` method.
* Aligns Dart and Flutter SDK constraints.
## 2.1.1
diff --git a/packages/shared_preferences/shared_preferences_platform_interface/lib/method_channel_shared_preferences.dart b/packages/shared_preferences/shared_preferences_platform_interface/lib/method_channel_shared_preferences.dart
index 2974f0a..5c415b1 100644
--- a/packages/shared_preferences/shared_preferences_platform_interface/lib/method_channel_shared_preferences.dart
+++ b/packages/shared_preferences/shared_preferences_platform_interface/lib/method_channel_shared_preferences.dart
@@ -39,13 +39,25 @@
}
@override
- Future<Map<String, Object>> getAll() async {
- final Map<String, Object>? preferences =
- await _kChannel.invokeMapMethod<String, Object>('getAll');
+ Future<bool> clearWithPrefix(String prefix) async {
+ return (await _kChannel.invokeMethod<bool>(
+ 'clearWithPrefix',
+ <String, dynamic>{'prefix': prefix},
+ ))!;
+ }
- if (preferences == null) {
- return <String, Object>{};
- }
- return preferences;
+ @override
+ Future<Map<String, Object>> getAllWithPrefix(String prefix) async {
+ return await _kChannel.invokeMapMethod<String, Object>(
+ 'getAllWithPrefix',
+ <String, dynamic>{'prefix': prefix},
+ ) ??
+ <String, Object>{};
+ }
+
+ @override
+ Future<Map<String, Object>> getAll() async {
+ return await _kChannel.invokeMapMethod<String, Object>('getAll') ??
+ <String, Object>{};
}
}
diff --git a/packages/shared_preferences/shared_preferences_platform_interface/lib/shared_preferences_platform_interface.dart b/packages/shared_preferences/shared_preferences_platform_interface/lib/shared_preferences_platform_interface.dart
index ced6aa5..c9e4aa6 100644
--- a/packages/shared_preferences/shared_preferences_platform_interface/lib/shared_preferences_platform_interface.dart
+++ b/packages/shared_preferences/shared_preferences_platform_interface/lib/shared_preferences_platform_interface.dart
@@ -62,11 +62,29 @@
/// * Value type "StringList" must be passed if the value is of type `List<String>`.
Future<bool> setValue(String valueType, String key, Object value);
- /// Removes all keys and values in the store.
+ /// Removes all keys and values in the store where the key starts with 'flutter.'.
+ ///
+ /// This default behavior is for backwards compatibility with older versions of this
+ /// plugin, which did not support custom prefixes, and instead always used the
+ /// prefix 'flutter.'.
Future<bool> clear();
- /// Returns all key/value pairs persisted in this store.
+ /// Removes all keys and values in the store with given prefix.
+ Future<bool> clearWithPrefix(String prefix) {
+ throw UnimplementedError('clearWithPrefix is not implemented.');
+ }
+
+ /// Returns all key/value pairs persisted in this store where the key starts with 'flutter.'.
+ ///
+ /// This default behavior is for backwards compatibility with older versions of this
+ /// plugin, which did not support custom prefixes, and instead always used the
+ /// prefix 'flutter.'.
Future<Map<String, Object>> getAll();
+
+ /// Returns all key/value pairs persisting in this store that have given [prefix].
+ Future<Map<String, Object>> getAllWithPrefix(String prefix) {
+ throw UnimplementedError('getAllWithPrefix is not implemented.');
+ }
}
/// Stores data in memory.
@@ -81,16 +99,29 @@
: _data = Map<String, Object>.from(data);
final Map<String, Object> _data;
+ static const String _defaultPrefix = 'flutter.';
@override
Future<bool> clear() async {
- _data.clear();
+ return clearWithPrefix(_defaultPrefix);
+ }
+
+ @override
+ Future<bool> clearWithPrefix(String prefix) async {
+ _data.removeWhere((String key, _) => key.startsWith(prefix));
return true;
}
@override
Future<Map<String, Object>> getAll() async {
- return Map<String, Object>.from(_data);
+ return getAllWithPrefix(_defaultPrefix);
+ }
+
+ @override
+ Future<Map<String, Object>> getAllWithPrefix(String prefix) async {
+ final Map<String, Object> preferences = Map<String, Object>.from(_data);
+ preferences.removeWhere((String key, _) => !key.startsWith(prefix));
+ return preferences;
}
@override
diff --git a/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml b/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml
index 589a632..15e5e59 100644
--- a/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml
+++ b/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml
@@ -2,7 +2,7 @@
description: A common platform interface for the shared_preferences plugin.
repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_platform_interface
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22
-version: 2.1.1
+version: 2.2.0
environment:
sdk: ">=2.17.0 <4.0.0"
diff --git a/packages/shared_preferences/shared_preferences_platform_interface/test/method_channel_shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_platform_interface/test/method_channel_shared_preferences_test.dart
index 296592e..a13ef5c 100644
--- a/packages/shared_preferences/shared_preferences_platform_interface/test/method_channel_shared_preferences_test.dart
+++ b/packages/shared_preferences/shared_preferences_platform_interface/test/method_channel_shared_preferences_test.dart
@@ -15,7 +15,7 @@
'plugins.flutter.io/shared_preferences',
);
- const Map<String, Object> kTestValues = <String, Object>{
+ const Map<String, Object> flutterTestValues = <String, Object>{
'flutter.String': 'hello world',
'flutter.Bool': true,
'flutter.Int': 42,
@@ -23,6 +23,28 @@
'flutter.StringList': <String>['foo', 'bar'],
};
+ const Map<String, Object> prefixTestValues = <String, Object>{
+ 'prefix.String': 'hello world',
+ 'prefix.Bool': true,
+ 'prefix.Int': 42,
+ 'prefix.Double': 3.14159,
+ 'prefix.StringList': <String>['foo', 'bar'],
+ };
+
+ const Map<String, Object> nonPrefixTestValues = <String, Object>{
+ 'String': 'hello world',
+ 'Bool': true,
+ 'Int': 42,
+ 'Double': 3.14159,
+ 'StringList': <String>['foo', 'bar'],
+ };
+
+ final Map<String, Object> allTestValues = <String, Object>{};
+
+ allTestValues.addAll(flutterTestValues);
+ allTestValues.addAll(prefixTestValues);
+ allTestValues.addAll(nonPrefixTestValues);
+
late InMemorySharedPreferencesStore testData;
final List<MethodCall> log = <MethodCall>[];
@@ -43,6 +65,12 @@
if (methodCall.method == 'getAll') {
return testData.getAll();
}
+ if (methodCall.method == 'getAllWithPrefix') {
+ final Map<String, Object?> arguments =
+ getArgumentDictionary(methodCall);
+ final String prefix = arguments['prefix']! as String;
+ return testData.getAllWithPrefix(prefix);
+ }
if (methodCall.method == 'remove') {
final Map<String, Object?> arguments =
getArgumentDictionary(methodCall);
@@ -52,6 +80,12 @@
if (methodCall.method == 'clear') {
return testData.clear();
}
+ if (methodCall.method == 'clearWithPrefix') {
+ final Map<String, Object?> arguments =
+ getArgumentDictionary(methodCall);
+ final String prefix = arguments['prefix']! as String;
+ return testData.clearWithPrefix(prefix);
+ }
final RegExp setterRegExp = RegExp(r'set(.*)');
final Match? match = setterRegExp.matchAsPrefix(methodCall.method);
if (match?.groupCount == 1) {
@@ -73,13 +107,19 @@
});
test('getAll', () async {
- testData = InMemorySharedPreferencesStore.withData(kTestValues);
- expect(await store.getAll(), kTestValues);
+ testData = InMemorySharedPreferencesStore.withData(allTestValues);
+ expect(await store.getAll(), flutterTestValues);
expect(log.single.method, 'getAll');
});
+ test('getAllWithPrefix', () async {
+ testData = InMemorySharedPreferencesStore.withData(allTestValues);
+ expect(await store.getAllWithPrefix('prefix.'), prefixTestValues);
+ expect(log.single.method, 'getAllWithPrefix');
+ });
+
test('remove', () async {
- testData = InMemorySharedPreferencesStore.withData(kTestValues);
+ testData = InMemorySharedPreferencesStore.withData(allTestValues);
expect(await store.remove('flutter.String'), true);
expect(await store.remove('flutter.Bool'), true);
expect(await store.remove('flutter.Int'), true);
@@ -96,13 +136,13 @@
test('setValue', () async {
expect(await testData.getAll(), isEmpty);
- for (final String key in kTestValues.keys) {
- final Object value = kTestValues[key]!;
+ for (final String key in allTestValues.keys) {
+ final Object value = allTestValues[key]!;
expect(await store.setValue(key.split('.').last, key, value), true);
}
- expect(await testData.getAll(), kTestValues);
+ expect(await testData.getAll(), flutterTestValues);
- expect(log, hasLength(5));
+ expect(log, hasLength(15));
expect(log[0].method, 'setString');
expect(log[1].method, 'setBool');
expect(log[2].method, 'setInt');
@@ -111,12 +151,34 @@
});
test('clear', () async {
- testData = InMemorySharedPreferencesStore.withData(kTestValues);
+ testData = InMemorySharedPreferencesStore.withData(allTestValues);
expect(await testData.getAll(), isNotEmpty);
expect(await store.clear(), true);
expect(await testData.getAll(), isEmpty);
expect(log.single.method, 'clear');
});
+
+ test('clearWithPrefix', () async {
+ testData = InMemorySharedPreferencesStore.withData(allTestValues);
+
+ expect(await testData.getAllWithPrefix('prefix.'), isNotEmpty);
+ expect(await store.clearWithPrefix('prefix.'), true);
+ expect(await testData.getAllWithPrefix('prefix.'), isEmpty);
+ });
+
+ test('getAllWithNoPrefix', () async {
+ testData = InMemorySharedPreferencesStore.withData(allTestValues);
+
+ expect(await testData.getAllWithPrefix(''), hasLength(15));
+ });
+
+ test('clearWithNoPrefix', () async {
+ testData = InMemorySharedPreferencesStore.withData(allTestValues);
+
+ expect(await testData.getAllWithPrefix(''), isNotEmpty);
+ expect(await store.clearWithPrefix(''), true);
+ expect(await testData.getAllWithPrefix(''), isEmpty);
+ });
});
}
diff --git a/packages/shared_preferences/shared_preferences_platform_interface/test/shared_preferences_platform_interface_test.dart b/packages/shared_preferences/shared_preferences_platform_interface/test/shared_preferences_platform_interface_test.dart
index ed078e8..01b3754 100644
--- a/packages/shared_preferences/shared_preferences_platform_interface/test/shared_preferences_platform_interface_test.dart
+++ b/packages/shared_preferences/shared_preferences_platform_interface/test/shared_preferences_platform_interface_test.dart
@@ -47,11 +47,21 @@
}
@override
+ Future<bool> clearWithPrefix(String prefix) {
+ throw UnimplementedError();
+ }
+
+ @override
Future<Map<String, Object>> getAll() {
throw UnimplementedError();
}
@override
+ Future<Map<String, Object>> getAllWithPrefix(String prefix) {
+ throw UnimplementedError();
+ }
+
+ @override
Future<bool> remove(String key) {
throw UnimplementedError();
}
@@ -72,11 +82,21 @@
}
@override
+ Future<bool> clearWithPrefix(String prefix) {
+ throw UnimplementedError();
+ }
+
+ @override
Future<Map<String, Object>> getAll() {
throw UnimplementedError();
}
@override
+ Future<Map<String, Object>> getAllWithPrefix(String prefix) {
+ throw UnimplementedError();
+ }
+
+ @override
Future<bool> remove(String key) {
throw UnimplementedError();
}
@@ -99,11 +119,21 @@
}
@override
+ Future<bool> clearWithPrefix(String prefix) {
+ throw UnimplementedError();
+ }
+
+ @override
Future<Map<String, Object>> getAll() {
throw UnimplementedError();
}
@override
+ Future<Map<String, Object>> getAllWithPrefix(String prefix) {
+ throw UnimplementedError();
+ }
+
+ @override
Future<bool> remove(String key) {
throw UnimplementedError();
}