如何在 flutter 构建方法中访问共享首选项字符串
How to access Shared Preferences String in build method flutter
我正在尝试在我的构建方法中访问共享首选项中的 userEmail。这是上下文的一些代码:
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(),
...
我遇到的问题是,突出显示 await
时出现错误。当我将光标悬停在它上面以获取更多信息时,它显示 The await expression can only be used in an async function. Try marking the function body with either 'async' or 'async*'.
然后可以选择添加 'async' 修饰符。所以我点击了它,将代码转换为:
Future<Widget> build(BuildContext context) async {
...
这会导致另一个错误消息:'_HomeScreenState.build' ('Future<Widget> Function(BuildContext)') isn't a valid override of 'State.build' ('Widget Function(BuildContext)').
有什么解决这个问题的想法吗?我尝试使用 Provider 包保存 userEmail。这在用户首次登录或注册时非常有效,但如果您热重新加载,流将不起作用。
您可以使用 WidgetsBinding.instance.addPostFrameCallback
,这有助于您在帧期间 运行 回调,就在持久帧回调之后(即主渲染管道已被刷新时)。如果帧正在进行中并且尚未执行 post-frame 回调,则注册的回调仍会在该帧期间执行。否则,注册的回调将在下一帧执行。
在代码中,你可以像这样使用它。
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
final prefs = await SharedPreferences.getInstance();
final userEmail = prefs.getString('userEmail') ?? '';
});
希望这能回答您的问题。
长话短说,您不应该在构建方法中执行任何副作用。参见 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());
},);
}
}
由于您的构建方法可以被多次调用,因此您不应执行网络调用或调用复杂的方法,因为正如文档所说。此方法可以而且将会被多次调用。
在你的例子中,我使用了 FutureBuilder 来处理未来的状态,并在 initState 中等待它在一个有状态的小部件中。
查看此 article 了解更多信息
正如@croxx5f 和@AhmetKAYGISIZ 所建议的,我最终使用 FutureBuilder 来解决这个问题。非常感谢你们的帮助。
这里是为遇到此问题的其他人提供的最终代码:
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(),
...
总而言之,我将我的 streambuilder 包装在了 futurebuilder 中。
我正在尝试在我的构建方法中访问共享首选项中的 userEmail。这是上下文的一些代码:
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(),
...
我遇到的问题是,突出显示 await
时出现错误。当我将光标悬停在它上面以获取更多信息时,它显示 The await expression can only be used in an async function. Try marking the function body with either 'async' or 'async*'.
然后可以选择添加 'async' 修饰符。所以我点击了它,将代码转换为:
Future<Widget> build(BuildContext context) async {
...
这会导致另一个错误消息:'_HomeScreenState.build' ('Future<Widget> Function(BuildContext)') isn't a valid override of 'State.build' ('Widget Function(BuildContext)').
有什么解决这个问题的想法吗?我尝试使用 Provider 包保存 userEmail。这在用户首次登录或注册时非常有效,但如果您热重新加载,流将不起作用。
您可以使用 WidgetsBinding.instance.addPostFrameCallback
,这有助于您在帧期间 运行 回调,就在持久帧回调之后(即主渲染管道已被刷新时)。如果帧正在进行中并且尚未执行 post-frame 回调,则注册的回调仍会在该帧期间执行。否则,注册的回调将在下一帧执行。
在代码中,你可以像这样使用它。
@override
Widget build(BuildContext context) {
WidgetsBinding.instance.addPostFrameCallback((_) async {
final prefs = await SharedPreferences.getInstance();
final userEmail = prefs.getString('userEmail') ?? '';
});
希望这能回答您的问题。
长话短说,您不应该在构建方法中执行任何副作用。参见 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());
},);
}
}
由于您的构建方法可以被多次调用,因此您不应执行网络调用或调用复杂的方法,因为正如文档所说。此方法可以而且将会被多次调用。
在你的例子中,我使用了 FutureBuilder 来处理未来的状态,并在 initState 中等待它在一个有状态的小部件中。
查看此 article 了解更多信息
正如@croxx5f 和@AhmetKAYGISIZ 所建议的,我最终使用 FutureBuilder 来解决这个问题。非常感谢你们的帮助。
这里是为遇到此问题的其他人提供的最终代码:
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(),
...
总而言之,我将我的 streambuilder 包装在了 futurebuilder 中。