【问题标题】:Flutter Provider rebuilt widget before parent's ConsumerFlutter Provider 在父级 Consumer 之前重建了小部件
【发布时间】:2021-03-03 17:19:10
【问题描述】:

我的提供程序包有问题。

我希望能够清除提供者ChangeNotifier 类的属性(_user = null)(这是一个注销功能)。

问题是当我从一个使用来自此提供程序的信息的小部件中执行此操作时。

我的主要应用是这样的:

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => AuthProvider(),
      builder: (context, _) => App(),
    ),
  );
}

class App extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Consumer<AuthProvider>(builder: (_, auth, __) {
      Widget displayedWidget;
      switch (auth.loginState) {
        case ApplicationLoginState.initializing:
          displayedWidget = LoadingAppScreen();
          break;
        case ApplicationLoginState.loggedIn:
          displayedWidget = HomeScreen();
          break;
        case ApplicationLoginState.loggedOut:
        default:
          displayedWidget = AuthenticationScreen(
            signInWithEmailAndPassword: auth.signInWithEmailAndPassword,
            registerAccount: auth.registerAccount,
          );
      }

      return MaterialApp(
        title: 'My App',
        home: displayedWidget,
        routes: {
          ProfileScreen.routeName: (_) => ProfileScreen(),
        },
      );
    });
  }
}

我的 Provider 类(简体):

class AuthProvider extends ChangeNotifier {
  ApplicationLoginState _loginState;
  ApplicationLoginState get loginState => _loginState;

  bool get loggedIn => _loginState == ApplicationLoginState.loggedIn;

  User _user;
  User get user => _user;

  void signOut() async {
    // Cleaning the user which lead to the error later
    _user = null;
    _loginState = ApplicationLoginState.loggedOut;
    notifyListeners();
  }
}

可通过命名路线访问的“我的个人资料”屏幕

class ProfileScreen extends StatelessWidget {
  static const routeName = '/profile';

  @override
  Widget build(BuildContext context) {
    final User user = Provider.of<AuthProvider>(context).user;

    return Scaffold(
      // drawer: AppDrawer(),
      appBar: AppBar(
        title: Text('Profile'),
      ),
      body: Column(
        children: [
          Text(user.displayName),
          FlatButton(
            child: Text('logout'),
            onPressed: () {
              // Navigator.pushAndRemoveUntil(
              //   context,
              //   MaterialPageRoute(builder: (BuildContext context) => App()),
              //   ModalRoute.withName('/'),
              // );
              Provider.of<AuthProvider>(context, listen: false).signOut();
            },
          )
        ],
      ),
    );
  }
}

当我从个人资料屏幕单击注销按钮时,我不明白为什么会出现错误:

由于我在我的应用程序的顶层使用Consumer&lt;AuthProvider&gt;(这包括我的路线(ProfileScreen),我认为它会重定向到AuthenticationScreen,因为displayedWidget 是从switch 计算出来的.

但似乎重建ProfileScreen首先导致错误。 displayedWidget 的更改似乎没有任何影响。

我对 Provider 还是很陌生。我不明白我在这里的提供者模式中缺少什么?我的App / Consumer 用错了吗?

我希望你能帮助我理解我在这里做错了什么!谢谢。

注意:注释的Navigator.pushAndRemoveUntil 正确重定向到登录屏幕,但我可以在几毫秒内看到错误屏幕。

【问题讨论】:

    标签: flutter flutter-provider


    【解决方案1】:

    您的用户为空,您尝试获取他的姓名。您需要在使用前检查它。它看起来像这样:

       user == null ?
         Text("User Not Found!"),
         Text(user.displayName),
    

    【讨论】:

    • 我知道user 为空。我的问题更多是关于为什么ProfileScreen 小部件在顶级App(使用Consumer)之前重建。由于ProfileScreen 是一条私有路由,因此只有在我有用户时才应该显示它。因此,如果我的用户是否为空,我不想签入该小部件。
    【解决方案2】:

    来自Provider.of的提供者API参考:

    获取其小部件树上最近的 Provider 并返回其 价值。

    如果listen为真,以后的值变化会触发一个新的State.build 到小部件,以及 StatefulWidget 的 State.didChangeDependencies。

    所以我认为当 _user 变量被修改时,您的个人资料屏幕中的 final User user = Provider.of&lt;AuthProvider&gt;(context).user; 行会调用重建,然后您的 ProfileScreen 中的 _user 可以为空。

    在清除 _user 变量之前,您是否尝试过 Navigator.pop 配置文件屏幕?

    【讨论】:

      猜你喜欢
      • 2020-12-22
      • 2019-12-09
      • 2020-07-25
      • 2019-12-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-14
      • 2020-11-29
      相关资源
      最近更新 更多