| // 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/foundation.dart'; |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/rendering.dart'; |
| import 'package:flutter/services.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| import 'list_tile_test_utils.dart'; |
| |
| final LogicalKeyboardKey modifierKey = defaultTargetPlatform == TargetPlatform.macOS |
| ? LogicalKeyboardKey.metaLeft |
| : LogicalKeyboardKey.controlLeft; |
| |
| class _NoNotificationContextScrollable extends Scrollable { |
| const _NoNotificationContextScrollable({super.controller, required super.viewportBuilder}); |
| |
| @override |
| ScrollableState createState() => _NoNotificationContextScrollableState(); |
| } |
| |
| class _NoNotificationContextScrollableState extends ScrollableState { |
| @override |
| BuildContext? get notificationContext => null; |
| } |
| |
| void main() { |
| group('ScrollableDetails', () { |
| test('copyWith / == / hashCode', () { |
| final controller = ScrollController(); |
| addTearDown(controller.dispose); |
| final details = ScrollableDetails( |
| direction: AxisDirection.down, |
| controller: controller, |
| physics: const AlwaysScrollableScrollPhysics(), |
| decorationClipBehavior: Clip.hardEdge, |
| ); |
| ScrollableDetails copiedDetails = details.copyWith(); |
| expect(details, copiedDetails); |
| expect(details.hashCode, copiedDetails.hashCode); |
| |
| copiedDetails = details.copyWith( |
| direction: AxisDirection.left, |
| physics: const ClampingScrollPhysics(), |
| decorationClipBehavior: Clip.none, |
| ); |
| expect( |
| copiedDetails, |
| ScrollableDetails( |
| direction: AxisDirection.left, |
| controller: controller, |
| physics: const ClampingScrollPhysics(), |
| decorationClipBehavior: Clip.none, |
| ), |
| ); |
| }); |
| |
| test('toString', () { |
| final controller = ScrollController(); |
| addTearDown(controller.dispose); |
| const bareDetails = ScrollableDetails(direction: AxisDirection.right); |
| expect( |
| bareDetails.toString(), |
| equalsIgnoringHashCodes('ScrollableDetails#00000(axisDirection: AxisDirection.right)'), |
| ); |
| final fullDetails = ScrollableDetails( |
| direction: AxisDirection.down, |
| controller: controller, |
| physics: const AlwaysScrollableScrollPhysics(), |
| decorationClipBehavior: Clip.hardEdge, |
| ); |
| expect( |
| fullDetails.toString(), |
| equalsIgnoringHashCodes( |
| 'ScrollableDetails#00000(' |
| 'axisDirection: AxisDirection.down, ' |
| 'scroll controller: ScrollController#00000(no clients), ' |
| 'scroll physics: AlwaysScrollableScrollPhysics, ' |
| 'decorationClipBehavior: Clip.hardEdge)', |
| ), |
| ); |
| }); |
| |
| test('deprecated clipBehavior is backwards compatible', () { |
| const deprecatedClip = ScrollableDetails( |
| direction: AxisDirection.right, |
| clipBehavior: Clip.hardEdge, |
| ); |
| expect(deprecatedClip.clipBehavior, Clip.hardEdge); |
| expect(deprecatedClip.decorationClipBehavior, Clip.hardEdge); |
| |
| const newClip = ScrollableDetails( |
| direction: AxisDirection.right, |
| decorationClipBehavior: Clip.hardEdge, |
| ); |
| expect(newClip.clipBehavior, Clip.hardEdge); |
| expect(newClip.decorationClipBehavior, Clip.hardEdge); |
| }); |
| }); |
| |
| testWidgets( |
| "Keyboard scrolling doesn't happen if scroll physics are set to NeverScrollableScrollPhysics", |
| (WidgetTester tester) async { |
| final controller = ScrollController(); |
| addTearDown(controller.dispose); |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(platform: TargetPlatform.fuchsia), |
| home: CustomScrollView( |
| controller: controller, |
| physics: const NeverScrollableScrollPhysics(), |
| slivers: List<Widget>.generate(20, (int index) { |
| return SliverToBoxAdapter( |
| child: Focus( |
| autofocus: index == 0, |
| child: SizedBox(key: ValueKey<String>('Box $index'), height: 50.0), |
| ), |
| ); |
| }), |
| ), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); |
| expect(controller.position.pixels, equals(0.0)); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 0.0, 800.0, 50.0)), |
| ); |
| await tester.sendKeyDownEvent(modifierKey); |
| await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); |
| await tester.sendKeyUpEvent(modifierKey); |
| await tester.pumpAndSettle(); |
| expect(controller.position.pixels, equals(0.0)); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 0.0, 800.0, 50.0)), |
| ); |
| await tester.sendKeyDownEvent(modifierKey); |
| await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); |
| await tester.sendKeyUpEvent(modifierKey); |
| await tester.pumpAndSettle(); |
| expect(controller.position.pixels, equals(0.0)); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 0.0, 800.0, 50.0)), |
| ); |
| await tester.sendKeyEvent(LogicalKeyboardKey.pageDown); |
| await tester.pumpAndSettle(); |
| expect(controller.position.pixels, equals(0.0)); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 0.0, 800.0, 50.0)), |
| ); |
| await tester.sendKeyEvent(LogicalKeyboardKey.pageUp); |
| await tester.pumpAndSettle(); |
| expect(controller.position.pixels, equals(0.0)); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 0.0, 800.0, 50.0)), |
| ); |
| }, |
| variant: KeySimulatorTransitModeVariant.all(), |
| ); |
| |
| testWidgets( |
| 'Vertical scrollables are scrolled when activated via keyboard.', |
| (WidgetTester tester) async { |
| final controller = ScrollController(); |
| addTearDown(controller.dispose); |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(platform: TargetPlatform.fuchsia), |
| home: CustomScrollView( |
| controller: controller, |
| slivers: List<Widget>.generate(20, (int index) { |
| return SliverToBoxAdapter( |
| child: Focus( |
| autofocus: index == 0, |
| child: SizedBox(key: ValueKey<String>('Box $index'), height: 50.0), |
| ), |
| ); |
| }), |
| ), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); |
| expect(controller.position.pixels, equals(0.0)); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 0.0, 800.0, 50.0)), |
| ); |
| // We exclude the modifier keys here for web testing since default web shortcuts |
| // do not use a modifier key with arrow keys for ScrollActions. |
| if (!kIsWeb) { |
| await tester.sendKeyDownEvent(modifierKey); |
| } |
| await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); |
| if (!kIsWeb) { |
| await tester.sendKeyUpEvent(modifierKey); |
| } |
| await tester.pumpAndSettle(); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, -50.0, 800.0, 0.0)), |
| ); |
| if (!kIsWeb) { |
| await tester.sendKeyDownEvent(modifierKey); |
| } |
| await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); |
| if (!kIsWeb) { |
| await tester.sendKeyUpEvent(modifierKey); |
| } |
| await tester.pumpAndSettle(); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 0.0, 800.0, 50.0)), |
| ); |
| await tester.sendKeyEvent(LogicalKeyboardKey.pageDown); |
| await tester.pumpAndSettle(); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, -400.0, 800.0, -350.0)), |
| ); |
| await tester.sendKeyEvent(LogicalKeyboardKey.pageUp); |
| await tester.pumpAndSettle(); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 0.0, 800.0, 50.0)), |
| ); |
| }, |
| variant: KeySimulatorTransitModeVariant.all(), |
| ); |
| |
| testWidgets( |
| 'Horizontal scrollables are scrolled when activated via keyboard.', |
| (WidgetTester tester) async { |
| final controller = ScrollController(); |
| addTearDown(controller.dispose); |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(platform: TargetPlatform.fuchsia), |
| home: CustomScrollView( |
| controller: controller, |
| scrollDirection: Axis.horizontal, |
| slivers: List<Widget>.generate(20, (int index) { |
| return SliverToBoxAdapter( |
| child: Focus( |
| autofocus: index == 0, |
| child: SizedBox(key: ValueKey<String>('Box $index'), width: 50.0), |
| ), |
| ); |
| }), |
| ), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); |
| expect(controller.position.pixels, equals(0.0)); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 0.0, 50.0, 600.0)), |
| ); |
| // We exclude the modifier keys here for web testing since default web shortcuts |
| // do not use a modifier key with arrow keys for ScrollActions. |
| if (!kIsWeb) { |
| await tester.sendKeyDownEvent(modifierKey); |
| } |
| await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); |
| if (!kIsWeb) { |
| await tester.sendKeyUpEvent(modifierKey); |
| } |
| await tester.pumpAndSettle(); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(-50.0, 0.0, 0.0, 600.0)), |
| ); |
| if (!kIsWeb) { |
| await tester.sendKeyDownEvent(modifierKey); |
| } |
| await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft); |
| if (!kIsWeb) { |
| await tester.sendKeyUpEvent(modifierKey); |
| } |
| await tester.pumpAndSettle(); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 0.0, 50.0, 600.0)), |
| ); |
| }, |
| variant: KeySimulatorTransitModeVariant.all(), |
| ); |
| |
| testWidgets( |
| 'Horizontal scrollables are scrolled the correct direction in RTL locales.', |
| (WidgetTester tester) async { |
| final controller = ScrollController(); |
| addTearDown(controller.dispose); |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(platform: TargetPlatform.fuchsia), |
| home: Directionality( |
| textDirection: TextDirection.rtl, |
| child: CustomScrollView( |
| controller: controller, |
| scrollDirection: Axis.horizontal, |
| slivers: List<Widget>.generate(20, (int index) { |
| return SliverToBoxAdapter( |
| child: Focus( |
| autofocus: index == 0, |
| child: SizedBox(key: ValueKey<String>('Box $index'), width: 50.0), |
| ), |
| ); |
| }), |
| ), |
| ), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); |
| expect(controller.position.pixels, equals(0.0)); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(750.0, 0.0, 800.0, 600.0)), |
| ); |
| // We exclude the modifier keys here for web testing since default web shortcuts |
| // do not use a modifier key with arrow keys for ScrollActions. |
| if (!kIsWeb) { |
| await tester.sendKeyDownEvent(modifierKey); |
| } |
| await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft); |
| if (!kIsWeb) { |
| await tester.sendKeyUpEvent(modifierKey); |
| } |
| await tester.pumpAndSettle(); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(800.0, 0.0, 850.0, 600.0)), |
| ); |
| if (!kIsWeb) { |
| await tester.sendKeyDownEvent(modifierKey); |
| } |
| await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); |
| if (!kIsWeb) { |
| await tester.sendKeyUpEvent(modifierKey); |
| } |
| await tester.pumpAndSettle(); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(750.0, 0.0, 800.0, 600.0)), |
| ); |
| }, |
| variant: KeySimulatorTransitModeVariant.all(), |
| ); |
| |
| testWidgets( |
| 'Reversed vertical scrollables are scrolled when activated via keyboard.', |
| (WidgetTester tester) async { |
| final controller = ScrollController(); |
| addTearDown(controller.dispose); |
| final focusNode = FocusNode(debugLabel: 'SizedBox'); |
| addTearDown(focusNode.dispose); |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(platform: TargetPlatform.fuchsia), |
| home: CustomScrollView( |
| controller: controller, |
| reverse: true, |
| slivers: List<Widget>.generate(20, (int index) { |
| return SliverToBoxAdapter( |
| child: Focus( |
| focusNode: focusNode, |
| child: SizedBox(key: ValueKey<String>('Box $index'), height: 50.0), |
| ), |
| ); |
| }), |
| ), |
| ), |
| ); |
| |
| focusNode.requestFocus(); |
| await tester.pumpAndSettle(); |
| expect(controller.position.pixels, equals(0.0)); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 550.0, 800.0, 600.0)), |
| ); |
| // We exclude the modifier keys here for web testing since default web shortcuts |
| // do not use a modifier key with arrow keys for ScrollActions. |
| if (!kIsWeb) { |
| await tester.sendKeyDownEvent(modifierKey); |
| } |
| await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); |
| if (!kIsWeb) { |
| await tester.sendKeyUpEvent(modifierKey); |
| } |
| await tester.pumpAndSettle(); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 600.0, 800.0, 650.0)), |
| ); |
| if (!kIsWeb) { |
| await tester.sendKeyDownEvent(modifierKey); |
| } |
| await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); |
| if (!kIsWeb) { |
| await tester.sendKeyUpEvent(modifierKey); |
| } |
| await tester.pumpAndSettle(); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 550.0, 800.0, 600.0)), |
| ); |
| await tester.sendKeyEvent(LogicalKeyboardKey.pageUp); |
| await tester.pumpAndSettle(); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 950.0, 800.0, 1000.0)), |
| ); |
| await tester.sendKeyEvent(LogicalKeyboardKey.pageDown); |
| await tester.pumpAndSettle(); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 550.0, 800.0, 600.0)), |
| ); |
| }, |
| variant: KeySimulatorTransitModeVariant.all(), |
| ); |
| |
| testWidgets( |
| 'Reversed horizontal scrollables are scrolled when activated via keyboard.', |
| (WidgetTester tester) async { |
| final controller = ScrollController(); |
| addTearDown(controller.dispose); |
| final focusNode = FocusNode(debugLabel: 'SizedBox'); |
| addTearDown(focusNode.dispose); |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(platform: TargetPlatform.fuchsia), |
| home: CustomScrollView( |
| controller: controller, |
| scrollDirection: Axis.horizontal, |
| reverse: true, |
| slivers: List<Widget>.generate(20, (int index) { |
| return SliverToBoxAdapter( |
| child: Focus( |
| focusNode: focusNode, |
| child: SizedBox(key: ValueKey<String>('Box $index'), width: 50.0), |
| ), |
| ); |
| }), |
| ), |
| ), |
| ); |
| |
| focusNode.requestFocus(); |
| await tester.pumpAndSettle(); |
| expect(controller.position.pixels, equals(0.0)); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(750.0, 0.0, 800.0, 600.00)), |
| ); |
| // We exclude the modifier keys here for web testing since default web shortcuts |
| // do not use a modifier key with arrow keys for ScrollActions. |
| if (!kIsWeb) { |
| await tester.sendKeyDownEvent(modifierKey); |
| } |
| await tester.sendKeyEvent(LogicalKeyboardKey.arrowLeft); |
| if (!kIsWeb) { |
| await tester.sendKeyUpEvent(modifierKey); |
| } |
| await tester.pumpAndSettle(); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(800.0, 0.0, 850.0, 600.0)), |
| ); |
| if (!kIsWeb) { |
| await tester.sendKeyDownEvent(modifierKey); |
| } |
| await tester.sendKeyEvent(LogicalKeyboardKey.arrowRight); |
| if (!kIsWeb) { |
| await tester.sendKeyUpEvent(modifierKey); |
| } |
| await tester.pumpAndSettle(); |
| }, |
| variant: KeySimulatorTransitModeVariant.all(), |
| ); |
| |
| testWidgets( |
| 'Custom scrollables with a center sliver are scrolled when activated via keyboard.', |
| (WidgetTester tester) async { |
| final controller = ScrollController(); |
| addTearDown(controller.dispose); |
| final items = List<String>.generate(20, (int index) => 'Item $index'); |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(platform: TargetPlatform.fuchsia), |
| home: CustomScrollView( |
| controller: controller, |
| center: const ValueKey<String>('Center'), |
| slivers: items.map<Widget>((String item) { |
| return SliverToBoxAdapter( |
| key: item == 'Item 10' ? const ValueKey<String>('Center') : null, |
| child: Focus( |
| autofocus: item == 'Item 10', |
| child: Container( |
| key: ValueKey<String>(item), |
| alignment: Alignment.center, |
| height: 100, |
| child: Text(item), |
| ), |
| ), |
| ); |
| }).toList(), |
| ), |
| ), |
| ); |
| |
| await tester.pumpAndSettle(); |
| expect(controller.position.pixels, equals(0.0)); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Item 10'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 0.0, 800.0, 100.0)), |
| ); |
| for (var i = 0; i < 10; ++i) { |
| // We exclude the modifier keys here for web testing since default web shortcuts |
| // do not use a modifier key with arrow keys for ScrollActions. |
| if (!kIsWeb) { |
| await tester.sendKeyDownEvent(modifierKey); |
| } |
| await tester.sendKeyEvent(LogicalKeyboardKey.arrowDown); |
| if (!kIsWeb) { |
| await tester.sendKeyUpEvent(modifierKey); |
| } |
| await tester.pumpAndSettle(); |
| } |
| // Starts at #10 already, so doesn't work out to 500.0 because it hits bottom. |
| expect(controller.position.pixels, equals(400.0)); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Item 10'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, -400.0, 800.0, -300.0)), |
| ); |
| for (var i = 0; i < 10; ++i) { |
| if (!kIsWeb) { |
| await tester.sendKeyDownEvent(modifierKey); |
| } |
| await tester.sendKeyEvent(LogicalKeyboardKey.arrowUp); |
| if (!kIsWeb) { |
| await tester.sendKeyUpEvent(modifierKey); |
| } |
| await tester.pumpAndSettle(); |
| } |
| // Goes up two past "center" where it started, so negative. |
| expect(controller.position.pixels, equals(-100.0)); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Item 10'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 100.0, 800.0, 200.0)), |
| ); |
| }, |
| variant: KeySimulatorTransitModeVariant.all(), |
| ); |
| |
| testWidgets('Can scroll using intents only', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| MaterialApp( |
| home: ListView( |
| children: const <Widget>[ |
| SizedBox(height: 600.0, child: Text('The cow as white as milk')), |
| SizedBox(height: 600.0, child: Text('The cape as red as blood')), |
| SizedBox(height: 600.0, child: Text('The hair as yellow as corn')), |
| ], |
| ), |
| ), |
| ); |
| expect(find.text('The cow as white as milk'), findsOneWidget); |
| expect(find.text('The cape as red as blood'), findsNothing); |
| expect(find.text('The hair as yellow as corn'), findsNothing); |
| Actions.invoke( |
| tester.element(find.byType(SliverList)), |
| const ScrollIntent(direction: AxisDirection.down, type: ScrollIncrementType.page), |
| ); |
| await tester.pump(); // start scroll |
| await tester.pump(const Duration(milliseconds: 1000)); // end scroll |
| expect(find.text('The cow as white as milk'), findsOneWidget); |
| expect(find.text('The cape as red as blood'), findsOneWidget); |
| expect(find.text('The hair as yellow as corn'), findsNothing); |
| Actions.invoke( |
| tester.element(find.byType(SliverList)), |
| const ScrollIntent(direction: AxisDirection.down, type: ScrollIncrementType.page), |
| ); |
| await tester.pump(); // start scroll |
| await tester.pump(const Duration(milliseconds: 1000)); // end scroll |
| expect(find.text('The cow as white as milk'), findsNothing); |
| expect(find.text('The cape as red as blood'), findsOneWidget); |
| expect(find.text('The hair as yellow as corn'), findsOneWidget); |
| }); |
| |
| // Regression test for https://github.com/flutter/flutter/issues/158063. |
| testWidgets( |
| 'Invoking a ScrollAction when notificationContext is null does not cause an exception.', |
| (WidgetTester tester) async { |
| const keysWithModifier = <LogicalKeyboardKey>[ |
| LogicalKeyboardKey.arrowDown, |
| LogicalKeyboardKey.arrowUp, |
| ]; |
| const keys = <LogicalKeyboardKey>[ |
| ...keysWithModifier, |
| LogicalKeyboardKey.pageDown, |
| LogicalKeyboardKey.pageUp, |
| ]; |
| final controller = ScrollController(); |
| addTearDown(controller.dispose); |
| await tester.pumpWidget( |
| MaterialApp( |
| theme: ThemeData(platform: TargetPlatform.fuchsia), |
| home: PrimaryScrollController( |
| controller: controller, |
| child: Focus( |
| autofocus: true, |
| child: _NoNotificationContextScrollable( |
| controller: controller, |
| viewportBuilder: (BuildContext context, ViewportOffset offset) => Viewport( |
| offset: offset, |
| slivers: List<Widget>.generate( |
| 20, |
| (int index) => SliverToBoxAdapter( |
| child: SizedBox(key: ValueKey<String>('Box $index'), height: 50.0), |
| ), |
| ), |
| ), |
| ), |
| ), |
| ), |
| ), |
| ); |
| |
| // Verify the initial scroll offset. |
| await tester.pumpAndSettle(); |
| expect(controller.position.pixels, equals(0.0)); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 0.0, 800.0, 50.0)), |
| ); |
| |
| for (final key in keys) { |
| // The default web shortcuts do not use a modifier key for ScrollActions. |
| if (!kIsWeb && keysWithModifier.contains(key)) { |
| await tester.sendKeyDownEvent(modifierKey); |
| } |
| |
| await tester.sendKeyEvent(key); |
| expect(tester.takeException(), isNull); |
| |
| if (!kIsWeb && keysWithModifier.contains(key)) { |
| await tester.sendKeyUpEvent(modifierKey); |
| } |
| |
| // No scrollable is found, so the scroll position should not change. |
| await tester.pumpAndSettle(); |
| expect(controller.position.pixels, equals(0.0)); |
| expect( |
| tester.getRect(find.byKey(const ValueKey<String>('Box 0'), skipOffstage: false)), |
| equals(const Rect.fromLTRB(0.0, 0.0, 800.0, 50.0)), |
| ); |
| } |
| }, |
| variant: KeySimulatorTransitModeVariant.all(), |
| ); |
| |
| testWidgets('EdgeDraggingAutoScroller handles drag target size correctly with Transform.scale', ( |
| WidgetTester tester, |
| ) async { |
| final controller = ScrollController(); |
| addTearDown(controller.dispose); |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| body: Center( |
| child: Transform.scale( |
| scale: 0.5, |
| child: SizedBox( |
| width: 400, |
| height: 400, |
| child: ListView.builder( |
| controller: controller, |
| itemCount: 20, |
| itemBuilder: (BuildContext context, int index) { |
| return SizedBox(height: 100, child: Center(child: Text('Item $index'))); |
| }, |
| ), |
| ), |
| ), |
| ), |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| |
| final ScrollableState scrollableState = tester.state(find.byType(Scrollable)); |
| final scroller = EdgeDraggingAutoScroller(scrollableState, velocityScalar: 1.0); |
| final scrollRenderBox = scrollableState.context.findRenderObject()! as RenderBox; |
| final dragTarget = Rect.fromLTWH(0, 0, scrollRenderBox.size.width, scrollRenderBox.size.height); |
| |
| scroller.startAutoScrollIfNecessary(dragTarget); |
| await tester.pump(); |
| |
| expect(tester.takeException(), isNull); |
| |
| scroller.stopAutoScroll(); |
| await tester.pumpAndSettle(); |
| }); |
| |
| testWidgets( |
| 'ReorderableListView in Flexible with one item does not assert when dragged to edge', |
| (WidgetTester tester) async { |
| final items = <String>['Item 1']; |
| |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Scaffold( |
| body: Column( |
| children: <Widget>[ |
| Flexible( |
| child: StatefulBuilder( |
| builder: (BuildContext context, StateSetter setState) { |
| return ReorderableListView( |
| onReorder: (int oldIndex, int newIndex) { |
| setState(() { |
| if (newIndex > oldIndex) { |
| newIndex -= 1; |
| } |
| final String item = items.removeAt(oldIndex); |
| items.insert(newIndex, item); |
| }); |
| }, |
| children: <Widget>[ |
| TestListTile( |
| key: const ValueKey<String>('Item 1'), |
| title: Text(items.first), |
| ), |
| ], |
| ); |
| }, |
| ), |
| ), |
| ], |
| ), |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| |
| final Offset startLocation = tester.getCenter(find.byKey(const ValueKey<String>('Item 1'))); |
| final TestGesture gesture = await tester.startGesture(startLocation); |
| await tester.pump(); |
| await gesture.moveTo(tester.getBottomRight(find.byType(Scaffold)) - const Offset(10, 10)); |
| await tester.pump(const Duration(seconds: 1)); |
| |
| expect(tester.takeException(), isNull); |
| |
| await gesture.up(); |
| await tester.pumpAndSettle(); |
| }, |
| ); |
| } |