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

Flutter Provider场景下Timer定时器无法取消问题咨询

问题解决方案

核心问题根因

  1. Dart值传递特性导致Timer实例未更新:Dart中参数按值传递,你在toggleCompanyTimer方法内给传入的timer参数赋值Timer.periodic,仅修改了方法内的局部变量,外部存储的timer实例完全没有更新,点击stop时拿到的还是初始的空Timer甚至null
  2. Timer实例定义位置错误:你把Timer timer1定义在build方法内,每次页面触发重绘都会重新创建一个全新的空Timer实例,之前启动的Timer实例直接丢失,根本拿不到用来取消
  3. showNotification参数传递错误:调用toggleTimer时你直接写了showNotification('title', 'body'),这是直接执行该方法,把返回的Future对象传给了定时器回调,定时器要执行时尝试调用这个Future就会报错。

分步修复方案

第一步:把Timer实例托管到StrategyProvider中

不要在页面层或者组件层存储Timer,直接放到Provider里统一管理,避免实例丢失,修改StrategyProvider.dart代码:

class StrategyProvider with ChangeNotifier {
  // 切换状态
  bool _companyStratOneToggle = false;
  bool get companyStratOneToggle => _companyStratOneToggle;
  // 新增:存储当前生效的Timer实例
  Timer? _companyStratOneTimer;
  ChartLiveData _companyChartLiveData = ChartLiveData(
    ...
  );

  ChartLiveData get companyChartLiveData => _companyChartLiveData;
  
  // 修改:不再接收外部timer参数,直接操作内部存储的Timer实例
  toggleCompanyTimer(ref, Function showNotification, bool toggle) {
    if (toggle == false) {
      // 先取消之前可能存在的Timer避免重复启动
      _companyStratOneTimer?.cancel();
      _companyStratOneTimer = Timer.periodic(
        const Duration(seconds: 5),
        (Timer t) {
          fetchCompanyLiveStockData( 
            ref,
            showNotification,
          );
        },
      );
    } else {
      _companyStratOneTimer?.cancel();
      _companyStratOneTimer = null;
      print("Timer Canceled!");
    }
  }

  // 不同策略的切换状态设置方法
  setCompanyStratOneToggle() {
    _companyStratOneToggle = !_companyStratOneToggle;
    notifyListeners();
  }

  // 可选:页面销毁时调用清理Timer
  void disposeTimer() {
    _companyStratOneTimer?.cancel();
    _companyStratOneTimer = null;
  }
}

第二步:修改主页面代码,删除build内的Timer定义

class StrategyOneScreen extends StatefulWidget {
  const StrategyOneScreen({Key? key}) : super(key: key);

  @override
  State<StrategyOneScreen> createState() => _StrategyOneScreenState();
}

class _StrategyOneScreenState extends State<StrategyOneScreen> {
  @override
  void dispose() {
    // 页面销毁时清理Timer避免内存泄漏
    Provider.of<StrategyProvider>(context, listen: false).disposeTimer();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // 删掉这里的Timer定义,不需要再传Timer给StrategyScreen
    return StrategyScreen(
      stratName: 'Free Strategy',
      toggle: Provider.of<StrategyProvider>(
        context,
        listen: true,
      ).companyStratOneToggle,
      toggleTimer: Provider.of<StrategyProvider>(
        context,
        listen: false,
      ).toggleCompanyTimer,
      setToggle: Provider.of<StrategyProvider>(
        context,
        listen: false,
      ).setCompanyStratOneToggle,
      chartLiveData: Provider.of<StrategyProvider>(
        context,
        listen: true,
      ).companyChartLiveData,
    );
  }
}

第三步:修改StrategyScreen组件的参数和按钮点击逻辑

  1. 删掉StrategyScreen构造函数里的timer参数
  2. 修改按钮点击事件,把showNotification改成函数传递,不要直接执行:
class StrategyScreen extends StatefulWidget {
  const StrategyScreen({
    Key? key,
    required this.stratName,
    required this.toggle,
    required this.toggleTimer,
    required this.setToggle,
    required this.chartLiveData,
  }) : super(key: key);

  final String stratName;
  final bool toggle;
  final Function toggleTimer;
  final Function setToggle;
  final ChartLiveData chartLiveData;

  @override
  _StrategyScreenState createState() => _StrategyScreenState();
}

class _StrategyScreenState extends State<StrategyScreen> {

 @override
  Widget build(BuildContext context) {
    return Scaffold(
      ...
      Row(
            children: [
              Expanded(
                child: Center(
                  child: FloatingActionButton.extended(
                    heroTag: 'btn1',
                    onPressed: widget.toggle == false
                        ? () => {
                              widget.toggleTimer(
                                ref,
                                // 改成函数包裹,不要直接执行showNotification
                                () => showNotification('title', 'body'),
                                widget.toggle,
                              ),
                              widget.setToggle(),
                            }
                        : null,
                    label: const Text('Start'),
                    icon: const Icon(Icons.launch),
                    backgroundColor: Colors.greenAccent,
                  ),
                ),
              ),
              Expanded(
                child: Center(
                  child: FloatingActionButton.extended(
                    heroTag: 'btn2',
                    onPressed: widget.toggle
                        ? () => {
                              widget.toggleTimer(
                                ref,
                                () => showNotification('title', 'body'),
                                widget.toggle,
                              ),
                              widget.setToggle(),
                            }
                        : null,
                    label: const Text('Stop'),
                    icon: const Icon(Icons.stop),
                    backgroundColor: Colors.pink,
                  ),
                ),
              ),
            ],
          ),

额外优化点

  • 多个策略可以在Provider里用Map存储不同策略对应的Timer实例,不用每个策略单独写变量
  • 启动Timer前先判断当前Timer是否活跃,避免重复创建多个定时器
  • 页面dispose生命周期必须调用Timer的cancel方法,避免内存泄漏和后台继续执行请求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 14:54:03