【问题标题】:Flutter widget state not updating - throws 'Looking up a deactivated widget's ancestor is unsafe'Flutter 小部件状态未更新 - 抛出“查找已停用小部件的祖先是不安全的”
【发布时间】:2018-07-06 14:19:57
【问题描述】:

我正在尝试使用this example 在 Flutter 中实现登录/注销。登录工作正常,控制台输出为:

flutter: LOGIN WIDGET BUILD CONTEXT:
flutter: LoginScreen(dirty, state: LoginScreenState#552db)
flutter: _ctx:
flutter: LoginScreen(state: LoginScreenState#552db)

但注销后,我无法重新登录(上下文丢失)。注销并尝试重新登录后,onAuthStateChanged() 中的上下文在注销后丢失:

flutter: LOGIN WIDGET BUILD CONTEXT:
flutter: LoginScreen(dirty, state: LoginScreenState#d112e)
flutter: _ctx
flutter: LoginScreen

login.dart

class LoginScreen extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return new LoginScreenState();
  }
}
class LoginScreenState extends State<LoginScreen>

   BuildContext _ctx;

   @override
   onAuthStateChanged(AuthState state) {
      print("_ctx");
      print(_ctx.toString());   
      if(state == AuthState.LOGGED_IN) { 
         print("ready to login");
         Navigator.of(_ctx).pushReplacementNamed("/home");  
      } 
   }

   @override
   Widget build(BuildContext context) {
      _ctx = context;
      print("LOGIN WIDGET BUILD CONTEXT:");
      print(_ctx.toString());
   }
}

home.dart

class Settings extends StatelessWidget {

  @override
  Widget build(BuildContext context) => new Container(
    child: new ListView(
        children: <Widget>[
          new ListTile(
            //leading: Icon(Icons.map),
            title: new Text('About')
          ),
          new ListTile(
            //leading: Icon(Icons.photo_album),
            title: new Text('Logout'),
            onTap: () {
               Navigator.of(context).pushReplacementNamed("/login");
            }
          )
        ],
      ),
    );  

路线:

final routes = {
  '/login': (BuildContext context) => new LoginScreen(),
  '/home': (BuildContext context) => new Tabs(),
  '/' : (BuildContext context) => new LoginScreen(),
};

_ctx 没有在onAuthStateChanged() 中更新,有没有更好的方法来处理登录状态?

【问题讨论】:

  • 听起来你已经过时了context
  • 为什么要将上下文存储在本地字段中?
  • @RémiRousselet 原始示例代码使用了本地字段。
  • State 类中已存在本地化字段。无需自己制作
  • 您解决了吗?如果有,怎么做?

标签: flutter


【解决方案1】:

您正在重用旧的 BuildContext 实例。不要将实例保存在 build 方法中。你永远不应该这样做。

您的 LoginScreen 是一个 StatefulWidget 并且它的状态 (LoginScreenState) 已经具有 context 属性。请尝试改用它。

你还需要dispose监听器,例子中已经有dispose(AuthListener)方法可以使用了。

@override
void dispose() {
  super.dispose(); // always call super for dispose/initState
  AuthStateProvider().dispose(this);
}

一般来说,这个例子已经很老了,我建议找一个更新的教程。 Flutter 和 Dart 的发展速度非常快,2 年前的例子不是要走的路。

【讨论】:

    【解决方案2】:

    我为我的应用程序引用了相同的示例,问题是在 dispose() 期间 AuthStateProvider 没有“取消订阅”。尝试在 State 中实现以下内容:

    @override
    void dispose() {
      var authStateProvider = new AuthStateProvider();
      authStateProvider.unSubscribe(this);
    }
    

    【讨论】:

    • 何时/何地调用dispose()
    • @Aman auth.dart 中有 dispose 方法,但是我们应该在哪里添加这个 dispose 方法或取消订阅代码?
    • 谢谢阿曼,我已经成功实施了解决方案,并在下面发布了答案。
    【解决方案3】:

    正如 Aman Chu 的回答,在 login_screen.dart 和 home_screen.dart 中添加以下方法 每当我们切换屏幕时,它都会取消订阅监听器。

    @override
    void dispose() {
      var authStateProvider = new AuthStateProvider();
      authStateProvider.unSubscribe(this);
    }
    

    您还需要在 auth.dart 上有 unSubscribe 方法,如下所示:

    void unSubscribe(AuthStateListener listener) {
        for(var l in _subscribers) {
          if(l == listener)
             _subscribers.remove(l);
        }
      }
    

    【讨论】:

      猜你喜欢
      • 2020-08-08
      • 2021-06-03
      • 2021-04-29
      • 2021-07-28
      • 2020-06-19
      • 2021-11-15
      • 2020-12-26
      • 2022-06-26
      • 1970-01-01
      相关资源
      最近更新 更多