|  | // 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 asynchronously. | 
|  | 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; | 
|  |  | 
|  | /// Returns all keys in the persistent storage. | 
|  | Set<String> getKeys() => new Set<String>.from(_preferenceCache.keys); | 
|  |  | 
|  | /// 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]. | 
|  | Future<bool> 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]. | 
|  | Future<bool> 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]. | 
|  | Future<bool> 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]. | 
|  | Future<bool> 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]. | 
|  | Future<bool> setStringList(String key, List<String> value) => | 
|  | _setValue('StringList', key, value); | 
|  |  | 
|  | /// Removes an entry from persistent storage. | 
|  | Future<bool> remove(String key) => _setValue(null, key, null); | 
|  |  | 
|  | Future<bool> _setValue(String valueType, String key, Object value) { | 
|  | final Map<String, dynamic> params = <String, dynamic>{ | 
|  | 'key': '$_prefix$key', | 
|  | }; | 
|  | if (value == null) { | 
|  | _preferenceCache.remove(key); | 
|  | return _kChannel | 
|  | .invokeMethod('remove', params) | 
|  | .then<bool>((dynamic result) => result); | 
|  | } else { | 
|  | _preferenceCache[key] = value; | 
|  | params['value'] = value; | 
|  | return _kChannel | 
|  | .invokeMethod('set$valueType', params) | 
|  | .then<bool>((dynamic result) => result); | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Always returns true. | 
|  | /// On iOS, synchronize is marked deprecated. On Android, we commit every set. | 
|  | @deprecated | 
|  | 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; | 
|  | }); | 
|  | } | 
|  | } |