We are finally ready to write some code! From now on, we’ll work at creating “My Recipes”, the app that you’ll build in this course.
In this part we’ll create the settings screen of the app, that you see on the slide. This screen allows users to set the preferences for the app: the name for the list of recipes (that you’ll create later), the number of calories , and a choice that sets whether you want to show the file size of the recipe or not. Later on you’ll also add the show date option. These fields will give you a good idea on how to use sharedPreferences with different data types.
We’ll begin by creating a helper class that contains all the methods to interact with SharedPreferences from the app. I’ll use Visual Studio code for all the demos in this, but feel free to use any other editor.
I’ve already created an empty project. You can create one from scratch or download the starting project from the resources for this course.
To use SharedPreferences in our project, we need to add the package in our pubspec.yaml file. To get the latest version, open your editor, and type
flutter pub add shared_preferences
with an underscore between shared and preferences.
If you open your pubspec now, you’ll see that the dependency has been added to the project.
Next, in the lib folder, add a new file, and call it sp_helper.dart. Here let’s import the sharedpreferences package at the top of the file:
import 'package:shared_preferences/shared_preferences.dart';
Next, let’s create a new class called SPHelper
class SPHelper {}
At the top pf the class, let’s add a few constants that contain the name of the keys that we’ll be using in sharedpreferences: a static const for the name of the list of recipes, another one for the calories key, and the last one for the key that sets whether the file size should be shown or not.
static const String _listNameKey = 'listName';
static const String _caloriesKey = 'calories';
static const String _showFileSizeKey = 'showFileSize';
Using constants for the keys, instead of hardcoded strings is considered a good pattern, as this code is less error prone and easier to maintain. For this class we’ll use the singleton pattern: first create a private constructor, called internal:
SPHelper._internal();
In this case the underscore before internal makes sure that this constructor cannot be called from outside.
Next, let’s create a private static SPHelper, called _instance. This will call the constructor we have just created.
static final SPHelper _instance = SPHelper._internal();
_instance will hold the single instance of the class.
Now let’s create a late, SharedPreferences instance called _preferences: this will contain the SharedPreferences instance
late SharedPreferences _preferences;
Finally, let’s create an asynchronous static method, that we’ll use to access the singleton instance of this class. IT returns a Future of SPHelper, we’ll call it getInstance, and mark it as async.
Inside the method, we’ll set the instance.preferences to await SharedPreferences.getInstance. Then we’ll just return the instance
static Future<SPHelper> getInstance() async {
_instance._preferences = await SharedPreferences.getInstance();
return _instance;
}
Now we are ready to write the methods that interact with sharedPreferences: we’ll begin with the list name setter. THis is asynchronous and returns a Future of type bool. Let’s call it setListName. As a parameter it takes a String, called listName. Let’s mark it async.
Inside the method, let’s return await preferences.setString, passing listNameKey and listName. Basically this takes a String called listName and sets the value in sharedpreferences using the _listNameKey. It returns a true if the action was successful, otherwise it returns false.
Future<bool> setListName(String listName) async {
return await _preferences.setString(_listNameKey, listName);
}
Now let’s read the values for the list name: as this is a reading task, it’s not asynchronous in SharedPreferences. So this returns a string, we’ll call it getListName.
Inside the method, let’s return preferences.getString, passing listNameKey. As this might return null if no key is found or the value has not been set, let’s return “My recipes” when this happens.
String getListName() {
return _preferences.getString(_listNameKey) ?? 'My Recipes';
}
Now we need to repeat the same for the other two properties: calories and showFIleSize. THe pattern is the same: the only difference is as these two fields are an integer and a boolean, the methods we need to call are different as well. Let’s begin with the calories:
Future<bool> setCalories(int calories) async {
return await _preferences.setInt(_caloriesKey, calories);
}
int getCalories() {
return _preferences.getInt(_caloriesKey) ?? 2000;
}
And finally for the showFIleSize boolean:
Future<bool> setShowFileSize(bool showFileSize) async {
return await _preferences.setBool(_showFileSizeKey, showFileSize);
}
bool getShowFileSize() {
return _preferences.getBool(_showFileSizeKey) ?? true;
}
The last method we want to add here, is one that deletes all values from SharedPreferences. This may be useful when users want to reset the settings:
Future<bool> deleteSettings() async {
return await _preferences.remove(_listNameKey) &&
await _preferences.remove(_caloriesKey) &&
await _preferences.remove(_showFileSizeKey);
}
This completes this helper class. Now, although this is a relatively simple class, I believe it shows several good patterns you can use in your apps:
Having a SharedPreferences helper class have a single point of access for all the business logic that deals with SharedPreferences, and this makes the code more organized, maintainable, and easier to understand. Whenever you need to access or modify SharedPreferences, you can use the helper class’s methods instead of duplicating code.
Using the singleton approach in a SharedPreferences helper class also makes sense because in this way you make sure to always have only one instance of the helper. This has several benefits, including an efficient use of memory and processing power, and it’s also important when dealing with asynchronous tasks.
Great, we are now ready to create the user interface. Let’s do that next!