You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中StatefulWidget调用dispose后仍不消失的问题排查

问题排查:登出后再次登录页面不跳转异常

具备Flutter开发经验,熟悉组件生命周期,当前代码实现了登录相关逻辑,出现如下异常:首次安装应用后登录正常,页面跳转无问题;但用户登出后再次登录,虽已确认登录成功(Login页面的dispose方法已调用),但页面并未自动跳转,需重启应用才能恢复正常。


相关代码片段

1. Login页面组件代码

class Login extends StatefulWidget {
  const Login({super.key});

  @override
  State<Login> createState() => _LoginWidgetState();
}

class _LoginWidgetState extends State<Login> with WidgetsBindingObserver {

  @override
  void initState() {
    super.initState();
    Connectivity().checkConnectivity().then((connectivityResult) {
      setState(() {
        isConnected = connectivityResult != ConnectivityResult.none;
      });
    });

    _connectivitySubscription = Connectivity()
        .onConnectivityChanged
        .listen((ConnectivityResult result) {
      if (result == ConnectivityResult.none) {
        setState(() {
          isConnected = false;
        });
      } else {
        setState(() {
          isConnected = true;
        });
      }
    });
    print("init");
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    print("dispose-login");
    _connectivitySubscription.cancel();
    WidgetsBinding.instance?.removeObserver(this);
    super.dispose();
  }

... more code
}

2. AuthProvider中的login方法

login(String email, String password) async {
ApiService apiService = ApiService(this);

final response = await apiService.postUnauthenticated(
    "", {"email": email, "password": password});

if (response.statusCode == 200) {
  final jsonResponse = jsonDecode(response.body);
  final token = jsonResponse['token'] as String;
  apiService.setAuthToken(token);
  saveAuthorizationToken(token);
  _token = token;
  _status = Status.authenticated;
  print("Status: Authenticated");
  notifyListeners();
} else {
  apiService.removeAuthToken();
  removeAuthorizationToken();
  _status = Status.unauthenticated;
  notifyListeners();
}

notifyListeners();
getAccessToken();
return response.statusCode;
}

3. App页面根据状态切换页面逻辑

@override
  Widget build(BuildContext context) {
    final authProvider = Provider.of<AuthProvider>(context, listen: false);
    return Consumer<AuthProvider>(
      builder: (context, user, child) {
        switch (user.status) {
          case Status.hasBikes:
            print("hasBike");
            return const HomePage();
          case Status.uninitialized:
            print("uninitialized");
            return const InitView();
          case Status.unauthenticated:
            print("unauth");
            return const Login();
          case Status.authenticated:
            print("auth");
            return const LoadView();
          case Status.unpaired:
            print("unpair");
            return const PairingHome(hasError: false);
          case Status.error:
            print("error");
            return const ServerError();
          default:
            return const InitView();
        }
      },
    );
  }

4. 登出逻辑代码

onTap: () async {
              Provider.of<AuthProvider>(context, listen: false).logout();
              Navigator.push(
                context,
                MaterialPageRoute(builder: (context) => const Login()),
              );
            }

问题原因分析

  1. Navigator栈叠加问题:登出时使用Navigator.push跳转Login页面,导致路由栈中存在多个页面实例(比如之前的Home页面+新的Login页面)。当再次登录成功触发notifyListeners()时,App页面的路由切换逻辑虽然会重建,但此时Navigator栈顶部已经是Login页面,覆盖了状态变更后的页面跳转。

  2. 状态通知冗余问题:login方法中在if-else分支内已经调用了notifyListeners(),分支外又重复调用一次,可能导致状态通知顺序混乱,影响页面响应。

  3. 登出状态同步问题:若logout方法未正确设置_status = Status.unauthenticated并调用notifyListeners(),会导致后续登录的状态变更无法被正确监听。


解决方案

1. 修正登出时的路由跳转方式

使用Navigator.pushAndRemoveUntil清空路由栈,确保Login页面是栈中唯一页面,这样状态变更时路由切换逻辑能正常生效:

onTap: () async {
  Provider.of<AuthProvider>(context, listen: false).logout();
  Navigator.pushAndRemoveUntil(
    context,
    MaterialPageRoute(builder: (context) => const Login()),
    (route) => false, // 移除所有之前的路由
  );
}

2. 优化login方法的状态通知

移除重复的notifyListeners(),避免状态通知混乱:

login(String email, String password) async {
  ApiService apiService = ApiService(this);

  final response = await apiService.postUnauthenticated(
      "", {"email": email, "password": password});

  if (response.statusCode == 200) {
    final jsonResponse = jsonDecode(response.body);
    final token = jsonResponse['token'] as String;
    apiService.setAuthToken(token);
    saveAuthorizationToken(token);
    _token = token;
    _status = Status.authenticated;
    print("Status: Authenticated");
  } else {
    apiService.removeAuthToken();
    removeAuthorizationToken();
    _status = Status.unauthenticated;
  }
  notifyListeners();
  getAccessToken();
  return response.statusCode;
}

3. 确保logout方法的状态同步

检查logout方法实现,确保正确更新状态并通知监听者:

void logout() {
  _token = null;
  _status = Status.unauthenticated;
  removeAuthorizationToken();
  apiService.removeAuthToken();
  notifyListeners();
}

内容的提问来源于stack exchange,提问作者Ilian Gion Häsler

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 19:17:15