Skip to content

Commit

Permalink
[shared_preferences] Add shared preferences devtool (#8494)
Browse files Browse the repository at this point in the history
This PR adds the shared_preferences_tool package. This package uses 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


## 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 [linked to 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].

<!-- Links -->
[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
[linked to at least one issue that this PR fixes]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#overview
[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
  • Loading branch information
adsonpleal authored Jan 24, 2025
1 parent f7efc2b commit 258f6dc
Show file tree
Hide file tree
Showing 39 changed files with 4,581 additions and 2 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ jobs:
# the change if it doesn't.
run: |
cd $HOME
git clone https://github.com/flutter/flutter.git --depth 1 -b 3.27.0 _flutter
git clone https://github.com/flutter/flutter.git --depth 1 -b 3.27.3 _flutter
echo "$HOME/_flutter/bin" >> $GITHUB_PATH
cd $GITHUB_WORKSPACE
# Checks out a copy of the repo.
Expand Down
4 changes: 4 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -124,3 +124,7 @@ packages/local_auth/local_auth_windows/** @cbracken
packages/path_provider/path_provider_windows/** @cbracken
packages/shared_preferences/shared_preferences_windows/** @cbracken
packages/url_launcher/url_launcher_windows/** @cbracken

# - DevTools extensions
# @adsonpleal is the actual maintainer of shared_preferences_tool but is not yet a committer, so can't be listed as the owner.
packages/shared_preferences/shared_preferences_tool/** @tarrinneal
4 changes: 4 additions & 0 deletions packages/shared_preferences/shared_preferences/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.5.0

* Adds shared preferences devtools extension.

## 2.4.0

* Adds migration tool to move from legacy `SharedPreferences` to `SharedPreferencesAsync`.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
!build
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
name: shared_preferences
issueTracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22
version: 1.0.0
materialIconCodePoint: '0xe683'
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import 'package:flutter/foundation.dart';
import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart';
import 'package:shared_preferences_platform_interface/types.dart';

import 'shared_preferences_devtools_extension_data.dart';

/// Provides a persistent store for simple data.
///
/// Data is persisted to and fetched from the disk asynchronously.
Expand Down Expand Up @@ -401,3 +403,10 @@ class SharedPreferencesWithCache {
return _cacheOptions.allowList?.contains(key) ?? true;
}
}

// Include an unused import to ensure this library is included
// when running `flutter run -d chrome`.
// Check this discussion for more info: https://github.com/flutter/packages/pull/6749/files/6eb1b4fdce1eba107294770d581713658ff971e9#discussion_r1755375409
// ignore: unused_element
final bool _fieldToKeepDevtoolsExtensionReachable =
fieldToKeepDevtoolsExtensionLibraryAlive;
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// 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.

import 'dart:convert';
import 'dart:developer' as developer;

import 'package:flutter/foundation.dart';

import '../shared_preferences.dart';

const String _eventPrefix = 'shared_preferences.';

/// A typedef for the post event function.
@visibleForTesting
typedef PostEvent = void Function(
String eventKind,
Map<String, Object?> eventData,
);

/// A helper class that provides data to the DevTools extension.
///
/// It is only visible for testing and eval.
@visibleForTesting
class SharedPreferencesDevToolsExtensionData {
/// The default constructor for [SharedPreferencesDevToolsExtensionData].
///
/// Accepts an optional [PostEvent] that should only be overwritten when testing.
const SharedPreferencesDevToolsExtensionData([
this._postEvent = developer.postEvent,
]);

final PostEvent _postEvent;

/// Requests all legacy and async keys and post an event with the result.
Future<void> requestAllKeys() async {
final SharedPreferences legacyPrefs = await SharedPreferences.getInstance();
final Set<String> legacyKeys = legacyPrefs.getKeys();
final Set<String> asyncKeys = await SharedPreferencesAsync().getKeys();

_postEvent('${_eventPrefix}all_keys', <String, List<String>>{
'asyncKeys': asyncKeys.toList(),
'legacyKeys': legacyKeys.toList(),
});
}

/// Requests the value for a given key and posts an event with the result.
Future<void> requestValue(String key, bool legacy) async {
final Object? value;
if (legacy) {
final SharedPreferences legacyPrefs =
await SharedPreferences.getInstance();
value = legacyPrefs.get(key);
} else {
value = await SharedPreferencesAsync().getAll(allowList: <String>{
key
}).then((Map<String, Object?> map) => map.values.firstOrNull);
}

_postEvent('${_eventPrefix}value', <String, Object?>{
'value': value,
// It is safe to use `runtimeType` here. This code
// will only ever run in debug mode.
'kind': value.runtimeType.toString(),
});
}

/// Requests the value change for the given key and posts an empty event when finished.
Future<void> requestValueChange(
String key,
String serializedValue,
String kind,
bool legacy,
) async {
final Object? value = jsonDecode(serializedValue);
if (legacy) {
final SharedPreferences legacyPrefs =
await SharedPreferences.getInstance();
// we need to check the kind because sometimes a double
// gets interpreted as an int. If this was not an issue
// we'd only need to do a simple pattern matching on value.
switch (kind) {
case 'int':
await legacyPrefs.setInt(key, value! as int);
case 'bool':
await legacyPrefs.setBool(key, value! as bool);
case 'double':
await legacyPrefs.setDouble(key, value! as double);
case 'String':
await legacyPrefs.setString(key, value! as String);
case 'List<String>':
await legacyPrefs.setStringList(
key,
(value! as List<Object?>).cast(),
);
}
} else {
final SharedPreferencesAsync prefs = SharedPreferencesAsync();
// we need to check the kind because sometimes a double
// gets interpreted as an int. If this was not an issue
// we'd only need to do a simple pattern matching on value.
switch (kind) {
case 'int':
await prefs.setInt(key, value! as int);
case 'bool':
await prefs.setBool(key, value! as bool);
case 'double':
await prefs.setDouble(key, value! as double);
case 'String':
await prefs.setString(key, value! as String);
case 'List<String>':
await prefs.setStringList(
key,
(value! as List<Object?>).cast(),
);
}
}
_postEvent('${_eventPrefix}change_value', <String, Object?>{});
}

/// Requests a key removal and posts an empty event when removed.
Future<void> requestRemoveKey(String key, bool legacy) async {
if (legacy) {
final SharedPreferences legacyPrefs =
await SharedPreferences.getInstance();
await legacyPrefs.remove(key);
} else {
await SharedPreferencesAsync().remove(key);
}
_postEvent('${_eventPrefix}remove', <String, Object?>{});
}
}

/// Include a variable to keep the library alive in web builds.
/// It must be a `final` variable.
/// Check this discussion for more info: https://github.com/flutter/packages/pull/6749/files/6eb1b4fdce1eba107294770d581713658ff971e9#discussion_r1755375409
// ignore: prefer_const_declarations
final bool fieldToKeepDevtoolsExtensionLibraryAlive = false;
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import 'package:flutter/foundation.dart' show visibleForTesting;
import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart';
import 'package:shared_preferences_platform_interface/types.dart';

import 'shared_preferences_devtools_extension_data.dart';

/// Wraps NSUserDefaults (on iOS) and SharedPreferences (on Android), providing
/// a persistent store for simple data.
///
Expand Down Expand Up @@ -288,3 +290,10 @@ Either update the implementation to support setPrefix, or do not call setPrefix.
_completer = null;
}
}

// Include an unused import to ensure this library is included
// when running `flutter run -d chrome`.
// Check this discussion for more info: https://github.com/flutter/packages/pull/6749/files/6eb1b4fdce1eba107294770d581713658ff971e9#discussion_r1755375409
// ignore: unused_element
final bool _fieldToKeepDevtoolsExtensionReachable =
fieldToKeepDevtoolsExtensionLibraryAlive;
3 changes: 2 additions & 1 deletion packages/shared_preferences/shared_preferences/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: Flutter plugin for reading and writing simple key-value pairs.
Wraps NSUserDefaults on iOS and SharedPreferences on Android.
repository: https://github.com/flutter/packages/tree/main/packages/shared_preferences/shared_preferences
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22
version: 2.4.0
version: 2.5.0

environment:
sdk: ^3.5.0
Expand Down Expand Up @@ -40,6 +40,7 @@ dev_dependencies:
sdk: flutter
integration_test:
sdk: flutter
path: ^1.9.0

topics:
- persistence
Expand Down
Loading

0 comments on commit 258f6dc

Please sign in to comment.