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(); } }); } }
问题原因及修复方案
核心原因
- 启动Timer后手动调用
context.go('/secondscreen'),直接修改路由栈,绕过了Wrapper的状态判断逻辑。 - 周期Timer会每隔10秒强制将
loggedIn设为true,即使在页面2把它改成false,下一个Timer周期又会覆盖回去,导致Wrapper检测到的loggedIn始终为true,无法切换回登录页。
修复步骤
- 移除手动路由跳转:删除screen #1中
context.go('/secondscreen')代码,完全依赖Wrapper的状态判断自动切换页面。 - 停止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) { } }
- 确保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
相关产品推荐
相关产品推荐

