0

我的应用程序的目标是,当我点击start按钮时,应该启动一个计时器,该计时器将每 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
4

1 回答 1

1

最终,您的问题是您正在尝试在其中构造一个Timer对象toggleCompanyTimer并尝试在调用者中分配对该对象的引用。但是,Dart不是pass-by-reference,因此没有直接的方法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!");
    }
  }
}
于 2021-09-30T19:42:30.387 回答