| // 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/widgets.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| import 'states.dart'; |
| |
| void main() { |
| testWidgets('ScrollController control test', (WidgetTester tester) async { |
| final ScrollController controller = ScrollController(); |
| |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: ListView( |
| controller: controller, |
| children: kStates.map<Widget>((String state) { |
| return SizedBox( |
| height: 200.0, |
| child: Text(state), |
| ); |
| }).toList(), |
| ), |
| ), |
| ); |
| |
| double realOffset() { |
| return tester.state<ScrollableState>(find.byType(Scrollable)).position.pixels; |
| } |
| |
| expect(controller.offset, equals(0.0)); |
| expect(realOffset(), equals(controller.offset)); |
| |
| controller.jumpTo(653.0); |
| |
| expect(controller.offset, equals(653.0)); |
| expect(realOffset(), equals(controller.offset)); |
| |
| await tester.pump(); |
| |
| expect(controller.offset, equals(653.0)); |
| expect(realOffset(), equals(controller.offset)); |
| |
| controller.animateTo(326.0, duration: const Duration(milliseconds: 300), curve: Curves.ease); |
| await tester.pumpAndSettle(); |
| |
| expect(controller.offset, equals(326.0)); |
| expect(realOffset(), equals(controller.offset)); |
| |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: ListView( |
| key: const Key('second'), |
| controller: controller, |
| children: kStates.map<Widget>((String state) { |
| return SizedBox( |
| height: 200.0, |
| child: Text(state), |
| ); |
| }).toList(), |
| ), |
| ), |
| ); |
| |
| expect(controller.offset, equals(0.0)); |
| expect(realOffset(), equals(controller.offset)); |
| |
| controller.jumpTo(653.0); |
| |
| expect(controller.offset, equals(653.0)); |
| expect(realOffset(), equals(controller.offset)); |
| |
| final ScrollController controller2 = ScrollController(); |
| |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: ListView( |
| key: const Key('second'), |
| controller: controller2, |
| children: kStates.map<Widget>((String state) { |
| return SizedBox( |
| height: 200.0, |
| child: Text(state), |
| ); |
| }).toList(), |
| ), |
| ), |
| ); |
| |
| expect(() => controller.offset, throwsAssertionError); |
| expect(controller2.offset, equals(653.0)); |
| expect(realOffset(), equals(controller2.offset)); |
| |
| expect(() => controller.jumpTo(120.0), throwsAssertionError); |
| expect(() => controller.animateTo(132.0, duration: const Duration(milliseconds: 300), curve: Curves.ease), throwsAssertionError); |
| |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: ListView( |
| key: const Key('second'), |
| controller: controller2, |
| physics: const BouncingScrollPhysics(), |
| children: kStates.map<Widget>((String state) { |
| return SizedBox( |
| height: 200.0, |
| child: Text(state), |
| ); |
| }).toList(), |
| ), |
| ), |
| ); |
| |
| expect(controller2.offset, equals(653.0)); |
| expect(realOffset(), equals(controller2.offset)); |
| |
| controller2.jumpTo(432.0); |
| |
| expect(controller2.offset, equals(432.0)); |
| expect(realOffset(), equals(controller2.offset)); |
| |
| await tester.pump(); |
| |
| expect(controller2.offset, equals(432.0)); |
| expect(realOffset(), equals(controller2.offset)); |
| }); |
| |
| testWidgets('ScrollController control test', (WidgetTester tester) async { |
| final ScrollController controller = ScrollController( |
| initialScrollOffset: 209.0, |
| ); |
| |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: GridView.count( |
| crossAxisCount: 4, |
| controller: controller, |
| children: kStates.map<Widget>((String state) => Text(state)).toList(), |
| ), |
| ), |
| ); |
| |
| double realOffset() { |
| return tester.state<ScrollableState>(find.byType(Scrollable)).position.pixels; |
| } |
| |
| expect(controller.offset, equals(209.0)); |
| expect(realOffset(), equals(controller.offset)); |
| |
| controller.jumpTo(105.0); |
| |
| await tester.pump(); |
| |
| expect(controller.offset, equals(105.0)); |
| expect(realOffset(), equals(controller.offset)); |
| |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: GridView.count( |
| crossAxisCount: 2, |
| controller: controller, |
| children: kStates.map<Widget>((String state) => Text(state)).toList(), |
| ), |
| ), |
| ); |
| |
| expect(controller.offset, equals(105.0)); |
| expect(realOffset(), equals(controller.offset)); |
| }); |
| |
| testWidgets('DrivenScrollActivity ending after dispose', (WidgetTester tester) async { |
| final ScrollController controller = ScrollController(); |
| |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: ListView( |
| controller: controller, |
| children: <Widget>[ Container(height: 200000.0) ], |
| ), |
| ), |
| ); |
| |
| controller.animateTo(1000.0, duration: const Duration(seconds: 1), curve: Curves.linear); |
| |
| await tester.pump(); // Start the animation. |
| |
| // We will now change the tree on the same frame as the animation ends. |
| await tester.pumpWidget(Container(), const Duration(seconds: 2)); |
| }); |
| |
| testWidgets('Read operations on ScrollControllers with no positions fail', (WidgetTester tester) async { |
| final ScrollController controller = ScrollController(); |
| expect(() => controller.offset, throwsAssertionError); |
| expect(() => controller.position, throwsAssertionError); |
| }); |
| |
| testWidgets('Read operations on ScrollControllers with more than one position fail', (WidgetTester tester) async { |
| final ScrollController controller = ScrollController(); |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: ListView( |
| children: <Widget>[ |
| Container( |
| constraints: const BoxConstraints(maxHeight: 500.0), |
| child: ListView( |
| controller: controller, |
| children: kStates.map<Widget>((String state) { |
| return SizedBox(height: 200.0, child: Text(state)); |
| }).toList(), |
| ), |
| ), |
| Container( |
| constraints: const BoxConstraints(maxHeight: 500.0), |
| child: ListView( |
| controller: controller, |
| children: kStates.map<Widget>((String state) { |
| return SizedBox(height: 200.0, child: Text(state)); |
| }).toList(), |
| ), |
| ), |
| ], |
| ), |
| ), |
| ); |
| |
| expect(() => controller.offset, throwsAssertionError); |
| expect(() => controller.position, throwsAssertionError); |
| }); |
| |
| testWidgets('Write operations on ScrollControllers with no positions fail', (WidgetTester tester) async { |
| final ScrollController controller = ScrollController(); |
| expect(() => controller.animateTo(1.0, duration: const Duration(seconds: 1), curve: Curves.linear), throwsAssertionError); |
| expect(() => controller.jumpTo(1.0), throwsAssertionError); |
| }); |
| |
| testWidgets('Write operations on ScrollControllers with more than one position do not throw', (WidgetTester tester) async { |
| final ScrollController controller = ScrollController(); |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: ListView( |
| children: <Widget>[ |
| Container( |
| constraints: const BoxConstraints(maxHeight: 500.0), |
| child: ListView( |
| controller: controller, |
| children: kStates.map<Widget>((String state) { |
| return SizedBox(height: 200.0, child: Text(state)); |
| }).toList(), |
| ), |
| ), |
| Container( |
| constraints: const BoxConstraints(maxHeight: 500.0), |
| child: ListView( |
| controller: controller, |
| children: kStates.map<Widget>((String state) { |
| return SizedBox(height: 200.0, child: Text(state)); |
| }).toList(), |
| ), |
| ), |
| ], |
| ), |
| ), |
| ); |
| |
| controller.jumpTo(1.0); |
| controller.animateTo(1.0, duration: const Duration(seconds: 1), curve: Curves.linear); |
| await tester.pumpAndSettle(); |
| }); |
| |
| testWidgets('Scroll controllers notify when the position changes', (WidgetTester tester) async { |
| final ScrollController controller = ScrollController(); |
| |
| final List<double> log = <double>[]; |
| |
| controller.addListener(() { |
| log.add(controller.offset); |
| }); |
| |
| await tester.pumpWidget( |
| Directionality( |
| textDirection: TextDirection.ltr, |
| child: ListView( |
| controller: controller, |
| children: kStates.map<Widget>((String state) { |
| return SizedBox(height: 200.0, child: Text(state)); |
| }).toList(), |
| ), |
| ), |
| ); |
| |
| expect(log, isEmpty); |
| |
| await tester.drag(find.byType(ListView), const Offset(0.0, -250.0)); |
| |
| expect(log, equals(<double>[ 20.0, 250.0 ])); |
| log.clear(); |
| |
| controller.dispose(); |
| |
| await tester.drag(find.byType(ListView), const Offset(0.0, -130.0)); |
| expect(log, isEmpty); |
| }); |
| |
| testWidgets('keepScrollOffset', (WidgetTester tester) async { |
| final PageStorageBucket bucket = PageStorageBucket(); |
| |
| Widget buildFrame(ScrollController controller) { |
| return Directionality( |
| textDirection: TextDirection.ltr, |
| child: PageStorage( |
| bucket: bucket, |
| child: KeyedSubtree( |
| key: const PageStorageKey<String>('ListView'), |
| child: ListView( |
| key: UniqueKey(), // it's a different ListView every time |
| controller: controller, |
| children: List<Widget>.generate(50, (int index) { |
| return SizedBox(height: 100.0, child: Text('Item $index')); |
| }).toList(), |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| // keepScrollOffset: true (the default). The scroll offset is restored |
| // when the ListView is recreated with a new ScrollController. |
| |
| // The initialScrollOffset is used in this case, because there's no saved |
| // scroll offset. |
| ScrollController controller = ScrollController(initialScrollOffset: 200.0); |
| await tester.pumpWidget(buildFrame(controller)); |
| expect(tester.getTopLeft(find.widgetWithText(SizedBox, 'Item 2')), Offset.zero); |
| |
| controller.jumpTo(2000.0); |
| await tester.pump(); |
| expect(tester.getTopLeft(find.widgetWithText(SizedBox, 'Item 20')), Offset.zero); |
| |
| // The initialScrollOffset isn't used in this case, because the scrolloffset |
| // can be restored. |
| controller = ScrollController(initialScrollOffset: 25.0); |
| await tester.pumpWidget(buildFrame(controller)); |
| expect(controller.offset, 2000.0); |
| expect(tester.getTopLeft(find.widgetWithText(SizedBox, 'Item 20')), Offset.zero); |
| |
| // keepScrollOffset: false. The scroll offset is -not- restored |
| // when the ListView is recreated with a new ScrollController and |
| // the initialScrollOffset is used. |
| |
| controller = ScrollController(keepScrollOffset: false, initialScrollOffset: 100.0); |
| await tester.pumpWidget(buildFrame(controller)); |
| expect(controller.offset, 100.0); |
| expect(tester.getTopLeft(find.widgetWithText(SizedBox, 'Item 1')), Offset.zero); |
| |
| }); |
| } |