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

Flutter页面切换取消Timer触发LateInitializationError的解决方法

问题:切换NavigationRail页面时取消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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 16:24:58