scroll_notification_test.dart 10.1 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
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
9 10

void main() {
11
  testWidgetsWithLeakTracking('ScrollMetricsNotification test', (WidgetTester tester) async {
12 13 14 15 16 17 18 19 20 21 22 23 24
    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));
25
    expect(events.length, 1);
26

27
    events.clear();
28 29 30 31 32 33 34
    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);
35
    expect(event.metrics.extentTotal, 1000.0);
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57

    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);
58
    expect(event.metrics.extentTotal, 600.0);
59 60 61 62 63 64 65

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

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

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

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

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

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

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

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

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

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

158 159
    expect(depth0Values, equals(<int>[0, 0, 0, 0, 0]));
    expect(depth1Values, equals(<int>[1, 1, 1, 1, 1]));
160
  });
161

162
  testWidgetsWithLeakTracking('ScrollNotifications bubble past Scaffold Material', (WidgetTester tester) async {
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
    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,
182
                      child: SizedBox(height: 1200.0),
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 209
                    ),
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );

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

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

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

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