how to implement Shared_preferences in my own class elegantly on Flutter? - flutter

I created such class to store and update the api address locally. But it doesn't work well. How to add the Shared_preferences in the normal class instead of the flutter state widget? So it would make things clearly.
// The Server class
import 'package:shared_preferences/shared_preferences.dart';
class Server{
String _listUrl;
String _itemUrl;
static String _cache1;
static String _cache2;
static final Server _server = new Server._internal();
factory Server({String listUrl, String itemUrl}) {
_cache1 = listUrl;
_cache2 = itemUrl;
return _server;
}
Server._internal() {
read();
_listUrl=_cache1??"https://www.sjjg.uk./eat/food-items/";
_itemUrl=_cache2??"https://www.sjjg.uk/eat/recipe-details/";
}
String listUrl()=>_listUrl;
String itemUrl()=>_listUrl;
void update({String listUrl, String itemUrl}){
_listUrl = listUrl??_listUrl;
_itemUrl = itemUrl??_itemUrl;
save();
}
void read() async{
SharedPreferences prefs = await SharedPreferences.getInstance();
_cache1=prefs.getString('_listUrl')??"https://www.sjjg.uk./eat/food-items/";
_cache2=prefs.getString('_itemUrl')??"https://www.sjjg.uk/eat/recipe-details/";
// print(_cache1);
// print(_cache1);
}
void save() async{
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('_listUrl', _listUrl);
prefs.setString('_itemUrl', _itemUrl);
}
}

I found the problem.
I should not import 'package:flutter_test_app/server.dart';
Still difference between it with import 'server.dart';

Related

Flutter Null Safe Config Class with shared_preferences

In flutter 1.x, I implemented a Config class using the Flutter shared_preferences package; the code looks like this:
import 'package:shared_preferences/shared_preferences.dart';
class Config {
static final Config _config = Config._internal();
factory Config() => _config;
final accessTokenKey = 'accessToken';
String _accessToken;
SharedPreferences prefs;
Config._internal() {
loadData();
}
void loadData() async {
prefs = await SharedPreferences.getInstance();
_accessToken = prefs.getString(accessTokenKey) ?? '';
}
String get accessToken {
return _accessToken;
}
set accessToken(String accessToken) {
_accessToken = accessToken;
_saveString(accessTokenKey, accessToken);
}
_saveString(String key, String value, {String printValue = ''}) {
String printVal = printValue.length > 0 ? printValue : value;
prefs.setString(key, value);
}
}
I’m creating a new project in Flutter 2.x and trying to use the same code, but due to changes associated with null safety I’m having some difficulty getting the updated code just right.
The updated documentation for the package says to initialize the _prefs object like this:
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Then create a local prefs object using:
final SharedPreferences prefs = await _prefs;
This is fine, but I don’t want to have to make every class method that uses shared_preferences async then recreate the variable. At the same time I can’t create it as a class variable without initializing it first. Can someone please show me a cleaner way to do this, or do I just have to redeclare it every time I use it?
Also, how do I initialize the config object in my other classes? In my 1.x code, I would just do this:
final Config config = new Config();
then start accessing the properties of the config object. How do I initialize it with all of the async code in the class now?
Here’s where the updated code is today:
import 'package:shared_preferences/shared_preferences.dart';
import '../models/device.dart';
class Config {
static final Config _config = Config._internal();
factory Config() => _config;
final accessTokenKey = 'accessToken';
String _accessToken = '';
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Config._internal() {
print('Config constructor');
loadData();
}
Future<void> loadData() async {
final SharedPreferences prefs = await _prefs;
_accessToken = prefs.getString(accessTokenKey) ?? '';
}
String get accessToken {
return _accessToken;
}
set accessToken(String accessToken) {
_accessToken = accessToken;
_saveString(accessTokenKey, accessToken);
}
_saveString(String key, String value, {String printValue = ''}) {
String printVal = printValue.length > 0 ? printValue : value;
print('Config: _saveString("$key", "$printVal")');
final SharedPreferences prefs = await _prefs;
prefs.setString(key, value);
}
}
You can get instance of SharedPreferences as static field in init method:
static SharedPreferences? _prefs; //or: static late SharedPreferences _prefs;
static init() async {
_prefs = await SharedPreferences.getInstance();
}
And call init() somewhere like in build() method of first widget run, for once.Now you can use _prefs everywhere as you want.
If I want to show you a complete class to use SharedPreferences, it looks like this:
import 'package:shared_preferences/shared_preferences.dart';
class SharedPreferencesRepository {
static SharedPreferences? _prefs;
static init() async {
_prefs = await SharedPreferences.getInstance();
}
static putInteger(String key, int value) {
if (_prefs != null) _prefs!.setInt(key, value);
}
static int getInteger(String key) {
return _prefs == null ? 0 : _prefs!.getInt(key) ?? 0;
}
static putString(String key, String value) {
if (_prefs != null) _prefs!.setString(key, value);
}
static String getString(String key) {
return _prefs == null ? 'DEFAULT_VALUE' : _prefs!.getString(key) ?? "";
}
static putBool(String key, bool value) {
if (_prefs != null) _prefs!.setBool(key, value);
}
static bool getBool(String key) {
return _prefs == null ? false : _prefs!.getBool(key) ?? false;
}
}
I hope this useful for you.
If you need to wait for some async work to finish before getting an instance of a class, consider using a static method (not a factory constructor, since constructors must always return the base type).
You can use late fields to allow them to be non-null before you initialize them:
class Config {
late String _accessToken;
String get accessToken => _accessToken;
Config._(); // private constructor to prevent accidental creation
static Future<Config> create() async {
final config = Config();
final preferences = await SharedPreferences.getInstance();
config._accessToken = await preferences.getString('<your key>');
return config;
}
}
If you want to make sure this is initialized before running your app, you can initialize it in your main() method before you call runApp() to give control to the Flutter framework:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); // make sure all plugins loaded etc.
final config = await Config.create();
print(config.accessToken);
runApp(MyApp());
}

Flutter: I want to create a global variant. After close the app, it can be saved for the next time I open the app again

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Global {
static SharedPreferences _prefs;
static String key;
static Future init() async {
WidgetsFlutterBinding.ensureInitialized();
_prefs = await SharedPreferences.getInstance();
var _key = _prefs.getString("key");
if (_key != null) {
key = _key;
}
}
static saveKey(value) => _prefs.setString("key", value);
}
Here I create a global class. And I set and get value by this way.
Global.key = value; //set
Global.key; //get
But after I close the app, the value is gone. Any suggestions?
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class Global {
static SharedPreferences _prefs;
static String key;
String get init => key; // adding a getter
static Future init() async {
WidgetsFlutterBinding.ensureInitialized();
_prefs = await SharedPreferences.getInstance();
var _key = _prefs.getString("key");
if (_key != null) {
key = _key;
}
}
static saveKey(value) => _prefs.setString("key", value);
}
Global().init =value; or Global.init = value; //set
Global().init; //get Global.init; //get
Are you ever calling your Init function in your Main method?
void main() async {
await Global.init();
runApp(MyApp());
}
Assuming you're doing that, the other mistake here is how you're trying to store your value. Use the saveKey function you have in place that's what it's there for. Throw this on one of your pages.
ElevatedButton(
onPressed: () {
Global.saveKey('Test');
},
child: Text('Test'),
),
Your Global class was perfectly fine, its how you were trying to use it. Try this though, only change is that I included a null check so you don't have to create an extra variable that's not used. Also added a print statement that will print the stored value when you re-start the app. It all works fine on my end.
class Global {
static SharedPreferences _prefs;
static String key;
static Future init() async {
WidgetsFlutterBinding.ensureInitialized();
_prefs = await SharedPreferences.getInstance();
key = _prefs.getString('key') ?? ''; // null check here
debugPrint(key);
}
static saveKey(value) => _prefs.setString("key", value);
}

How to delete data of shared-preferences from singleton in flutter?

I make one file to shared preferences to store data in it from my app. It's work fine.But now I want to delete data from store as button log-out for user. So if user click button of log-out data will be clear from shared preferences file. How I can do it from different class?
import 'package:shared_preferences/shared_preferences.dart';
class MyPreferences{
static const USER = "user";
static const PASSWORD = "password";
static final MyPreferences instance = MyPreferences._internal();
//Campos a manejar
SharedPreferences _sharedPreferences;
String user = "";
String password = "";
MyPreferences._internal(){
}
factory MyPreferences()=>instance;
Future<SharedPreferences> get preferences async{
if(_sharedPreferences != null){
return _sharedPreferences;
}else{
_sharedPreferences = await SharedPreferences.getInstance();
user = _sharedPreferences.getString(USER);
password = _sharedPreferences.getString(PASSWORD);
return _sharedPreferences;
}
}
Future<bool> commit() async {
await _sharedPreferences.setString(USER, user);
await _sharedPreferences.setString(PASSWORD, password);
}
Future<MyPreferences> init() async{
_sharedPreferences = await preferences;
return this;
}
}
Define your shared preference manager class as a singleton as given,
class SharedPreferenceManager{
static final SharedPreferenceManager _singleton = new SharedPreferenceManager._internal();
factory SharedPreferenceManager() {
return _singleton;
}
SharedPreferenceManager._internal() {
... // initialization logic here
}
... // rest of the class
}
By this, you can create and access the single, reusable instance of that class. You can define a static method in the class which will be accessible from outside. As the static method can have access to only static data members, You should define the sharedPrefernece member variable as static. Here is how you can clear all the data.
static Future<bool> clearSharedPrefs(){
SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.clear();
}
After this, you'll be able to call this method from any class, just as SharedPreferenceManager.clearSharedPrefs().
It's a good practice to follow the singleton pattern for database, network and shared preference related tasks.
Here is the code you should go with.
import 'package:shared_preferences/shared_preferences.dart';
class MyPreferences{
static const USER = "user";
static const PASSWORD = "password";
static final MyPreferences instance = MyPreferences._internal();
static SharedPreferences _sharedPreferences;
String user = "";
String password = "";
MyPreferences._internal(){}
factory MyPreferences()=>instance;
Future<SharedPreferences> get preferences async{
if(_sharedPreferences != null){
return _sharedPreferences;
}else{
_sharedPreferences = await SharedPreferences.getInstance();
user = _sharedPreferences.getString(USER);
password = _sharedPreferences.getString(PASSWORD);
return _sharedPreferences;
}
}
Future<bool> commit() async {
await _sharedPreferences.setString(USER, user);
await _sharedPreferences.setString(PASSWORD, password);
}
Future<MyPreferences> init() async{
_sharedPreferences = await preferences;
return this;
}
static Future<bool> clearPreference() async{
if(_sharedPreferences){
_sharedPreferences.clear();
}
}
}
You can use remove or clear on shared preferences.
SharedPreferences preferences = await SharedPreferences.getInstance();
preferences.clear();
// OR
preferences.remove("MY KEY HERE");

Flutter SharedPreferences getInstance return null

Although I set the _sharedPreferences in the constructor, it gets null in getUsername. I don't know missing what:
class PreferencesProvider {
SharedPreferences _sharedPreferences;
PreferencesProvider() {
SharedPreferences.getInstance().then((prefs) => _sharedPreferences = prefs);
}
String getUsername() {
return _sharedPreferences.getString("Username");
}
String getX() {
return _sharedPreferences.getString("X");
}
String getY() {
return _sharedPreferences.getString("Y");
}
String getZ() {
return _sharedPreferences.getString("Z");
}
}
alternatively it didn't work either:
class LoginProvider {
SharedPreferences _sharedPreferences;
LoginProvider._internal();
static final LoginProvider _instance = LoginProvider._internal();
factory LoginProvider() {
_instance.initPreferences();
return _instance;
}
initPreferences() async {
_sharedPreferences = await SharedPreferences.getInstance();
}
I want to use this in MaterialApp:
initialRoute: PreferencesProvider().isLoggedIn() ? "MainPage" : "LoginPage"
Edit: I know I should use await. But then keyword isn't same? I don't want to wait the instance again for all returns. In the other hand, I can't use await in initialRoute.
The way i manage to login the user for my application for the similar scenario is,
String startPage="LoginPage";
void main() {
SharedPreferences prefs = await SharedPreferences.getInstance();
String user=prefs.getString("Username");
if(user!=null && user.length>0){
startPage="MainPage";
}
runApp(MyApp());
}
Now, set your initialRoute as follow,
initialRoute: startPage,
This solution works in every scenario because i am fetching the data before the runApp() function in my application. Your application renders your initialPage after calling the runApp() function.
This is the best way to manage your login page based on data retrieval from the sharedpreferences as SharedPreferences takes time to fetch the data. Till the data is retrieved from sharedpreferences your build method gets completed its UI rendering.
While using preferences you should use Future, await and async
Future<String> getUsername() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String storeUserDetails = prefs.getString("Username");
return (storeUserDetails != null);
}
Hope this helps!
You need to wait a little bit for get username from shared preferences. getInstance is an async process.
Below code will work, because getString will work after getInstance
Future<String> getUsername() async {
_sharedPreferences = await SharedPreferences.getInstance();
return _sharedPreferences.getString("Username");
}
You need to modify your PreferencesProvider class

Access other Class method in Flutter/dart

I was working on login with preference. Everything is working fine when I wrote all code in main.dart.
Problem:
When I create separate class on MySharePref then I am getting some error.
MySharePref.dart
import 'package:first_app/UserModel.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SharePrefClass {
void _saveData(UserModel model) async{
SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString("Username",model.userName);
await prefs.setString("Password", model.password);
}
Future<UserModel> _getData() async{
SharedPreferences preferences = await SharedPreferences.getInstance();
String username = preferences.getString("Username");
String password = preferences.getString("Password");
UserModel model = UserModel(username,password);
return model;
}
}
I want to access these both functions in main.dart:
_checkLogin() async {
UserModel userModel = new UserModel(
userNameEditText.text , passwordEditText.text);
SharePrefClass mySharedPref = new SharePrefClass();
final UserModel returnModel = mySharedPref._getData() ;
if(returnModel.userName == ""){
print("No data");
}else{
print("else executed");
}
}
I am getting error:
The prefix "_" means private field in dart.
Change the method name _getData() to getData() will let you can access this method in main.dart