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

Provider中Timer触发变量更新后页面未正常跳转问题

问题描述

登录页(screen #1)点击按钮启动10秒周期的全局Timer,Timer会把Provider中的loggedIn设为true,之后跳转到页面2(screen #2)。在页面2点击按钮可将loggedIn设为false,终端显示变量已变更,但应用并未自动返回登录页。预期逻辑是loggedIn=true时显示页面2,false时显示登录页。直接通过按钮修改变量的测试正常,仅Timer场景下出现异常。

相关代码

GlobalsProvider

void startLoginStream() {
    timer = Timer.periodic(const Duration(seconds: 10), (Timer t) => setToTrue());

    notifyListeners();
}

void setToTrue() {
    try {
      loggedIn = true;
      loggedInUser = 'user';

      notifyListeners();
    } catch (e) {

    }
}

void setToFalse() {
    try {
      loggedIn = false;
      loggedInUser = '';

      notifyListeners();
    } catch (e) {

    }
}

screen #1(登录页)

@override
Widget build(BuildContext context) {
  return Consumer<GlobalsProvider>(
      builder: (context, value, child) => Scaffold(
          appBar: AppBar(
            title: const Text('Login'),
          ),
          body: Center(
            child: Column(
              children: [
                Expanded(
                  child: TextButton(
                    onPressed: () async {
                        //其他异步操作...
                        value.startLoginStream();
                        context.go('/secondscreen');
                    },
                    child: const Text('TRUE TIMER'),
                  )
                 //...

screen #2(表单页)

@override
Widget build(BuildContext context) {
  return Consumer<GlobalsProvider>(
      builder: (context, value, child) => Scaffold(
          appBar: AppBar(
            title: const Text('Forms'),
          ),
        body: Center(
          child: Column(
            children: [
              Expanded(
                flex: 2,
                child: TextButton(
                  onPressed: () async {
                    value.setToFalse();
                  },
                  child: const Text('FALSE'),
                ),
              ],
            ),
          ),
        ),
      )
  );
}

wrapper.dart(路由控制)

class Wrapper extends StatelessWidget {
  const Wrapper({super.key});

  @override
  Widget build(BuildContext context) {
    return Consumer<GlobalsProvider>(builder: (context, value, child) {
      if (value.loggedIn == true) {
        return const Screen2();
      } else {
        return const Screen1(); 
      }
    });
  }
}
问题原因及修复方案

核心原因

  1. 启动Timer后手动调用context.go('/secondscreen'),直接修改路由栈,绕过了Wrapper的状态判断逻辑。
  2. 周期Timer会每隔10秒强制将loggedIn设为true,即使在页面2把它改成false,下一个Timer周期又会覆盖回去,导致Wrapper检测到的loggedIn始终为true,无法切换回登录页。

修复步骤

  1. 移除手动路由跳转:删除screen #1中context.go('/secondscreen')代码,完全依赖Wrapper的状态判断自动切换页面。
  2. 停止Timer避免重复赋值:修改GlobalsProvider,在调用setToFalse()时停止Timer,同时启动Timer前先清理旧实例:
// 在Provider中声明Timer变量
Timer? timer;

void startLoginStream() {
    // 启动前停止已有Timer,防止重复创建
    timer?.cancel();
    timer = Timer.periodic(const Duration(seconds: 10), (Timer t) => setToTrue());
    // 此处无需调用notifyListeners(),setToTrue内已包含该逻辑
}

void setToFalse() {
    try {
      // 停止Timer,避免持续覆盖loggedIn值
      timer?.cancel();
      timer = null;
      
      loggedIn = false;
      loggedInUser = '';

      notifyListeners();
    } catch (e) {

    }
}
  1. 确保Wrapper为路由根组件:应用路由结构需以Wrapper作为首页,让它全权控制页面切换,示例如下:
MaterialApp(
  home: const Wrapper(),
  // 使用GoRouter的写法
  routerConfig: GoRouter(
    routes: [
      GoRoute(
        path: '/',
        builder: (context, state) => const Wrapper(),
      ),
    ],
  ),
);

修改后,Timer触发setToTrue()时,Wrapper会监听状态变化自动切换到screen #2;点击screen #2按钮调用setToFalse()时,Timer停止,loggedIn设为false,Wrapper自动切换回screen #1。

内容的提问来源于stack exchange,提问作者TheMortiestMorty

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 04:35:54