Skip to content

Commit 2fc3390

Browse files
authored
[shared_preferences] Add shared preferences devtools (#6749)
This PR adds the shared_preferences_tools package. This package user the [devtools_extension](https://pub.dev/packages/devtools_extensions) tooling to create a tool for shared preferences. The idea of this PR came from @kenzieschmoll on this [issue](flutter/flutter#145433). Initially I've published this tool as a [separate package](https://pub.dev/packages/shared_preferences_tools), but this PR aims to bring the functionality to the main shared_preferences package. Once this PR gets merged I'll archive the `shared_preferences_tools` package. https://github.com/flutter/packages/assets/11666470/fcf71145-c330-4397-b62e-c0c4c8bc9f01
1 parent 645621e commit 2fc3390

39 files changed

+5156
-3
lines changed

CODEOWNERS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,7 @@ packages/local_auth/local_auth_windows/** @cbracken
124124
packages/path_provider/path_provider_windows/** @cbracken
125125
packages/shared_preferences/shared_preferences_windows/** @cbracken
126126
packages/url_launcher/url_launcher_windows/** @cbracken
127+
128+
# - DevTools extensions
129+
# @adsonpleal is the actual maintainer of shared_preferences_tool but is not yet a committer, so can't be listed as the owner.
130+
packages/shared_preferences/shared_preferences_tool/** @tarrinneal

packages/shared_preferences/shared_preferences/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 2.4.0
2+
3+
* Adds shared preferences devtools extension
4+
15
## 2.3.3
26

37
* Clarifies scope of prefix handling in README.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
build
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
!build
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
name: shared_preferences
2+
issueTracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22
3+
version: 1.0.0
4+
materialIconCodePoint: '0xe683'

packages/shared_preferences/shared_preferences/lib/src/shared_preferences_async.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import 'package:flutter/foundation.dart';
88
import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart';
99
import 'package:shared_preferences_platform_interface/types.dart';
1010

11+
import 'shared_preferences_devtools_extension_data.dart';
12+
1113
/// Provides a persistent store for simple data.
1214
///
1315
/// Data is persisted to and fetched from the disk asynchronously.
@@ -401,3 +403,10 @@ class SharedPreferencesWithCache {
401403
return _cacheOptions.allowList?.contains(key) ?? true;
402404
}
403405
}
406+
407+
// Include an unused import to ensure this library is included
408+
// when running `flutter run -d chrome`.
409+
// Check this discussion for more info: https://github.com/flutter/packages/pull/6749/files/6eb1b4fdce1eba107294770d581713658ff971e9#discussion_r1755375409
410+
// ignore: unused_element
411+
final bool _fieldToKeepDevtoolsExtensionReachable =
412+
fieldToKeepDevtoolsExtensionLibraryAlive;
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
import 'dart:convert';
6+
import 'dart:developer' as developer;
7+
8+
import 'package:flutter/foundation.dart';
9+
10+
import '../shared_preferences.dart';
11+
12+
const String _eventPrefix = 'shared_preferences.';
13+
14+
/// A typedef for the post event function.
15+
@visibleForTesting
16+
typedef PostEvent = void Function(
17+
String eventKind,
18+
Map<String, Object?> eventData,
19+
);
20+
21+
/// A helper class that provides data to the DevTools extension.
22+
///
23+
/// It is only visible for testing and eval.
24+
@visibleForTesting
25+
class SharedPreferencesDevToolsExtensionData {
26+
/// The default constructor for [SharedPreferencesDevToolsExtensionData].
27+
///
28+
/// Accepts an optional [PostEvent] that should only be overwritten when testing.
29+
const SharedPreferencesDevToolsExtensionData([
30+
this._postEvent = developer.postEvent,
31+
]);
32+
33+
final PostEvent _postEvent;
34+
35+
/// Requests all legacy and async keys and post an event with the result.
36+
Future<void> requestAllKeys() async {
37+
final SharedPreferences legacyPrefs = await SharedPreferences.getInstance();
38+
final Set<String> legacyKeys = legacyPrefs.getKeys();
39+
final Set<String> asyncKeys = await SharedPreferencesAsync().getKeys();
40+
41+
_postEvent('${_eventPrefix}all_keys', <String, List<String>>{
42+
'asyncKeys': asyncKeys.toList(),
43+
'legacyKeys': legacyKeys.toList(),
44+
});
45+
}
46+
47+
/// Requests the value for a given key and posts an event with the result.
48+
Future<void> requestValue(String key, bool legacy) async {
49+
final Object? value;
50+
if (legacy) {
51+
final SharedPreferences legacyPrefs =
52+
await SharedPreferences.getInstance();
53+
value = legacyPrefs.get(key);
54+
} else {
55+
value = await SharedPreferencesAsync().getAll(allowList: <String>{
56+
key
57+
}).then((Map<String, Object?> map) => map.values.firstOrNull);
58+
}
59+
60+
_postEvent('${_eventPrefix}value', <String, Object?>{
61+
'value': value,
62+
// It is safe to use `runtimeType` here. This code
63+
// will only ever run in debug mode.
64+
'kind': value.runtimeType.toString(),
65+
});
66+
}
67+
68+
/// Requests the value change for the given key and posts an empty event when finished.
69+
Future<void> requestValueChange(
70+
String key,
71+
String serializedValue,
72+
String kind,
73+
bool legacy,
74+
) async {
75+
final Object? value = jsonDecode(serializedValue);
76+
if (legacy) {
77+
final SharedPreferences legacyPrefs =
78+
await SharedPreferences.getInstance();
79+
// we need to check the kind because sometimes a double
80+
// gets interpreted as an int. If this was not an issue
81+
// we'd only need to do a simple pattern matching on value.
82+
switch (kind) {
83+
case 'int':
84+
await legacyPrefs.setInt(key, value! as int);
85+
case 'bool':
86+
await legacyPrefs.setBool(key, value! as bool);
87+
case 'double':
88+
await legacyPrefs.setDouble(key, value! as double);
89+
case 'String':
90+
await legacyPrefs.setString(key, value! as String);
91+
case 'List<String>':
92+
await legacyPrefs.setStringList(
93+
key,
94+
(value! as List<Object?>).cast(),
95+
);
96+
}
97+
} else {
98+
final SharedPreferencesAsync prefs = SharedPreferencesAsync();
99+
// we need to check the kind because sometimes a double
100+
// gets interpreted as an int. If this was not an issue
101+
// we'd only need to do a simple pattern matching on value.
102+
switch (kind) {
103+
case 'int':
104+
await prefs.setInt(key, value! as int);
105+
case 'bool':
106+
await prefs.setBool(key, value! as bool);
107+
case 'double':
108+
await prefs.setDouble(key, value! as double);
109+
case 'String':
110+
await prefs.setString(key, value! as String);
111+
case 'List<String>':
112+
await prefs.setStringList(
113+
key,
114+
(value! as List<Object?>).cast(),
115+
);
116+
}
117+
}
118+
_postEvent('${_eventPrefix}change_value', <String, Object?>{});
119+
}
120+
121+
/// Requests a key removal and posts an empty event when removed.
122+
Future<void> requestRemoveKey(String key, bool legacy) async {
123+
if (legacy) {
124+
final SharedPreferences legacyPrefs =
125+
await SharedPreferences.getInstance();
126+
await legacyPrefs.remove(key);
127+
} else {
128+
await SharedPreferencesAsync().remove(key);
129+
}
130+
_postEvent('${_eventPrefix}remove', <String, Object?>{});
131+
}
132+
}
133+
134+
/// Include a variable to keep the library alive in web builds.
135+
/// It must be a `final` variable.
136+
/// Check this discussion for more info: https://github.com/flutter/packages/pull/6749/files/6eb1b4fdce1eba107294770d581713658ff971e9#discussion_r1755375409
137+
// ignore: prefer_const_declarations
138+
final bool fieldToKeepDevtoolsExtensionLibraryAlive = false;

packages/shared_preferences/shared_preferences/lib/src/shared_preferences_legacy.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import 'package:flutter/foundation.dart' show visibleForTesting;
88
import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart';
99
import 'package:shared_preferences_platform_interface/types.dart';
1010

11+
import 'shared_preferences_devtools_extension_data.dart';
12+
1113
/// Wraps NSUserDefaults (on iOS) and SharedPreferences (on Android), providing
1214
/// a persistent store for simple data.
1315
///
@@ -285,3 +287,10 @@ Either update the implementation to support setPrefix, or do not call setPrefix.
285287
_completer = null;
286288
}
287289
}
290+
291+
// Include an unused import to ensure this library is included
292+
// when running `flutter run -d chrome`.
293+
// Check this discussion for more info: https://github.com/flutter/packages/pull/6749/files/6eb1b4fdce1eba107294770d581713658ff971e9#discussion_r1755375409
294+
// ignore: unused_element
295+
final bool _fieldToKeepDevtoolsExtensionReachable =
296+
fieldToKeepDevtoolsExtensionLibraryAlive;

packages/shared_preferences/shared_preferences/pubspec.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
name: shared_preferences
2-
description: Flutter plugin for reading and writing simple key-value pairs.
3-
Wraps NSUserDefaults on iOS and SharedPreferences on Android.
2+
description: Flutter plugin for reading and writing simple key-value pairs. Wraps NSUserDefaults on iOS and SharedPreferences on Android.
43
repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences
54
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22
6-
version: 2.3.3
5+
version: 2.4.0
76

87
environment:
98
sdk: ^3.4.0
@@ -40,6 +39,7 @@ dev_dependencies:
4039
sdk: flutter
4140
integration_test:
4241
sdk: flutter
42+
path: ^1.9.0
4343

4444
topics:
4545
- persistence

0 commit comments

Comments
 (0)