Flutter页面切换取消Timer触发LateInitializationError的解决方法
想要通过NavigationRail切换页面时停止MonitorPage中的Timer,但执行切换操作时抛出以下异常:
======== Exception caught by gesture =============================================================== The following LateError was thrown while handling a gesture: LateInitializationError: Field 'timer' has not been initialized. ...
问题代码
//imports late Timer timer; class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { var selectedIndex = 0; @override Widget build(BuildContext context) { Widget page; switch (selectedIndex) { case 0: page = const MonitorPage(); case 1: page = const ReportPage(); case 2: page = const Placeholder(); default: throw UnimplementedError("not found"); } return LayoutBuilder(builder: (context, constraints) { return Scaffold( body: Row( children: [ SafeArea( child: NavigationRail( extended: constraints.maxWidth >= 600, destinations: const [ NavigationRailDestination( icon: Icon(Icons.home), label: Text('main'), ), NavigationRailDestination( icon: Icon(Icons.list), label: Text('report'), ), NavigationRailDestination( icon: Icon(Icons.settings), label: Text("setting"), ), ], selectedIndex: selectedIndex, onDestinationSelected: (value) { setState(() { if (selectedIndex == 0) { timer.cancel(); // 尝试取消timer } selectedIndex = value; }); }, ), ), Expanded( child: Container( color: Theme.of(context).colorScheme.primaryContainer, child: page, ), ) ], ), ); }); } } class _MonitorPageState extends State<MonitorPage> { late String _time; @override void initState() { _time = "${DateTime.now().year.toString().padLeft(4, '0')}-${DateTime.now().month.toString().padLeft(2, '0')}-${DateTime.now().day.toString().padLeft(2, '0')} ${DateTime.now().hour.toString().padLeft(2, '0')}:${DateTime.now().minute.toString().padLeft(2, '0')}:${DateTime.now().second.toString().padLeft(2, '0')}"; timer = Timer.periodic(const Duration(seconds: 1), (timer) { // 给全局timer赋值 _updateTime(); }); super.initState(); } @override Widget build(BuildContext context) { // ... 页面布局 } }
错误原因
- 全局变量
timer的状态耦合逻辑错误:父组件_MyHomePageState依赖子组件_MonitorPageState初始化全局timer,这种跨组件的状态依赖会导致状态不一致——比如热重载、页面重建时,全局timer可能未初始化就被调用;或者子页面销毁后,全局变量仍持有无效引用。 - 切换页面时父组件尝试访问
timer,但该变量可能因未完成初始化(如子页面initState未执行)触发LateInitializationError。
修复方案
1. 子组件自行管理Timer生命周期
将Timer的声明和控制移到_MonitorPageState内部,利用State的dispose方法自动取消Timer,无需父组件干预。
修改后的完整代码
//imports import 'dart:async'; import 'package:flutter/material.dart'; class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { var selectedIndex = 0; @override Widget build(BuildContext context) { Widget page; switch (selectedIndex) { case 0: page = const MonitorPage(); case 1: page = const ReportPage(); case 2: page = const Placeholder(); default: throw UnimplementedError("not found"); } return LayoutBuilder(builder: (context, constraints) { return Scaffold( body: Row( children: [ SafeArea( child: NavigationRail( extended: constraints.maxWidth >= 600, destinations: const [ NavigationRailDestination( icon: Icon(Icons.home), label: Text('main'), ), NavigationRailDestination( icon: Icon(Icons.list), label: Text('report'), ), NavigationRailDestination( icon: Icon(Icons.settings), label: Text("setting"), ), ], selectedIndex: selectedIndex, onDestinationSelected: (value) { setState(() { selectedIndex = value; // 移除对全局timer的依赖,无需在这里取消 }); }, ), ), Expanded( child: Container( color: Theme.of(context).colorScheme.primaryContainer, child: page, ), ) ], ), ); }); } } // 补全MonitorPage的StatefulWidget定义 class MonitorPage extends StatefulWidget { const MonitorPage({super.key}); @override State<MonitorPage> createState() => _MonitorPageState(); } class _MonitorPageState extends State<MonitorPage> { late String _time; late Timer _timer; // 改为子组件私有变量 @override void initState() { super.initState(); _updateTime(); _timer = Timer.periodic(const Duration(seconds: 1), (timer) { _updateTime(); }); } void _updateTime() { final now = DateTime.now(); setState(() { _time = "${now.year.toString().padLeft(4, '0')}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')} ${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}:${now.second.toString().padLeft(2, '0')}"; }); } @override void dispose() { _timer.cancel(); // 页面销毁时自动取消Timer,避免内存泄漏 super.dispose(); } @override Widget build(BuildContext context) { return Center( child: Text(_time, style: const TextStyle(fontSize: 18)), ); } } class ReportPage extends StatelessWidget { const ReportPage({super.key}); @override Widget build(BuildContext context) { return const Center(child: Text("Report Page")); } }
关键修改点
- 删除全局
timer变量,将Timer改为_MonitorPageState的私有变量_timer,确保状态仅由子组件管理。 - 在
_MonitorPageState的dispose方法中调用_timer.cancel(),当页面被切换移除时自动清理Timer,避免内存泄漏。 - 移除父组件
onDestinationSelected中对timer.cancel()的调用,消除跨组件的状态依赖。
内容的提问来源于stack exchange,提问作者知識日常 Know Scratcher
相关产品推荐
相关产品推荐

