How to access Shared Preferences String in build method flutter
Solution 1
As @croxx5f and @AhmetKAYGISIZ suggested, I ended up using FutureBuilder to solve this problem. Thank you both so much for your help with this.
Here's the final code for anyone else who is stuck on this problem:
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
var prefs;
@override
void initState() {
super.initState();
getUserEmailFromSharedPrefs();
}
Future<String> getUserEmailFromSharedPrefs() async {
prefs = await SharedPreferences.getInstance();
final userEmail = prefs.getString('userEmail') ?? '';
return userEmail;
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: getUserEmailFromSharedPrefs(),
builder: (context, AsyncSnapshot<String> snapshot) {
if(snapshot.hasData) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: StreamBuilder<QuerySnapshot>(
stream: _firestore.collection(snapshot.data).orderBy('time', descending: false).snapshots(),
...
So in summary, I wrapped my streambuilder in a futurebuilder.
Solution 2
You can use WidgetsBinding.instance.addPostFrameCallback
, this helps you to run a callback during a frame, just after the persistent frame callbacks (which is when the main rendering pipeline has been flushed). If a frame is in progress and post-frame callbacks haven't been executed yet, then the registered callback is still executed during the frame. Otherwise, the registered callback is executed during the next frame.
In code, you can use it something like this.
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
final prefs = await SharedPreferences.getInstance();
final userEmail = prefs.getString('userEmail') ?? '';
});
Hope this answers your question.
Solution 3
Long Story short you should not perform any side effects inside your build method . See here
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late final prefs ;
@override
void initState() async{
super.initState();
prefs = await SharedPreferences.getInstance();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: prefs,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if(snapshot.hasData){}else if (snapshot.hasError)
return Center(child: CircularProgressIndicator());
},);
}
}
As your build method can be called multiple times you should not perform network calls or call complex methods because as the docs say. This method can and will be called multiple times.
In your case I used a FutureBuilder to handle the future's state and awaited it in the initState insida a stateful widget.
Check this article for more info
Jason Lloyd
Updated on December 29, 2022Comments
-
Jason Lloyd over 1 year
I'm trying to access userEmail in shared preferences, inside my build method. Here's some of the code for context:
Widget build(BuildContext context) { final prefs = await SharedPreferences.getInstance(); final userEmail = prefs.getString('userEmail') ?? ''; ... Return Scaffold( body: SingleChildScrollView( scrollDirection: Axis.horizontal, child: StreamBuilder<QuerySnapshot>( stream: _firestore.collection(userEmail).orderBy('time', descending: false).snapshots(), ...
The issue I have is, an error comes up highlighting the
await
. When i hover over it with my cursor for more info, it sayThe await expression can only be used in an async function. Try marking the function body with either 'async' or 'async*'.
There is then an option to add 'async' modifier. So i clicked that, which transformed code into this:
Future<Widget> build(BuildContext context) async { ...
This causes another error message:
'_HomeScreenState.build' ('Future<Widget> Function(BuildContext)') isn't a valid override of 'State.build' ('Widget Function(BuildContext)').
Any ideas how to solve this issue? I've tried saving the userEmail using the Provider package. This works perfectly when the user first signs in or registers, but if you hot reload, the stream doesn't work.
-
Ahmet KAYGISIZ almost 3 yearsSharedPreferences is an async work. You cannot use it like this. Yo need to use
Future.builder
-
Jason Lloyd almost 3 yearsHi @AhmetKAYGISIZ thanks for the response. Are you saying I need to use FutureBuilder instead of StreamBuilder?
-
Ahmet KAYGISIZ almost 3 yearsNo brother, first, you should not write functions in build function. You can use initState for this. First you get your shared preferences, then you will build your StreamBuilder. link this issue is like yours. You can check it. If you have more questions, please ask.
-
-
Ahmet KAYGISIZ almost 3 yearsHi, is it a better solution or FutureBuilder?
-
Jason Lloyd almost 3 yearsHi Darsh, thank you for your response to my question. I tried implementing what you suggested, but now my streambuilder is getting an eror: Undefined name 'userEmail'. Any ideas?
-
Jason Lloyd almost 3 yearsHi @croxx5f thank you for your response to my question. So for a bit more context, the list of widgets that my streambuilder is returning are basically tasks that the user can press a text button to 'complete task', which then updates the document in firestore, and should then should immediately update the stream. If I use futurebuilder, will that happen, or will the user have to reload the page to see the latest, most up to date stream?
-
croxx5f almost 3 yearsHi @JasonLloyd, the user won't have to refresh the page, the futurebuilder is above the stream and its future it's stored in the
prefs
variable inside the state so is preserved between rebuilds(the email). The stream is below and will continue to rebuild as normally. -
Darsh Shah almost 3 yearsYes, its a better solution than using FutureBuilder as a SharedPrefs are readily available.
-
croxx5f almost 3 yearsHi @JasonLloyd , please mark any of the answers as accepted so that future members can see it is solved before entering the post and having to read all of the answers.
-
Jason Lloyd almost 3 yearsHi @croxx5f, just tried to but it says I need to wait 2 days to accept my own answer. But yes I will do as soon as I am allowed to. Thank you for all of your help, really grateful, you basically solved my problem straight away, I only had to adjust the code slightly