blob: 34c33ce82cfd97162be1bdc0e746c5e8389652c0 [file] [log] [blame]
// 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:async';
import 'dart:convert' show json;
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].
///
/// This class implements the `package:shared_preferences` functionality for the web.
class SharedPreferencesPlugin extends SharedPreferencesStorePlatform {
/// Registers this class as the default instance of [SharedPreferencesStorePlatform].
static void registerWith(Registrar? registrar) {
SharedPreferencesStorePlatform.instance = SharedPreferencesPlugin();
}
static const String _defaultPrefix = 'flutter.';
@override
Future<bool> clear() async {
return clearWithParameters(
ClearParameters(
filter: PreferencesFilter(prefix: _defaultPrefix),
),
);
}
@override
Future<bool> clearWithPrefix(String prefix) async {
return clearWithParameters(
ClearParameters(filter: PreferencesFilter(prefix: prefix)));
}
@override
Future<bool> 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
_getFilteredKeys(filter.prefix, allowList: filter.allowList)
.forEach(html.window.localStorage.remove);
return true;
}
@override
Future<Map<String, Object>> getAll() async {
return getAllWithParameters(
GetAllParameters(
filter: PreferencesFilter(prefix: _defaultPrefix),
),
);
}
@override
Future<Map<String, Object>> getAllWithPrefix(String prefix) async {
return getAllWithParameters(
GetAllParameters(filter: PreferencesFilter(prefix: prefix)));
}
@override
Future<Map<String, Object>> getAllWithParameters(
GetAllParameters parameters) async {
final PreferencesFilter filter = parameters.filter;
final Map<String, Object> allData = <String, Object>{};
for (final String key
in _getFilteredKeys(filter.prefix, allowList: filter.allowList)) {
allData[key] = _decodeValue(html.window.localStorage[key]!);
}
return allData;
}
@override
Future<bool> remove(String key) async {
html.window.localStorage.remove(key);
return true;
}
@override
Future<bool> setValue(String valueType, String key, Object? value) async {
html.window.localStorage[key] = _encodeValue(value);
return true;
}
Iterable<String> _getFilteredKeys(
String prefix, {
Set<String>? allowList,
}) {
return html.window.localStorage.keys.where((String key) =>
key.startsWith(prefix) && (allowList?.contains(key) ?? true));
}
String _encodeValue(Object? value) {
return json.encode(value);
}
Object _decodeValue(String encodedValue) {
final Object? decodedValue = json.decode(encodedValue);
if (decodedValue is List) {
// JSON does not preserve generics. The encode/decode roundtrip is
// `List<String>` => JSON => `List<dynamic>`. We have to explicitly
// restore the RTTI.
return decodedValue.cast<String>();
}
return decodedValue!;
}
}