Таймер не отменяется - провайдер Flutter

Цель моего приложения - когда я нажимаю кнопку, должен запускаться таймер, который запускает функцию каждые 5 секунд, а когда stoppedтаймер должен отменить и остановить выполнение этой функции. (Теперь у меня есть разные экраны для одной и той же цели, поэтому я сделал общий экран и теперь отправляю Timerтаймер к этому общему конструктору класса с отслеживанием состояния). Итак, проблема в том, что таймер не отменяется, когда я нажимаю startфункция выполняется каждые 5 секунд, но не останавливается. Ниже я предоставил несколько фрагментов кода, относящихся к моему запросу.

Главный экран:

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

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

class _StrategyOneScreenState extends State<StrategyOneScreen> {
  @override
  Widget build(BuildContext context) {
    Timer timer1 = Timer(Duration(), () {});

    return StrategyScreen(
      stratName: 'Free Strategy',
      timer: timer1,
      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:

      class StrategyScreen extends StatefulWidget {
  const StrategyScreen({
    Key? key,
    required this.stratName,
    required this.timer,
    required this.toggle,
    required this.toggleTimer,
    required this.setToggle,
    required this.chartLiveData,
  }) : super(key: key);

  final stratName;
  final timer;
  final toggle;
  final toggleTimer;
  final setToggle;
  final chartLiveData;

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

class _StrategyScreenState extends State<StrategyScreen> {

 @override
  Widget build(BuildContext context) {
    print("Timer: ${widget.timer}"); // console logs:=> Timer: null
    return Scaffold(
      ...
      Row(
            children: [
              Expanded(
                child: Center(
                  child: FloatingActionButton.extended(
                    heroTag: 'btn1',
                    onPressed: widget.toggle == false
                        ? () => {
                              widget.toggleTimer(
                                ref,
                                showNotification('title', 'body'),
                                widget.timer,
                                widget.toggle,
                              ),
                              widget.setToggle(),
                            }
                        : null,
                    label: Text('Start'),
                    icon: Icon(Icons.launch),
                    backgroundColor: Colors.greenAccent,
                  ),
                ),
              ),
              Expanded(
                child: Center(
                  child: FloatingActionButton.extended(
                    heroTag: 'btn2',
                    onPressed: widget.toggle
                        ? () => {
                              widget.toggleTimer(
                                ref,
                                showNotification('title', 'body'),
                                widget.timer,
                                widget.toggle,
                              ),
                              widget.setToggle(),
                            }
                        : null,
                    label: Text('Stop'),
                    icon: Icon(Icons.stop),
                    backgroundColor: Colors.pink,
                  ),
                ),
              ),
            ],
          ),

StrategyProvider.dart :

      class StrategyProvider with ChangeNotifier {
  // Toggles
  bool _companyStratOneToggle = false;
  bool get companyStratOneToggle => _companyStratOneToggle;
  ChartLiveData _companyChartLiveData = ChartLiveData(
    ...
  );

  ChartLiveData get companyChartLiveData => _companyChartLiveData;
  
  toggleCompanyTimer(ref, showNotification, timer, bool toggle) {
    if (toggle == false) {
      timer = Timer.periodic(
        Duration(seconds: 5),
        (Timer t) => {
          fetchCompanyLiveStockData( // The function I want to run every 5 seconds
            ref,
            showNotification,
          ),
        },
      );
    } else {
      timer.cancel();
      print("Timer Canceled!");
    }
  }

  // Toggle setters for different strategies
  setCompanyStratOneToggle() {
    _companyStratOneToggle = !_companyStratOneToggle;
    notifyListeners();
  }
}

Как я уже говорил ранее, я могу запустить таймер, но не могу его отменить (так как он null) и продолжает работать каждые 5 секунд. Ниже представлен вывод консоли:

      Unhandled Exception: NoSuchMethodError: Class 'Future<dynamic>' has no instance method 'call'.
E/flutter ( 2746): Receiver: Instance of 'Future<dynamic>'
E/flutter ( 2746): <asynchronous suspension>
E/flutter ( 2746):
I/flutter ( 2746): Timer: null

И когда я нажимаю cancel кнопка:

      The following NoSuchMethodError was thrown while handling a gesture:
The method 'cancel' was called on null.
Receiver: null
Tried calling: cancel()

When the exception was thrown, this was the stack
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:63:5)
#1      StrategyProvider.toggleSbinTimer
package:myapp/…/global/strategy_provider.dart:67
#2      _StrategyScreenState.build.<anonymous closure>
package:myapp/…/global/strategy_screen.dart:152

1 ответ

Решение

В конечном итоге ваша проблема в том, что вы пытаетесь создать объект и пытаетесь назначить ссылку на этот объект в вызывающей стороне. Однако Dart не передается по ссылке , поэтому прямого пути для toggleCompanyTimer сделать это.

Вам было бы намного лучше, если бы ваш StrategyProvider класс владел и управлял Timerобъект полностью сам по себе. Например:

      class StrategyProvider with ChangeNotifier {
  Timer? timer;

  // ... Other code...

  void toggleCompanyTimer(ref, showNotification, bool stopTimer) {
    // Cancel any existing Timer before creating a new one.
    timer?.cancel();
    timer = null;

    if (!stopTimer) {
      timer = Timer.periodic(
        Duration(seconds: 5),
        (Timer t) => {
          fetchCompanyLiveStockData(
            ref,
            showNotification,
          ),
        },
      );
    } else {
      print("Timer Canceled!");
    }
  }
}
Другие вопросы по тегам