如何在 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 中。