|  | // 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<Object, 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 of any type from persistent storage. | 
|  | dynamic get(String key) => _preferenceCache[key]; | 
|  |  | 
|  | /// 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) { | 
|  | List<Object> list = _preferenceCache[key]; | 
|  | if (list != null && list is! List<String>) { | 
|  | list = list.cast<String>().toList(); | 
|  | _preferenceCache[key] = list; | 
|  | } | 
|  | return list; | 
|  | } | 
|  |  | 
|  | /// 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; | 
|  | }); | 
|  | } | 
|  | } |