scroll_notification_test.dart 9.81 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 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
    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);

    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);

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

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

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

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

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

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

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

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

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

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

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

  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,
179
                      child: SizedBox(height: 1200.0),
180 181 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
                    ),
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );

    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));
  });

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

    void handleNotification(ScrollNotification value) {
212
      if (value is ScrollStartNotification || value is ScrollUpdateNotification || value is ScrollEndNotification) {
213
        notification = value;
214
      }
215 216 217 218 219 220 221 222 223 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
    }

    await tester.pumpWidget(
      ScrollNotificationObserver(
        child: Builder(
          builder: (BuildContext context) {
            observer = ScrollNotificationObserver.of(context)!;
            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);
  });
273
}