scroll_notification_test.dart 9.9 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
import 'package:flutter/gestures.dart';
6
import 'package:flutter/material.dart';
7
import 'package:flutter_test/flutter_test.dart';
8 9

void main() {
10 11 12 13 14 15 16 17 18 19 20 21 22 23
  testWidgets('ScrollMetricsNotification test', (WidgetTester tester) async {
    final List<Notification> events = <Notification>[];
    Widget buildFrame(double height) {
      return NotificationListener<Notification>(
        onNotification: (Notification value) {
          events.add(value);
          return false;
        },
        child: SingleChildScrollView(
          child: SizedBox(height: height),
        ),
      );
    }
    await tester.pumpWidget(buildFrame(1200.0));
24
    expect(events.length, 1);
25

26
    events.clear();
27 28 29 30 31 32 33
    await tester.pumpWidget(buildFrame(1000.0));
    // Change the content dimensions will trigger a new event.
    expect(events.length, 1);
    ScrollMetricsNotification event = events[0] as ScrollMetricsNotification;
    expect(event.metrics.extentBefore, 0.0);
    expect(event.metrics.extentInside, 600.0);
    expect(event.metrics.extentAfter, 400.0);
34
    expect(event.metrics.extentTotal, 1000.0);
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56

    events.clear();
    final TestGesture gesture = await tester.startGesture(const Offset(100.0, 100.0));
    expect(events.length, 1);
    // user scroll do not trigger the ScrollContentMetricsNotification.
    expect(events[0] is ScrollStartNotification, true);

    events.clear();
    await gesture.moveBy(const Offset(-10.0, -10.0));
    expect(events.length, 2);
    // User scroll do not trigger the ScrollContentMetricsNotification.
    expect(events[0] is UserScrollNotification, true);
    expect(events[1] is ScrollUpdateNotification, true);

    events.clear();
    // Change the content dimensions again.
    await tester.pumpWidget(buildFrame(500.0));
    expect(events.length, 1);
    event = events[0] as ScrollMetricsNotification;
    expect(event.metrics.extentBefore, 10.0);
    expect(event.metrics.extentInside, 590.0);
    expect(event.metrics.extentAfter, 0.0);
57
    expect(event.metrics.extentTotal, 600.0);
58 59 60 61 62 63 64

    events.clear();
    // The content dimensions does not change.
    await tester.pumpWidget(buildFrame(500.0));
    expect(events.length, 0);
  });

65
  testWidgets('Scroll notification basics', (WidgetTester tester) async {
66
    late ScrollNotification notification;
67

68
    await tester.pumpWidget(NotificationListener<ScrollNotification>(
Adam Barth's avatar
Adam Barth committed
69
      onNotification: (ScrollNotification value) {
70
        if (value is ScrollStartNotification || value is ScrollUpdateNotification || value is ScrollEndNotification) {
71
          notification = value;
72
        }
73 74
        return false;
      },
75
      child: const SingleChildScrollView(
76 77
        child: SizedBox(height: 1200.0),
      ),
78 79
    ));

80
    final TestGesture gesture = await tester.startGesture(const Offset(100.0, 100.0));
81
    await tester.pump(const Duration(seconds: 1));
Dan Field's avatar
Dan Field committed
82
    expect(notification, isA<ScrollStartNotification>());
83
    expect(notification.depth, equals(0));
84
    final ScrollStartNotification start = notification as ScrollStartNotification;
85
    expect(start.dragDetails, isNotNull);
86
    expect(start.dragDetails!.globalPosition, equals(const Offset(100.0, 100.0)));
87

88
    await gesture.moveBy(const Offset(-10.0, -10.0));
89
    await tester.pump(const Duration(seconds: 1));
Dan Field's avatar
Dan Field committed
90
    expect(notification, isA<ScrollUpdateNotification>());
91
    expect(notification.depth, equals(0));
92
    final ScrollUpdateNotification update = notification as ScrollUpdateNotification;
93
    expect(update.dragDetails, isNotNull);
94 95
    expect(update.dragDetails!.globalPosition, equals(const Offset(90.0, 90.0)));
    expect(update.dragDetails!.delta, equals(const Offset(0.0, -10.0)));
96 97 98

    await gesture.up();
    await tester.pump(const Duration(seconds: 1));
Dan Field's avatar
Dan Field committed
99
    expect(notification, isA<ScrollEndNotification>());
100
    expect(notification.depth, equals(0));
101
    final ScrollEndNotification end = notification as ScrollEndNotification;
102
    expect(end.dragDetails, isNotNull);
103
    expect(end.dragDetails!.velocity, equals(Velocity.zero));
104 105
  });

106
  testWidgets('Scroll notification depth', (WidgetTester tester) async {
107 108
    final List<Type> depth0Types = <Type>[];
    final List<Type> depth1Types = <Type>[];
109 110 111
    final List<int> depth0Values = <int>[];
    final List<int> depth1Values = <int>[];

112
    await tester.pumpWidget(NotificationListener<ScrollNotification>(
Adam Barth's avatar
Adam Barth committed
113
      onNotification: (ScrollNotification value) {
114
        depth1Types.add(value.runtimeType);
115 116 117
        depth1Values.add(value.depth);
        return false;
      },
118
      child: SingleChildScrollView(
119
        dragStartBehavior: DragStartBehavior.down,
120
        child: SizedBox(
121
          height: 1200.0,
122
          child: NotificationListener<ScrollNotification>(
Adam Barth's avatar
Adam Barth committed
123
            onNotification: (ScrollNotification value) {
124
              depth0Types.add(value.runtimeType);
125 126 127
              depth0Values.add(value.depth);
              return false;
            },
128
            child: Container(
129
              padding: const EdgeInsets.all(50.0),
130 131
              child: const SingleChildScrollView(
                dragStartBehavior: DragStartBehavior.down,
132
                child: SizedBox(height: 1200.0),
133 134 135 136 137
              ),
            ),
          ),
        ),
      ),
138 139
    ));

140
    final TestGesture gesture = await tester.startGesture(const Offset(100.0, 100.0));
141
    await tester.pump(const Duration(seconds: 1));
142
    await gesture.moveBy(const Offset(-10.0, -40.0));
143 144 145 146
    await tester.pump(const Duration(seconds: 1));
    await gesture.up();
    await tester.pump(const Duration(seconds: 1));

147 148 149 150 151 152
    final List<Type> types = <Type>[
      ScrollStartNotification,
      UserScrollNotification,
      ScrollUpdateNotification,
      ScrollEndNotification,
      UserScrollNotification,
153
    ];
154 155
    expect(depth0Types, equals(types));
    expect(depth1Types, equals(types));
156

157 158
    expect(depth0Values, equals(<int>[0, 0, 0, 0, 0]));
    expect(depth1Values, equals(<int>[1, 1, 1, 1, 1]));
159
  });
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180

  testWidgets('ScrollNotifications bubble past Scaffold Material', (WidgetTester tester) async {
    final List<Type> notificationTypes = <Type>[];

    await tester.pumpWidget(
      MaterialApp(
        home: NotificationListener<ScrollNotification>(
          onNotification: (ScrollNotification value) {
            notificationTypes.add(value.runtimeType);
            return false;
          },
          child: Scaffold(
            body: SizedBox.expand(
              child: SingleChildScrollView(
                dragStartBehavior: DragStartBehavior.down,
                child: SizedBox(
                  height: 1200.0,
                  child: Container(
                    padding: const EdgeInsets.all(50.0),
                    child: const SingleChildScrollView(
                      dragStartBehavior: DragStartBehavior.down,
181
                      child: SizedBox(height: 1200.0),
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
                    ),
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );

    final TestGesture gesture = await tester.startGesture(const Offset(100.0, 100.0));
    await tester.pump(const Duration(seconds: 1));
    await gesture.moveBy(const Offset(-10.0, -40.0));
    await tester.pump(const Duration(seconds: 1));
    await gesture.up();
    await tester.pump(const Duration(seconds: 1));

    final List<Type> types = <Type>[
      ScrollStartNotification,
      UserScrollNotification,
      ScrollUpdateNotification,
      ScrollEndNotification,
      UserScrollNotification,
    ];
    expect(notificationTypes, equals(types));
  });

209 210 211 212 213
  testWidgets('ScrollNotificationObserver', (WidgetTester tester) async {
    late ScrollNotificationObserverState observer;
    ScrollNotification? notification;

    void handleNotification(ScrollNotification value) {
214
      if (value is ScrollStartNotification || value is ScrollUpdateNotification || value is ScrollEndNotification) {
215
        notification = value;
216
      }
217 218 219 220 221 222
    }

    await tester.pumpWidget(
      ScrollNotificationObserver(
        child: Builder(
          builder: (BuildContext context) {
223
            observer = ScrollNotificationObserver.of(context);
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
            return const SingleChildScrollView(
              child: SizedBox(height: 1200.0),
            );
          },
        ),
      ),
    );

    observer.addListener(handleNotification);

    TestGesture gesture = await tester.startGesture(const Offset(100.0, 100.0));
    await tester.pumpAndSettle();
    expect(notification, isA<ScrollStartNotification>());
    expect(notification!.depth, equals(0));

    final ScrollStartNotification start = notification! as ScrollStartNotification;
    expect(start.dragDetails, isNotNull);
    expect(start.dragDetails!.globalPosition, equals(const Offset(100.0, 100.0)));

    await gesture.moveBy(const Offset(-10.0, -10.0));
    await tester.pumpAndSettle();
    expect(notification, isA<ScrollUpdateNotification>());
    expect(notification!.depth, equals(0));
    final ScrollUpdateNotification update = notification! as ScrollUpdateNotification;
    expect(update.dragDetails, isNotNull);
    expect(update.dragDetails!.globalPosition, equals(const Offset(90.0, 90.0)));
    expect(update.dragDetails!.delta, equals(const Offset(0.0, -10.0)));

    await gesture.up();
    await tester.pumpAndSettle();
    expect(notification, isA<ScrollEndNotification>());
    expect(notification!.depth, equals(0));
    final ScrollEndNotification end = notification! as ScrollEndNotification;
    expect(end.dragDetails, isNotNull);
    expect(end.dragDetails!.velocity, equals(Velocity.zero));

    observer.removeListener(handleNotification);
    notification = null;

    gesture = await tester.startGesture(const Offset(100.0, 100.0));
    await tester.pumpAndSettle();
    expect(notification, isNull);

    await gesture.moveBy(const Offset(-10.0, -10.0));
    await tester.pumpAndSettle();
    expect(notification, isNull);

    await gesture.up();
    await tester.pumpAndSettle();
    expect(notification, isNull);
  });
275
}