pumpForDuration function
- WidgetTester tester, {
- required Duration duration,
- Duration step = const Duration(milliseconds: 100),
Pumps for a total duration, advancing time in steps of step.
Works correctly in both binding types:
-
Automated binding (FakeAsync): advances the fake clock in
stepincrements untildurationis reached, firing timers and rendering frames along the way. -
Live/Preview binding: calls
tester.pump()(no duration) in a tight loop untildurationwall-clock time has elapsed. Eachpump()renders immediately and yields to the event loop, letting real async work (API responses, state updates) progress between frames.Why not
pump(duration)orFuture.delayed+pump()? PreviewTestBinding'spump(duration)creates a Timer but only sets_expectingFrame = trueafter the Timer fires. If a framework-scheduled frame (from setState/scheduleFrame) runs during the Timer wait, it sees_expectingFrame = falsewithfadePointersframe policy, skipssuper.handleBeginFrame(), and leaves_hasScheduledFramestale. This prevents subsequentscheduleFrame()calls from requesting a new engine frame — deadlockingpump(). Callingpump()without duration avoids this by setting_expectingFrame = truesynchronously beforescheduleFrame(), so any engine frame callback sees it as expected.
Use this after a widget is visible to let animations/loading complete, or to drain pending timers at the end of a test.
Implementation
Future<void> pumpForDuration(
WidgetTester tester, {
required Duration duration,
Duration step = const Duration(milliseconds: 100),
}) async {
final stopwatch = Stopwatch()..start();
if (isLiveBinding) {
// In live/preview bindings: pump() without duration renders immediately
// on the next vsync. Each await yields to the event loop, letting real
// async work (API responses, Provider state updates, timers) progress.
// The Stopwatch measures wall-clock time including rendering overhead,
// so each iteration naturally takes some real time (~100-200ms with
// gRPC frame capture), producing a reasonable number of frames.
while (stopwatch.elapsed < duration) {
await tester.pump();
}
} else {
// In automated binding (FakeAsync): pump(step) advances the fake clock
// synchronously, firing timers and rendering frames along the way.
while (stopwatch.elapsed < duration) {
final remaining = duration - stopwatch.elapsed;
final pumpStep = remaining < step ? remaining : step;
await tester.pump(pumpStep);
}
}
}