Storing locally user credentials or other sensitive data is a rather common task for apps. While SharedPreferences is ideal to store simple data, it’s certainly not the right tool to store sensitive data, as data is not encrypted.
And here comes Flutter Secure Storage, the plugin that we’ll be using here: it stores simple data, exactly like SharedPreferences, but in a secure manner: in fact, it automatically uses Keychain on iOS and macOS, KeyStore on Android, webCrypto on the web, built in libraries on Windows, and libSecret on Linux. But you don’t have to worry about these specifics: SecureStorage will handle encryption on each target system for you.
In short, Flutter Secure Storage is a key value store, with an emphasis on security.
Using this plugin is very easy:
First you Initialize an instance of FlutterSecureStorage:
final storage = FlutterSecureStorage();
Storing data: to write data, quite predictable you can use the “write” method:
await storage.write(key: 'your_key', value: 'your_value');
Note that you can currently store strings only, but of course if you need to store other data, like numbers or booleans, you can convert them manually.
To read values, again, quite predictably you use the “read” method, passing the key name:
String value = await storage.read(key: 'your_key');
You can also delete data, with the… Can you guess? Yes, it’s the delete method:
await storage.delete(key: 'your_key');
OK, let’s see how to store the settings in our app:
First, we need to add flutter_secure_storage to our app. So, from your Terminal, type:
Flutter pub add flutter_secure_storage
Probably the easiest way to add Secure Storage is making a copy of the sp_helper.dart file, as the methods we need to write are the same for secure storage as well: we’ll only need to make a few fixes here to make it work. It’s also interesting to see how the different implementations of two plugins can achieve the same results in different ways.
So, copy and paste sp_helper.dart, and rename the copy to secure_storage_helper.dart.
At the top of the file, instead of importing shared_preferences, import flutter_secure_storage.
Ok, this breaks this code in several ways, but don’t worry: it looks worse than it really is.
Let’s begin by renaming the class: the new name is SecureStorageHelper.
At the top of the class, in the singleton pattern section, let’s replace each SPHelper mention with SecureStorageHelper.
Then let’s replace The SharedPreferences declaration with a FlutterSecureStorage one:
final FlutterSecureStorage _secureStorage = const FlutterSecureStorage();
Now let’s replace every call to _preferences, with _secureStorage.
Next, in the getInstance method, let’s just return instance, as it’s been set here at the top.
Now, the write method does not return anything, or better returns void, so all our setters should just return a Future, or a Future of type void.
So the setListName method returns a future. Here we can remove result, and instead of setString let’s call write here. The write method has named parameters, instead of positional parameters: in particular it takes a key, which is _listNameKey in this case, and a value: listName.
No need for a return statement here.
Let’s repeat the same for all our setters: setCalories returns a Future, let’s delete result and the return statement, and call write instead of setInt. The key is _caloriesKey, and the value is calories. As you can see, we have an error here, because write takes strings both for the key and for the value, and calories is in integer. So let’s transform it into a String with the toString method.
We’ll do the same thing with setShowFileSize: just a Future, no result and no return, write instead of setBool, key, and value, transformed into a String.
The last setter is setShowDate, again a Future, no result and no return, write instead of setBool, key, and value, transformed into a String.
OK, the getters now. An important difference here is that with FlutterSecureStorage read tasks are asynchronous as well. So all our getters must return a Future, instead of the type. If you are using VS code, you can make this change very easily: just press the alt key, and then click at the beginning of each return type for the getter methods: you’ll see four cursors appearing in your editor. Here type future, and a less then character, then control and arrow right, and the bigger then symbol.
Let’s do the same for the methods: instead of getString, getInt and getBool, let’s just call “read” here.
Now, the getListName is fine, but as the read method returns a future of string, the other methods need to be fixed.
GetCalories returns an integer, so instead of returning the result of the read method, let’s place it into a variable: just add final calories here, and enclose the 2000 number into single quotes:
final calories = await _secureStorage.read(key: _caloriesKey) ?? ‘2000’;
Now we need to transform this into an int, so let’s type
return int.parse(calories);
Let’s repeat the same for the getShowFileSize method:
final showFileSize = await _secureStorage.read(key: _showFileSizeKey)
?? 'true';
And return the value parsed into a boolean:
return bool.parse(showFileSize);
Again, let’s fix getShowDate:
final showDate =await _secureStorage.read(key: _showDateKey) ?? 'true';
return bool.parse(showDate);
Now only deleteSettings needs fixing: here we use the delete method instead of remove, and we need the named key parameter.
Future deleteSettings() async {
await _secureStorage.delete(key: _listNameKey);
await _secureStorage.delete(key: _caloriesKey);
await _secureStorage.delete(key: _showFileSizeKey);
}
OK, we are ready to try this: one last step. In the settings_screen.dart file, instead of importing sp_helper.dart, let’s import secure_storage_helper.dart.
In the loadSettings method, let’s await SecureStorageHelper.
Let’s add await before the getters.
Then in the saveSettings method, again let’s await SecureStorageHelper.
OK, let’s try this: you see that we have the default values here. Let’s change things a little bit. Then we can save, and hot restart our app, and as you can see everything works as before. Our user may not know it, but we are keeping their data more secure with FlutterSecureStorage.
Well done!
Let’s deal with files next!