Problem implementing shared_preferrences in flutter - flutter

The following code is part of my login screen. I am using shared_preferences to save the remember me checkbox and the user name. The checkbox value is working, but the user name is not being set. I know it is being saved because I can see the value using print. But I guess it is being set too late, as my TextField is blank. Any ideas?
class RmoLogin extends StatefulWidget {
static const String id = 'login_screen';
#override
_RmoLoginState createState() => _RmoLoginState();
}
class _RmoLoginState extends State<RmoLogin> {
final TextStyle style = TextStyle(fontFamily: 'Montserrat', fontSize: 20.0);
final TextEditingController usernameController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
final GlobalKey<ScaffoldState> _scaffoldKey = new GlobalKey<ScaffoldState>();
bool showSpinner = false;
bool rememberMe = false;
String userName = '';
_saveRememberUser() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString('username', usernameController.text);
prefs.setBool('remember', rememberMe);
}
_getRememberUser() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
userName = prefs.getString('username') ?? '';
rememberMe = prefs.getBool('remember') ?? false;
}
#override
void initState() {
super.initState();
_getRememberUser();
if (rememberMe) {
usernameController.text = userName;
}
}
#override
Widget build(BuildContext context) {
final userField = UserTextField(style: style, usernameController: usernameController);
final passwordField = PasswordTextField(style: style, passwordController: passwordController);
final rememberMeCheckbox = Checkbox(
value: rememberMe,
onChanged: (newValue) {
setState(() {
rememberMe = newValue;
});
},
);

Because the SharedPreferences instantiation is asynchronous and the build method itself is not, by the time the instance is created and the value is accessed, the build method would have already been called and your widgets built.
However, For things like SharedPreferences and databases, the best way to implement them is to use the Singleton approach, i.e, one instance that you can use across the entire application.
In this case instead of creating a new SharedPreferences instance whenever either method is called, it will be best to create a single instance of it and use that instance to update the set and get the values.
You could also create a service for the SharedPreferences which you could use across the entire application.
class SharedPreferencesService {
final SharedPreferences _prefs;
SharedPreferencesService(this._prefs);
// define methods here
}
Then in your main method, you can create an instance of SharedPreferences and use it to initialize the SharedPreferencesService.
eg.
SharedPreferencesService service;
Future<void> main() async {
// flutter will complain if this isn't present
WidgetsFlutterBinding.ensureInitialized();
final prefs = await SharedPreferences.getInstance();
service = SharedPreferencesService(prefs);
runApp(YourApp());
}
Then wherever you want to use it in your app, you can call the method name on the service. It is best to use Dependency Injection for the SharedPreferencesService though, you can try the get_it library.

Just a brief look.... but
You are calling _getRememberUser synchronously within initState() when it is an async method
initState() isn't the correct place to be calling it as initState() itself is a synchronus #override.
My solution would be to use a FutureBuilder in your build() method, and call _getRememberUser there.

You just need to update the state that's it check the below solution:
bool rememberMe = false;
String userName = '';
#override
void initState() {
// TODO: implement initState
super.initState();
_getRememberUser();
}
_getRememberUser() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
userName = prefs.getString('username') ?? '';
rememberMe = prefs.getBool('remember')?? false;
});
if (rememberMe) {
usernameController.text = userName;
}
print(userName +'-->'+rememberMe.toString());
}
Happy Coding!!

Related

SharedPrefrences delay before initializing - Flutter

I'm using Shared preferences to save the user's name and login state even after closing the app. the Shared Preference I used in main.dart is fine because I used it in the main function and made it async, but when I'm trying to use it in other classes, I see a dark red screen for less than a second before loading the page and it makes my app so ugly. what can I do to fix it?
Here's my code:
late bool _isEditingText;
TextEditingController _editingController = TextEditingController();
late String initialText ;
SharedPreferences? _prefs;
#override
void initState(){
super.initState();
initializePreference().whenComplete((){
setState(() {});
});
}
Future<void> initializePreference() async{
_prefs = await SharedPreferences.getInstance();
String? name = _prefs?.getString('name');
if (name == null) {
_isEditingText = true;
initialText = 'Enter ur name';
} else {
_isEditingText = false;
initialText = name;
}
}
Update:
sorry for not including my exact error... here it is :
LateInitializationError: Field '_isEditingText#37486951' has not been initialized.
I think you are performing setState before all widgets are get initialised. So for that you can update initState as below:
void initState(){
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((_) async {
initializePreference().whenComplete((){
setState(() {});
});
});
}
If it's not causing issue, than you have to show loading indicator. Like initially when there is no data indicator will be there and once you get data from SharedPreference in setState - you have to remove indicator and load your data.
You can use CircularProgressIndicator for that.
initialise your boolean variable,
var isDataLoad = false;
once you get data in whenComplete(), set it as true and based on this variable you can declare your widgets.
isDataLoad ? Container( // Your widgets where you use "initialText" ) : CircularProgressIndicator();

Problems with shared preference-flutter

I wanna ask how to save state from textfield? cause i can't saving this state, after hot restart my value always reset, i just want to save state after hot restart, can i know my problem where it's wrong?
it's my code:
class HomePage extends StatefulWidget {
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
TextEditingController nameController = TextEditingController();
var nama = 'nama';
void setPref() async {
var prefs = await SharedPreferences.getInstance();
prefs.setString(nama, nameController.text);
}
void load() async {
var prefs = await SharedPreferences.getInstance();
setState(() {
nama = prefs.getString(nameController.text) ?? '';
nameController.text = nama;
});
}
#override
void initState() {
// TODO: implement initState
super.initState();
load();
}
#override
Widget build(BuildContext context) {
.....
}
}
There is a problem in your code:
In shared Prefrences you have to use key to store and get value.
But you are using it in wrong way:
Here is correct code:
void load() async {
var prefs = await SharedPreferences.getInstance();
String value = '';
setState(() {
value= prefs.getString(nama) ?? '';
nameController.text = value;
});
}
The nama is a key name and you are also using it to receive value. And the key you are using is nameController.text is also wrong.
Bonus
The convection of writing keys in Flutter is Following:
String nameKey = 'NAMEKEY';

Flutter - How to pause application while shared preferences is loading?

I use method, that I call from InitState() where load SP with await.
But Widget is constructing before SP is loaded and have got empty SP values.
void getSP() async {
var prefs = await SharedPreferences.getInstance();
_todoItems = prefs.getStringList("key") ?? _todoItems;
}
Full code: https://pastebin.com/EnxfKgPH
there are many options, one i like is to use boolean variable like this
bool isLoaded = false;
#override
void initState() {
getSP();
super.initState();
}
void getSP() async {
var prefs = await SharedPreferences.getInstance();
_todoItems = prefs.getStringList("key") ?? _todoItems;
setState(() => isLoaded = true);
}
then check it to determine if build tree should load or not, like this..
#override
Widget build(BuildContext context) {
return !isLoaded ? CircularProgressIndicator() : Scaffold(...);
}

State variable is not updated after getting data from SharedPreferences

I have the following problem: there's a state class:
class _HomeScreenState extends State<HomeScreen> {
String _name = "David";
#override
void initState() {
super.initState();
getNameFromPreferences();
}
#override
Widget build(BuildContext context) {
...
child: Text(
"Good Morning, \n"+_name,
style: TextStyle(color: Colors.black, fontSize: 28, fontWeight: FontWeight.bold),
),
)
}
...........................
void getNameFromPreferences() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
_name = prefs.getString(Strings.nameKey);
log(_name);
}
So, here you can see that I'm trying to get data from preferences and set it to variable in state. I watched in logs, that this data is really exists and that it sets to _name almost in the moment. But on the screen I still see default value David but not my data from SharedPreferences. So, why such situation happens?
Just change your code as follow,
getNameFromPreferences() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
_name = prefs.getString(Strings.nameKey);
log(_name);
setState(() {});
}
This will solve your issue.
Because sharedprefs package in async manner. so it takes some time to configure your data. till then your build method gets built. So, you have to use setState again to rebuild your view to render ui changes.
You didn't call setState().
void getNameFromPreferences() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
_name = prefs.getString(Strings.nameKey);
log(_name);
setState(() {}); // add this
}

Problem in receiving Future data (SharedPreferences) in Flutter

I am trying to save the value of Switch in SharedPreferences. Here is my code :
bool isDarkTheme;
static const String KEY_DARK_THEME = "dark";
void setTheme(bool value) async {
SharedPreferences pref = await SharedPreferences.getInstance();
isDarkTheme = value;
pref.setBool(KEY_DARK_THEME, isDarkTheme);
print("DARKSet? $isDarkTheme");
}
void getTheme() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
isDarkTheme = sharedPreferences.getBool(KEY_DARK_THEME);
print("dark? $isDarkTheme");
}
#override
void initState() {
// TODO: implement initState
super.initState();
print("MYINIT");
getTheme();
}
And inside Build method...
#override
Widget build(BuildContext context) {
print("BUILD $isDarkTheme");
...
...
ListTile(
title: Text("Dark Theme"),
trailing: Switch(
value: isDarkTheme ?? false,
onChanged: (val) {
setState(() {
setTheme(val);
});
},
),
),
...
...
}
Though I get the correct value inside debug console, but Switch widget is not changed accordingly. I found build() method is run before getting the value from SharedPrefernces, as a result Switch widget is not getting value from SharedPreferences. How to solve this problem of receiving Future value?
You have two option
1). I think when you get value from SharedPreference at that time you just call setState() method
void getTheme() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
setState(() {
isDarkTheme = sharedPreferences.getBool(KEY_DARK_THEME);
print("dark? $isDarkTheme");
});}
2) You can use Provider for StateManagement so, when isDarkTheme value is changed notifyListener is called and your build method is rebuild and you see the change
Your main issue is that you're retrieving the SharedPreferences instance whenever you want to store or retrieve a value, beside the fact you're also using two instances of it (pref and sharedPreferences) :
retrieve a single SharedPreferences instance using a separate function:
SharedPreferences pref ;
Future loadPreferences() async {
pref = await SharedPreferences.getInstance();
}
Then modify getTheme and setTheme :
void setTheme(bool value) async {
isDarkTheme = value;
pref.setBool(KEY_DARK_THEME, isDarkTheme);
print("DARKSet? $isDarkTheme");
}
void getTheme() async {
isDarkTheme = pref.getBool(KEY_DARK_THEME);
print("dark? $isDarkTheme");
}
Also, call loadPreferences during initialization, so pref will be loaded by the time build is called:
#override
void initState() {
super.initState();
print("MYINIT");
loadPreferences().then((_){
getTheme();
});
}