overscroll_indicator_test.dart 10.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:math' as math;

import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/rendering.dart';

import '../rendering/mock_canvas.dart';

final Matcher doesNotOverscroll = isNot(paints..circle());

15
Future<Null> slowDrag(WidgetTester tester, Offset start, Offset offset) async {
16
  final TestGesture gesture = await tester.startGesture(start);
17 18 19 20 21 22 23 24 25 26
  for (int index = 0; index < 10; index += 1) {
    await gesture.moveBy(offset);
    await tester.pump(const Duration(milliseconds: 20));
  }
  await gesture.up();
}

void main() {
  testWidgets('Overscroll indicator color', (WidgetTester tester) async {
    await tester.pumpWidget(
27
      new CustomScrollView(
28
        slivers: <Widget>[
29
          const SliverToBoxAdapter(child: const SizedBox(height: 2000.0)),
30 31 32
        ],
      ),
    );
33
    final RenderObject painter = tester.renderObject(find.byType(CustomPaint));
34 35 36 37

    expect(painter, doesNotOverscroll);

    // the scroll gesture from tester.scroll happens in zero time, so nothing should appear:
38
    await tester.drag(find.byType(Scrollable), const Offset(0.0, 100.0));
39 40 41 42 43 44
    expect(painter, doesNotOverscroll);
    await tester.pump(); // allow the ticker to register itself
    expect(painter, doesNotOverscroll);
    await tester.pump(const Duration(milliseconds: 100)); // animate
    expect(painter, doesNotOverscroll);

45
    final TestGesture gesture = await tester.startGesture(const Offset(200.0, 200.0));
46 47 48 49 50
    await tester.pump(const Duration(milliseconds: 100)); // animate
    expect(painter, doesNotOverscroll);
    await gesture.up();
    expect(painter, doesNotOverscroll);

51
    await slowDrag(tester, const Offset(200.0, 200.0), const Offset(0.0, 5.0));
52 53
    expect(painter, paints..circle(color: const Color(0x0DFFFFFF)));

54
    await tester.pumpAndSettle(const Duration(seconds: 1));
55 56 57 58 59
    expect(painter, doesNotOverscroll);
  });

  testWidgets('Overscroll indicator changes side when you drag on the other side', (WidgetTester tester) async {
    await tester.pumpWidget(
60
      new CustomScrollView(
61
        slivers: <Widget>[
62
          const SliverToBoxAdapter(child: const SizedBox(height: 2000.0)),
63 64 65
        ],
      ),
    );
66
    final RenderObject painter = tester.renderObject(find.byType(CustomPaint));
67

68
    await slowDrag(tester, const Offset(400.0, 200.0), const Offset(0.0, 10.0));
69
    expect(painter, paints..circle(x: 400.0));
70
    await slowDrag(tester, const Offset(100.0, 200.0), const Offset(0.0, 10.0));
71 72 73
    expect(painter, paints..something((Symbol method, List<dynamic> arguments) {
      if (method != #drawCircle)
        return false;
74 75
      final Offset center = arguments[0];
      if (center.dx < 400.0)
76 77 78
        return true;
      throw 'Dragging on left hand side did not overscroll on left hand side.';
    }));
79
    await slowDrag(tester, const Offset(700.0, 200.0), const Offset(0.0, 10.0));
80 81 82
    expect(painter, paints..something((Symbol method, List<dynamic> arguments) {
      if (method != #drawCircle)
        return false;
83 84
      final Offset center = arguments[0];
      if (center.dx > 400.0)
85 86 87 88
        return true;
      throw 'Dragging on right hand side did not overscroll on right hand side.';
    }));

89
    await tester.pumpAndSettle(const Duration(seconds: 1));
90 91 92 93 94
    expect(painter, doesNotOverscroll);
  });

  testWidgets('Overscroll indicator changes side when you shift sides', (WidgetTester tester) async {
    await tester.pumpWidget(
95
      new CustomScrollView(
96
        slivers: <Widget>[
97
          const SliverToBoxAdapter(child: const SizedBox(height: 2000.0)),
98 99 100
        ],
      ),
    );
101
    final RenderObject painter = tester.renderObject(find.byType(CustomPaint));
102
    final TestGesture gesture = await tester.startGesture(const Offset(300.0, 200.0));
103 104 105 106 107 108 109 110 111
    await gesture.moveBy(const Offset(0.0, 10.0));
    await tester.pump(const Duration(milliseconds: 20));
    double oldX = 0.0;
    for (int index = 0; index < 10; index += 1) {
      await gesture.moveBy(const Offset(50.0, 50.0));
      await tester.pump(const Duration(milliseconds: 20));
      expect(painter, paints..something((Symbol method, List<dynamic> arguments) {
        if (method != #drawCircle)
          return false;
112 113
        final Offset center = arguments[0];
        if (center.dx <= oldX)
114
          throw 'Sliding to the right did not make the center of the radius slide to the right.';
115
        oldX = center.dx;
116 117 118 119 120
        return true;
      }));
    }
    await gesture.up();

121
    await tester.pumpAndSettle(const Duration(seconds: 1));
122 123 124 125 126 127
    expect(painter, doesNotOverscroll);
  });

  group('Flipping direction of scrollable doesn\'t change overscroll behavior', () {
    testWidgets('down', (WidgetTester tester) async {
      await tester.pumpWidget(
128
        new CustomScrollView(
129
          physics: const AlwaysScrollableScrollPhysics(),
130
          slivers: <Widget>[
131
            const SliverToBoxAdapter(child: const SizedBox(height: 20.0)),
132 133 134
          ],
        ),
      );
135
      final RenderObject painter = tester.renderObject(find.byType(CustomPaint));
136
      await slowDrag(tester, const Offset(200.0, 200.0), const Offset(0.0, 5.0));
137 138
      expect(painter, paints..save()..circle()..restore()..save()..scale(y: -1.0)..restore()..restore());

139
      await tester.pumpAndSettle(const Duration(seconds: 1));
140 141 142 143 144
      expect(painter, doesNotOverscroll);
    });

    testWidgets('up', (WidgetTester tester) async {
      await tester.pumpWidget(
145 146
        new CustomScrollView(
          reverse: true,
147
          physics: const AlwaysScrollableScrollPhysics(),
148
          slivers: <Widget>[
149
            const SliverToBoxAdapter(child: const SizedBox(height: 20.0)),
150 151 152
          ],
        ),
      );
153
      final RenderObject painter = tester.renderObject(find.byType(CustomPaint));
154
      await slowDrag(tester, const Offset(200.0, 200.0), const Offset(0.0, 5.0));
155 156
      expect(painter, paints..save()..scale(y: -1.0)..restore()..save()..circle()..restore()..restore());

157
      await tester.pumpAndSettle(const Duration(seconds: 1));
158 159 160 161 162 163
      expect(painter, doesNotOverscroll);
    });
  });

  testWidgets('Overscroll in both directions', (WidgetTester tester) async {
    await tester.pumpWidget(
164
      new CustomScrollView(
165
        physics: const AlwaysScrollableScrollPhysics(),
166
        slivers: <Widget>[
167
          const SliverToBoxAdapter(child: const SizedBox(height: 20.0)),
168 169 170
        ],
      ),
    );
171
    final RenderObject painter = tester.renderObject(find.byType(CustomPaint));
172
    await slowDrag(tester, const Offset(200.0, 200.0), const Offset(0.0, 5.0));
173 174
    expect(painter, paints..circle());
    expect(painter, isNot(paints..circle()..circle()));
175
    await slowDrag(tester, const Offset(200.0, 200.0), const Offset(0.0, -5.0));
176 177
    expect(painter, paints..circle()..circle());

178
    await tester.pumpAndSettle(const Duration(seconds: 1));
179 180 181 182 183
    expect(painter, doesNotOverscroll);
  });

  testWidgets('Overscroll horizontally', (WidgetTester tester) async {
    await tester.pumpWidget(
184 185
      new CustomScrollView(
        scrollDirection: Axis.horizontal,
186
        physics: const AlwaysScrollableScrollPhysics(),
187
        slivers: <Widget>[
188
          const SliverToBoxAdapter(child: const SizedBox(height: 20.0)),
189 190 191
        ],
      ),
    );
192
    final RenderObject painter = tester.renderObject(find.byType(CustomPaint));
193
    await slowDrag(tester, const Offset(200.0, 200.0), const Offset(5.0, 0.0));
194
    expect(painter, paints..rotate(angle: math.PI / 2.0)..circle()..saveRestore());
195
    expect(painter, isNot(paints..circle()..circle()));
196
    await slowDrag(tester, const Offset(200.0, 200.0), const Offset(-5.0, 0.0));
197 198
    expect(painter, paints..rotate(angle: math.PI / 2.0)..circle()
                          ..rotate(angle: math.PI / 2.0)..circle());
199

200
    await tester.pumpAndSettle(const Duration(seconds: 1));
201 202 203
    expect(painter, doesNotOverscroll);
  });

204 205 206 207 208 209 210 211 212
  testWidgets('Nested overscrolls do not throw exceptions', (WidgetTester tester) async {
    await tester.pumpWidget(
      new PageView(
        children: <Widget>[
          new ListView(
            children: <Widget>[
              new Container(
                width: 2000.0,
                height: 2000.0,
213
                color: const Color(0xFF00FF00),
214 215 216 217 218 219 220
              ),
            ],
          ),
        ],
      ),
    );

221
    await tester.dragFrom(const Offset(100.0, 100.0), const Offset(0.0, 2000.0));
222
    await tester.pumpAndSettle();
223 224
  });

225 226 227 228
  testWidgets('Changing settings', (WidgetTester tester) async {
    RenderObject painter;

    await tester.pumpWidget(
Adam Barth's avatar
Adam Barth committed
229
      new ScrollConfiguration(
230
        behavior: new TestScrollBehavior1(),
231 232
        child: new CustomScrollView(
          scrollDirection: Axis.horizontal,
233
          physics: const AlwaysScrollableScrollPhysics(),
234
          reverse: true,
235
          slivers: <Widget>[
236
            const SliverToBoxAdapter(child: const SizedBox(height: 20.0)),
237 238
          ],
        ),
239 240 241
      ),
    );
    painter = tester.renderObject(find.byType(CustomPaint));
242
    await slowDrag(tester, const Offset(200.0, 200.0), const Offset(5.0, 0.0));
243
    expect(painter, paints..rotate(angle: math.PI / 2.0)..circle(color: const Color(0x0A00FF00)));
244 245
    expect(painter, isNot(paints..circle()..circle()));

246
    await tester.pumpAndSettle(const Duration(seconds: 1));
247
    await tester.pumpWidget(
Adam Barth's avatar
Adam Barth committed
248
      new ScrollConfiguration(
249
        behavior: new TestScrollBehavior2(),
250 251
        child: new CustomScrollView(
          scrollDirection: Axis.horizontal,
252
          physics: const AlwaysScrollableScrollPhysics(),
253
          slivers: <Widget>[
254
            const SliverToBoxAdapter(child: const SizedBox(height: 20.0)),
255 256
          ],
        ),
257 258 259
      ),
    );
    painter = tester.renderObject(find.byType(CustomPaint));
260
    await slowDrag(tester, const Offset(200.0, 200.0), const Offset(5.0, 0.0));
261
    expect(painter, paints..rotate(angle: math.PI / 2.0)..circle(color: const Color(0x0A0000FF))..saveRestore());
262 263 264 265
    expect(painter, isNot(paints..circle()..circle()));
  });
}

Adam Barth's avatar
Adam Barth committed
266
class TestScrollBehavior1 extends ScrollBehavior {
267
  @override
268 269 270 271 272 273
  Widget buildViewportChrome(BuildContext context, Widget child, AxisDirection axisDirection) {
    return new GlowingOverscrollIndicator(
      child: child,
      axisDirection: axisDirection,
      color: const Color(0xFF00FF00),
    );
274 275 276
  }
}

Adam Barth's avatar
Adam Barth committed
277
class TestScrollBehavior2 extends ScrollBehavior {
278
  @override
279 280 281 282 283 284
  Widget buildViewportChrome(BuildContext context, Widget child, AxisDirection axisDirection) {
    return new GlowingOverscrollIndicator(
      child: child,
      axisDirection: axisDirection,
      color: const Color(0xFF0000FF),
    );
285 286
  }
}