From d61537115c2dcebfb1acf60ce0a1d62d066337b4 Mon Sep 17 00:00:00 2001 From: Tarrin Neal Date: Thu, 29 Jun 2023 11:05:13 -0700 Subject: [PATCH] [shared_preferences] Adds new clearWithParameters and getAllWithParameters methods to all platforms. (#4262) Adds new `clearWithParameters` and `getAllWithParameters` methods. part of https://github.com/flutter/flutter/issues/128948 precursor to https://github.com/flutter/packages/pull/3794 awaiting https://github.com/flutter/packages/pull/4261 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [relevant style guides] and ran the auto-formatter. (Unlike the flutter/flutter repo, the flutter/packages repo does use `dart format`.) - [x] I signed the [CLA]. - [x] The title of the PR starts with the name of the package surrounded by square brackets, e.g. `[shared_preferences]` - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated `pubspec.yaml` with an appropriate new version according to the [pub versioning philosophy], or this PR is [exempt from version changes]. - [x] I updated `CHANGELOG.md` to add a description of the change, [following repository CHANGELOG style]. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [relevant style guides]: https://github.com/flutter/packages/blob/main/CONTRIBUTING.md#style [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat [pub versioning philosophy]: https://dart.dev/tools/pub/versioning [exempt from version changes]: https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#version-and-changelog-updates [following repository CHANGELOG style]: https://github.com/flutter/flutter/wiki/Contributing-to-Plugins-and-Packages#changelog-style [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests --- .../shared_preferences_android/CHANGELOG.md | 3 +- .../plugins/sharedpreferences/Messages.java | 16 +- .../SharedPreferencesPlugin.java | 20 +- .../SharedPreferencesTest.java | 73 ++- .../shared_preferences_test.dart | 397 +++++++++---- .../example/pubspec.yaml | 2 +- .../lib/shared_preferences_android.dart | 40 +- .../lib/src/messages.g.dart | 19 +- .../pigeons/messages.dart | 10 +- .../shared_preferences_android/pubspec.yaml | 4 +- .../test/shared_preferences_android_test.dart | 143 ++++- .../CHANGELOG.md | 3 +- .../Classes/SharedPreferencesPlugin.swift | 18 +- .../darwin/Classes/messages.g.swift | 26 +- .../darwin/Tests/RunnerTests.swift | 70 ++- .../shared_preferences_test.dart | 533 ++++++++++++------ .../example/pubspec.yaml | 2 +- .../lib/messages.g.dart | 26 +- .../lib/shared_preferences_foundation.dart | 41 +- .../pigeons/messages.dart | 4 +- .../pubspec.yaml | 4 +- .../shared_preferences_foundation_test.dart | 147 ++++- .../test/test_api.g.dart | 28 +- .../shared_preferences_linux/CHANGELOG.md | 3 +- .../shared_preferences_test.dart | 386 ++++++++++--- .../example/pubspec.yaml | 1 + .../lib/shared_preferences_linux.dart | 35 +- .../shared_preferences_linux/pubspec.yaml | 4 +- .../test/shared_preferences_linux_test.dart | 98 ++++ .../shared_preferences_web/CHANGELOG.md | 3 +- .../shared_preferences_web_test.dart | 487 +++++++++------- .../example/pubspec.yaml | 2 +- .../lib/shared_preferences_web.dart | 43 +- .../shared_preferences_web/pubspec.yaml | 4 +- .../shared_preferences_windows/CHANGELOG.md | 3 +- .../shared_preferences_test.dart | 383 ++++++++++--- .../example/pubspec.yaml | 1 + .../lib/shared_preferences_windows.dart | 35 +- .../shared_preferences_windows/pubspec.yaml | 4 +- .../test/shared_preferences_windows_test.dart | 98 ++++ 40 files changed, 2436 insertions(+), 783 deletions(-) diff --git a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md index e5dd9bcd5c..37cd5dedcc 100644 --- a/packages/shared_preferences/shared_preferences_android/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_android/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.2.0 +* Adds `clearWithParameters` and `getAllWithParameters` methods. * Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. ## 2.1.4 diff --git a/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/Messages.java b/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/Messages.java index 20efb4404d..26fb7958fa 100644 --- a/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/Messages.java +++ b/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/Messages.java @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v9.2.4), do not edit directly. +// Autogenerated from Pigeon (v9.2.5), do not edit directly. // See also: https://pub.dev/packages/pigeon package io.flutter.plugins.sharedpreferences; @@ -75,10 +75,10 @@ public class Messages { Boolean setStringList(@NonNull String key, @NonNull List value); /** Removes all properties from shared preferences data set with matching prefix. */ @NonNull - Boolean clearWithPrefix(@NonNull String prefix); + Boolean clear(@NonNull String prefix, @Nullable List allowList); /** Gets all properties from shared preferences data set with matching prefix. */ @NonNull - Map getAllWithPrefix(@NonNull String prefix); + Map getAll(@NonNull String prefix, @Nullable List allowList); /** The codec used by SharedPreferencesApi. */ static @NonNull MessageCodec getCodec() { @@ -263,7 +263,7 @@ public class Messages { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.SharedPreferencesApi.clearWithPrefix", + "dev.flutter.pigeon.SharedPreferencesApi.clear", getCodec(), taskQueue); if (api != null) { @@ -272,8 +272,9 @@ public class Messages { ArrayList wrapped = new ArrayList(); ArrayList args = (ArrayList) message; String prefixArg = (String) args.get(0); + List allowListArg = (List) args.get(1); try { - Boolean output = api.clearWithPrefix(prefixArg); + Boolean output = api.clear(prefixArg, allowListArg); wrapped.add(0, output); } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); @@ -290,7 +291,7 @@ public class Messages { BasicMessageChannel channel = new BasicMessageChannel<>( binaryMessenger, - "dev.flutter.pigeon.SharedPreferencesApi.getAllWithPrefix", + "dev.flutter.pigeon.SharedPreferencesApi.getAll", getCodec(), taskQueue); if (api != null) { @@ -299,8 +300,9 @@ public class Messages { ArrayList wrapped = new ArrayList(); ArrayList args = (ArrayList) message; String prefixArg = (String) args.get(0); + List allowListArg = (List) args.get(1); try { - Map output = api.getAllWithPrefix(prefixArg); + Map output = api.getAll(prefixArg, allowListArg); wrapped.add(0, output); } catch (Throwable exception) { ArrayList wrappedError = wrapError(exception); diff --git a/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.java b/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.java index 6f59b720ff..aecc43991c 100644 --- a/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.java +++ b/packages/shared_preferences/shared_preferences_android/android/src/main/java/io/flutter/plugins/sharedpreferences/SharedPreferencesPlugin.java @@ -9,6 +9,7 @@ import android.content.SharedPreferences; import android.util.Base64; import android.util.Log; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import io.flutter.embedding.engine.plugins.FlutterPlugin; import io.flutter.plugin.common.BinaryMessenger; @@ -21,6 +22,7 @@ import java.io.ObjectOutputStream; import java.math.BigInteger; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -111,18 +113,20 @@ public class SharedPreferencesPlugin implements FlutterPlugin, SharedPreferences } @Override - public @NonNull Map getAllWithPrefix(@NonNull String prefix) - throws RuntimeException { - return getAllPrefs(prefix); + public @NonNull Map getAll( + @NonNull String prefix, @Nullable List allowList) throws RuntimeException { + final Set allowSet = allowList == null ? null : new HashSet<>(allowList); + return getAllPrefs(prefix, allowSet); } @Override - public @NonNull Boolean clearWithPrefix(@NonNull String prefix) throws RuntimeException { + public @NonNull Boolean clear(@NonNull String prefix, @Nullable List allowList) + throws RuntimeException { SharedPreferences.Editor clearEditor = preferences.edit(); Map allPrefs = preferences.getAll(); ArrayList filteredPrefs = new ArrayList<>(); for (String key : allPrefs.keySet()) { - if (key.startsWith(prefix)) { + if (key.startsWith(prefix) && (allowList == null || allowList.contains(key))) { filteredPrefs.add(key); } } @@ -133,12 +137,14 @@ public class SharedPreferencesPlugin implements FlutterPlugin, SharedPreferences } // Gets all shared preferences, filtered to only those set with the given prefix. + // Optionally filtered also to only those items in the optional [allowList]. @SuppressWarnings("unchecked") - private @NonNull Map getAllPrefs(@NonNull String prefix) throws RuntimeException { + private @NonNull Map getAllPrefs( + @NonNull String prefix, @Nullable Set allowList) throws RuntimeException { Map allPrefs = preferences.getAll(); Map filteredPrefs = new HashMap<>(); for (String key : allPrefs.keySet()) { - if (key.startsWith(prefix)) { + if (key.startsWith(prefix) && (allowList == null || allowList.contains(key))) { filteredPrefs.put(key, transformPref(key, allPrefs.get(key))); } } diff --git a/packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/SharedPreferencesTest.java b/packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/SharedPreferencesTest.java index e793ea4a71..19debfeff7 100644 --- a/packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/SharedPreferencesTest.java +++ b/packages/shared_preferences/shared_preferences_android/android/src/test/java/io/flutter/plugins/sharedpreferences/SharedPreferencesTest.java @@ -67,12 +67,12 @@ public class SharedPreferencesTest { } @Test - public void getAllWithPrefix() { - assertEquals(plugin.getAllWithPrefix("").size(), 0); + public void getAll() { + assertEquals(plugin.getAll("", null).size(), 0); addData(); - Map flutterData = plugin.getAllWithPrefix("flutter."); + Map flutterData = plugin.getAll("flutter.", null); assertEquals(flutterData.size(), 5); assertEquals(flutterData.get("flutter.Language"), "Java"); @@ -81,17 +81,43 @@ public class SharedPreferencesTest { assertEquals(flutterData.get("flutter.Names"), Arrays.asList("Flutter", "Dart")); assertEquals(flutterData.get("flutter.NewToFlutter"), false); - Map allData = plugin.getAllWithPrefix(""); + Map allData = plugin.getAll("", null); assertEquals(allData, data); } + @Test + public void allowList() { + assertEquals(plugin.getAll("", null).size(), 0); + + addData(); + + final List allowList = Arrays.asList("flutter.Language"); + + Map allData = plugin.getAll("flutter.", allowList); + + assertEquals(allData.size(), 1); + assertEquals(allData.get("flutter.Language"), "Java"); + assertEquals(allData.get("flutter.Counter"), null); + + allData = plugin.getAll("", allowList); + + assertEquals(allData.size(), 1); + assertEquals(allData.get("flutter.Language"), "Java"); + assertEquals(allData.get("flutter.Counter"), null); + + allData = plugin.getAll("prefix.", allowList); + + assertEquals(allData.size(), 0); + assertEquals(allData.get("flutter.Language"), null); + } + @Test public void setString() { final String key = "language"; final String value = "Java"; plugin.setString(key, value); - Map flutterData = plugin.getAllWithPrefix(""); + Map flutterData = plugin.getAll("", null); assertEquals(flutterData.get(key), value); } @@ -100,7 +126,7 @@ public class SharedPreferencesTest { final String key = "Counter"; final Long value = 0L; plugin.setInt(key, value); - Map flutterData = plugin.getAllWithPrefix(""); + Map flutterData = plugin.getAll("", null); assertEquals(flutterData.get(key), value); } @@ -109,7 +135,7 @@ public class SharedPreferencesTest { final String key = "Pie"; final double value = 3.14; plugin.setDouble(key, value); - Map flutterData = plugin.getAllWithPrefix(""); + Map flutterData = plugin.getAll("", null); assertEquals(flutterData.get(key), value); } @@ -118,7 +144,7 @@ public class SharedPreferencesTest { final String key = "Names"; final List value = Arrays.asList("Flutter", "Dart"); plugin.setStringList(key, value); - Map flutterData = plugin.getAllWithPrefix(""); + Map flutterData = plugin.getAll("", null); assertEquals(flutterData.get(key), value); } @@ -127,30 +153,41 @@ public class SharedPreferencesTest { final String key = "NewToFlutter"; final boolean value = false; plugin.setBool(key, value); - Map flutterData = plugin.getAllWithPrefix(""); + Map flutterData = plugin.getAll("", null); assertEquals(flutterData.get(key), value); } @Test - public void clearWithPrefix() { + public void clearWithNoAllowList() { addData(); - assertEquals(plugin.getAllWithPrefix("").size(), 15); + assertEquals(plugin.getAll("", null).size(), 15); - plugin.clearWithPrefix("flutter."); + plugin.clear("flutter.", null); - assertEquals(plugin.getAllWithPrefix("").size(), 10); + assertEquals(plugin.getAll("", null).size(), 10); + } + + @Test + public void clearWithAllowList() { + addData(); + + assertEquals(plugin.getAll("", null).size(), 15); + + plugin.clear("flutter.", Arrays.asList("flutter.Language")); + + assertEquals(plugin.getAll("", null).size(), 14); } @Test public void clearAll() { addData(); - assertEquals(plugin.getAllWithPrefix("").size(), 15); + assertEquals(plugin.getAll("", null).size(), 15); - plugin.clearWithPrefix(""); + plugin.clear("", null); - assertEquals(plugin.getAllWithPrefix("").size(), 0); + assertEquals(plugin.getAll("", null).size(), 0); } @Test @@ -158,9 +195,9 @@ public class SharedPreferencesTest { final String key = "NewToFlutter"; final boolean value = true; plugin.setBool(key, value); - assert (plugin.getAllWithPrefix("").containsKey(key)); + assert (plugin.getAll("", null).containsKey(key)); plugin.remove(key); - assertFalse(plugin.getAllWithPrefix("").containsKey(key)); + assertFalse(plugin.getAll("", null).containsKey(key)); } private void addData() { diff --git a/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart index a08363ab2f..2bb1613384 100644 --- a/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_android/example/integration_test/shared_preferences_test.dart @@ -6,6 +6,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -47,14 +48,20 @@ void main() { preferences = SharedPreferencesStorePlatform.instance; }); - tearDown(() { - // ignore: deprecated_member_use - preferences.clearWithPrefix(''); + tearDown(() async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); }); testWidgets('reading', (WidgetTester _) async { - // ignore: deprecated_member_use - final Map values = await preferences.getAllWithPrefix(''); + final Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); expect(values['String'], isNull); expect(values['Bool'], isNull); expect(values['Int'], isNull); @@ -62,6 +69,35 @@ void main() { expect(values['StringList'], isNull); }); + Future addData() async { + await preferences.setValue('String', 'String', allTestValues['String']!); + await preferences.setValue('Bool', 'Bool', allTestValues['Bool']!); + await preferences.setValue('Int', 'Int', allTestValues['Int']!); + await preferences.setValue('Double', 'Double', allTestValues['Double']!); + await preferences.setValue( + 'StringList', 'StringList', allTestValues['StringList']!); + await preferences.setValue( + 'String', 'prefix.String', allTestValues['prefix.String']!); + await preferences.setValue( + 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!); + await preferences.setValue( + 'Int', 'prefix.Int', allTestValues['prefix.Int']!); + await preferences.setValue( + 'Double', 'prefix.Double', allTestValues['prefix.Double']!); + await preferences.setValue('StringList', 'prefix.StringList', + allTestValues['prefix.StringList']!); + await preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!); + await preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!); + await preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!); + await preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!); + await preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!); + } + testWidgets('getAllWithPrefix', (WidgetTester _) async { await Future.wait(>[ preferences.setValue( @@ -94,68 +130,160 @@ void main() { expect(values['prefix.StringList'], allTestValues['prefix.StringList']); }); - testWidgets('clearWithPrefix', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue( - 'String', 'prefix.String', allTestValues['prefix.String']!), - preferences.setValue( - 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!), - preferences.setValue('Int', 'prefix.Int', allTestValues['prefix.Int']!), - preferences.setValue( - 'Double', 'prefix.Double', allTestValues['prefix.Double']!), - preferences.setValue('StringList', 'prefix.StringList', - allTestValues['prefix.StringList']!), - preferences.setValue( - 'String', 'flutter.String', allTestValues['flutter.String']!), - preferences.setValue( - 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), - preferences.setValue( - 'Int', 'flutter.Int', allTestValues['flutter.Int']!), - preferences.setValue( - 'Double', 'flutter.Double', allTestValues['flutter.Double']!), - preferences.setValue('StringList', 'flutter.StringList', - allTestValues['flutter.StringList']!) - ]); - // ignore: deprecated_member_use - await preferences.clearWithPrefix('prefix.'); - Map values = - // ignore: deprecated_member_use - await preferences.getAllWithPrefix('prefix.'); - expect(values['prefix.String'], null); + group('withPrefix', () { + testWidgets('clearWithPrefix', (WidgetTester _) async { + await Future.wait(>[ + preferences.setValue( + 'String', 'prefix.String', allTestValues['prefix.String']!), + preferences.setValue( + 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!), + preferences.setValue( + 'Int', 'prefix.Int', allTestValues['prefix.Int']!), + preferences.setValue( + 'Double', 'prefix.Double', allTestValues['prefix.Double']!), + preferences.setValue('StringList', 'prefix.StringList', + allTestValues['prefix.StringList']!), + preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!), + preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), + preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!), + preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!), + preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!) + ]); + // ignore: deprecated_member_use + await preferences.clearWithPrefix('prefix.'); + Map values = + // ignore: deprecated_member_use + await preferences.getAllWithPrefix('prefix.'); + expect(values['prefix.String'], null); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + // ignore: deprecated_member_use + values = await preferences.getAllWithPrefix('flutter.'); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('getAllWithNoPrefix', (WidgetTester _) async { + await Future.wait(>[ + preferences.setValue('String', 'String', allTestValues['String']!), + preferences.setValue('Bool', 'Bool', allTestValues['Bool']!), + preferences.setValue('Int', 'Int', allTestValues['Int']!), + preferences.setValue('Double', 'Double', allTestValues['Double']!), + preferences.setValue( + 'StringList', 'StringList', allTestValues['StringList']!), + preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!), + preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), + preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!), + preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!), + preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!) + ]); + final Map values = + // ignore: deprecated_member_use + await preferences.getAllWithPrefix(''); + expect(values['String'], allTestValues['String']); + expect(values['Bool'], allTestValues['Bool']); + expect(values['Int'], allTestValues['Int']); + expect(values['Double'], allTestValues['Double']); + expect(values['StringList'], allTestValues['StringList']); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithNoPrefix', (WidgetTester _) async { + await Future.wait(>[ + preferences.setValue('String', 'String', allTestValues['String']!), + preferences.setValue('Bool', 'Bool', allTestValues['Bool']!), + preferences.setValue('Int', 'Int', allTestValues['Int']!), + preferences.setValue('Double', 'Double', allTestValues['Double']!), + preferences.setValue( + 'StringList', 'StringList', allTestValues['StringList']!), + preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!), + preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), + preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!), + preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!), + preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!) + ]); + // ignore: deprecated_member_use + await preferences.clearWithPrefix(''); + final Map values = + // ignore: deprecated_member_use + await preferences.getAllWithPrefix(''); + expect(values['String'], null); + expect(values['Bool'], null); + expect(values['Int'], null); + expect(values['Double'], null); + expect(values['StringList'], null); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); + }); + + testWidgets('get all with prefix', (WidgetTester _) async { + await addData(); + final Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + }); + + testWidgets('get all with allow list', (WidgetTester _) async { + await addData(); + final Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.String'}, + ), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); expect(values['prefix.Bool'], null); expect(values['prefix.Int'], null); expect(values['prefix.Double'], null); expect(values['prefix.StringList'], null); - // ignore: deprecated_member_use - values = await preferences.getAllWithPrefix('flutter.'); - expect(values['flutter.String'], allTestValues['flutter.String']); - expect(values['flutter.Bool'], allTestValues['flutter.Bool']); - expect(values['flutter.Int'], allTestValues['flutter.Int']); - expect(values['flutter.Double'], allTestValues['flutter.Double']); - expect(values['flutter.StringList'], allTestValues['flutter.StringList']); }); testWidgets('getAllWithNoPrefix', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue('String', 'String', allTestValues['String']!), - preferences.setValue('Bool', 'Bool', allTestValues['Bool']!), - preferences.setValue('Int', 'Int', allTestValues['Int']!), - preferences.setValue('Double', 'Double', allTestValues['Double']!), - preferences.setValue( - 'StringList', 'StringList', allTestValues['StringList']!), - preferences.setValue( - 'String', 'flutter.String', allTestValues['flutter.String']!), - preferences.setValue( - 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), - preferences.setValue( - 'Int', 'flutter.Int', allTestValues['flutter.Int']!), - preferences.setValue( - 'Double', 'flutter.Double', allTestValues['flutter.Double']!), - preferences.setValue('StringList', 'flutter.StringList', - allTestValues['flutter.StringList']!) - ]); - // ignore: deprecated_member_use - final Map values = await preferences.getAllWithPrefix(''); + await addData(); + final Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); expect(values['String'], allTestValues['String']); expect(values['Bool'], allTestValues['Bool']); expect(values['Int'], allTestValues['Int']); @@ -168,29 +296,84 @@ void main() { expect(values['flutter.StringList'], allTestValues['flutter.StringList']); }); + testWidgets('clearWithParameters', (WidgetTester _) async { + await addData(); + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], null); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'flutter.'), + ), + ); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect(values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithParameters with allow list', (WidgetTester _) async { + await addData(); + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: { + 'prefix.Double', + 'prefix.Int', + 'prefix.Bool', + 'prefix.String', + }, + ), + ), + ); + Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], null); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'flutter.'), + ), + ); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect(values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + testWidgets('clearWithNoPrefix', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue('String', 'String', allTestValues['String']!), - preferences.setValue('Bool', 'Bool', allTestValues['Bool']!), - preferences.setValue('Int', 'Int', allTestValues['Int']!), - preferences.setValue('Double', 'Double', allTestValues['Double']!), - preferences.setValue( - 'StringList', 'StringList', allTestValues['StringList']!), - preferences.setValue( - 'String', 'flutter.String', allTestValues['flutter.String']!), - preferences.setValue( - 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), - preferences.setValue( - 'Int', 'flutter.Int', allTestValues['flutter.Int']!), - preferences.setValue( - 'Double', 'flutter.Double', allTestValues['flutter.Double']!), - preferences.setValue('StringList', 'flutter.StringList', - allTestValues['flutter.StringList']!) - ]); - // ignore: deprecated_member_use - await preferences.clearWithPrefix(''); - // ignore: deprecated_member_use - final Map values = await preferences.getAllWithPrefix(''); + await addData(); + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + final Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); expect(values['String'], null); expect(values['Bool'], null); expect(values['Int'], null); @@ -204,18 +387,16 @@ void main() { }); testWidgets('getAll', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue( - 'String', 'flutter.String', allTestValues['flutter.String']!), - preferences.setValue( - 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), - preferences.setValue( - 'Int', 'flutter.Int', allTestValues['flutter.Int']!), - preferences.setValue( - 'Double', 'flutter.Double', allTestValues['flutter.Double']!), - preferences.setValue('StringList', 'flutter.StringList', - allTestValues['flutter.StringList']!) - ]); + await preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!); + await preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!); + await preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!); + await preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!); + await preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!); final Map values = await preferences.getAll(); expect(values['flutter.String'], allTestValues['flutter.String']); expect(values['flutter.Bool'], allTestValues['flutter.Bool']); @@ -235,8 +416,11 @@ void main() { await preferences.setValue( 'StringList', key, allTestValues['flutter.StringList']!); await preferences.remove(key); - // ignore: deprecated_member_use - final Map values = await preferences.getAllWithPrefix(''); + final Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); expect(values[key], isNull); }); @@ -270,8 +454,11 @@ void main() { // All writes should succeed. expect(result.where((bool element) => !element), isEmpty); // The last write should win. - // ignore: deprecated_member_use - final Map values = await preferences.getAllWithPrefix(''); + final Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); expect(values['Int'], writeCount); }); @@ -279,8 +466,11 @@ void main() { (WidgetTester _) async { const String key = 'akey'; const String value = 'a string value'; - // ignore: deprecated_member_use - await preferences.clearWithPrefix(''); + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); // Special prefixes used to store datatypes that can't be stored directly // in SharedPreferences as strings instead. @@ -296,8 +486,11 @@ void main() { expect(preferences.setValue('String', key, prefix + value), throwsA(isA())); final Map values = - // ignore: deprecated_member_use - await preferences.getAllWithPrefix(''); + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); expect(values[key], null); } }); diff --git a/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml index 3445345063..49065b1824 100644 --- a/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/example/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - shared_preferences_platform_interface: ^2.0.0 + shared_preferences_platform_interface: ^2.3.0 dev_dependencies: flutter_driver: diff --git a/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart b/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart index 9c26a03f53..352d36484e 100644 --- a/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart +++ b/packages/shared_preferences/shared_preferences_android/lib/shared_preferences_android.dart @@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; import 'src/messages.g.dart'; @@ -52,23 +53,50 @@ class SharedPreferencesAndroid extends SharedPreferencesStorePlatform { } @override - Future clear() { - return clearWithPrefix(_defaultPrefix); + Future clear() async { + return clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); } @override Future clearWithPrefix(String prefix) async { - return _api.clearWithPrefix(prefix); + return clearWithParameters( + ClearParameters(filter: PreferencesFilter(prefix: prefix))); } @override - Future> getAll() { - return getAllWithPrefix(_defaultPrefix); + Future clearWithParameters(ClearParameters parameters) async { + final PreferencesFilter filter = parameters.filter; + return _api.clear( + filter.prefix, + filter.allowList?.toList(), + ); + } + + @override + Future> getAll() async { + return getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); } @override Future> getAllWithPrefix(String prefix) async { - final Map data = await _api.getAllWithPrefix(prefix); + return getAllWithParameters( + GetAllParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future> getAllWithParameters( + GetAllParameters parameters) async { + final PreferencesFilter filter = parameters.filter; + final Map data = + await _api.getAll(filter.prefix, filter.allowList?.toList()); return data.cast(); } } diff --git a/packages/shared_preferences/shared_preferences_android/lib/src/messages.g.dart b/packages/shared_preferences/shared_preferences_android/lib/src/messages.g.dart index 4bb59d6062..1e84309897 100644 --- a/packages/shared_preferences/shared_preferences_android/lib/src/messages.g.dart +++ b/packages/shared_preferences/shared_preferences_android/lib/src/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v9.2.4), do not edit directly. +// Autogenerated from Pigeon (v9.2.5), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import @@ -190,12 +190,12 @@ class SharedPreferencesApi { } /// Removes all properties from shared preferences data set with matching prefix. - Future clearWithPrefix(String arg_prefix) async { + Future clear(String arg_prefix, List? arg_allowList) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SharedPreferencesApi.clearWithPrefix', codec, + 'dev.flutter.pigeon.SharedPreferencesApi.clear', codec, binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_prefix]) as List?; + final List? replyList = await channel + .send([arg_prefix, arg_allowList]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -218,12 +218,13 @@ class SharedPreferencesApi { } /// Gets all properties from shared preferences data set with matching prefix. - Future> getAllWithPrefix(String arg_prefix) async { + Future> getAll( + String arg_prefix, List? arg_allowList) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.SharedPreferencesApi.getAllWithPrefix', codec, + 'dev.flutter.pigeon.SharedPreferencesApi.getAll', codec, binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_prefix]) as List?; + final List? replyList = await channel + .send([arg_prefix, arg_allowList]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', diff --git a/packages/shared_preferences/shared_preferences_android/pigeons/messages.dart b/packages/shared_preferences/shared_preferences_android/pigeons/messages.dart index 6bff14700a..e359c32668 100644 --- a/packages/shared_preferences/shared_preferences_android/pigeons/messages.dart +++ b/packages/shared_preferences/shared_preferences_android/pigeons/messages.dart @@ -41,9 +41,15 @@ abstract class SharedPreferencesApi { /// Removes all properties from shared preferences data set with matching prefix. @TaskQueue(type: TaskQueueType.serialBackgroundThread) - bool clearWithPrefix(String prefix); + bool clear( + String prefix, + List? allowList, + ); /// Gets all properties from shared preferences data set with matching prefix. @TaskQueue(type: TaskQueueType.serialBackgroundThread) - Map getAllWithPrefix(String prefix); + Map getAll( + String prefix, + List? allowList, + ); } diff --git a/packages/shared_preferences/shared_preferences_android/pubspec.yaml b/packages/shared_preferences/shared_preferences_android/pubspec.yaml index 36fc4f41d8..895b2b69be 100644 --- a/packages/shared_preferences/shared_preferences_android/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_android/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_android description: Android implementation of the shared_preferences plugin repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.4 +version: 2.2.0 environment: sdk: ">=2.18.0 <4.0.0" @@ -20,7 +20,7 @@ flutter: dependencies: flutter: sdk: flutter - shared_preferences_platform_interface: ^2.2.0 + shared_preferences_platform_interface: ^2.3.0 dev_dependencies: flutter_test: diff --git a/packages/shared_preferences/shared_preferences_android/test/shared_preferences_android_test.dart b/packages/shared_preferences/shared_preferences_android/test/shared_preferences_android_test.dart index 2c50304627..421ccfe7ea 100644 --- a/packages/shared_preferences/shared_preferences_android/test/shared_preferences_android_test.dart +++ b/packages/shared_preferences/shared_preferences_android/test/shared_preferences_android_test.dart @@ -7,6 +7,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences_android/shared_preferences_android.dart'; import 'package:shared_preferences_android/src/messages.g.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); @@ -80,6 +81,61 @@ void main() { expect(all.length, 0); }); + test('clearWithParameters', () async { + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + + Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 5); + await plugin.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + all = await plugin.getAll(); + expect(all.length, 5); + all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 0); + }); + + test('clearWithParameters with allow list', () async { + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + + Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 5); + await plugin.clearWithParameters( + ClearParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.StringList'}, + ), + ), + ); + all = await plugin.getAll(); + expect(all.length, 5); + all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 4); + }); + test('getAll', () async { for (final String key in flutterTestValues.keys) { api.items[key] = flutterTestValues[key]!; @@ -89,15 +145,56 @@ void main() { expect(all, flutterTestValues); }); - test('getAllWithPrefix', () async { + test('getAllWithNoPrefix', () async { for (final String key in allTestValues.keys) { api.items[key] = allTestValues[key]!; } - final Map all = await plugin.getAllWithPrefix('prefix.'); + final Map all = await plugin.getAllWithPrefix(''); + expect(all.length, 15); + expect(all, allTestValues); + }); + + test('clearWithNoPrefix', () async { + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + + Map all = await plugin.getAllWithPrefix(''); + expect(all.length, 15); + await plugin.clearWithPrefix(''); + all = await plugin.getAllWithPrefix(''); + expect(all.length, 0); + }); + + test('getAllWithParameters', () async { + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + final Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); expect(all.length, 5); expect(all, prefixTestValues); }); + test('getAllWithParameters with allow list', () async { + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + final Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.Bool'}, + ), + ), + ); + expect(all.length, 1); + expect(all['prefix.Bool'], true); + }); + test('setValue', () async { expect(await plugin.setValue('Bool', 'flutter.Bool', true), isTrue); expect(api.items['flutter.Bool'], true); @@ -124,7 +221,11 @@ void main() { for (final String key in allTestValues.keys) { api.items[key] = allTestValues[key]!; } - final Map all = await plugin.getAllWithPrefix(''); + final Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); expect(all.length, 15); expect(all, allTestValues); }); @@ -134,10 +235,22 @@ void main() { api.items[key] = allTestValues[key]!; } - Map all = await plugin.getAllWithPrefix(''); + Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); expect(all.length, 15); - await plugin.clearWithPrefix(''); - all = await plugin.getAllWithPrefix(''); + await plugin.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); expect(all.length, 0); }); } @@ -146,10 +259,19 @@ class _FakeSharedPreferencesApi implements SharedPreferencesApi { final Map items = {}; @override - Future> getAllWithPrefix(String prefix) async { + Future> getAll( + String prefix, + List? allowList, + ) async { + Set? allowSet; + if (allowList != null) { + allowSet = Set.from(allowList); + } return { for (final String key in items.keys) - if (key.startsWith(prefix)) key: items[key] + if (key.startsWith(prefix) && + (allowSet == null || allowSet.contains(key))) + key: items[key] }; } @@ -172,9 +294,10 @@ class _FakeSharedPreferencesApi implements SharedPreferencesApi { } @override - Future clearWithPrefix(String prefix) async { + Future clear(String prefix, List? allowList) async { items.keys.toList().forEach((String key) { - if (key.startsWith(prefix)) { + if (key.startsWith(prefix) && + (allowList == null || allowList.contains(key))) { items.remove(key); } }); diff --git a/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md b/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md index 6d7777da90..d68c11162d 100644 --- a/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_foundation/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.3.0 +* Adds `clearWithParameters` and `getAllWithParameters` methods. * Updates minimum supported macOS version to 10.14. * Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/Classes/SharedPreferencesPlugin.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/Classes/SharedPreferencesPlugin.swift index b334f68d83..bb91ca0bd5 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/Classes/SharedPreferencesPlugin.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/Classes/SharedPreferencesPlugin.swift @@ -22,8 +22,8 @@ public class SharedPreferencesPlugin: NSObject, FlutterPlugin, UserDefaultsApi { UserDefaultsApiSetup.setUp(binaryMessenger: messenger, api: instance) } - func getAllWithPrefix(prefix: String) -> [String? : Any?] { - return getAllPrefs(prefix: prefix) + func getAll(prefix: String, allowList: [String]?) -> [String? : Any?] { + return getAllPrefs(prefix: prefix, allowList: allowList) } func setBool(key: String, value: Bool) { @@ -42,20 +42,26 @@ public class SharedPreferencesPlugin: NSObject, FlutterPlugin, UserDefaultsApi { UserDefaults.standard.removeObject(forKey: key) } - func clearWithPrefix(prefix: String) { + func clear(prefix: String, allowList: [String]?) -> Bool { let defaults = UserDefaults.standard - for (key, _) in getAllPrefs(prefix: prefix) { + for (key, _) in getAllPrefs(prefix: prefix, allowList: allowList) { defaults.removeObject(forKey: key) } + return true } /// Returns all preferences stored with specified prefix. - func getAllPrefs(prefix: String) -> [String: Any] { + /// If [allowList] is included, only items included will be returned. + func getAllPrefs(prefix: String, allowList: [String]?) -> [String: Any] { var filteredPrefs: [String: Any] = [:] + var allowSet: Set?; + if let allowList { + allowSet = Set(allowList) + } if let appDomain = Bundle.main.bundleIdentifier, let prefs = UserDefaults.standard.persistentDomain(forName: appDomain) { - for (key, value) in prefs where key.hasPrefix(prefix) { + for (key, value) in prefs where (key.hasPrefix(prefix) && (allowSet == nil || allowSet!.contains(key))) { filteredPrefs[key] = value } } diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/Classes/messages.g.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/Classes/messages.g.swift index 9795c520d1..723fbe2639 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/Classes/messages.g.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/Classes/messages.g.swift @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v9.2.4), do not edit directly. +// Autogenerated from Pigeon (v9.2.5), do not edit directly. // See also: https://pub.dev/packages/pigeon import Foundation @@ -42,8 +42,8 @@ protocol UserDefaultsApi { func setBool(key: String, value: Bool) throws func setDouble(key: String, value: Double) throws func setValue(key: String, value: Any) throws - func getAllWithPrefix(prefix: String) throws -> [String?: Any?] - func clearWithPrefix(prefix: String) throws + func getAll(prefix: String, allowList: [String]?) throws -> [String?: Any?] + func clear(prefix: String, allowList: [String]?) throws -> Bool } /// Generated setup class from Pigeon to handle messages through the `binaryMessenger`. @@ -114,35 +114,37 @@ class UserDefaultsApiSetup { } else { setValueChannel.setMessageHandler(nil) } - let getAllWithPrefixChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.UserDefaultsApi.getAllWithPrefix", binaryMessenger: binaryMessenger) + let getAllChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.UserDefaultsApi.getAll", binaryMessenger: binaryMessenger) if let api = api { - getAllWithPrefixChannel.setMessageHandler { message, reply in + getAllChannel.setMessageHandler { message, reply in let args = message as! [Any] let prefixArg = args[0] as! String + let allowListArg: [String]? = nilOrValue(args[1]) do { - let result = try api.getAllWithPrefix(prefix: prefixArg) + let result = try api.getAll(prefix: prefixArg, allowList: allowListArg) reply(wrapResult(result)) } catch { reply(wrapError(error)) } } } else { - getAllWithPrefixChannel.setMessageHandler(nil) + getAllChannel.setMessageHandler(nil) } - let clearWithPrefixChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.UserDefaultsApi.clearWithPrefix", binaryMessenger: binaryMessenger) + let clearChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.UserDefaultsApi.clear", binaryMessenger: binaryMessenger) if let api = api { - clearWithPrefixChannel.setMessageHandler { message, reply in + clearChannel.setMessageHandler { message, reply in let args = message as! [Any] let prefixArg = args[0] as! String + let allowListArg: [String]? = nilOrValue(args[1]) do { - try api.clearWithPrefix(prefix: prefixArg) - reply(wrapResult(nil)) + let result = try api.clear(prefix: prefixArg, allowList: allowListArg) + reply(wrapResult(result)) } catch { reply(wrapError(error)) } } } else { - clearWithPrefixChannel.setMessageHandler(nil) + clearChannel.setMessageHandler(nil) } } } diff --git a/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift b/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift index c33b99014a..64dd646ef8 100644 --- a/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift +++ b/packages/shared_preferences/shared_preferences_foundation/darwin/Tests/RunnerTests.swift @@ -25,7 +25,7 @@ class RunnerTests: XCTestCase { plugin.setValue(key: "\(aPrefix)aString", value: "hello world") plugin.setValue(key: "\(aPrefix)aStringList", value: ["hello", "world"]) - let storedValues = plugin.getAllWithPrefix(prefix: aPrefix) + let storedValues = plugin.getAll(prefix: aPrefix, allowList: nil) XCTAssertEqual(storedValues["\(aPrefix)aBool"] as? Bool, true) XCTAssertEqual(storedValues["\(aPrefix)aDouble"] as! Double, 3.14, accuracy: 0.0001) XCTAssertEqual(storedValues["\(aPrefix)anInt"] as? Int, 42) @@ -34,6 +34,25 @@ class RunnerTests: XCTestCase { } } + func testGetWithAllowList() throws { + for aPrefix in prefixes { + let plugin = SharedPreferencesPlugin() + + plugin.setBool(key: "\(aPrefix)aBool", value: true) + plugin.setDouble(key: "\(aPrefix)aDouble", value: 3.14) + plugin.setValue(key: "\(aPrefix)anInt", value: 42) + plugin.setValue(key: "\(aPrefix)aString", value: "hello world") + plugin.setValue(key: "\(aPrefix)aStringList", value: ["hello", "world"]) + + let storedValues = plugin.getAll(prefix: aPrefix, allowList: ["\(aPrefix)aBool"]) + XCTAssertEqual(storedValues["\(aPrefix)aBool"] as? Bool, true) + XCTAssertNil(storedValues["\(aPrefix)aDouble"] ?? nil) + XCTAssertNil(storedValues["\(aPrefix)anInt"] ?? nil) + XCTAssertNil(storedValues["\(aPrefix)aString"] ?? nil) + XCTAssertNil(storedValues["\(aPrefix)aStringList"] ?? nil) + } + } + func testRemove() throws { for aPrefix in prefixes { let plugin = SharedPreferencesPlugin() @@ -41,33 +60,50 @@ class RunnerTests: XCTestCase { plugin.setValue(key: testKey, value: 42) // Make sure there is something to remove, so the test can't pass due to a set failure. - let preRemovalValues = plugin.getAllWithPrefix(prefix: aPrefix) + let preRemovalValues = plugin.getAll(prefix: aPrefix, allowList: nil) XCTAssertEqual(preRemovalValues[testKey] as? Int, 42) // Then verify that removing it works. plugin.remove(key: testKey) - let finalValues = plugin.getAllWithPrefix(prefix: aPrefix) + let finalValues = plugin.getAll(prefix: aPrefix, allowList: nil) XCTAssertNil(finalValues[testKey] as Any?) } } + + func testClearWithNoAllowlist() throws { + for aPrefix in prefixes { + let plugin = SharedPreferencesPlugin() + let testKey = "\(aPrefix)foo" + plugin.setValue(key: testKey, value: 42) - func testClear() throws { - for aPrefix in prefixes { - let plugin = SharedPreferencesPlugin() - let testKey = "\(aPrefix)foo" - plugin.setValue(key: testKey, value: 42) + // Make sure there is something to clear, so the test can't pass due to a set failure. + let preRemovalValues = plugin.getAll(prefix: aPrefix, allowList: nil) + XCTAssertEqual(preRemovalValues[testKey] as? Int, 42) - // Make sure there is something to clear, so the test can't pass due to a set failure. - let preRemovalValues = plugin.getAllWithPrefix(prefix: aPrefix) - XCTAssertEqual(preRemovalValues[testKey] as? Int, 42) + // Then verify that clearing works. + plugin.clear(prefix: aPrefix, allowList: nil) - // Then verify that clearing works. - plugin.clearWithPrefix(prefix: aPrefix) - - let finalValues = plugin.getAllWithPrefix(prefix: aPrefix) - XCTAssertNil(finalValues[testKey] as Any?) + let finalValues = plugin.getAll(prefix: aPrefix, allowList: nil) + XCTAssertNil(finalValues[testKey] as Any?) + } + } + + func testClearWithAllowlist() throws { + for aPrefix in prefixes { + let plugin = SharedPreferencesPlugin() + let testKey = "\(aPrefix)foo" + plugin.setValue(key: testKey, value: 42) + + // Make sure there is something to clear, so the test can't pass due to a set failure. + let preRemovalValues = plugin.getAll(prefix: aPrefix, allowList: nil) + XCTAssertEqual(preRemovalValues[testKey] as? Int, 42) + + plugin.clear(prefix: aPrefix, allowList: ["\(aPrefix)notfoo"]) + + let finalValues = plugin.getAll(prefix: aPrefix, allowList: nil) + XCTAssertEqual(finalValues[testKey] as? Int, 42) + } } - } } diff --git a/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart index d19fedaaa3..49892ec18a 100644 --- a/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_foundation/example/integration_test/shared_preferences_test.dart @@ -5,6 +5,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -46,175 +47,375 @@ void main() { preferences = SharedPreferencesStorePlatform.instance; }); - tearDown(() { - // ignore: deprecated_member_use - preferences.clearWithPrefix(''); + tearDown(() async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); }); - testWidgets('reading', (WidgetTester _) async { - // ignore: deprecated_member_use - final Map values = await preferences.getAllWithPrefix(''); - expect(values['String'], isNull); - expect(values['Bool'], isNull); - expect(values['Int'], isNull); - expect(values['Double'], isNull); - expect(values['StringList'], isNull); + group('withPrefix', () { + testWidgets('reading', (WidgetTester _) async { + final Map values = + // ignore: deprecated_member_use + await preferences.getAllWithPrefix(''); + expect(values['String'], isNull); + expect(values['Bool'], isNull); + expect(values['Int'], isNull); + expect(values['Double'], isNull); + expect(values['StringList'], isNull); + }); + + testWidgets('getAllWithPrefix', (WidgetTester _) async { + await Future.wait(>[ + preferences.setValue( + 'String', 'prefix.String', allTestValues['prefix.String']!), + preferences.setValue( + 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!), + preferences.setValue( + 'Int', 'prefix.Int', allTestValues['prefix.Int']!), + preferences.setValue( + 'Double', 'prefix.Double', allTestValues['prefix.Double']!), + preferences.setValue('StringList', 'prefix.StringList', + allTestValues['prefix.StringList']!), + preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!), + preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), + preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!), + preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!), + preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!) + ]); + final Map values = + // ignore: deprecated_member_use + await preferences.getAllWithPrefix('prefix.'); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + }); + + testWidgets('clearWithPrefix', (WidgetTester _) async { + await Future.wait(>[ + preferences.setValue( + 'String', 'prefix.String', allTestValues['prefix.String']!), + preferences.setValue( + 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!), + preferences.setValue( + 'Int', 'prefix.Int', allTestValues['prefix.Int']!), + preferences.setValue( + 'Double', 'prefix.Double', allTestValues['prefix.Double']!), + preferences.setValue('StringList', 'prefix.StringList', + allTestValues['prefix.StringList']!), + preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!), + preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), + preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!), + preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!), + preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!) + ]); + // ignore: deprecated_member_use + await preferences.clearWithPrefix('prefix.'); + Map values = + // ignore: deprecated_member_use + await preferences.getAllWithPrefix('prefix.'); + expect(values['prefix.String'], null); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + // ignore: deprecated_member_use + values = await preferences.getAllWithPrefix('flutter.'); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('getAllWithNoPrefix', (WidgetTester _) async { + await Future.wait(>[ + preferences.setValue('String', 'String', allTestValues['String']!), + preferences.setValue('Bool', 'Bool', allTestValues['Bool']!), + preferences.setValue('Int', 'Int', allTestValues['Int']!), + preferences.setValue('Double', 'Double', allTestValues['Double']!), + preferences.setValue( + 'StringList', 'StringList', allTestValues['StringList']!), + preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!), + preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), + preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!), + preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!), + preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!) + ]); + final Map values = + // ignore: deprecated_member_use + await preferences.getAllWithPrefix(''); + expect(values['String'], allTestValues['String']); + expect(values['Bool'], allTestValues['Bool']); + expect(values['Int'], allTestValues['Int']); + expect(values['Double'], allTestValues['Double']); + expect(values['StringList'], allTestValues['StringList']); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithNoPrefix', (WidgetTester _) async { + await Future.wait(>[ + preferences.setValue('String', 'String', allTestValues['String']!), + preferences.setValue('Bool', 'Bool', allTestValues['Bool']!), + preferences.setValue('Int', 'Int', allTestValues['Int']!), + preferences.setValue('Double', 'Double', allTestValues['Double']!), + preferences.setValue( + 'StringList', 'StringList', allTestValues['StringList']!), + preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!), + preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), + preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!), + preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!), + preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!) + ]); + // ignore: deprecated_member_use + await preferences.clearWithPrefix(''); + final Map values = + // ignore: deprecated_member_use + await preferences.getAllWithPrefix(''); + expect(values['String'], null); + expect(values['Bool'], null); + expect(values['Int'], null); + expect(values['Double'], null); + expect(values['StringList'], null); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); }); - testWidgets('getAllWithPrefix', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue( - 'String', 'prefix.String', allTestValues['prefix.String']!), - preferences.setValue( - 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!), - preferences.setValue('Int', 'prefix.Int', allTestValues['prefix.Int']!), - preferences.setValue( - 'Double', 'prefix.Double', allTestValues['prefix.Double']!), - preferences.setValue('StringList', 'prefix.StringList', - allTestValues['prefix.StringList']!), - preferences.setValue( - 'String', 'flutter.String', allTestValues['flutter.String']!), - preferences.setValue( - 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), - preferences.setValue( - 'Int', 'flutter.Int', allTestValues['flutter.Int']!), - preferences.setValue( - 'Double', 'flutter.Double', allTestValues['flutter.Double']!), - preferences.setValue('StringList', 'flutter.StringList', - allTestValues['flutter.StringList']!) - ]); - final Map values = - // ignore: deprecated_member_use - await preferences.getAllWithPrefix('prefix.'); - expect(values['prefix.String'], allTestValues['prefix.String']); - expect(values['prefix.Bool'], allTestValues['prefix.Bool']); - expect(values['prefix.Int'], allTestValues['prefix.Int']); - expect(values['prefix.Double'], allTestValues['prefix.Double']); - expect(values['prefix.StringList'], allTestValues['prefix.StringList']); - }); + group('withParameters', () { + testWidgets('reading', (WidgetTester _) async { + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values['String'], isNull); + expect(values['Bool'], isNull); + expect(values['Int'], isNull); + expect(values['Double'], isNull); + expect(values['StringList'], isNull); + }); - testWidgets('clearWithPrefix', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue( - 'String', 'prefix.String', allTestValues['prefix.String']!), - preferences.setValue( - 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!), - preferences.setValue('Int', 'prefix.Int', allTestValues['prefix.Int']!), - preferences.setValue( - 'Double', 'prefix.Double', allTestValues['prefix.Double']!), - preferences.setValue('StringList', 'prefix.StringList', - allTestValues['prefix.StringList']!), - preferences.setValue( - 'String', 'flutter.String', allTestValues['flutter.String']!), - preferences.setValue( - 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), - preferences.setValue( - 'Int', 'flutter.Int', allTestValues['flutter.Int']!), - preferences.setValue( - 'Double', 'flutter.Double', allTestValues['flutter.Double']!), - preferences.setValue('StringList', 'flutter.StringList', - allTestValues['flutter.StringList']!) - ]); - // ignore: deprecated_member_use - await preferences.clearWithPrefix('prefix.'); - Map values = - // ignore: deprecated_member_use - await preferences.getAllWithPrefix('prefix.'); - expect(values['prefix.String'], null); - expect(values['prefix.Bool'], null); - expect(values['prefix.Int'], null); - expect(values['prefix.Double'], null); - expect(values['prefix.StringList'], null); - // ignore: deprecated_member_use - values = await preferences.getAllWithPrefix('flutter.'); - expect(values['flutter.String'], allTestValues['flutter.String']); - expect(values['flutter.Bool'], allTestValues['flutter.Bool']); - expect(values['flutter.Int'], allTestValues['flutter.Int']); - expect(values['flutter.Double'], allTestValues['flutter.Double']); - expect(values['flutter.StringList'], allTestValues['flutter.StringList']); - }); + Future addData() async { + await preferences.setValue( + 'String', 'String', allTestValues['String']!); + await preferences.setValue('Bool', 'Bool', allTestValues['Bool']!); + await preferences.setValue('Int', 'Int', allTestValues['Int']!); + await preferences.setValue( + 'Double', 'Double', allTestValues['Double']!); + await preferences.setValue( + 'StringList', 'StringList', allTestValues['StringList']!); + await preferences.setValue( + 'String', 'prefix.String', allTestValues['prefix.String']!); + await preferences.setValue( + 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!); + await preferences.setValue( + 'Int', 'prefix.Int', allTestValues['prefix.Int']!); + await preferences.setValue( + 'Double', 'prefix.Double', allTestValues['prefix.Double']!); + await preferences.setValue('StringList', 'prefix.StringList', + allTestValues['prefix.StringList']!); + await preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!); + await preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!); + await preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!); + await preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!); + await preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!); + } - testWidgets('getAllWithNoPrefix', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue('String', 'String', allTestValues['String']!), - preferences.setValue('Bool', 'Bool', allTestValues['Bool']!), - preferences.setValue('Int', 'Int', allTestValues['Int']!), - preferences.setValue('Double', 'Double', allTestValues['Double']!), - preferences.setValue( - 'StringList', 'StringList', allTestValues['StringList']!), - preferences.setValue( - 'String', 'flutter.String', allTestValues['flutter.String']!), - preferences.setValue( - 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), - preferences.setValue( - 'Int', 'flutter.Int', allTestValues['flutter.Int']!), - preferences.setValue( - 'Double', 'flutter.Double', allTestValues['flutter.Double']!), - preferences.setValue('StringList', 'flutter.StringList', - allTestValues['flutter.StringList']!) - ]); - // ignore: deprecated_member_use - final Map values = await preferences.getAllWithPrefix(''); - expect(values['String'], allTestValues['String']); - expect(values['Bool'], allTestValues['Bool']); - expect(values['Int'], allTestValues['Int']); - expect(values['Double'], allTestValues['Double']); - expect(values['StringList'], allTestValues['StringList']); - expect(values['flutter.String'], allTestValues['flutter.String']); - expect(values['flutter.Bool'], allTestValues['flutter.Bool']); - expect(values['flutter.Int'], allTestValues['flutter.Int']); - expect(values['flutter.Double'], allTestValues['flutter.Double']); - expect(values['flutter.StringList'], allTestValues['flutter.StringList']); - }); + testWidgets('get all with prefix', (WidgetTester _) async { + await addData(); + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + }); - testWidgets('clearWithNoPrefix', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue('String', 'String', allTestValues['String']!), - preferences.setValue('Bool', 'Bool', allTestValues['Bool']!), - preferences.setValue('Int', 'Int', allTestValues['Int']!), - preferences.setValue('Double', 'Double', allTestValues['Double']!), - preferences.setValue( - 'StringList', 'StringList', allTestValues['StringList']!), - preferences.setValue( - 'String', 'flutter.String', allTestValues['flutter.String']!), - preferences.setValue( - 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), - preferences.setValue( - 'Int', 'flutter.Int', allTestValues['flutter.Int']!), - preferences.setValue( - 'Double', 'flutter.Double', allTestValues['flutter.Double']!), - preferences.setValue('StringList', 'flutter.StringList', - allTestValues['flutter.StringList']!) - ]); - // ignore: deprecated_member_use - await preferences.clearWithPrefix(''); - // ignore: deprecated_member_use - final Map values = await preferences.getAllWithPrefix(''); - expect(values['String'], null); - expect(values['Bool'], null); - expect(values['Int'], null); - expect(values['Double'], null); - expect(values['StringList'], null); - expect(values['flutter.String'], null); - expect(values['flutter.Bool'], null); - expect(values['flutter.Int'], null); - expect(values['flutter.Double'], null); - expect(values['flutter.StringList'], null); + testWidgets('get all with allow list', (WidgetTester _) async { + await addData(); + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.String'}, + ), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + }); + + testWidgets('getAllWithNoPrefix', (WidgetTester _) async { + await addData(); + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values['String'], allTestValues['String']); + expect(values['Bool'], allTestValues['Bool']); + expect(values['Int'], allTestValues['Int']); + expect(values['Double'], allTestValues['Double']); + expect(values['StringList'], allTestValues['StringList']); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithParameters', (WidgetTester _) async { + await addData(); + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], null); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'flutter.'), + ), + ); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithParameters with allow list', + (WidgetTester _) async { + await addData(); + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.StringList'}, + ), + ), + ); + Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'flutter.'), + ), + ); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithNoPrefix', (WidgetTester _) async { + await addData(); + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values['String'], null); + expect(values['Bool'], null); + expect(values['Int'], null); + expect(values['Double'], null); + expect(values['StringList'], null); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); }); testWidgets('getAll', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue( - 'String', 'flutter.String', allTestValues['flutter.String']!), - preferences.setValue( - 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), - preferences.setValue( - 'Int', 'flutter.Int', allTestValues['flutter.Int']!), - preferences.setValue( - 'Double', 'flutter.Double', allTestValues['flutter.Double']!), - preferences.setValue('StringList', 'flutter.StringList', - allTestValues['flutter.StringList']!) - ]); + await preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!); + await preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!); + await preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!); + await preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!); + await preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!); final Map values = await preferences.getAll(); expect(values['flutter.String'], allTestValues['flutter.String']); expect(values['flutter.Bool'], allTestValues['flutter.Bool']); @@ -234,8 +435,11 @@ void main() { await preferences.setValue( 'StringList', key, allTestValues['flutter.StringList']!); await preferences.remove(key); - // ignore: deprecated_member_use - final Map values = await preferences.getAllWithPrefix(''); + final Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); expect(values[key], isNull); }); @@ -269,8 +473,11 @@ void main() { // All writes should succeed. expect(result.where((bool element) => !element), isEmpty); // The last write should win. - // ignore: deprecated_member_use - final Map values = await preferences.getAllWithPrefix(''); + final Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); expect(values['Int'], writeCount); }); }); diff --git a/packages/shared_preferences/shared_preferences_foundation/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_foundation/example/pubspec.yaml index 685231d222..92c460bca3 100644 --- a/packages/shared_preferences/shared_preferences_foundation/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_foundation/example/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - shared_preferences_platform_interface: ^2.0.0 + shared_preferences_platform_interface: ^2.3.0 dev_dependencies: flutter_driver: diff --git a/packages/shared_preferences/shared_preferences_foundation/lib/messages.g.dart b/packages/shared_preferences/shared_preferences_foundation/lib/messages.g.dart index 9f36470da1..9ef912414c 100644 --- a/packages/shared_preferences/shared_preferences_foundation/lib/messages.g.dart +++ b/packages/shared_preferences/shared_preferences_foundation/lib/messages.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v9.2.4), do not edit directly. +// Autogenerated from Pigeon (v9.2.5), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import @@ -109,12 +109,13 @@ class UserDefaultsApi { } } - Future> getAllWithPrefix(String arg_prefix) async { + Future> getAll( + String arg_prefix, List? arg_allowList) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.UserDefaultsApi.getAllWithPrefix', codec, + 'dev.flutter.pigeon.UserDefaultsApi.getAll', codec, binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_prefix]) as List?; + final List? replyList = await channel + .send([arg_prefix, arg_allowList]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -136,12 +137,12 @@ class UserDefaultsApi { } } - Future clearWithPrefix(String arg_prefix) async { + Future clear(String arg_prefix, List? arg_allowList) async { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.UserDefaultsApi.clearWithPrefix', codec, + 'dev.flutter.pigeon.UserDefaultsApi.clear', codec, binaryMessenger: _binaryMessenger); - final List? replyList = - await channel.send([arg_prefix]) as List?; + final List? replyList = await channel + .send([arg_prefix, arg_allowList]) as List?; if (replyList == null) { throw PlatformException( code: 'channel-error', @@ -153,8 +154,13 @@ class UserDefaultsApi { message: replyList[1] as String?, details: replyList[2], ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); } else { - return; + return (replyList[0] as bool?)!; } } } diff --git a/packages/shared_preferences/shared_preferences_foundation/lib/shared_preferences_foundation.dart b/packages/shared_preferences/shared_preferences_foundation/lib/shared_preferences_foundation.dart index f535ab9620..92699b7fbf 100644 --- a/packages/shared_preferences/shared_preferences_foundation/lib/shared_preferences_foundation.dart +++ b/packages/shared_preferences/shared_preferences_foundation/lib/shared_preferences_foundation.dart @@ -4,6 +4,7 @@ import 'package:flutter/services.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; import 'messages.g.dart'; typedef _Setter = Future Function(String key, Object value); @@ -12,7 +13,7 @@ typedef _Setter = Future Function(String key, Object value); class SharedPreferencesFoundation extends SharedPreferencesStorePlatform { final UserDefaultsApi _api = UserDefaultsApi(); - static const String _defautPrefix = 'flutter.'; + static const String _defaultPrefix = 'flutter.'; late final Map _setters = { 'Bool': (String key, Object value) { @@ -40,24 +41,50 @@ class SharedPreferencesFoundation extends SharedPreferencesStorePlatform { @override Future clear() async { - return clearWithPrefix(_defautPrefix); + return clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); } @override Future clearWithPrefix(String prefix) async { - await _api.clearWithPrefix(prefix); - return true; + return clearWithParameters( + ClearParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future clearWithParameters(ClearParameters parameters) async { + final PreferencesFilter filter = parameters.filter; + return _api.clear( + filter.prefix, + filter.allowList?.toList(), + ); } @override Future> getAll() async { - return getAllWithPrefix(_defautPrefix); + return getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); } @override Future> getAllWithPrefix(String prefix) async { - final Map result = await _api.getAllWithPrefix(prefix); - return result.cast(); + return getAllWithParameters( + GetAllParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future> getAllWithParameters( + GetAllParameters parameters) async { + final PreferencesFilter filter = parameters.filter; + final Map data = + await _api.getAll(filter.prefix, filter.allowList?.toList()); + return data.cast(); } @override diff --git a/packages/shared_preferences/shared_preferences_foundation/pigeons/messages.dart b/packages/shared_preferences/shared_preferences_foundation/pigeons/messages.dart index e651546b08..46deeef2bd 100644 --- a/packages/shared_preferences/shared_preferences_foundation/pigeons/messages.dart +++ b/packages/shared_preferences/shared_preferences_foundation/pigeons/messages.dart @@ -20,6 +20,6 @@ abstract class UserDefaultsApi { void setValue(String key, Object value); // TODO(stuartmorgan): Make these non-nullable once // https://github.com/flutter/flutter/issues/97848 is fixed. - Map getAllWithPrefix(String prefix); - void clearWithPrefix(String prefix); + Map getAll(String prefix, List? allowList); + bool clear(String prefix, List? allowList); } diff --git a/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml b/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml index d2ee5cb686..a6d714dcc0 100644 --- a/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_foundation/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_foundation description: iOS and macOS implementation of the shared_preferences plugin. repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_foundation issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.2.2 +version: 2.3.0 environment: sdk: ">=2.18.0 <4.0.0" @@ -24,7 +24,7 @@ flutter: dependencies: flutter: sdk: flutter - shared_preferences_platform_interface: ^2.2.0 + shared_preferences_platform_interface: ^2.3.0 dev_dependencies: flutter_test: diff --git a/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_foundation_test.dart b/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_foundation_test.dart index 5859aad9b6..496cdd9b0f 100644 --- a/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_foundation_test.dart +++ b/packages/shared_preferences/shared_preferences_foundation/test/shared_preferences_foundation_test.dart @@ -6,6 +6,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:shared_preferences_foundation/shared_preferences_foundation.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; import 'test_api.g.dart'; @@ -13,10 +14,19 @@ class _MockSharedPreferencesApi implements TestUserDefaultsApi { final Map items = {}; @override - Map getAllWithPrefix(String prefix) { + Map getAll( + String prefix, + List? allowList, + ) { + Set? allowSet; + if (allowList != null) { + allowSet = Set.from(allowList); + } return { for (final String key in items.keys) - if (key.startsWith(prefix)) key: items[key] + if (key.startsWith(prefix) && + (allowSet == null || allowSet.contains(key))) + key: items[key] }; } @@ -41,12 +51,14 @@ class _MockSharedPreferencesApi implements TestUserDefaultsApi { } @override - void clearWithPrefix(String prefix) { + bool clear(String prefix, List? allowList) { items.keys.toList().forEach((String key) { - if (key.startsWith(prefix)) { + if (key.startsWith(prefix) && + (allowList == null || allowList.contains(key))) { items.remove(key); } }); + return true; } } @@ -124,6 +136,63 @@ void main() { expect(all.length, 0); }); + test('clearWithParameters', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + + Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 5); + await plugin.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + all = await plugin.getAll(); + expect(all.length, 5); + all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 0); + }); + + test('clearWithParameters with allow list', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + + Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 5); + await plugin.clearWithParameters( + ClearParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.String'}, + ), + ), + ); + all = await plugin.getAll(); + expect(all.length, 5); + all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 4); + }); + test('getAll', () async { final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); for (final String key in flutterTestValues.keys) { @@ -144,6 +213,37 @@ void main() { expect(all, prefixTestValues); }); + test('getAllWithParameters', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + final Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(all.length, 5); + expect(all, prefixTestValues); + }); + + test('getAllWithParameters with allow list', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + final Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.Bool'}, + ), + ), + ); + expect(all.length, 1); + expect(all['prefix.Bool'], prefixTestValues['prefix.Bool']); + }); + test('setValue', () async { final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); expect(await plugin.setValue('Bool', 'flutter.Bool', true), isTrue); @@ -190,4 +290,43 @@ void main() { all = await plugin.getAllWithPrefix(''); expect(all.length, 0); }); + + test('getAllWithNoPrefix with param', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + final Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(all.length, 15); + expect(all, allTestValues); + }); + + test('clearWithNoPrefix with param', () async { + final SharedPreferencesFoundation plugin = SharedPreferencesFoundation(); + for (final String key in allTestValues.keys) { + api.items[key] = allTestValues[key]!; + } + + Map all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(all.length, 15); + await plugin.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + all = await plugin.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(all.length, 0); + }); } diff --git a/packages/shared_preferences/shared_preferences_foundation/test/test_api.g.dart b/packages/shared_preferences/shared_preferences_foundation/test/test_api.g.dart index c581d51182..6dd35a9df1 100644 --- a/packages/shared_preferences/shared_preferences_foundation/test/test_api.g.dart +++ b/packages/shared_preferences/shared_preferences_foundation/test/test_api.g.dart @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v9.2.4), do not edit directly. +// Autogenerated from Pigeon (v9.2.5), do not edit directly. // See also: https://pub.dev/packages/pigeon // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import // ignore_for_file: avoid_relative_lib_imports @@ -26,9 +26,9 @@ abstract class TestUserDefaultsApi { void setValue(String key, Object value); - Map getAllWithPrefix(String prefix); + Map getAll(String prefix, List? allowList); - void clearWithPrefix(String prefix); + bool clear(String prefix, List? allowList); static void setup(TestUserDefaultsApi? api, {BinaryMessenger? binaryMessenger}) { @@ -131,7 +131,7 @@ abstract class TestUserDefaultsApi { } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.UserDefaultsApi.getAllWithPrefix', codec, + 'dev.flutter.pigeon.UserDefaultsApi.getAll', codec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger @@ -141,20 +141,22 @@ abstract class TestUserDefaultsApi { .setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.UserDefaultsApi.getAllWithPrefix was null.'); + 'Argument for dev.flutter.pigeon.UserDefaultsApi.getAll was null.'); final List args = (message as List?)!; final String? arg_prefix = (args[0] as String?); assert(arg_prefix != null, - 'Argument for dev.flutter.pigeon.UserDefaultsApi.getAllWithPrefix was null, expected non-null String.'); + 'Argument for dev.flutter.pigeon.UserDefaultsApi.getAll was null, expected non-null String.'); + final List? arg_allowList = + (args[1] as List?)?.cast(); final Map output = - api.getAllWithPrefix(arg_prefix!); + api.getAll(arg_prefix!, arg_allowList); return [output]; }); } } { final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.UserDefaultsApi.clearWithPrefix', codec, + 'dev.flutter.pigeon.UserDefaultsApi.clear', codec, binaryMessenger: binaryMessenger); if (api == null) { _testBinaryMessengerBinding!.defaultBinaryMessenger @@ -164,13 +166,15 @@ abstract class TestUserDefaultsApi { .setMockDecodedMessageHandler(channel, (Object? message) async { assert(message != null, - 'Argument for dev.flutter.pigeon.UserDefaultsApi.clearWithPrefix was null.'); + 'Argument for dev.flutter.pigeon.UserDefaultsApi.clear was null.'); final List args = (message as List?)!; final String? arg_prefix = (args[0] as String?); assert(arg_prefix != null, - 'Argument for dev.flutter.pigeon.UserDefaultsApi.clearWithPrefix was null, expected non-null String.'); - api.clearWithPrefix(arg_prefix!); - return []; + 'Argument for dev.flutter.pigeon.UserDefaultsApi.clear was null, expected non-null String.'); + final List? arg_allowList = + (args[1] as List?)?.cast(); + final bool output = api.clear(arg_prefix!, arg_allowList); + return [output]; }); } } diff --git a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md index be38e8420a..86ba0e99f1 100644 --- a/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_linux/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.3.0 +* Adds `clearWithParameters` and `getAllWithParameters` methods. * Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. ## 2.2.0 diff --git a/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart index 8939d0a03b..5f06c87c31 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_linux/example/integration_test/shared_preferences_test.dart @@ -5,101 +5,335 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:shared_preferences_linux/shared_preferences_linux.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('SharedPreferencesLinux', () { - const Map kTestValues = { - 'flutter.String': 'hello world', - 'flutter.bool': true, - 'flutter.int': 42, - 'flutter.double': 3.14159, - 'flutter.List': ['foo', 'bar'], - }; - - const Map kTestValues2 = { - 'flutter.String': 'goodbye world', - 'flutter.bool': false, - 'flutter.int': 1337, - 'flutter.double': 2.71828, - 'flutter.List': ['baz', 'quox'], - }; - late SharedPreferencesLinux preferences; + const Map flutterTestValues = { + 'flutter.String': 'hello world', + 'flutter.Bool': true, + 'flutter.Int': 42, + 'flutter.Double': 3.14159, + 'flutter.StringList': ['foo', 'bar'], + }; + + const Map prefixTestValues = { + 'prefix.String': 'hello world', + 'prefix.Bool': true, + 'prefix.Int': 42, + 'prefix.Double': 3.14159, + 'prefix.StringList': ['foo', 'bar'], + }; + + const Map nonPrefixTestValues = { + 'String': 'hello world', + 'Bool': true, + 'Int': 42, + 'Double': 3.14159, + 'StringList': ['foo', 'bar'], + }; + + final Map allTestValues = {}; + + allTestValues.addAll(flutterTestValues); + allTestValues.addAll(prefixTestValues); + allTestValues.addAll(nonPrefixTestValues); + + Future addData() async { + await preferences.setValue('String', 'String', allTestValues['String']!); + await preferences.setValue('Bool', 'Bool', allTestValues['Bool']!); + await preferences.setValue('Int', 'Int', allTestValues['Int']!); + await preferences.setValue('Double', 'Double', allTestValues['Double']!); + await preferences.setValue( + 'StringList', 'StringList', allTestValues['StringList']!); + await preferences.setValue( + 'String', 'prefix.String', allTestValues['prefix.String']!); + await preferences.setValue( + 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!); + await preferences.setValue( + 'Int', 'prefix.Int', allTestValues['prefix.Int']!); + await preferences.setValue( + 'Double', 'prefix.Double', allTestValues['prefix.Double']!); + await preferences.setValue('StringList', 'prefix.StringList', + allTestValues['prefix.StringList']!); + await preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!); + await preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!); + await preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!); + await preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!); + await preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!); + } + setUp(() async { preferences = SharedPreferencesLinux(); + await addData(); }); - tearDown(() { - preferences.clear(); + tearDown(() async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); }); - testWidgets('reading', (WidgetTester _) async { - final Map all = await preferences.getAll(); - expect(all['flutter.String'], isNull); - expect(all['flutter.bool'], isNull); - expect(all['flutter.int'], isNull); - expect(all['flutter.double'], isNull); - expect(all['flutter.List'], isNull); + testWidgets('getAll', (WidgetTester _) async { + final Map values = await preferences.getAll(); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect(values['flutter.StringList'], allTestValues['flutter.StringList']); }); - testWidgets('writing', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue( - 'String', 'flutter.String', kTestValues2['flutter.String']!), - preferences.setValue( - 'Bool', 'flutter.bool', kTestValues2['flutter.bool']!), - preferences.setValue( - 'Int', 'flutter.int', kTestValues2['flutter.int']!), - preferences.setValue( - 'Double', 'flutter.double', kTestValues2['flutter.double']!), - preferences.setValue( - 'StringList', 'flutter.List', kTestValues2['flutter.List']!) - ]); - final Map all = await preferences.getAll(); - expect(all['flutter.String'], kTestValues2['flutter.String']); - expect(all['flutter.bool'], kTestValues2['flutter.bool']); - expect(all['flutter.int'], kTestValues2['flutter.int']); - expect(all['flutter.double'], kTestValues2['flutter.double']); - expect(all['flutter.List'], kTestValues2['flutter.List']); + group('withPrefix', () { + testWidgets('remove', (WidgetTester _) async { + const String key = 'flutter.String'; + await preferences.remove(key); + final Map values = + await preferences.getAllWithPrefix(''); + expect(values[key], isNull); + }); + + testWidgets('clear', (WidgetTester _) async { + await preferences.clear(); + final Map values = await preferences.getAll(); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); + + testWidgets('get all with prefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithPrefix('prefix.'); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + }); + + testWidgets('getAllWithNoPrefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithPrefix(''); + expect(values['String'], allTestValues['String']); + expect(values['Bool'], allTestValues['Bool']); + expect(values['Int'], allTestValues['Int']); + expect(values['Double'], allTestValues['Double']); + expect(values['StringList'], allTestValues['StringList']); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithPrefix', (WidgetTester _) async { + await preferences.clearWithPrefix('prefix.'); + Map values = + await preferences.getAllWithPrefix('prefix.'); + expect(values['prefix.String'], null); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithPrefix('flutter.'); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithNoPrefix', (WidgetTester _) async { + await preferences.clearWithPrefix(''); + final Map values = + await preferences.getAllWithPrefix(''); + expect(values['String'], null); + expect(values['Bool'], null); + expect(values['Int'], null); + expect(values['Double'], null); + expect(values['StringList'], null); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); }); - testWidgets('removing', (WidgetTester _) async { - const String key = 'flutter.testKey'; + group('withParameters', () { + testWidgets('remove', (WidgetTester _) async { + const String key = 'flutter.String'; + await preferences.remove(key); + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values[key], isNull); + }); - await Future.wait(>[ - preferences.setValue('String', key, kTestValues['flutter.String']!), - preferences.setValue('Bool', key, kTestValues['flutter.bool']!), - preferences.setValue('Int', key, kTestValues['flutter.int']!), - preferences.setValue('Double', key, kTestValues['flutter.double']!), - preferences.setValue('StringList', key, kTestValues['flutter.List']!) - ]); - await preferences.remove(key); - final Map all = await preferences.getAll(); - expect(all[key], isNull); - }); + testWidgets('clear', (WidgetTester _) async { + await preferences.clear(); + final Map values = await preferences.getAll(); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); - testWidgets('clearing', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue( - 'String', 'flutter.String', kTestValues['flutter.String']!), - preferences.setValue( - 'Bool', 'flutter.bool', kTestValues['flutter.bool']!), - preferences.setValue('Int', 'flutter.int', kTestValues['flutter.int']!), - preferences.setValue( - 'Double', 'flutter.double', kTestValues['flutter.double']!), - preferences.setValue( - 'StringList', 'flutter.List', kTestValues['flutter.List']!) - ]); - await preferences.clear(); - final Map all = await preferences.getAll(); - expect(all['flutter.String'], null); - expect(all['flutter.bool'], null); - expect(all['flutter.int'], null); - expect(all['flutter.double'], null); - expect(all['flutter.List'], null); + testWidgets('get all with prefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + }); + + testWidgets('get all with allow list', (WidgetTester _) async { + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.String'}, + ), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + }); + + testWidgets('getAllWithNoPrefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values['String'], allTestValues['String']); + expect(values['Bool'], allTestValues['Bool']); + expect(values['Int'], allTestValues['Int']); + expect(values['Double'], allTestValues['Double']); + expect(values['StringList'], allTestValues['StringList']); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithParameters', (WidgetTester _) async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], null); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'flutter.'), + ), + ); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithParameters with allow list', + (WidgetTester _) async { + await addData(); + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.StringList'}, + ), + ), + ); + Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'flutter.'), + ), + ); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithNoPrefix', (WidgetTester _) async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values['String'], null); + expect(values['Bool'], null); + expect(values['Int'], null); + expect(values['Double'], null); + expect(values['StringList'], null); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); }); }); } diff --git a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml index d67cd69865..5687f4981b 100644 --- a/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/example/pubspec.yaml @@ -16,6 +16,7 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ + shared_preferences_platform_interface: ^2.3.0 dev_dependencies: flutter_driver: diff --git a/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart b/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart index 738ccdc6cb..e9f93489b6 100644 --- a/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart +++ b/packages/shared_preferences/shared_preferences_linux/lib/shared_preferences_linux.dart @@ -11,6 +11,7 @@ import 'package:flutter/foundation.dart' show debugPrint, visibleForTesting; import 'package:path/path.dart' as path; import 'package:path_provider_linux/path_provider_linux.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; /// The Linux implementation of [SharedPreferencesStorePlatform]. /// @@ -94,26 +95,52 @@ class SharedPreferencesLinux extends SharedPreferencesStorePlatform { @override Future clear() async { - return clearWithPrefix(_defaultPrefix); + return clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); } @override Future clearWithPrefix(String prefix) async { + return clearWithParameters( + ClearParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future clearWithParameters(ClearParameters parameters) async { + final PreferencesFilter filter = parameters.filter; final Map preferences = await _readPreferences(); - preferences.removeWhere((String key, _) => key.startsWith(prefix)); + preferences.removeWhere((String key, _) => + key.startsWith(filter.prefix) && + (filter.allowList == null || filter.allowList!.contains(key))); return _writePreferences(preferences); } @override Future> getAll() async { - return getAllWithPrefix(_defaultPrefix); + return getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); } @override Future> getAllWithPrefix(String prefix) async { + return getAllWithParameters( + GetAllParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future> getAllWithParameters( + GetAllParameters parameters) async { + final PreferencesFilter filter = parameters.filter; final Map withPrefix = Map.from(await _readPreferences()); - withPrefix.removeWhere((String key, _) => !key.startsWith(prefix)); + withPrefix.removeWhere((String key, _) => !(key.startsWith(filter.prefix) && + (filter.allowList?.contains(key) ?? true))); return withPrefix; } diff --git a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml index 59ca5c4dfa..b4a7656e89 100644 --- a/packages/shared_preferences/shared_preferences_linux/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_linux/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_linux description: Linux implementation of the shared_preferences plugin repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_linux issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.2.0 +version: 2.3.0 environment: sdk: ">=2.18.0 <4.0.0" @@ -22,7 +22,7 @@ dependencies: path: ^1.8.0 path_provider_linux: ^2.0.0 path_provider_platform_interface: ^2.0.0 - shared_preferences_platform_interface: ^2.2.0 + shared_preferences_platform_interface: ^2.3.0 dev_dependencies: flutter_test: diff --git a/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart b/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart index a4f2854314..185feb58c4 100644 --- a/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart +++ b/packages/shared_preferences/shared_preferences_linux/test/shared_preferences_linux_test.dart @@ -10,6 +10,7 @@ import 'package:path_provider_linux/path_provider_linux.dart'; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:shared_preferences_linux/shared_preferences_linux.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; void main() { late MemoryFileSystem fs; @@ -98,6 +99,35 @@ void main() { expect(values, prefixTestValues); }); + test('getAllWithParameters', () async { + await writeTestFile(json.encode(allTestValues)); + final SharedPreferencesLinux prefs = getPreferences(); + + final Map values = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values, hasLength(5)); + expect(values, prefixTestValues); + }); + + test('getAllWithParameters with allow list', () async { + await writeTestFile(json.encode(allTestValues)); + final SharedPreferencesLinux prefs = getPreferences(); + + final Map all = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.Bool'}, + ), + ), + ); + expect(all.length, 1); + expect(all['prefix.Bool'], prefixTestValues['prefix.Bool']); + }); + test('remove', () async { await writeTestFile('{"key1":"one","key2":2}'); final SharedPreferencesLinux prefs = getPreferences(); @@ -155,6 +185,74 @@ void main() { final Map noValues = await prefs.getAllWithPrefix(''); expect(noValues, hasLength(0)); }); + + test('clearWithParameters', () async { + await writeTestFile(json.encode(flutterTestValues)); + final SharedPreferencesLinux prefs = getPreferences(); + await prefs.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + final Map noValues = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(noValues, hasLength(0)); + + final Map values = await prefs.getAll(); + expect(values, hasLength(5)); + expect(values, flutterTestValues); + }); + + test('clearWithParameters with allow list', () async { + await writeTestFile(json.encode(prefixTestValues)); + final SharedPreferencesLinux prefs = getPreferences(); + await prefs.clearWithParameters( + ClearParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.StringList'}, + ), + ), + ); + final Map someValues = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(someValues, hasLength(4)); + }); + + test('getAllWithNoPrefix', () async { + await writeTestFile(json.encode(allTestValues)); + final SharedPreferencesLinux prefs = getPreferences(); + + final Map values = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values, hasLength(15)); + expect(values, allTestValues); + }); + + test('clearWithNoPrefix', () async { + await writeTestFile(json.encode(flutterTestValues)); + final SharedPreferencesLinux prefs = getPreferences(); + await prefs.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + final Map noValues = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(noValues, hasLength(0)); + }); } /// Fake implementation of PathProviderLinux that returns hard-coded paths, diff --git a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md index a446c0c721..437171fd32 100644 --- a/packages/shared_preferences/shared_preferences_web/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_web/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.2.0 +* Adds `clearWithParameters` and `getAllWithParameters` methods. * Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. ## 2.1.0 diff --git a/packages/shared_preferences/shared_preferences_web/example/integration_test/shared_preferences_web_test.dart b/packages/shared_preferences/shared_preferences_web/example/integration_test/shared_preferences_web_test.dart index 78237a8d63..53a47db784 100644 --- a/packages/shared_preferences/shared_preferences_web/example/integration_test/shared_preferences_web_test.dart +++ b/packages/shared_preferences/shared_preferences_web/example/integration_test/shared_preferences_web_test.dart @@ -8,6 +8,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:shared_preferences_platform_interface/method_channel_shared_preferences.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; import 'package:shared_preferences_web/shared_preferences_web.dart'; const Map kTestValues = { @@ -21,94 +22,124 @@ const Map kTestValues = { void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - group('SharedPreferencesPlugin', () { - setUp(() { - html.window.localStorage.clear(); - }); + setUp(() { + html.window.localStorage.clear(); + }); - testWidgets('registers itself', (WidgetTester tester) async { - SharedPreferencesStorePlatform.instance = - MethodChannelSharedPreferencesStore(); - expect(SharedPreferencesStorePlatform.instance, - isNot(isA())); - SharedPreferencesPlugin.registerWith(null); - expect(SharedPreferencesStorePlatform.instance, - isA()); - }); + testWidgets('registers itself', (WidgetTester tester) async { + SharedPreferencesStorePlatform.instance = + MethodChannelSharedPreferencesStore(); + expect(SharedPreferencesStorePlatform.instance, + isNot(isA())); + SharedPreferencesPlugin.registerWith(null); + expect(SharedPreferencesStorePlatform.instance, + isA()); + }); - const Map flutterTestValues = { - 'flutter.String': 'hello world', - 'flutter.Bool': true, - 'flutter.Int': 42, - 'flutter.Double': 3.14159, - 'flutter.StringList': ['foo', 'bar'], - }; + const Map flutterTestValues = { + 'flutter.String': 'hello world', + 'flutter.Bool': true, + 'flutter.Int': 42, + 'flutter.Double': 3.14159, + 'flutter.StringList': ['foo', 'bar'], + }; - const Map prefixTestValues = { - 'prefix.String': 'hello world', - 'prefix.Bool': true, - 'prefix.Int': 42, - 'prefix.Double': 3.14159, - 'prefix.StringList': ['foo', 'bar'], - }; + const Map prefixTestValues = { + 'prefix.String': 'hello world', + 'prefix.Bool': true, + 'prefix.Int': 42, + 'prefix.Double': 3.14159, + 'prefix.StringList': ['foo', 'bar'], + }; - const Map nonPrefixTestValues = { - 'String': 'hello world', - 'Bool': true, - 'Int': 42, - 'Double': 3.14159, - 'StringList': ['foo', 'bar'], - }; + const Map nonPrefixTestValues = { + 'String': 'hello world', + 'Bool': true, + 'Int': 42, + 'Double': 3.14159, + 'StringList': ['foo', 'bar'], + }; - final Map allTestValues = {}; + final Map allTestValues = {}; - allTestValues.addAll(flutterTestValues); - allTestValues.addAll(prefixTestValues); - allTestValues.addAll(nonPrefixTestValues); + allTestValues.addAll(flutterTestValues); + allTestValues.addAll(prefixTestValues); + allTestValues.addAll(nonPrefixTestValues); - late SharedPreferencesStorePlatform preferences; + late SharedPreferencesStorePlatform preferences; + setUp(() async { + preferences = SharedPreferencesStorePlatform.instance; + }); + + tearDown(() async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + }); + + testWidgets('reading', (WidgetTester _) async { + final Map values = await preferences.getAll(); + expect(values.length, 0); + }); + + Future addData() async { + await preferences.setValue('String', 'String', allTestValues['String']!); + await preferences.setValue('Bool', 'Bool', allTestValues['Bool']!); + await preferences.setValue('Int', 'Int', allTestValues['Int']!); + await preferences.setValue('Double', 'Double', allTestValues['Double']!); + await preferences.setValue( + 'StringList', 'StringList', allTestValues['StringList']!); + await preferences.setValue( + 'String', 'prefix.String', allTestValues['prefix.String']!); + await preferences.setValue( + 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!); + await preferences.setValue( + 'Int', 'prefix.Int', allTestValues['prefix.Int']!); + await preferences.setValue( + 'Double', 'prefix.Double', allTestValues['prefix.Double']!); + await preferences.setValue( + 'StringList', 'prefix.StringList', allTestValues['prefix.StringList']!); + await preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!); + await preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!); + await preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!); + await preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!); + await preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!); + } + + testWidgets('clear', (WidgetTester _) async { + await addData(); + await preferences.clear(); + final Map values = await preferences.getAll(); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); + + group('withPrefix', () { setUp(() async { - preferences = SharedPreferencesStorePlatform.instance; + await addData(); }); - tearDown(() { - // ignore: deprecated_member_use - preferences.clearWithPrefix(''); + testWidgets('remove', (WidgetTester _) async { + const String key = 'flutter.String'; + await preferences.remove(key); + final Map values = + // ignore: deprecated_member_use + await preferences.getAllWithPrefix(''); + expect(values[key], isNull); }); - testWidgets('reading', (WidgetTester _) async { - // ignore: deprecated_member_use - final Map values = await preferences.getAllWithPrefix(''); - expect(values['String'], isNull); - expect(values['Bool'], isNull); - expect(values['Int'], isNull); - expect(values['Double'], isNull); - expect(values['StringList'], isNull); - }); - - testWidgets('getAllWithPrefix', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue( - 'String', 'prefix.String', allTestValues['prefix.String']!), - preferences.setValue( - 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!), - preferences.setValue('Int', 'prefix.Int', allTestValues['prefix.Int']!), - preferences.setValue( - 'Double', 'prefix.Double', allTestValues['prefix.Double']!), - preferences.setValue('StringList', 'prefix.StringList', - allTestValues['prefix.StringList']!), - preferences.setValue( - 'String', 'flutter.String', allTestValues['flutter.String']!), - preferences.setValue( - 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), - preferences.setValue( - 'Int', 'flutter.Int', allTestValues['flutter.Int']!), - preferences.setValue( - 'Double', 'flutter.Double', allTestValues['flutter.Double']!), - preferences.setValue('StringList', 'flutter.StringList', - allTestValues['flutter.StringList']!) - ]); + testWidgets('get all with prefix', (WidgetTester _) async { final Map values = // ignore: deprecated_member_use await preferences.getAllWithPrefix('prefix.'); @@ -119,28 +150,23 @@ void main() { expect(values['prefix.StringList'], allTestValues['prefix.StringList']); }); + testWidgets('getAllWithNoPrefix', (WidgetTester _) async { + final Map values = + // ignore: deprecated_member_use + await preferences.getAllWithPrefix(''); + expect(values['String'], allTestValues['String']); + expect(values['Bool'], allTestValues['Bool']); + expect(values['Int'], allTestValues['Int']); + expect(values['Double'], allTestValues['Double']); + expect(values['StringList'], allTestValues['StringList']); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect(values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + testWidgets('clearWithPrefix', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue( - 'String', 'prefix.String', allTestValues['prefix.String']!), - preferences.setValue( - 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!), - preferences.setValue('Int', 'prefix.Int', allTestValues['prefix.Int']!), - preferences.setValue( - 'Double', 'prefix.Double', allTestValues['prefix.Double']!), - preferences.setValue('StringList', 'prefix.StringList', - allTestValues['prefix.StringList']!), - preferences.setValue( - 'String', 'flutter.String', allTestValues['flutter.String']!), - preferences.setValue( - 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), - preferences.setValue( - 'Int', 'flutter.Int', allTestValues['flutter.Int']!), - preferences.setValue( - 'Double', 'flutter.Double', allTestValues['flutter.Double']!), - preferences.setValue('StringList', 'flutter.StringList', - allTestValues['flutter.StringList']!) - ]); // ignore: deprecated_member_use await preferences.clearWithPrefix('prefix.'); Map values = @@ -160,27 +186,76 @@ void main() { expect(values['flutter.StringList'], allTestValues['flutter.StringList']); }); - testWidgets('getAllWithNoPrefix', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue('String', 'String', allTestValues['String']!), - preferences.setValue('Bool', 'Bool', allTestValues['Bool']!), - preferences.setValue('Int', 'Int', allTestValues['Int']!), - preferences.setValue('Double', 'Double', allTestValues['Double']!), - preferences.setValue( - 'StringList', 'StringList', allTestValues['StringList']!), - preferences.setValue( - 'String', 'flutter.String', allTestValues['flutter.String']!), - preferences.setValue( - 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), - preferences.setValue( - 'Int', 'flutter.Int', allTestValues['flutter.Int']!), - preferences.setValue( - 'Double', 'flutter.Double', allTestValues['flutter.Double']!), - preferences.setValue('StringList', 'flutter.StringList', - allTestValues['flutter.StringList']!) - ]); + testWidgets('clearWithNoPrefix', (WidgetTester _) async { // ignore: deprecated_member_use - final Map values = await preferences.getAllWithPrefix(''); + await preferences.clearWithPrefix(''); + final Map values = + // ignore: deprecated_member_use + await preferences.getAllWithPrefix(''); + expect(values['String'], null); + expect(values['Bool'], null); + expect(values['Int'], null); + expect(values['Double'], null); + expect(values['StringList'], null); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); + }); + + group('withParameters', () { + setUp(() async { + await addData(); + }); + + testWidgets('remove', (WidgetTester _) async { + const String key = 'flutter.String'; + await preferences.remove(key); + final Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values[key], isNull); + }); + + testWidgets('get all with prefix', (WidgetTester _) async { + final Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + }); + + testWidgets('get all with allow list', (WidgetTester _) async { + final Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.String'}, + ), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + }); + + testWidgets('getAllWithNoPrefix', (WidgetTester _) async { + final Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); expect(values['String'], allTestValues['String']); expect(values['Bool'], allTestValues['Bool']); expect(values['Int'], allTestValues['Int']); @@ -193,29 +268,77 @@ void main() { expect(values['flutter.StringList'], allTestValues['flutter.StringList']); }); + testWidgets('clearWithParameters', (WidgetTester _) async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], null); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'flutter.'), + ), + ); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect(values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithParameters with allow list', (WidgetTester _) async { + await addData(); + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.StringList'}, + ), + ), + ); + Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'flutter.'), + ), + ); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect(values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + testWidgets('clearWithNoPrefix', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue('String', 'String', allTestValues['String']!), - preferences.setValue('Bool', 'Bool', allTestValues['Bool']!), - preferences.setValue('Int', 'Int', allTestValues['Int']!), - preferences.setValue('Double', 'Double', allTestValues['Double']!), - preferences.setValue( - 'StringList', 'StringList', allTestValues['StringList']!), - preferences.setValue( - 'String', 'flutter.String', allTestValues['flutter.String']!), - preferences.setValue( - 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), - preferences.setValue( - 'Int', 'flutter.Int', allTestValues['flutter.Int']!), - preferences.setValue( - 'Double', 'flutter.Double', allTestValues['flutter.Double']!), - preferences.setValue('StringList', 'flutter.StringList', - allTestValues['flutter.StringList']!) - ]); - // ignore: deprecated_member_use - await preferences.clearWithPrefix(''); - // ignore: deprecated_member_use - final Map values = await preferences.getAllWithPrefix(''); + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + final Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); expect(values['String'], null); expect(values['Bool'], null); expect(values['Int'], null); @@ -227,77 +350,23 @@ void main() { expect(values['flutter.Double'], null); expect(values['flutter.StringList'], null); }); + }); - testWidgets('getAll', (WidgetTester _) async { - await Future.wait(>[ - preferences.setValue( - 'String', 'flutter.String', allTestValues['flutter.String']!), - preferences.setValue( - 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!), - preferences.setValue( - 'Int', 'flutter.Int', allTestValues['flutter.Int']!), - preferences.setValue( - 'Double', 'flutter.Double', allTestValues['flutter.Double']!), - preferences.setValue('StringList', 'flutter.StringList', - allTestValues['flutter.StringList']!) - ]); - final Map values = await preferences.getAll(); - expect(values['flutter.String'], allTestValues['flutter.String']); - expect(values['flutter.Bool'], allTestValues['flutter.Bool']); - expect(values['flutter.Int'], allTestValues['flutter.Int']); - expect(values['flutter.Double'], allTestValues['flutter.Double']); - expect(values['flutter.StringList'], allTestValues['flutter.StringList']); - }); - - testWidgets('remove', (WidgetTester _) async { - const String key = 'testKey'; - await preferences.setValue( - 'String', key, allTestValues['flutter.String']!); - await preferences.setValue('Bool', key, allTestValues['flutter.Bool']!); - await preferences.setValue('Int', key, allTestValues['flutter.Int']!); - await preferences.setValue( - 'Double', key, allTestValues['flutter.Double']!); - await preferences.setValue( - 'StringList', key, allTestValues['flutter.StringList']!); - await preferences.remove(key); - // ignore: deprecated_member_use - final Map values = await preferences.getAllWithPrefix(''); - expect(values[key], isNull); - }); - - testWidgets('clear', (WidgetTester _) async { - await preferences.setValue( - 'String', 'flutter.String', allTestValues['flutter.String']!); - await preferences.setValue( - 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!); - await preferences.setValue( - 'Int', 'flutter.Int', allTestValues['flutter.Int']!); - await preferences.setValue( - 'Double', 'flutter.Double', allTestValues['flutter.Double']!); - await preferences.setValue('StringList', 'flutter.StringList', - allTestValues['flutter.StringList']!); - await preferences.clear(); - final Map values = await preferences.getAll(); - expect(values['flutter.String'], null); - expect(values['flutter.Bool'], null); - expect(values['flutter.Int'], null); - expect(values['flutter.Double'], null); - expect(values['flutter.StringList'], null); - }); - - testWidgets('simultaneous writes', (WidgetTester _) async { - final List> writes = >[]; - const int writeCount = 100; - for (int i = 1; i <= writeCount; i++) { - writes.add(preferences.setValue('Int', 'Int', i)); - } - final List result = await Future.wait(writes, eagerError: true); - // All writes should succeed. - expect(result.where((bool element) => !element), isEmpty); - // The last write should win. - // ignore: deprecated_member_use - final Map values = await preferences.getAllWithPrefix(''); - expect(values['Int'], writeCount); - }); + testWidgets('simultaneous writes', (WidgetTester _) async { + final List> writes = >[]; + const int writeCount = 100; + for (int i = 1; i <= writeCount; i++) { + writes.add(preferences.setValue('Int', 'Int', i)); + } + final List result = await Future.wait(writes, eagerError: true); + // All writes should succeed. + expect(result.where((bool element) => !element), isEmpty); + // The last write should win. + final Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values['Int'], writeCount); }); } diff --git a/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml index 92ace9c6d7..c812ea13c0 100644 --- a/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/example/pubspec.yaml @@ -8,7 +8,7 @@ environment: dependencies: flutter: sdk: flutter - shared_preferences_platform_interface: ^2.0.0 + shared_preferences_platform_interface: ^2.3.0 shared_preferences_web: path: ../ diff --git a/packages/shared_preferences/shared_preferences_web/lib/shared_preferences_web.dart b/packages/shared_preferences/shared_preferences_web/lib/shared_preferences_web.dart index 6ee692e1d9..34c33ce82c 100644 --- a/packages/shared_preferences/shared_preferences_web/lib/shared_preferences_web.dart +++ b/packages/shared_preferences/shared_preferences_web/lib/shared_preferences_web.dart @@ -8,6 +8,7 @@ import 'dart:html' as html; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; /// The web implementation of [SharedPreferencesStorePlatform]. /// @@ -22,27 +23,52 @@ class SharedPreferencesPlugin extends SharedPreferencesStorePlatform { @override Future clear() async { - return clearWithPrefix(_defaultPrefix); + return clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); } @override Future clearWithPrefix(String prefix) async { + return clearWithParameters( + ClearParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future clearWithParameters(ClearParameters parameters) async { + final PreferencesFilter filter = parameters.filter; // IMPORTANT: Do not use html.window.localStorage.clear() as that will // remove _all_ local data, not just the keys prefixed with // _prefix - _getStoredFlutterKeys(prefix).forEach(html.window.localStorage.remove); + _getFilteredKeys(filter.prefix, allowList: filter.allowList) + .forEach(html.window.localStorage.remove); return true; } @override Future> getAll() async { - return getAllWithPrefix(_defaultPrefix); + return getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); } @override Future> getAllWithPrefix(String prefix) async { + return getAllWithParameters( + GetAllParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future> getAllWithParameters( + GetAllParameters parameters) async { + final PreferencesFilter filter = parameters.filter; final Map allData = {}; - for (final String key in _getStoredFlutterKeys(prefix)) { + for (final String key + in _getFilteredKeys(filter.prefix, allowList: filter.allowList)) { allData[key] = _decodeValue(html.window.localStorage[key]!); } return allData; @@ -60,9 +86,12 @@ class SharedPreferencesPlugin extends SharedPreferencesStorePlatform { return true; } - Iterable _getStoredFlutterKeys(String prefix) { - return html.window.localStorage.keys - .where((String key) => key.startsWith(prefix)); + Iterable _getFilteredKeys( + String prefix, { + Set? allowList, + }) { + return html.window.localStorage.keys.where((String key) => + key.startsWith(prefix) && (allowList?.contains(key) ?? true)); } String _encodeValue(Object? value) { diff --git a/packages/shared_preferences/shared_preferences_web/pubspec.yaml b/packages/shared_preferences/shared_preferences_web/pubspec.yaml index f7f7903f8a..5d8802461b 100644 --- a/packages/shared_preferences/shared_preferences_web/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_web/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_web description: Web platform implementation of shared_preferences repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_web issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.1.0 +version: 2.2.0 environment: sdk: ">=2.18.0 <4.0.0" @@ -21,7 +21,7 @@ dependencies: sdk: flutter flutter_web_plugins: sdk: flutter - shared_preferences_platform_interface: ^2.2.0 + shared_preferences_platform_interface: ^2.3.0 dev_dependencies: flutter_test: diff --git a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md index b7c6410277..b6f3ab2f8d 100644 --- a/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_windows/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 2.3.0 +* Adds `clearWithParameters` and `getAllWithParameters` methods. * Updates minimum supported SDK version to Flutter 3.3/Dart 2.18. ## 2.2.0 diff --git a/packages/shared_preferences/shared_preferences_windows/example/integration_test/shared_preferences_test.dart b/packages/shared_preferences/shared_preferences_windows/example/integration_test/shared_preferences_test.dart index cef19a6864..bd30ddeab4 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/integration_test/shared_preferences_test.dart +++ b/packages/shared_preferences/shared_preferences_windows/example/integration_test/shared_preferences_test.dart @@ -4,95 +4,336 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; import 'package:shared_preferences_windows/shared_preferences_windows.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); group('SharedPreferencesWindows', () { - const Map kTestValues = { + late SharedPreferencesWindows preferences; + + const Map flutterTestValues = { 'flutter.String': 'hello world', - 'flutter.bool': true, - 'flutter.int': 42, - 'flutter.double': 3.14159, - 'flutter.List': ['foo', 'bar'], + 'flutter.Bool': true, + 'flutter.Int': 42, + 'flutter.Double': 3.14159, + 'flutter.StringList': ['foo', 'bar'], }; - const Map kTestValues2 = { - 'flutter.String': 'goodbye world', - 'flutter.bool': false, - 'flutter.int': 1337, - 'flutter.double': 2.71828, - 'flutter.List': ['baz', 'quox'], + const Map prefixTestValues = { + 'prefix.String': 'hello world', + 'prefix.Bool': true, + 'prefix.Int': 42, + 'prefix.Double': 3.14159, + 'prefix.StringList': ['foo', 'bar'], }; - testWidgets('reading', (WidgetTester _) async { - final SharedPreferencesWindows preferences = SharedPreferencesWindows(); - await preferences.clear(); - final Map values = await preferences.getAll(); - expect(values['String'], isNull); - expect(values['bool'], isNull); - expect(values['int'], isNull); - expect(values['double'], isNull); - expect(values['List'], isNull); + const Map nonPrefixTestValues = { + 'String': 'hello world', + 'Bool': true, + 'Int': 42, + 'Double': 3.14159, + 'StringList': ['foo', 'bar'], + }; + + final Map allTestValues = {}; + + allTestValues.addAll(flutterTestValues); + allTestValues.addAll(prefixTestValues); + allTestValues.addAll(nonPrefixTestValues); + + Future addData() async { + await preferences.setValue('String', 'String', allTestValues['String']!); + await preferences.setValue('Bool', 'Bool', allTestValues['Bool']!); + await preferences.setValue('Int', 'Int', allTestValues['Int']!); + await preferences.setValue('Double', 'Double', allTestValues['Double']!); + await preferences.setValue( + 'StringList', 'StringList', allTestValues['StringList']!); + await preferences.setValue( + 'String', 'prefix.String', allTestValues['prefix.String']!); + await preferences.setValue( + 'Bool', 'prefix.Bool', allTestValues['prefix.Bool']!); + await preferences.setValue( + 'Int', 'prefix.Int', allTestValues['prefix.Int']!); + await preferences.setValue( + 'Double', 'prefix.Double', allTestValues['prefix.Double']!); + await preferences.setValue('StringList', 'prefix.StringList', + allTestValues['prefix.StringList']!); + await preferences.setValue( + 'String', 'flutter.String', allTestValues['flutter.String']!); + await preferences.setValue( + 'Bool', 'flutter.Bool', allTestValues['flutter.Bool']!); + await preferences.setValue( + 'Int', 'flutter.Int', allTestValues['flutter.Int']!); + await preferences.setValue( + 'Double', 'flutter.Double', allTestValues['flutter.Double']!); + await preferences.setValue('StringList', 'flutter.StringList', + allTestValues['flutter.StringList']!); + } + + setUp(() async { + preferences = SharedPreferencesWindows(); + await addData(); }); - testWidgets('writing', (WidgetTester _) async { - final SharedPreferencesWindows preferences = SharedPreferencesWindows(); - await preferences.clear(); - await preferences.setValue( - 'String', 'flutter.String', kTestValues2['flutter.String']!); - await preferences.setValue( - 'Bool', 'flutter.bool', kTestValues2['flutter.bool']!); - await preferences.setValue( - 'Int', 'flutter.int', kTestValues2['flutter.int']!); - await preferences.setValue( - 'Double', 'flutter.double', kTestValues2['flutter.double']!); - await preferences.setValue( - 'StringList', 'flutter.List', kTestValues2['flutter.List']!); - final Map values = await preferences.getAll(); - expect(values['flutter.String'], kTestValues2['flutter.String']); - expect(values['flutter.bool'], kTestValues2['flutter.bool']); - expect(values['flutter.int'], kTestValues2['flutter.int']); - expect(values['flutter.double'], kTestValues2['flutter.double']); - expect(values['flutter.List'], kTestValues2['flutter.List']); + tearDown(() async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); }); - testWidgets('removing', (WidgetTester _) async { - final SharedPreferencesWindows preferences = SharedPreferencesWindows(); - await preferences.clear(); - const String key = 'flutter.testKey'; - await preferences.setValue('String', key, kTestValues['flutter.String']!); - await preferences.setValue('Bool', key, kTestValues['flutter.bool']!); - await preferences.setValue('Int', key, kTestValues['flutter.int']!); - await preferences.setValue('Double', key, kTestValues['flutter.double']!); - await preferences.setValue( - 'StringList', key, kTestValues['flutter.List']!); - await preferences.remove(key); + testWidgets('getAll', (WidgetTester _) async { final Map values = await preferences.getAll(); - expect(values[key], isNull); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect(values['flutter.StringList'], allTestValues['flutter.StringList']); }); - testWidgets('clearing', (WidgetTester _) async { - final SharedPreferencesWindows preferences = SharedPreferencesWindows(); - await preferences.clear(); - await preferences.setValue( - 'String', 'flutter.String', kTestValues['flutter.String']!); - await preferences.setValue( - 'Bool', 'flutter.bool', kTestValues['flutter.bool']!); - await preferences.setValue( - 'Int', 'flutter.int', kTestValues['flutter.int']!); - await preferences.setValue( - 'Double', 'flutter.double', kTestValues['flutter.double']!); - await preferences.setValue( - 'StringList', 'flutter.List', kTestValues['flutter.List']!); - await preferences.clear(); - final Map values = await preferences.getAll(); - expect(values['flutter.String'], null); - expect(values['flutter.bool'], null); - expect(values['flutter.int'], null); - expect(values['flutter.double'], null); - expect(values['flutter.List'], null); + group('withPrefix', () { + testWidgets('remove', (WidgetTester _) async { + const String key = 'flutter.String'; + await preferences.remove(key); + final Map values = + await preferences.getAllWithPrefix(''); + expect(values[key], isNull); + }); + + testWidgets('clear', (WidgetTester _) async { + await preferences.clear(); + final Map values = await preferences.getAll(); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); + + testWidgets('get all with prefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithPrefix('prefix.'); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + }); + + testWidgets('getAllWithNoPrefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithPrefix(''); + expect(values['String'], allTestValues['String']); + expect(values['Bool'], allTestValues['Bool']); + expect(values['Int'], allTestValues['Int']); + expect(values['Double'], allTestValues['Double']); + expect(values['StringList'], allTestValues['StringList']); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithPrefix', (WidgetTester _) async { + await preferences.clearWithPrefix('prefix.'); + Map values = + await preferences.getAllWithPrefix('prefix.'); + expect(values['prefix.String'], null); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithPrefix('flutter.'); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithNoPrefix', (WidgetTester _) async { + await preferences.clearWithPrefix(''); + final Map values = + await preferences.getAllWithPrefix(''); + expect(values['String'], null); + expect(values['Bool'], null); + expect(values['Int'], null); + expect(values['Double'], null); + expect(values['StringList'], null); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); + }); + + group('withParameters', () { + testWidgets('remove', (WidgetTester _) async { + const String key = 'flutter.String'; + await preferences.remove(key); + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values[key], isNull); + }); + + testWidgets('clear', (WidgetTester _) async { + await preferences.clear(); + final Map values = await preferences.getAll(); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); + + testWidgets('get all with prefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], allTestValues['prefix.StringList']); + }); + + testWidgets('get all with allow list', (WidgetTester _) async { + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.String'}, + ), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + }); + + testWidgets('getAllWithNoPrefix', (WidgetTester _) async { + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values['String'], allTestValues['String']); + expect(values['Bool'], allTestValues['Bool']); + expect(values['Int'], allTestValues['Int']); + expect(values['Double'], allTestValues['Double']); + expect(values['StringList'], allTestValues['StringList']); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithParameters', (WidgetTester _) async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], null); + expect(values['prefix.Bool'], null); + expect(values['prefix.Int'], null); + expect(values['prefix.Double'], null); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'flutter.'), + ), + ); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithParameters with allow list', + (WidgetTester _) async { + await addData(); + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.StringList'}, + ), + ), + ); + Map values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values['prefix.String'], allTestValues['prefix.String']); + expect(values['prefix.Bool'], allTestValues['prefix.Bool']); + expect(values['prefix.Int'], allTestValues['prefix.Int']); + expect(values['prefix.Double'], allTestValues['prefix.Double']); + expect(values['prefix.StringList'], null); + values = await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'flutter.'), + ), + ); + expect(values['flutter.String'], allTestValues['flutter.String']); + expect(values['flutter.Bool'], allTestValues['flutter.Bool']); + expect(values['flutter.Int'], allTestValues['flutter.Int']); + expect(values['flutter.Double'], allTestValues['flutter.Double']); + expect( + values['flutter.StringList'], allTestValues['flutter.StringList']); + }); + + testWidgets('clearWithNoPrefix', (WidgetTester _) async { + await preferences.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + final Map values = + await preferences.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values['String'], null); + expect(values['Bool'], null); + expect(values['Int'], null); + expect(values['Double'], null); + expect(values['StringList'], null); + expect(values['flutter.String'], null); + expect(values['flutter.Bool'], null); + expect(values['flutter.Int'], null); + expect(values['flutter.Double'], null); + expect(values['flutter.StringList'], null); + }); }); }); } diff --git a/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml index f0280cb4d2..180549b185 100644 --- a/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/example/pubspec.yaml @@ -9,6 +9,7 @@ environment: dependencies: flutter: sdk: flutter + shared_preferences_platform_interface: ^2.3.0 shared_preferences_windows: # When depending on this package from a real application you should use: # shared_preferences_windows: ^x.y.z diff --git a/packages/shared_preferences/shared_preferences_windows/lib/shared_preferences_windows.dart b/packages/shared_preferences/shared_preferences_windows/lib/shared_preferences_windows.dart index 63437594ce..097e0a6c63 100644 --- a/packages/shared_preferences/shared_preferences_windows/lib/shared_preferences_windows.dart +++ b/packages/shared_preferences/shared_preferences_windows/lib/shared_preferences_windows.dart @@ -11,6 +11,7 @@ import 'package:flutter/foundation.dart' show debugPrint, visibleForTesting; import 'package:path/path.dart' as path; import 'package:path_provider_windows/path_provider_windows.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; /// The Windows implementation of [SharedPreferencesStorePlatform]. /// @@ -100,26 +101,52 @@ class SharedPreferencesWindows extends SharedPreferencesStorePlatform { @override Future clear() async { - return clearWithPrefix(_defaultPrefix); + return clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); } @override Future clearWithPrefix(String prefix) async { + return clearWithParameters( + ClearParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future clearWithParameters(ClearParameters parameters) async { + final PreferencesFilter filter = parameters.filter; final Map preferences = await _readPreferences(); - preferences.removeWhere((String key, _) => key.startsWith(prefix)); + preferences.removeWhere((String key, _) => + key.startsWith(filter.prefix) && + (filter.allowList == null || filter.allowList!.contains(key))); return _writePreferences(preferences); } @override Future> getAll() async { - return getAllWithPrefix(_defaultPrefix); + return getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: _defaultPrefix), + ), + ); } @override Future> getAllWithPrefix(String prefix) async { + return getAllWithParameters( + GetAllParameters(filter: PreferencesFilter(prefix: prefix))); + } + + @override + Future> getAllWithParameters( + GetAllParameters parameters) async { + final PreferencesFilter filter = parameters.filter; final Map withPrefix = Map.from(await _readPreferences()); - withPrefix.removeWhere((String key, _) => !key.startsWith(prefix)); + withPrefix.removeWhere((String key, _) => !(key.startsWith(filter.prefix) && + (filter.allowList?.contains(key) ?? true))); return withPrefix; } diff --git a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml index 03e3880b4e..ea426340f0 100644 --- a/packages/shared_preferences/shared_preferences_windows/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_windows description: Windows implementation of shared_preferences repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.2.0 +version: 2.3.0 environment: sdk: ">=2.18.0 <4.0.0" @@ -22,7 +22,7 @@ dependencies: path: ^1.8.0 path_provider_platform_interface: ^2.0.0 path_provider_windows: ^2.0.0 - shared_preferences_platform_interface: ^2.2.0 + shared_preferences_platform_interface: ^2.3.0 dev_dependencies: flutter_test: diff --git a/packages/shared_preferences/shared_preferences_windows/test/shared_preferences_windows_test.dart b/packages/shared_preferences/shared_preferences_windows/test/shared_preferences_windows_test.dart index c23ac46e83..499a74eeca 100644 --- a/packages/shared_preferences/shared_preferences_windows/test/shared_preferences_windows_test.dart +++ b/packages/shared_preferences/shared_preferences_windows/test/shared_preferences_windows_test.dart @@ -10,6 +10,7 @@ import 'package:path/path.dart' as path; import 'package:path_provider_platform_interface/path_provider_platform_interface.dart'; import 'package:path_provider_windows/path_provider_windows.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; +import 'package:shared_preferences_platform_interface/types.dart'; import 'package:shared_preferences_windows/shared_preferences_windows.dart'; void main() { @@ -99,6 +100,35 @@ void main() { expect(values, prefixTestValues); }); + test('getAllWithParameters with Prefix', () async { + await writeTestFile(json.encode(allTestValues)); + final SharedPreferencesWindows prefs = getPreferences(); + + final Map values = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(values, hasLength(5)); + expect(values, prefixTestValues); + }); + + test('getAllWithParameters with Prefix with allow list', () async { + await writeTestFile(json.encode(allTestValues)); + final SharedPreferencesWindows prefs = getPreferences(); + + final Map all = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.Bool'}, + ), + ), + ); + expect(all.length, 1); + expect(all['prefix.Bool'], prefixTestValues['prefix.Bool']); + }); + test('remove', () async { await writeTestFile('{"key1":"one","key2":2}'); final SharedPreferencesWindows prefs = getPreferences(); @@ -156,6 +186,74 @@ void main() { final Map noValues = await prefs.getAllWithPrefix(''); expect(noValues, hasLength(0)); }); + + test('clearWithParameters with Prefix', () async { + await writeTestFile(json.encode(flutterTestValues)); + final SharedPreferencesWindows prefs = getPreferences(); + await prefs.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + final Map noValues = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(noValues, hasLength(0)); + + final Map values = await prefs.getAll(); + expect(values, hasLength(5)); + expect(values, flutterTestValues); + }); + + test('clearWithParameters with allow list', () async { + await writeTestFile(json.encode(prefixTestValues)); + final SharedPreferencesWindows prefs = getPreferences(); + await prefs.clearWithParameters( + ClearParameters( + filter: PreferencesFilter( + prefix: 'prefix.', + allowList: {'prefix.StringList'}, + ), + ), + ); + final Map noValues = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: 'prefix.'), + ), + ); + expect(noValues, hasLength(4)); + }); + + test('getAllWithNoPrefix', () async { + await writeTestFile(json.encode(allTestValues)); + final SharedPreferencesWindows prefs = getPreferences(); + + final Map values = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(values, hasLength(15)); + expect(values, allTestValues); + }); + + test('clearWithNoPrefix', () async { + await writeTestFile(json.encode(flutterTestValues)); + final SharedPreferencesWindows prefs = getPreferences(); + await prefs.clearWithParameters( + ClearParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + final Map noValues = await prefs.getAllWithParameters( + GetAllParameters( + filter: PreferencesFilter(prefix: ''), + ), + ); + expect(noValues, hasLength(0)); + }); } /// Fake implementation of PathProviderWindows that returns hard-coded paths,