Skip to content

Commit

Permalink
Re-land [shared_preferences] Add shared preferences devtool (#8531)
Browse files Browse the repository at this point in the history
Re-re-lands #8494 with a change to remove `--ignored` from the `git status` check in the repo tooling `publish` command, which is what caused the `release` failure in the last landing. `pub publish` has ignored files in `.gitignore` for years, so there's no longer any value in checking that they don't exist.

I've verified that `publish --dry-run` passes locally with this change. Server-only check failures could still potentially cause another revert, but those can't be found other than the hard way.
  • Loading branch information
stuartmorgan authored Jan 29, 2025
1 parent 91da100 commit b66b6d6
Show file tree
Hide file tree
Showing 40 changed files with 4,582 additions and 4 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 b66b6d6

Please sign in to comment.