Flutter小部件状态未更新-引发“查找已停用的小部件的祖先是不安全的”

时间:2018-07-06 14:19:58

标签: flutter

我正在尝试使用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()没有更新,有没有更好的方法来处理登录状态?

3 个答案:

答案 0 :(得分:4)

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

您的LoginScreenStatefulWidget,它的状态(LoginScreenState)已经具有context属性。尝试改用它。

您还需要配置侦听器,该示例已经具有可以使用的dispose(AuthListener)方法。

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

总的来说,这个例子已经很老了,我建议您找到一个最新的教程。 Flutter和Dart的发展非常迅速,要走2年之久的榜样并不是路要走。

答案 1 :(得分:1)

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);
    }
  }

答案 2 :(得分:0)

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

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