scroll_events_test.dart 9.04 KB
Newer Older
Ian Hickson's avatar
Ian Hickson committed
1
// Copyright 2014 The Flutter Authors. All rights reserved.
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5 6
import 'dart:async';

7
import 'package:flutter/widgets.dart';
8
import 'package:flutter_test/flutter_test.dart';
9

10
Widget _buildScroller({ required List<String> log }) {
11
  return NotificationListener<ScrollNotification>(
Adam Barth's avatar
Adam Barth committed
12
    onNotification: (ScrollNotification notification) {
13 14 15 16 17 18 19 20
      if (notification is ScrollStartNotification) {
        log.add('scroll-start');
      } else if (notification is ScrollUpdateNotification) {
        log.add('scroll-update');
      } else if (notification is ScrollEndNotification) {
        log.add('scroll-end');
      }
      return false;
21
    },
22 23
    child: const SingleChildScrollView(
      child: SizedBox(width: 1000.0, height: 1000.0),
24
    ),
25 26 27 28
  );
}

void main() {
29
  Completer<void> animateTo(WidgetTester tester, double newScrollOffset, { required Duration duration }) {
30
    final Completer<void> completer = Completer<void>();
Adam Barth's avatar
Adam Barth committed
31
    final ScrollableState scrollable = tester.state(find.byType(Scrollable));
32
    scrollable.position.animateTo(newScrollOffset, duration: duration, curve: Curves.linear).whenComplete(completer.complete);
33 34 35
    return completer;
  }

36
  void jumpTo(WidgetTester tester, double newScrollOffset) {
Adam Barth's avatar
Adam Barth committed
37
    final ScrollableState scrollable = tester.state(find.byType(Scrollable));
38 39 40
    scrollable.position.jumpTo(newScrollOffset);
  }

41
  testWidgets('Scroll event drag', (WidgetTester tester) async {
42
    final List<String> log = <String>[];
43
    await tester.pumpWidget(_buildScroller(log: log));
44

45
    expect(log, equals(<String>[]));
46
    final TestGesture gesture = await tester.startGesture(const Offset(100.0, 100.0));
47
    expect(log, equals(<String>['scroll-start']));
48
    await tester.pump(const Duration(seconds: 1));
49
    expect(log, equals(<String>['scroll-start']));
50
    await gesture.moveBy(const Offset(-10.0, -10.0));
51
    expect(log, equals(<String>['scroll-start', 'scroll-update']));
52
    await tester.pump(const Duration(seconds: 1));
53
    expect(log, equals(<String>['scroll-start', 'scroll-update']));
54
    await gesture.up();
55
    expect(log, equals(<String>['scroll-start', 'scroll-update', 'scroll-end']));
56
    await tester.pump(const Duration(seconds: 1));
57
    expect(log, equals(<String>['scroll-start', 'scroll-update', 'scroll-end']));
58 59
  });

60
  testWidgets('Scroll animateTo', (WidgetTester tester) async {
61
    final List<String> log = <String>[];
62
    await tester.pumpWidget(_buildScroller(log: log));
63

64
    expect(log, equals(<String>[]));
65
    final Completer<void> completer = animateTo(tester, 100.0, duration: const Duration(seconds: 1));
66
    expect(completer.isCompleted, isFalse);
67
    expect(log, equals(<String>['scroll-start']));
68
    await tester.pump(const Duration(milliseconds: 100));
69
    expect(log, equals(<String>['scroll-start']));
70
    await tester.pump(const Duration(milliseconds: 100));
71
    expect(log, equals(<String>['scroll-start', 'scroll-update']));
72
    await tester.pump(const Duration(milliseconds: 1500));
73
    expect(log, equals(<String>['scroll-start', 'scroll-update', 'scroll-update', 'scroll-end']));
74
    expect(completer.isCompleted, isTrue);
75 76
  });

77
  testWidgets('Scroll jumpTo', (WidgetTester tester) async {
78
    final List<String> log = <String>[];
79
    await tester.pumpWidget(_buildScroller(log: log));
80

81
    expect(log, equals(<String>[]));
82 83
    jumpTo(tester, 100.0);
    expect(log, equals(<String>['scroll-start', 'scroll-update', 'scroll-end']));
84
    await tester.pump();
85
    expect(log, equals(<String>['scroll-start', 'scroll-update', 'scroll-end']));
86 87
  });

88
  testWidgets('Scroll jumpTo during animation', (WidgetTester tester) async {
89
    final List<String> log = <String>[];
90
    await tester.pumpWidget(_buildScroller(log: log));
91

92
    expect(log, equals(<String>[]));
93
    final Completer<void> completer = animateTo(tester, 100.0, duration: const Duration(seconds: 1));
94
    expect(completer.isCompleted, isFalse);
95
    expect(log, equals(<String>['scroll-start']));
96
    await tester.pump(const Duration(milliseconds: 100));
97
    expect(log, equals(<String>['scroll-start']));
98
    await tester.pump(const Duration(milliseconds: 100));
99
    expect(log, equals(<String>['scroll-start', 'scroll-update']));
100 101
    expect(completer.isCompleted, isFalse);

102
    jumpTo(tester, 100.0);
103
    expect(completer.isCompleted, isFalse);
104
    expect(log, equals(<String>['scroll-start', 'scroll-update', 'scroll-end', 'scroll-start', 'scroll-update', 'scroll-end']));
105
    await tester.pump(const Duration(milliseconds: 100));
106 107
    expect(log, equals(<String>['scroll-start', 'scroll-update', 'scroll-end', 'scroll-start', 'scroll-update', 'scroll-end']));
    expect(completer.isCompleted, isTrue);
108
    await tester.pump(const Duration(milliseconds: 1500));
109
    expect(log, equals(<String>['scroll-start', 'scroll-update', 'scroll-end', 'scroll-start', 'scroll-update', 'scroll-end']));
110
    expect(completer.isCompleted, isTrue);
111
  });
112

113
  testWidgets('Scroll scrollTo during animation', (WidgetTester tester) async {
114
    final List<String> log = <String>[];
115
    await tester.pumpWidget(_buildScroller(log: log));
116

117
    expect(log, equals(<String>[]));
118
    Completer<void> completer = animateTo(tester, 100.0, duration: const Duration(seconds: 1));
119
    expect(completer.isCompleted, isFalse);
120
    expect(log, equals(<String>['scroll-start']));
121
    await tester.pump(const Duration(milliseconds: 100));
122
    expect(log, equals(<String>['scroll-start']));
123
    await tester.pump(const Duration(milliseconds: 100));
124
    expect(log, equals(<String>['scroll-start', 'scroll-update']));
125 126
    expect(completer.isCompleted, isFalse);

127
    completer = animateTo(tester, 100.0, duration: const Duration(seconds: 1));
128
    expect(completer.isCompleted, isFalse);
129
    expect(log, equals(<String>['scroll-start', 'scroll-update']));
130
    await tester.pump(const Duration(milliseconds: 100));
131
    expect(log, equals(<String>['scroll-start', 'scroll-update']));
132
    await tester.pump(const Duration(milliseconds: 1500));
133
    expect(log, equals(<String>['scroll-start', 'scroll-update', 'scroll-update', 'scroll-end']));
134
    expect(completer.isCompleted, isTrue);
135 136
  });

137
  testWidgets('fling, fling generates two start/end pairs', (WidgetTester tester) async {
138
    final List<String> log = <String>[];
139
    await tester.pumpWidget(_buildScroller(log: log));
140

141
    // The ideal behavior here would be a single start/end pair, but for
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
    // simplicity of implementation we compromise here and accept two. Should
    // you find a way to make this work with just one without complicating the
    // API, feel free to change the expectation here.

    expect(log, equals(<String>[]));
    await tester.flingFrom(const Offset(100.0, 100.0), const Offset(-50.0, -50.0), 500.0);
    await tester.pump(const Duration(seconds: 1));
    log.removeWhere((String value) => value == 'scroll-update');
    expect(log, equals(<String>['scroll-start']));
    await tester.flingFrom(const Offset(100.0, 100.0), const Offset(-50.0, -50.0), 500.0);
    log.removeWhere((String value) => value == 'scroll-update');
    expect(log, equals(<String>['scroll-start', 'scroll-end', 'scroll-start']));
    await tester.pump(const Duration(seconds: 1));
    await tester.pump(const Duration(seconds: 1));
    log.removeWhere((String value) => value == 'scroll-update');
    expect(log, equals(<String>['scroll-start', 'scroll-end', 'scroll-start', 'scroll-end']));
  });

  testWidgets('fling, pause, fling generates two start/end pairs', (WidgetTester tester) async {
    final List<String> log = <String>[];
    await tester.pumpWidget(_buildScroller(log: log));

164
    expect(log, equals(<String>[]));
165
    await tester.flingFrom(const Offset(100.0, 100.0), const Offset(-50.0, -50.0), 500.0);
166
    await tester.pump(const Duration(seconds: 1));
167 168
    log.removeWhere((String value) => value == 'scroll-update');
    expect(log, equals(<String>['scroll-start']));
169
    await tester.pump(const Duration(minutes: 1));
170
    await tester.flingFrom(const Offset(100.0, 100.0), const Offset(-50.0, -50.0), 500.0);
171 172
    log.removeWhere((String value) => value == 'scroll-update');
    expect(log, equals(<String>['scroll-start', 'scroll-end', 'scroll-start']));
173 174
    await tester.pump(const Duration(seconds: 1));
    await tester.pump(const Duration(seconds: 1));
175 176
    log.removeWhere((String value) => value == 'scroll-update');
    expect(log, equals(<String>['scroll-start', 'scroll-end', 'scroll-start', 'scroll-end']));
177
  });
178

179
  testWidgets('fling up ends', (WidgetTester tester) async {
180
    final List<String> log = <String>[];
181
    await tester.pumpWidget(_buildScroller(log: log));
182

183
    expect(log, equals(<String>[]));
184
    await tester.flingFrom(const Offset(100.0, 100.0), const Offset(50.0, 50.0), 500.0);
185 186 187
    await tester.pump(const Duration(seconds: 1));
    await tester.pump(const Duration(seconds: 1));
    await tester.pump(const Duration(seconds: 1));
188 189 190
    expect(log.first, equals('scroll-start'));
    expect(log.last, equals('scroll-end'));
    log.removeWhere((String value) => value == 'scroll-update');
191
    expect(log.length, equals(2));
Adam Barth's avatar
Adam Barth committed
192
    expect(tester.state<ScrollableState>(find.byType(Scrollable)).position.pixels, equals(0.0));
193
  });
194
}