// 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_test/flutter_test.dart'; import 'package:flutter/widgets.dart'; const Key blockKey = Key('test'); void main() { testWidgets('Cannot scroll a non-overflowing block', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: ListView( key: blockKey, children: <Widget>[ Container( height: 200.0, // less than 600, the height of the test area child: const Text('Hello'), ), ], ), ), ); final Offset middleOfContainer = tester.getCenter(find.text('Hello')); final Offset target = tester.getCenter(find.byKey(blockKey)); final TestGesture gesture = await tester.startGesture(target); await gesture.moveBy(const Offset(0.0, -10.0)); await tester.pump(const Duration(milliseconds: 1)); expect(tester.getCenter(find.text('Hello')) == middleOfContainer, isTrue); await gesture.up(); }); testWidgets('Can scroll an overflowing block', (WidgetTester tester) async { await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: ListView( key: blockKey, children: <Widget>[ Container( height: 2000.0, // more than 600, the height of the test area child: const Text('Hello'), ), ], ), ), ); final Offset middleOfContainer = tester.getCenter(find.text('Hello')); expect(middleOfContainer.dx, equals(400.0)); expect(middleOfContainer.dy, equals(1000.0)); final Offset target = tester.getCenter(find.byKey(blockKey)); final TestGesture gesture = await tester.startGesture(target); await gesture.moveBy(const Offset(0.0, -10.0)); await tester.pump(); // redo layout expect(tester.getCenter(find.text('Hello')), isNot(equals(middleOfContainer))); await gesture.up(); }); testWidgets('ListView reverse', (WidgetTester tester) async { int first = 0; int second = 0; Widget buildBlock({ bool reverse = false }) { return Directionality( textDirection: TextDirection.ltr, child: ListView( key: UniqueKey(), reverse: reverse, children: <Widget>[ GestureDetector( onTap: () { first += 1; }, child: Container( height: 350.0, // more than half the height of the test area color: const Color(0xFF00FF00), ), ), GestureDetector( onTap: () { second += 1; }, child: Container( height: 350.0, // more than half the height of the test area color: const Color(0xFF0000FF), ), ), ], ), ); } await tester.pumpWidget(buildBlock(reverse: true)); const Offset target = Offset(200.0, 200.0); await tester.tapAt(target); expect(first, equals(0)); expect(second, equals(1)); await tester.pumpWidget(buildBlock(reverse: false)); await tester.tapAt(target); expect(first, equals(1)); expect(second, equals(1)); }); testWidgets('ListView controller', (WidgetTester tester) async { final ScrollController controller = ScrollController(); Widget buildBlock() { return Directionality( textDirection: TextDirection.ltr, child: ListView( controller: controller, children: const <Widget>[Text('A'), Text('B'), Text('C')], ), ); } await tester.pumpWidget(buildBlock()); expect(controller.offset, equals(0.0)); }); testWidgets('SliverBlockChildListDelegate.estimateMaxScrollOffset hits end', (WidgetTester tester) async { final SliverChildListDelegate delegate = SliverChildListDelegate(<Widget>[ Container(), Container(), Container(), Container(), Container(), ]); await tester.pumpWidget( Directionality( textDirection: TextDirection.ltr, child: CustomScrollView( slivers: <Widget>[ SliverList( delegate: delegate, ), ], ), ), ); final SliverMultiBoxAdaptorElement element = tester.element(find.byType(SliverList, skipOffstage: false)); final double maxScrollOffset = element.estimateMaxScrollOffset( null, firstIndex: 3, lastIndex: 4, leadingScrollOffset: 25.0, trailingScrollOffset: 26.0, ); expect(maxScrollOffset, equals(26.0)); }); testWidgets('Resizing a ListView child restores scroll offset', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/9221 final AnimationController controller = AnimationController( vsync: const TestVSync(), duration: const Duration(milliseconds: 200), ); // The overall height of the frame is (as ever) 600 Widget buildFrame() { return Directionality( textDirection: TextDirection.ltr, child: Column( children: <Widget>[ Flexible( // The overall height of the ListView's contents is 500 child: ListView( children: const <Widget>[ SizedBox( height: 150.0, child: Center( child: Text('top'), ), ), SizedBox( height: 200.0, child: Center( child: Text('middle'), ), ), SizedBox( height: 150.0, child: Center( child: Text('bottom'), ), ), ], ), ), // If this widget's height is > 100 the ListView can scroll. SizeTransition( sizeFactor: controller.view, child: const SizedBox( height: 300.0, child: Text('keyboard'), ), ), ], ), ); } await tester.pumpWidget(buildFrame()); expect(find.text('top'), findsOneWidget); final ScrollPosition position = Scrollable.of(tester.element(find.text('middle')))!.position; expect(position.viewportDimension, 600.0); expect(position.pixels, 0.0); // Animate the 'keyboard' height from 0 to 300 controller.forward(); await tester.pumpAndSettle(); expect(position.viewportDimension, 300.0); // Scroll the ListView upwards position.jumpTo(200.0); await tester.pumpAndSettle(); expect(position.pixels, 200.0); expect(find.text('top'), findsNothing); // Animate the 'keyboard' height back to 0. This causes the scroll // offset to return to 0.0 controller.reverse(); await tester.pumpAndSettle(); expect(position.viewportDimension, 600.0); expect(position.pixels, 0.0); expect(find.text('top'), findsOneWidget); }); }