One of the most basic storage options available on Android is SharedPreferences. Flutter also has a SP-based plugin. This plugin encapsulates NSUserDefaults(IOS) and SharedPreferences(Android). Since data is stored asynchronously to disk, it is not guaranteed to take effect after you return, so try not to use this plugin to store some key data.

Since we are going to analyze the source code, we will first provide the basic usage.

Basic usage

In the project’s pubspec.yaml file, add the following:

Dependencies: shared_preferences: ^ 0.5.3 + 4Copy the code

Then perform Packages GET. Next create a new DART file and paste in the following code:

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() {
  runApp(MaterialApp(
    home: Scaffold(
      body: Center(
      child: RaisedButton(
        onPressed: _incrementCounter,
        child: Text('Increment Counter'),),),),)); } _incrementCounter() async { SharedPreferences prefs = await SharedPreferences.getInstance(); int counter = (prefs.getInt('counter')?? 0) + 1;print('Pressed $counter times.');
  await prefs.setInt('counter', counter);
}
Copy the code

Run and click the button in the center of the screen to see the following print:

I/flutter (30837): Pressed 1 times.
I/flutter (30837): Pressed 2 times.
...
Copy the code

Source code analysis

Ok, so that’s how sp is used, isn’t it very simple? ^_^ together next look at the source, the first is to get sp instance: SharedPreferences prefs = await SharedPreferences. GetInstance (); // Note that await must be used in async modified functions.

const MethodChannel _kChannel =
    MethodChannel('plugins.flutter.io/shared_preferences'); Class SharedPreferences {// Cache data, Data is synchronized with SharedPreferences or NSUserDefaults using setter methods. Final Map<String, Object> _preferenceCache; SharedPreferences._(this._preferencecache); SharedPreferences. static const String _prefix ='flutter.'; static SharedPreferences _instance; Sp static Future<SharedPreferences> getInstance() async {if(_instance == null) { final Map<String, Object> preferencesMap = await _getSharedPreferencesMap(); _instance = SharedPreferences._(preferencesMap); }return _instance;
  }

  static Future<Map<String, Object>> _getSharedPreferencesMap() async {
    final Map<String, Object> fromSystem =
        await _kChannel.invokeMapMethod<String, Object>('getAll'); // This is a native method call, and the corresponding implementation class is SharedPreferencesPlugin. 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];
    }
    returnpreferencesMap; }... }Copy the code

Methodchannels can be used as native to communicate with a flutter, as detailed in my other article. The SharedPreferencesPlugin section in the portal comment is as follows:

MethodCallHandler public class SharedPreferencesPlugin implements MethodCallHandler when a MethodChannel receives a flutter call { private static final String SHARED_PREFERENCES_NAME ="FlutterSharedPreferences"; private final android.content.SharedPreferences preferences; private SharedPreferencesPlugin(Context context) { preferences = context.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); Public void onMethodCall(MethodCall call, methodchannel. Result Result) {switch (call.method) {...case "getAll":
	          result.success(getAllPrefs());
	          return;
	        case "setInt"Number Number = call.argument("value");
	          if (number instanceof BigInteger) {
	            BigInteger integerValue = (BigInteger) number;
	            commitAsync(
	                preferences
	                    .edit()
	                    .putString(
	                        key, BIG_INTEGER_PREFIX + integerValue.toString(Character.MAX_RADIX)),
	                result);
	          } else {
	            commitAsync(preferences.edit().putLong(key, number.longValue()), result);
	          }
	          break; . }... } // Enable asyncTask to execute sp commit logic and inform flutter private void commitAsync(final Editor Editor, final MethodChannel.Result result) { new AsyncTask<Void, Void, Boolean>() { @Override protected BooleandoInBackground(Void... voids) {
	        returneditor.commit(); } @Override protected void onPostExecute(Boolean value) { result.success(value); } }.execute(); } private Map<String, Object> getAllPrefs() throws IOException { Map<String, ? > allPrefs = preferences.getAll(); Map<String, Object> filteredPrefs = new HashMap<>();for (String key : allPrefs.keySet()) {
		if (key.startsWith("flutter."// Filter out the contents of a flutter header and return... }...return filteredPrefs;
	}

Copy the code

To sum up, the sp in a flutter takes all the data stored in the flutter from the native end and passes it to the instance Map

_preferenceCache. Finally get the flutter side SharedPreferences instance. Getprefs. GetInt (‘counter’); set prefs. SetInt (‘counter’, counter);
,>

// Reads a value from persistent storage, throwing an exceptionif 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 exceptionif 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]; // Set data Future<bool>setInt(String key, int value) => _setValue('Int', key, value);

  Future<bool> _setValue(String valueType, String key, Object value) {
    final Map<String, dynamic> params = <String, dynamic>{
      'key': '$_prefix$key'}; Static const String _prefix ='flutter.'", echoing getAll as seen earlierif (value == null) {
      _preferenceCache.remove(key);
      return _kChannel
          .invokeMethod<bool>('remove', params)
          .then<bool>((dynamic result) => result);
    } else {
      if (value is List<String>) {
        // Make a copy of the list so that later mutations won't propagate _preferenceCache[key] = value.toList(); } else { _preferenceCache[key] = value; } params['value'] = value; Return _kchannel. invokeMethod
      
       ('
      set$valueType', params) / / synchronization to the native side setInt method, can come back here to see SharedPreferencesPlugin. Java ` onMethodCall ` of ` setInt ` method. .then
      
       ((dynamic result) => result); }}
      Copy the code

At this point, flutter SP source code has been basically analyzed. To sum up:

  • Use MethodChannel for native and flutter communication
  • Obtain all the values in the FLUTTER SP whose native name is FlutterSharedPreferences when the flutter SP is initializedflutter.Prefixed with data data and cached in map
  • GetXXX returns directly using map
  • SetXXX notifies native to update SP and update map cache in flutter through methodChannel. Native update SP uses AsyncTask to implement asynchrony.

If you think this article is useful to you, please share and like it. Thanks a million.