From 6ee23c926712bf5b7e417510623e2d2b9d5c0ef9 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Thu, 29 Jun 2023 13:59:57 -0700 Subject: [PATCH 1/4] just main package --- .../shared_preferences_test.dart | 41 ++++++++++++++--- .../lib/shared_preferences.dart | 34 ++++++++++++-- .../test/shared_preferences_test.dart | 46 ++++++++++++++++--- 3 files changed, 103 insertions(+), 18 deletions(-) diff --git a/packages/shared_preferences/shared_preferences/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences/example/integration_test/shared_preferences_test.dart index d047d1a596b4..443ac6fc4a79 100644 --- a/packages/shared_preferences/shared_preferences/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences/example/integration_test/shared_preferences_test.dart @@ -96,8 +96,8 @@ void main() { preferences = await SharedPreferences.getInstance(); }); - tearDown(() { - preferences.clear(); + tearDown(() async { + await preferences.clear(); SharedPreferences.resetStatic(); }); @@ -111,8 +111,8 @@ void main() { preferences = await SharedPreferences.getInstance(); }); - tearDown(() { - preferences.clear(); + tearDown(() async { + await preferences.clear(); SharedPreferences.resetStatic(); }); @@ -126,11 +126,40 @@ void main() { preferences = await SharedPreferences.getInstance(); }); - tearDown(() { - preferences.clear(); + tearDown(() async { + await preferences.clear(); SharedPreferences.resetStatic(); }); runAllTests(); }); + + testWidgets('allowList only gets allowed items', (WidgetTester _) async { + const String allowedString = 'stringKey'; + const String allowedBool = 'boolKey'; + const String notAllowedDouble = 'doubleKey'; + const String resultString = 'resultString'; + + const Set allowList = {allowedString, allowedBool}; + + SharedPreferences.resetStatic(); + SharedPreferences.setPrefix('', allowList: allowList); + + final SharedPreferences prefs = await SharedPreferences.getInstance(); + + await prefs.setString(allowedString, resultString); + await prefs.setBool(allowedBool, true); + await prefs.setDouble(notAllowedDouble, 3.14); + + await prefs.reload(); + + final String? testString = prefs.getString(allowedString); + expect(testString, resultString); + + final bool? testBool = prefs.getBool(allowedBool); + expect(testBool, true); + + final double? testDouble = prefs.getDouble(notAllowedDouble); + expect(testDouble, null); + }); } diff --git a/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart b/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart index e6d3c72efa80..6164f1bb2423 100644 --- a/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart +++ b/packages/shared_preferences/shared_preferences/lib/shared_preferences.dart @@ -6,6 +6,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart' show visibleForTesting; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; /// Wraps NSUserDefaults (on iOS) and SharedPreferences (on Android), providing /// a persistent store for simple data. @@ -18,6 +19,8 @@ class SharedPreferences { static bool _prefixHasBeenChanged = false; + static Set? _allowList; + static Completer? _completer; static SharedPreferencesStorePlatform get _store => @@ -33,17 +36,23 @@ class SharedPreferences { /// previous behavior of this plugin. To use preferences with no prefix, /// set [prefix] to ''. /// + /// If [prefix] is set to '', you may encounter preferences that are + /// incompatible with shared_preferences. The optional parameter + /// [allowList] will cause the plugin to only return preferences that + /// are both contained in the list AND match the provided prefix. + /// /// No migration of existing preferences is performed by this method. /// If you set a different prefix, and have previously stored preferences, /// you will need to handle any migration yourself. /// /// This cannot be called after `getInstance`. - static void setPrefix(String prefix) { + static void setPrefix(String prefix, {Set? allowList}) { if (_completer != null) { throw StateError('setPrefix cannot be called after getInstance'); } _prefix = prefix; _prefixHasBeenChanged = true; + _allowList = allowList; } /// Resets class's static values to allow for testing of setPrefix flow. @@ -52,6 +61,7 @@ class SharedPreferences { _completer = null; _prefix = 'flutter.'; _prefixHasBeenChanged = false; + _allowList = null; } /// Loads and parses the [SharedPreferences] for this app from disk. @@ -182,8 +192,14 @@ class SharedPreferences { _preferenceCache.clear(); if (_prefixHasBeenChanged) { try { - // ignore: deprecated_member_use - return _store.clearWithPrefix(_prefix); + return _store.clearWithParameters( + ClearParameters( + filter: PreferencesFilter( + prefix: _prefix, + allowList: _allowList, + ), + ), + ); } catch (e) { // Catching and clarifying UnimplementedError to provide a more robust message. if (e is UnimplementedError) { @@ -214,8 +230,16 @@ Either update the implementation to support setPrefix, or do not call setPrefix. final Map fromSystem = {}; if (_prefixHasBeenChanged) { try { - // ignore: deprecated_member_use - fromSystem.addAll(await _store.getAllWithPrefix(_prefix)); + fromSystem.addAll( + await _store.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter( + prefix: _prefix, + allowList: _allowList, + ), + ), + ), + ); } catch (e) { // Catching and clarifying UnimplementedError to provide a more robust message. if (e is UnimplementedError) { diff --git a/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart index 882bdba7d153..0cdfaea5e4b5 100755 --- a/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart @@ -6,6 +6,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -256,6 +257,30 @@ void main() { expect(testDouble, 3.14); }); + test('allowList only gets allowed items', () async { + const Set allowList = {'stringKey', 'boolKey'}; + + SharedPreferences.resetStatic(); + SharedPreferences.setPrefix('', allowList: allowList); + + final SharedPreferences prefs = await SharedPreferences.getInstance(); + + await prefs.setString('stringKey', 'test'); + await prefs.setBool('boolKey', true); + await prefs.setDouble('doubleKey', 3.14); + + await prefs.reload(); + + final String? testString = prefs.getString('stringKey'); + expect(testString, 'test'); + + final bool? testBool = prefs.getBool('boolKey'); + expect(testBool, true); + + final double? testDouble = prefs.getDouble('doubleKey'); + expect(testDouble, null); + }); + test('using reload after setPrefix properly reloads the cache', () async { const String newPrefix = 'newPrefix'; @@ -274,7 +299,7 @@ void main() { expect(testStrings, 'test'); }); - test('unimplemented errors in withPrefix methods are updated', () async { + test('unimplemented errors in withOptions methods are updated', () async { final UnimplementedSharedPreferencesStore localStore = UnimplementedSharedPreferencesStore(); SharedPreferencesStorePlatform.instance = localStore; @@ -294,7 +319,7 @@ void main() { "Shared Preferences doesn't yet support the setPrefix method")); }); - test('non-Unimplemented errors pass through withPrefix methods correctly', + test('non-Unimplemented errors pass through withOptions methods correctly', () async { final ThrowingSharedPreferencesStore localStore = ThrowingSharedPreferencesStore(); @@ -326,6 +351,12 @@ class FakeSharedPreferencesStore extends SharedPreferencesStorePlatform { return backend.clear(); } + @override + Future clearWithParameters(ClearParameters parameters) { + log.add(const MethodCall('clearWithParameters')); + return backend.clearWithParameters(parameters); + } + @override Future> getAll() { log.add(const MethodCall('getAll')); @@ -333,10 +364,10 @@ class FakeSharedPreferencesStore extends SharedPreferencesStorePlatform { } @override - Future> getAllWithPrefix(String prefix) { - log.add(const MethodCall('getAllWithPrefix')); - // ignore: deprecated_member_use - return backend.getAllWithPrefix(prefix); + Future> getAllWithParameters( + GetAllParameters parameters) { + log.add(const MethodCall('getAllWithParameters')); + return backend.getAllWithParameters(parameters); } @override @@ -406,7 +437,8 @@ class ThrowingSharedPreferencesStore extends SharedPreferencesStorePlatform { } @override - Future> getAllWithPrefix(String prefix) { + Future> getAllWithParameters( + GetAllParameters parameters) { throw StateError('State Error'); } } From 2a7e6e678e83da5e3cdd988d400ac5c9916eab9e Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Thu, 29 Jun 2023 14:01:56 -0700 Subject: [PATCH 2/4] changelog --- packages/shared_preferences/shared_preferences/CHANGELOG.md | 4 ++++ packages/shared_preferences/shared_preferences/pubspec.yaml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/shared_preferences/shared_preferences/CHANGELOG.md b/packages/shared_preferences/shared_preferences/CHANGELOG.md index af254c9aa780..aac9038c4163 100644 --- a/packages/shared_preferences/shared_preferences/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.2.0 + +* Adds `allowList` option to setPrefix. + ## 2.1.2 * Fixes singleton initialization race condition introduced during NNBD diff --git a/packages/shared_preferences/shared_preferences/pubspec.yaml b/packages/shared_preferences/shared_preferences/pubspec.yaml index 2771b99eea61..fb6224b98abe 100644 --- a/packages/shared_preferences/shared_preferences/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android. repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.2 +version: 2.2.0 environment: sdk: ">=2.18.0 <4.0.0" @@ -31,7 +31,7 @@ dependencies: shared_preferences_android: ^2.1.0 shared_preferences_foundation: ^2.2.0 shared_preferences_linux: ^2.2.0 - shared_preferences_platform_interface: ^2.2.0 + shared_preferences_platform_interface: ^2.3.0 shared_preferences_web: ^2.1.0 shared_preferences_windows: ^2.2.0 From 2213760f609a1af4b2ef325a125608e5e87bce6b Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Thu, 29 Jun 2023 14:07:15 -0700 Subject: [PATCH 3/4] rename tests --- .../shared_preferences/test/shared_preferences_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart index 0cdfaea5e4b5..f7f5ea355d1f 100755 --- a/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences/test/shared_preferences_test.dart @@ -299,7 +299,7 @@ void main() { expect(testStrings, 'test'); }); - test('unimplemented errors in withOptions methods are updated', () async { + test('unimplemented errors in withParameters methods are updated', () async { final UnimplementedSharedPreferencesStore localStore = UnimplementedSharedPreferencesStore(); SharedPreferencesStorePlatform.instance = localStore; @@ -319,7 +319,7 @@ void main() { "Shared Preferences doesn't yet support the setPrefix method")); }); - test('non-Unimplemented errors pass through withOptions methods correctly', + test('non-Unimplemented errors pass through withParameters methods correctly', () async { final ThrowingSharedPreferencesStore localStore = ThrowingSharedPreferencesStore(); From 9355b020a4391da8cb2d7dd54fdde10ba088c135 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Fri, 30 Jun 2023 11:47:23 -0700 Subject: [PATCH 4/4] README --- packages/shared_preferences/shared_preferences/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/shared_preferences/shared_preferences/README.md b/packages/shared_preferences/shared_preferences/README.md index 527db6e8fe3a..35769d56d3d8 100644 --- a/packages/shared_preferences/shared_preferences/README.md +++ b/packages/shared_preferences/shared_preferences/README.md @@ -100,6 +100,7 @@ by any non-flutter versions of the app (for migrating from a native app to flutt If the prefix is set to a value such as `''` that causes it to read values that were not originally stored by the `SharedPreferences`, initializing `SharedPreferences` may fail if any of the values are of types that are not supported by `SharedPreferences`. +In this case, you can set an `allowList` that contains only preferences of supported types. If you decide to remove the prefix entirely, you can still access previously created preferences by manually adding the previous prefix `flutter.` to the beginning of