SharedPrefrences delay before initializing - Flutter - 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();

Related

Flutter SharedPrefs Cubit InitState is ... strange

You can see the strange behaviour in this video: https://streamable.com/r5ld2y
The InitValue is the correct one, but when I restart the App it first goes to zero, AFTER loading the Screen OR press a button, it loads the shared prefs...
This is my Cubit (Only with the LoadCounter Func):
class DrinkCubit extends Cubit<DrinkState> {
DrinkCubit() : super(DrinkState(drinkValue: 0));
Future<void> loadCounter() async {
final prefs = await SharedPreferences.getInstance();
state.drinkValue = (prefs.getInt('counter') ?? 0);
}
}
And this is my InitFunction in the main window!
#override
void initState() {
super.initState();
Future.delayed(Duration.zero,()
{
BlocProvider.of<DrinkCubit>(context).loadCounter();
});
}
So how do I fix this, that the correct value is directly after starting the app showed
Try this:
getData(){
BlocProvider.of<DrinkCubit>(context).loadCounter();
}
#override
void initState() {
SchedulerBinding.instance.addPostFrameCallback((_) {
getData();
});
super.initState();
}
SchedulerBinding.instance.addPostFrameCallback ensures that code inside run before UI code.
And if it allows me to give you a hint, is better remove SharedPreferences of your Bloc and put in another class.

Flutter : Why build widget runs first before the initState()?

This is my controller.dart file which checks if users is verified or not and then return the page according to the conditions.
My question is that why the build widget is executing first before initState() ? I tried to debug this code using breakpoints and noticed that build() widget is running first and then the initState()Why this is happening and how could I fix it ?
This is my code :
class _ControllerState extends State<Controller> {
late bool auth;
#override
Widget build(BuildContext context) {
return (auth==false) ? Onbording() : IndexPage();
}
#override
void initState() {
super.initState();
WidgetsBinding.instance!.addPostFrameCallback((_) async {
await this.checked_if_logged();
});
}
Future<void> checked_if_logged() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
if(prefs.getBool('verified')==true){
setState(() {
auth = true;
});
}else{
setState(() {
auth = false;
});
}
}
}
This is a snapshot of my debug code where the blue line is showing that it runs first before init and because the bool auth is a late type so it throws lateInitializationErrror and after that initState() is called which initializes the auth variable which rebuild the widget and removes the error
Update:
I noticed that when I replace the WidgetsBinding.instance!.addPostFrameCallback((_) with just check_if_logged(), the initState() is calling first but before completion of check_if_logged() the build widget executes first which again throws lateInitializationError
I don't know where you got addPostFrameCallback from or what you want to achieve, but this is not the way.
Your problem is, that checked_if_logged is async and there is no way to await an async method in initState. That is by design and there is no way around that.
The proper way to handle this is to use a FutureBuilder widget.
See What is a Future and how do I use it?

Flutter how to invoke initState() properly

Hi IM trying to load initial data from db and sharepref as user first open page.
...
List questionsList = [];
bool _languageA = false;
#override
void initState() {
super.initState();
loadData(); // seting for some dropdown menu
_getLanguageChoise(); //geting from sharepref bool value
_getData(arabic: _languageArabic).then((value) { //async db call load List ext...
setState(() {});
});
}
Problem is that "questionsList" and "_languageA" bool is not filed in initState , so I get null or initial value, only when I refresh state or reload
List get filed and var get value... So what I need to do in order to have initial filed variables before build method so user can see..
assign questionsList and _languageA values inside initState method just like this in order to initialize a field value when the widget is created
void initState() {
super.initState();
questionsList=["hello"];
_languageA=true;
}
Make your initstate like this
#override
void initState() {
super.initState();
questionsList=[""];
_languageA=true;
loadData(); // seting for some dropdown menu
_getLanguageChoise(); //geting from sharepref bool value
_getData(arabic: _languageArabic).then((value) { //async db call load List ext...
setState(() {});
});
}

Problem implementing shared_preferrences in 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!!

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(...);
}