scroll_notification_test.dart 9.79 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 69
        if (value is ScrollStartNotification || value is ScrollUpdateNotification || value is ScrollEndNotification)
          notification = value;
70 71
        return false;
      },
72
      child: const SingleChildScrollView(
73 74
        child: SizedBox(height: 1200.0),
      ),
75 76
    ));

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

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

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

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

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

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

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

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

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

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

206 207 208 209 210 211 212 213 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
  testWidgets('ScrollNotificationObserver', (WidgetTester tester) async {
    late ScrollNotificationObserverState observer;
    ScrollNotification? notification;

    void handleNotification(ScrollNotification value) {
      if (value is ScrollStartNotification || value is ScrollUpdateNotification || value is ScrollEndNotification)
        notification = value;
    }

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