为何在Widget #1启动的定时器无法在Widget #2中持续运行?
Flutter Provider全局登录状态定时校验仅执行一次问题修复
我使用provider实现了GlobalsProvider类管理全局变量,核心是维护用户登录状态:包含loggedIn布尔变量,以及每10秒调用SQL Server存储过程校验登录有效性的定时任务。之前在login.dart中运行正常,移至全局Provider后,定时任务仅执行一次,未按10秒间隔触发,已通过打印日志确认该问题。
相关代码
GlobalsProvider
Timer? timer; void startLoginStream(/*...parms...*/) { timer = Timer.periodic(const Duration(seconds: 10), (Timer t) => loginStream(/*...parms...*/); notifyListeners(); } void loginStream(/*...parms...*/) async { final previousLoggedIn = loggedIn; await sqlConnect(/*...parms...*/); var res = await SqlConn.readData("exec spHandheldLogin"); res = json.decode(res); res = res[0]; //存储过程只会返回一行数据,取第一个元素无问题 var loginState = res['LoginState']; if (kDebugMode) {print('${StaticGlobals.tagATS} loginStream loginState: ${loginState.toString()}');} if (loginState == 1) { loggedIn = true; loggedInUser = username; } else { loggedIn = false; loggedInUser = ''; } await sqlDisconnect(); //仅登录状态变化时通知监听者 if (loggedIn != previousLoggedIn) { notifyListeners(); } }
login.dart
@override Widget build(BuildContext context) { return Consumer<GlobalsProvider>( builder: (context, value, child) => Scaffold(appBar: AppBar( title: const Text('Login'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: TextButton( onPressed: () async { final status = await value.login(/*...parms...*/); if (value.loggedIn == true) { //登录成功后启动定时校验 value.loginStream(/*...parms...*/); } else { Fluttertoast.showToast( msg: "ERROR", toastLength: Toast.LENGTH_LONG, ); } }, child: const Text('Submit'), ), ) //...
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 Forms(); //登录后页面 } else { return const Login(); //登录页 } }); } }
问题原因与修复方案
- 核心问题:登录成功后调用的是单次执行的
loginStream(),而非启动定时任务的startLoginStream(),导致仅执行一次校验,未触发周期性定时器。 - 修复步骤:
- 修改
login.dart中登录成功分支的代码,将value.loginStream(/*...parms...*/)替换为value.startLoginStream(/*...parms...*/),启动真正的周期性任务。 - 优化
startLoginStream方法,添加定时器判空取消逻辑,避免重复创建定时器:void startLoginStream(/*...parms...*/) { //先取消已有定时器,防止重复创建 timer?.cancel(); timer = Timer.periodic(const Duration(seconds: 10), (Timer t) => loginStream(/*...parms...*/)); notifyListeners(); } - 添加资源清理逻辑,在Provider销毁时取消定时器,避免内存泄漏:
@override void dispose() { timer?.cancel(); super.dispose(); }
- 修改
内容的提问来源于stack exchange,提问作者TheMortiestMorty
相关产品推荐
相关产品推荐

