Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-land [shared_preferences] Add shared preferences devtool #8531

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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;
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