【问题标题】:Problem using Flutter Provider when updating a value during build在构建期间更新值时使用 Flutter Provider 出现问题
【发布时间】:2020-07-04 07:39:42
【问题描述】:

在检查用户是否已登录后,我正尝试在提供程序中更新我的 uid。当我这样做时,即使应用程序没有崩溃,也会在构建小部件时引发错误。代码如下:

class HandleAuth extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var user = Provider.of<FirebaseUser>(context);
    if (user != null) {
      print('user.uid is ${user.uid}');
      final loggedUserInfo = Provider.of<LoggedUserInfo>(context, listen: false);
      loggedUserInfo.updateUserInfo(user.uid);
      print('first scan screen user: ${loggedUserInfo.userUid}');

    }
    return (user == null)
            ? WelcomeNewUserScreen()
            : ServicesAroundMe();
  }
}

这里是提供者:

class LoggedUserInfo with ChangeNotifier {
  String _uid;

  String get userUid {
    return _uid;
  }

  void updateUserInfo(String updatedUid) {
    _uid = updatedUid;
    print('updated uid is $_uid');
    notifyListeners();
  }

}

它会抛出这个错误:

这个 ListenableProvider 小部件不能被标记为需要构建,因为框架已经在构建小部件的过程中。仅当其祖先之一当前正在构建时,小部件才能在构建阶段标记为需要构建。这个例外是允许的,因为框架在子组件之前构建父窗口小部件,这意味着将始终构建脏后代。否则,框架可能不会在此构建阶段访问此小部件。 调用 setState() 或 markNeedsBuild() 的小部件是:ListenableProvider

【问题讨论】:

    标签: flutter


    【解决方案1】:

    您必须记住,每次调用 updateUserInfo 方法时,都会触发 notifyListeners() 来尝试重建脏小部件。 Provider.of&lt;T&gt;(context) 没有参数 listen: false 也做同样的事情。因此,调用了 2 个重建触发器,导致错误。

    与提供者合作时,建议使用流。

    要使您的代码更具可扩展性,请为您的用户创建一个自定义类,然后使用 ChangeNotifier 或提供程序和流。

    例如;

    class User {
      final String uid;
      final String displayName;
      User({ @required this.uid, this.displayName });
    }
    
    class Auth {
    
       User _firebaseUserMapper( FirebaseUser user) {
           if(user == null){
               return null;
           }
           return User(uid: user.uid, displayName: user.displayName);
       }
    
       Stream<User> get onAuthStateChange {
           return Firebase.instance.onAuthStateChanged.map(_firebaseUserMapper);
       }
    
    
    }
    

    在您的页面屏幕中,您可以像下面这样使用

    class HandleAuth extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        final auth = Provider.of<Auth>(context, listen: false);
        return StreamBuilder<User>(
             stream: auth.onAuthStateChanged,
             builder: (BuildContext context, AsyncSnapshot snapshot) {
                 if( snapshot.connectionState == ConnectionState.active) {
                       User user = snapshot.data;
                       if (user == null ){
                           return WelcomeNewUserScreen();
                       }
                       return Provider<User>.value(
                            value: user,
                            child: ServicesAroundMe(),
                       );
                  }
                  return Scaffold(
                      body: Center(
                          child: CircularProgressIndicator();
                      ),
                  );
             }
        );
    }
    

    流将永远监听 currentUser/newUser 并导航到相应的页面。

    【讨论】:

    • 您好@Loic,非常感谢您的快速回答。我是 Flutter 的新手,所以我需要一些时间来弄清楚这一点。由于您使用 Provider.of 我知道我需要在 MyApp 中声明一个 ChangeNotifierProvider 然后将 mixin 添加到 Auth 类中?
    • 根据我的理解,使用参数listen: false 应该避免重建小部件,所以我不明白为什么在我调用updateUserInfo 时它会在我的代码中重建。
    • 当更新不会实时影响 UI 时,无需调用 notifyListener()。因此,您无需在 updateUserInfo() 中调用 notifyListener(),因为 Provider.of(context) 会更新状态(Firebase 也是状态管理器)。简而言之,要修复您的代码,请将 listen:false 添加到 Provider.of(context) 或删除 notifyListener() ;
    • 好的,谢谢。我认为仅将 listen: false 添加到 loggedUserInfo 提供程序就足够了,我不知道一旦调用了通知侦听器,所有提供程序都已重建。我测试了你的解决方案,它有效,就是这样。试图投票,但我是新人,没有足够的声誉:(
    • 不客气。如果您能接受正确的答案,我将不胜感激。
    猜你喜欢
    • 2020-06-29
    • 2020-08-11
    • 2020-07-19
    • 2020-04-10
    • 2018-10-19
    • 1970-01-01
    • 2021-02-15
    • 2019-06-13
    • 2021-06-04
    相关资源
    最近更新 更多