flingFrom method

Future<void> flingFrom(
  1. Offset startLocation,
  2. Offset offset,
  3. double speed,
  4. {int? pointer,
  5. int buttons = kPrimaryButton,
  6. Duration frameInterval = const Duration(milliseconds: 16),
  7. Offset initialOffset = Offset.zero,
  8. Duration initialOffsetDelay = const Duration(seconds: 1),
  9. PointerDeviceKind deviceKind = PointerDeviceKind.touch}
)

Attempts a fling gesture starting from the given location, moving the given distance, reaching the given speed.

This can pump frames.

Exactly 50 pointer events are synthesized.

The speed is in pixels per second in the direction given by offset.

The offset and speed control the interval between each pointer event. For example, if the offset is 200 pixels down, and the speed is 800 pixels per second, the pointer events will be sent for each increment of 4 pixels (200/50), over 250ms (200/800), meaning events will be sent every 1.25ms (250/200).

To make tests more realistic, frames may be pumped during this time (using calls to pump). If the total duration is longer than frameInterval, then one frame is pumped each time that amount of time elapses while sending events, or each time an event is synthesized, whichever is rarer.

See LiveTestWidgetsFlutterBindingFramePolicy.benchmarkLive if the method is used in a live environment and accurate time control is important.

The initialOffset argument, if non-zero, causes the pointer to first apply that offset, then pump a delay of initialOffsetDelay. This can be used to simulate a drag followed by a fling, including dragging in the opposite direction of the fling (e.g. dragging 200 pixels to the right, then fling to the left over 200 pixels, ending at the exact point that the drag started).

A fling is essentially a drag that ends at a particular speed. If you just want to drag and end without a fling, use dragFrom.

Implementation

Future<void> flingFrom(
  Offset startLocation,
  Offset offset,
  double speed, {
  int? pointer,
  int buttons = kPrimaryButton,
  Duration frameInterval = const Duration(milliseconds: 16),
  Offset initialOffset = Offset.zero,
  Duration initialOffsetDelay = const Duration(seconds: 1),
  PointerDeviceKind deviceKind = PointerDeviceKind.touch,
}) {
  assert(offset.distance > 0.0);
  assert(speed > 0.0); // speed is pixels/second
  return TestAsyncUtils.guard<void>(() async {
    final TestPointer testPointer = TestPointer(pointer ?? _getNextPointer(), deviceKind, null, buttons);
    const int kMoveCount = 50; // Needs to be >= kHistorySize, see _LeastSquaresVelocityTrackerStrategy
    final double timeStampDelta = 1000000.0 * offset.distance / (kMoveCount * speed);
    double timeStamp = 0.0;
    double lastTimeStamp = timeStamp;
    await sendEventToBinding(testPointer.down(startLocation, timeStamp: Duration(microseconds: timeStamp.round())));
    if (initialOffset.distance > 0.0) {
      await sendEventToBinding(testPointer.move(startLocation + initialOffset, timeStamp: Duration(microseconds: timeStamp.round())));
      timeStamp += initialOffsetDelay.inMicroseconds;
      await pump(initialOffsetDelay);
    }
    for (int i = 0; i <= kMoveCount; i += 1) {
      final Offset location = startLocation + initialOffset + Offset.lerp(Offset.zero, offset, i / kMoveCount)!;
      await sendEventToBinding(testPointer.move(location, timeStamp: Duration(microseconds: timeStamp.round())));
      timeStamp += timeStampDelta;
      if (timeStamp - lastTimeStamp > frameInterval.inMicroseconds) {
        await pump(Duration(microseconds: (timeStamp - lastTimeStamp).truncate()));
        lastTimeStamp = timeStamp;
      }
    }
    await sendEventToBinding(testPointer.up(timeStamp: Duration(microseconds: timeStamp.round())));
  });
}