scrollable_dispose_test.dart 3.46 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/material.dart';
6
import 'package:flutter_test/flutter_test.dart';
7
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
8 9 10 11

import 'test_widgets.dart';

void main() {
12
  testWidgetsWithLeakTracking('simultaneously dispose a widget and end the scroll animation', (WidgetTester tester) async {
13
    await tester.pumpWidget(
14
      Directionality(
15
        textDirection: TextDirection.ltr,
16
        child: FlipWidget(
17
          left: ListView(children: List<Widget>.generate(250, (int i) => Text('$i'))),
18
          right: Container(),
19 20 21
        ),
      ),
    );
22

23
    await tester.fling(find.byType(ListView), const Offset(0.0, -200.0), 1000.0);
24 25
    await tester.pump();

26
    tester.state<FlipWidgetState>(find.byType(FlipWidget)).flip();
27
    await tester.pump(const Duration(hours: 5));
28
  });
29

30
  testWidgetsWithLeakTracking('Disposing a (nested) Scrollable while holding in overscroll does not crash', (WidgetTester tester) async {
31 32 33
    // Regression test for https://github.com/flutter/flutter/issues/27707.

    final ScrollController controller = ScrollController();
34
    addTearDown(controller.dispose);
35
    final Key outerContainer = GlobalKey();
36 37 38 39 40

    await tester.pumpWidget(
      MaterialApp(
        home: Center(
          child: Container(
41
            key: outerContainer,
42 43 44 45
            color: Colors.purple,
            width: 400.0,
            child: SingleChildScrollView(
              scrollDirection: Axis.horizontal,
46
              child: SizedBox(
47 48 49 50 51
                width: 500.0,
                child: ListView.builder(
                  controller: controller,
                  itemBuilder: (BuildContext context, int index) {
                    return Container(
52
                      color: index.isEven ? Colors.red : Colors.green,
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
                      height: 200.0,
                      child: Text('Hello $index'),
                    );
                  },
                ),
              ),
            ),
          ),
        ),
      ),
    );

    // Go into overscroll.
    double lastScrollOffset;
    await tester.fling(find.text('Hello 0'), const Offset(0.0, 1000.0), 1000.0);
    await tester.pump(const Duration(milliseconds: 100));
    expect(lastScrollOffset = controller.offset, lessThan(0.0));

    // Reduce the overscroll a little, but don't let it go back to 0.0.
    await tester.pump(const Duration(milliseconds: 100));
    expect(controller.offset, greaterThan(lastScrollOffset));
    expect(controller.offset, lessThan(0.0));
    final double currentOffset = controller.offset;

    // Start a hold activity by putting one pointer down.
78
    final TestGesture gesture = await tester.startGesture(tester.getTopLeft(find.byKey(outerContainer)) + const Offset(50.0, 50.0));
79 80 81 82 83 84 85
    await tester.pumpAndSettle(); // This shouldn't change the scroll offset because of the down event above.
    expect(controller.offset, currentOffset);

    // Dispose the scrollables while the finger is still down, this should not crash.
    await tester.pumpWidget(
      MaterialApp(
        home: Container(),
86
      ),
87 88 89
    );
    await tester.pumpAndSettle();
    expect(controller.hasClients, isFalse);
90 91 92 93

    // Finish gesture to release resources.
    await gesture.up();
    await tester.pumpAndSettle();
Dan Field's avatar
Dan Field committed
94
  }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS,  TargetPlatform.macOS }));
95
}