Flutter SharedPreferences value to Provider on applcation start - flutter

I'm trying to to set a value from sharedpreferences to provider at application start.
this what I have so far, sharedpreferences to widget is working:
https://gist.github.com/andraskende/a19c806aeef0ce88e9a9cafa49660ab4#file-main-dart-L211-L223

Finally i figured out with trial and error... It can be done in the constructor as:
class BarcodeProvider with ChangeNotifier {
BarcodeProvider() {
setup();
}
void setup() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String url = (await prefs.getString('url') ?? '');
_url = url;
notifyListeners();
}
......
}

// global variable, that can be accessed from anywhere
SharedPreferences sharedPrefs;
void main() async { // make it async
WidgetsFlutterBinding.ensureInitialized(); // mandatory when awaiting on main
sharedPrefs = await SharedPreferences.getInstance(); // get the prefs
// do whatever you need to do with it
runApp(MyApp()); // rest of your app code
}

Related

How to initialize SharedPreferences 2.0.15 in flutter? (Dart - Flutter)

I am using shared_preferences: ^2.0.15 and saving my values locally.
When I change my screen and get my values, I get an error.
How can I initialize SharedPreferences correctly?
Video
late SharedPreferences _preferences;
#override
void initState() {
super.initState();
getLocalData();
}
Future getLocalData() async {
_preferences = await SharedPreferences.getInstance();
}
I've checked the video you shared.
You're just missing to call the getLocalData method inside the LoginViewModel.
I'd suggest adding a line inside your loginRequest method to call getLocalData method. Well, don't forget to await.
await getLocalData();
That's it :)
actually you are not fetching data from your shared preference
you have to get the data that u saved
first set data that you want to save
addStringToSF() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('email', "email#email.com");
}
then in your second screen you can get your data
late SharedPreferences _preferences;
late String email;
#override
void initState() {
super.initState();
getLocalData();
}
Future getLocalData() async {
_preferences = await SharedPreferences.getInstance();
// for exemple you saved a string value with key ='email'
email= _preferences.getString('email');
}
check this : https://medium.flutterdevs.com/using-sharedpreferences-in-flutter-251755f07127

"Flutter" Fetch data from shared_preferences and provider before running the app

I am creating a flutter app, I am using shared_preferences to store the themeMode and using provider to manage state. I am able to fetch the themeMode before my app starts but not able to run the provider function that sets the themeMode.
Following is my code
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedPreferences.getInstance().then((prefs) {
var isDarkTheme = prefs.getBool("isDarkTheme") ?? false;
return runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => ThemeManager(),
),
],
child: MyApp(isDarkActive: isDarkTheme),
),
);
});
}
My Provider "State Management" code
class ThemeManager with ChangeNotifier {
bool _isDark = false;
void setThemeMode(bool themeMode) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool("isDarkTheme", themeMode);
_isDark = themeMode;
debugPrint(_isDark.toString());
notifyListeners();
// return true;
}
void fetchTheme() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
var darkMode = prefs.getBool('isDarkTheme');
if (darkMode != null) {
_isDark = darkMode;
} else {
_isDark = false;
}
notifyListeners();
}
bool get isDark => _isDark;
}
You need to initialise a single global shared preference variable which you can use everywhere in your app so that you don't have to initialise it every time you when you want to use it.
so create a signleton class like this,
class SharedPreferencesHelper {
SharedPreferencesHelper._();
late SharedPreferences prefs;
Future<void> initialise() async {
prefs = await SharedPreferences.getInstance();
}
static final SharedPreferencesHelper instance = SharedPreferencesHelper._();
}
now initialise this in main() like this,
void main()async{
WidgetsFlutterBinding.ensureInitialized();
await SharedPreferencesHelper.instance.initialise();
}
now only use this variable where ever you want like this,
SharedPreferencesHelper.instance.prefs.getBool('key');
or create some functions inside your helper class,
Future<bool> getProperty(String key) async {
return await prefs.getBool(key);
}

Flutter shared preference code optimization suggestion?

I am using the shared_preferences package. https://pub.dev/packages/shared_preferences/example
In my repository class, for each function, I am doing this to get the instance.
SharedPreferences prefs = await SharedPreferences.getInstance();
class AuthenticationRepository {
Future<dynamic> logIn({required String email, required String password}) async {
SharedPreferences prefs = await SharedPreferences.getInstance(); <--------
....
prefs.clear();
prefs.setString('user', encodedUser);
}
Future<String> logOut() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); <---------
prefs.clear();
if(prefs.containsKey('user')){
return 'failed';
}else{
return 'cleared';
}
}
}
I am just wondering if this is initiating a new sharedPreference object or as the function implies, we are only getting the same instance?
Is there a better way to create the instance once, maybe as a class variable like below?
class AuthenticationRepository {
SharedPreferences prefs = await SharedPreferences.getInstance();
Future<dynamic> logIn({required String email, required String password}) async {
....
this.prefs.clear();
prefs.setString('user', encodedUser);
}
Future<String> logOut() async {
this.prefs.clear();
if(prefs.containsKey('user')){
return 'failed';
}else{
return 'cleared';
}
}
}
Please advice, thanks in advance :)
Yes, you can get the same instance. In the shared_preference.dart file, there is a static value _completer. Here is getInstance() function. You can see the if (_completer == null), and it immediately returns a value when the _completer had been initialized.
static Completer<SharedPreferences>? _completer;
...
static Future<SharedPreferences> getInstance() async {
if (_completer == null) {
final completer = Completer<SharedPreferences>();
try {
final Map<String, Object> preferencesMap =
await _getSharedPreferencesMap();
completer.complete(SharedPreferences._(preferencesMap));
} on Exception catch (e) {
// If there's an error, explicitly return the future with an error.
// then set the completer to null so we can retry.
completer.completeError(e);
final Future<SharedPreferences> sharedPrefsFuture = completer.future;
_completer = null;
return sharedPrefsFuture;
}
_completer = completer;
}
return _completer!.future;
}
I think it is a better way to use the getInstance() function not to create another class.

how to retrieve a value from shared preferences instantly? - FLUTTER

I'm trying to show a page as an initial login, this is only displayed when my switch value is set to true.
The switch value is stored with shared preferences but when I open the application it is not recovered, only after an application update is it actually recovered. how can i get it to be recovered instantly when i open my application?
below the code:
Future<bool> saveSwitchState(bool value) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setBool("switched", value);
print('Switch Value saved $value');
return prefs.setBool("switched", value);
}
Future<bool> getSwitchState() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
SettingsPage.switched = prefs.getBool("switched")!;
print(SettingsPage.switched);
return SettingsPage.switched;
}
on another page then the value that is actually recovered:
if(AuthPage.authenticated == false && SettingsPage.switched == true ) {
yield ProfileNoAuth();
return; }
you can use dependency injection follow these steps :
get it package
create a Separate dart containing the following code file like this:
GetIt locator = GetIt.instance;
Future<void> setupLocator() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
locator.registerLazySingleton<SharedPreferences>(() => sharedPreferences);
}
call the setupLocator() method and wait for it in your main function
void main() async {
await setupLocator();
runApp(App());
}
access SharedPreferences Instance from anywhere like this:
locator();
now the SharedPreferences Instance if available anywhere in your project
please note that you dont have to wait for getting the Instance anymore, because you have only one Instance sharable across the application
bool getSwitchState() {
final prefs = locator<SharedPreferences>();
SettingsPage.switched = prefs.getBool("switched")!;
print(SettingsPage.switched);
return SettingsPage.switched;
}

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