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

Flutter周期性定时器重启后加速问题求助

定时器连续点击后加速异常的解决方法

问题现象

使用周期性Timer实现倒计时逻辑,启动时初始化定时器。但连续点击InkWell组件后,等待2秒重启定时器,会出现定时器加速运行的异常情况。

问题原因

  1. 旧定时器未彻底取消:连续快速点击InkWell时,多个异步延迟任务会在2秒后先后调用startTimer(),每次调用都会创建新的Timer实例,旧Timer可能未被正确取消,导致多个Timer同时执行,造成加速效果。
  2. 异步延迟期间并发冲突:连续点击会触发多个onTap回调,每个回调都会发起2秒延迟,延迟结束后都会执行startTimer(),导致Timer被重复创建。

修复方案

  • 点击时立即取消当前定时器,无需嵌套在setState中(取消操作不涉及UI状态更新)。
  • 添加状态标记,防止延迟期间重复触发定时器重启逻辑。
  • 在启动新定时器前强制取消旧实例,确保全局只有一个Timer在运行。

修复后的完整代码

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:wordgame/controller.dart';

void main() => runApp(const MyApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(title: 'Material App', home: MyWidget());
  }
}

class MyWidget extends StatefulWidget {
  const MyWidget({super.key});

  @override
  State<MyWidget> createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  @override
  void initState() {
    startTimer();
    super.initState();
  }

  late Duration duration;
  Timer? timer;
  bool _isRestarting = false; // 标记是否处于定时器重启流程中

  void startTimer() {
    // 启动新定时器前先取消旧实例,确保唯一性
    cancelTimer();
    
    duration = const Duration(seconds: 5);
    myController.selectMainWord();
    myController.selectValueWords();
    myController.selectedIndex = -1;

    timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if (duration.inSeconds > 0) {
        duration -= const Duration(seconds: 1);
        print(duration.inSeconds.toString());
      } else {
        duration = const Duration(seconds: 5);
      }
      setState(() {});
    });
  }

  void cancelTimer() {
    timer?.cancel();
    timer = null;
  }

  MyController myController = MyController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: [
          Container(
            child: Text(
              myController.mainWord,
              style: const TextStyle(fontSize: 36),
            ),
          ),
          Expanded(
            child: GridView.builder(
              gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
                  maxCrossAxisExtent: 200,
                  childAspectRatio: 3 / 2,
                  crossAxisSpacing: 20,
                  mainAxisSpacing: 20),
              itemCount: myController.valueWordList.length,
              itemBuilder: (BuildContext context, int index) {
                return InkWell(
                  onTap: () async {
                    // 处于重启流程时直接返回,避免重复触发
                    if (_isRestarting) return;
                    
                    myController.selectedIndex = index;
                    setState(() {});
                    
                    // 立即取消当前定时器
                    cancelTimer();
                    
                    _isRestarting = true;
                    await Future.delayed(const Duration(seconds: 2));
                    startTimer();
                    _isRestarting = false;
                  },
                  child: Container(
                    color: myController.isRightWord(index) &&
                            myController.selectedIndex == index
                        ? Colors.green
                        : Colors.blue,
                    alignment: Alignment.center,
                    child: Text(
                      myController.valueWordList[index],
                      style: const TextStyle(fontSize: 36),
                    ),
                  ),
                );
              },
            ),
          ),
          Container(
            child: Text(duration.inSeconds.toString()),
          )
        ],
      ),
    );
  }

  @override
  void dispose() {
    // 页面销毁时取消定时器,避免内存泄漏
    cancelTimer();
    super.dispose();
  }
}

关键修复点

  • 新增_isRestarting布尔标记,拦截延迟期间的重复点击请求。
  • 在startTimer开头强制调用cancelTimer(),确保每次启动新定时器前旧实例已被销毁。
  • 将取消定时器的操作从setState中移出,简化逻辑并保证执行时机。
  • 重写dispose方法,在页面销毁时清理定时器,避免内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 04:55:30