// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'test_widgets.dart'; void main() { testWidgets('simultaneously dispose a widget and end the scroll animation', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: FlipWidget( left: ListView(children: List<Widget>.generate(250, (int i) => Text('$i'))), right: Container(), ), ), ); await tester.fling(find.byType(ListView), const Offset(0.0, -200.0), 1000.0); await tester.pump(); tester.state<FlipWidgetState>(find.byType(FlipWidget)).flip(); await tester.pump(const Duration(hours: 5)); }); testWidgets('Disposing a (nested) Scrollable while holding in overscroll does not crash', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/27707. final ScrollController controller = ScrollController(); final Key outerContainer = GlobalKey(); await tester.pumpWidget( MaterialApp( home: Center( child: Container( key: outerContainer, color: Colors.purple, width: 400.0, child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: SizedBox( width: 500.0, child: ListView.builder( controller: controller, itemBuilder: (BuildContext context, int index) { return Container( color: index.isEven ? Colors.red : Colors.green, 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. await tester.startGesture(tester.getTopLeft(find.byKey(outerContainer)) + const Offset(50.0, 50.0)); 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(), ), ); await tester.pumpAndSettle(); expect(controller.hasClients, isFalse); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }