| // Copyright 2017 The Chromium 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 'package:flutter/services.dart'; |
| import 'package:meta/meta.dart'; |
| |
| const MethodChannel _kChannel = |
| const MethodChannel('plugins.flutter.io/shared_preferences'); |
| |
| /// Wraps NSUserDefaults (on iOS) and SharedPreferences (on Android), providing |
| /// a persistent store for simple data. |
| /// |
| /// Data is persisted to disk automatically and asynchronously. Use [commit()] |
| /// to be notified when a save is successful. |
| class SharedPreferences { |
| SharedPreferences._(this._preferenceCache); |
| |
| static const String _prefix = 'flutter.'; |
| static SharedPreferences _instance; |
| static Future<SharedPreferences> getInstance() async { |
| if (_instance == null) { |
| final Map<String, Object> fromSystem = |
| await _kChannel.invokeMethod('getAll'); |
| assert(fromSystem != null); |
| // Strip the flutter. prefix from the returned preferences. |
| final Map<String, Object> preferencesMap = <String, Object>{}; |
| for (String key in fromSystem.keys) { |
| assert(key.startsWith(_prefix)); |
| preferencesMap[key.substring(_prefix.length)] = fromSystem[key]; |
| } |
| _instance = new SharedPreferences._(preferencesMap); |
| } |
| return _instance; |
| } |
| |
| /// The cache that holds all preferences. |
| /// |
| /// It is instantiated to the current state of the SharedPreferences or |
| /// NSUserDefaults object and then kept in sync via setter methods in this |
| /// class. |
| /// |
| /// It is NOT guaranteed that this cache and the device prefs will remain |
| /// in sync since the setter method might fail for any reason. |
| final Map<String, Object> _preferenceCache; |
| |
| /// Reads a value from persistent storage, throwing an exception if it's not a |
| /// bool. |
| bool getBool(String key) => _preferenceCache[key]; |
| |
| /// Reads a value from persistent storage, throwing an exception if it's not |
| /// an int. |
| int getInt(String key) => _preferenceCache[key]; |
| |
| /// Reads a value from persistent storage, throwing an exception if it's not a |
| /// double. |
| double getDouble(String key) => _preferenceCache[key]; |
| |
| /// Reads a value from persistent storage, throwing an exception if it's not a |
| /// String. |
| String getString(String key) => _preferenceCache[key]; |
| |
| /// Reads a set of string values from persistent storage, throwing an |
| /// exception if it's not a string set. |
| List<String> getStringList(String key) => _preferenceCache[key]; |
| |
| /// Saves a boolean [value] to persistent storage in the background. |
| /// |
| /// If [value] is null, this is equivalent to calling [remove()] on the [key]. |
| void setBool(String key, bool value) => _setValue('Bool', key, value); |
| |
| /// Saves an integer [value] to persistent storage in the background. |
| /// |
| /// If [value] is null, this is equivalent to calling [remove()] on the [key]. |
| void setInt(String key, int value) => _setValue('Int', key, value); |
| |
| /// Saves a double [value] to persistent storage in the background. |
| /// |
| /// Android doesn't support storing doubles, so it will be stored as a float. |
| /// |
| /// If [value] is null, this is equivalent to calling [remove()] on the [key]. |
| void setDouble(String key, double value) => _setValue('Double', key, value); |
| |
| /// Saves a string [value] to persistent storage in the background. |
| /// |
| /// If [value] is null, this is equivalent to calling [remove()] on the [key]. |
| void setString(String key, String value) => _setValue('String', key, value); |
| |
| /// Saves a list of strings [value] to persistent storage in the background. |
| /// |
| /// If [value] is null, this is equivalent to calling [remove()] on the [key]. |
| void setStringList(String key, List<String> value) => |
| _setValue('StringList', key, value); |
| |
| /// Removes an entry from persistent storage. |
| void remove(String key) => _setValue(null, key, null); |
| |
| void _setValue(String valueType, String key, Object value) { |
| // Set the value in the background. |
| final Map<String, dynamic> params = <String, dynamic>{ |
| 'key': '$_prefix$key', |
| }; |
| if (value == null) { |
| _preferenceCache.remove(key); |
| _kChannel.invokeMethod('remove', params); |
| } else { |
| _preferenceCache[key] = value; |
| params['value'] = value; |
| _kChannel.invokeMethod('set$valueType', params); |
| } |
| } |
| |
| /// Completes with true once saved values have been persisted to local |
| /// storage, or false if the save failed. |
| /// |
| /// It's usually sufficient to just wait for the set methods to complete which |
| /// ensure the preferences have been modified in memory. Commit is necessary |
| /// only if you need to be absolutely sure that the data is in persistent |
| /// storage before taking some other action. |
| Future<bool> commit() async => await _kChannel.invokeMethod('commit'); |
| |
| /// Completes with true once the user preferences for the app has been cleared. |
| Future<bool> clear() async { |
| _preferenceCache.clear(); |
| return await _kChannel.invokeMethod('clear'); |
| } |
| |
| /// Initializes the shared preferences with mock values for testing. |
| @visibleForTesting |
| static void setMockInitialValues(Map<String, dynamic> values) { |
| _kChannel.setMockMethodCallHandler((MethodCall methodCall) async { |
| if (methodCall.method == 'getAll') { |
| return values; |
| } |
| return null; |
| }); |
| } |
| } |