Flutter: Timer Issue During Testing

痞子三分冷 提交于 2020-01-01 16:02:10

问题


I have a periodic timer in one of my StatelessWidget's. Without going too much into detail, here is a snippet of code producing a timer:

class AnniversaryHomePage extends StatelessWidget {

  . . .  

  void _updateDisplayTime(StoreInheritedWidget inheritedWidget) {
    String anniversaryString = inheritedWidget.prefs.getString('anniversaryDate');
    inheritedWidget.store.dispatch(DateTime.parse(anniversaryString));
  }

  /// Widget Methods
  @override
  Widget build(BuildContext context) {
    final inheritedWidget = StoreInheritedWidget.of(context);
    new Timer.periodic(this.refreshRate, (Timer timer) => _updateDisplayTime(inheritedWidget));

    . . .
}

When I try pumping my the application's starting point into flutter test, I get the following error message:

══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following assertion was thrown running a test:
A periodic Timer is still running even after the widget tree was disposed.
'package:flutter_test/src/binding.dart': Failed assertion: line 668 pos 7:
'_fakeAsync.periodicTimerCount == 0'

The question is, am I using my Timer.periodic incorrectly? If not, how do I mitigate this error?


回答1:


The issue is that creating a Timer creates a resource which must be disposed, and therefore your widget is actually Stateful and not stateless. Specifically, the build method may be called 60 times a second (or more if the platform is 120fps). Any less is just an optimization.

You have a very critical bug in your code - the build method is creating a new Timer every time it is called. And since your timers are never cancelled, you could have hundreds or potentially thousands of events dispatched to your store.

To avoid situations like this, the framework has a State class, with an initState and dispose lifecycle. The framework promises that if it rebuilds your widget, it won't call initState more than once and it will always call dispose. This allows you to create a Timer once and reuse it on subsequent calls to build.

For example, you can move most of your logic to the State like below. Leave the refreshRate on the widget, and you can even cancel and update your timer using the didUpdateWidget lifecycle too.

class AnniversaryHomePage extends StatefulWidget {
  @override
  State createState() => new AnniversaryHomePageState();
}

class AnniversaryHomePageState extends State<AnniversaryHomePage> {
  Timer _timer;

  @override
  void initState() {
    _timer = new Timer.periodic(widget.refreshRate, 
      (Timer timer) => _updateDisplayTime(inheritedWidget));
    super.initState();
  }

  @override
  void dispose() {
    _timer.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    ...
  }
}


来源:https://stackoverflow.com/questions/49952901/flutter-timer-issue-during-testing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!