| // 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 'dart:math' as math; |
| |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/rendering.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| import 'package:flutter/widgets.dart'; |
| import 'package:flutter/gestures.dart'; |
| |
| void main() { |
| testWidgets('Scrollable scaled up', (WidgetTester tester) async { |
| final ScrollController controller = ScrollController(); |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Transform.scale( |
| scale: 2.0, |
| child: Center( |
| child: Container( |
| width: 200, |
| child: ListView.builder( |
| controller: controller, |
| cacheExtent: 0.0, |
| itemBuilder: (BuildContext context, int index) { |
| return Container( |
| height: 100.0, |
| color: index % 2 == 0 ? Colors.blue : Colors.red, |
| child: Text('Tile $index'), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ), |
| ), |
| ); |
| expect(controller.offset, 0.0); |
| |
| await tester.drag(find.byType(ListView), const Offset(0.0, -100.0)); |
| await tester.pump(); |
| expect(controller.offset, 50.0); // 100.0 / 2.0 |
| |
| await tester.drag(find.byType(ListView), const Offset(80.0, -70.0)); |
| await tester.pump(); |
| expect(controller.offset, 85.0); // 50.0 + (70.0 / 2) |
| |
| await tester.drag(find.byType(ListView), const Offset(100.0, 0.0)); |
| await tester.pump(); |
| expect(controller.offset, 85.0); |
| |
| await tester.drag(find.byType(ListView), const Offset(0.0, 85.0)); |
| await tester.pump(); |
| expect(controller.offset, 42.5); // 85.0 - (85.0 / 2) |
| }); |
| |
| testWidgets('Scrollable scaled down', (WidgetTester tester) async { |
| final ScrollController controller = ScrollController(); |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Transform.scale( |
| scale: 0.5, |
| child: Center( |
| child: Container( |
| width: 200, |
| child: ListView.builder( |
| controller: controller, |
| cacheExtent: 0.0, |
| itemBuilder: (BuildContext context, int index) { |
| return Container( |
| height: 100.0, |
| color: index % 2 == 0 ? Colors.blue : Colors.red, |
| child: Text('Tile $index'), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ), |
| ), |
| ); |
| expect(controller.offset, 0.0); |
| |
| await tester.drag(find.byType(ListView), const Offset(0.0, -100.0)); |
| await tester.pump(); |
| expect(controller.offset, 200.0); // 100.0 * 2.0 |
| |
| await tester.drag(find.byType(ListView), const Offset(80.0, -70.0)); |
| await tester.pump(); |
| expect(controller.offset, 340.0); // 200.0 + (70.0 * 2) |
| |
| await tester.drag(find.byType(ListView), const Offset(100.0, 0.0)); |
| await tester.pump(); |
| expect(controller.offset, 340.0); |
| |
| await tester.drag(find.byType(ListView), const Offset(0.0, 170.0)); |
| await tester.pump(); |
| expect(controller.offset, 0.0); // 340.0 - (170.0 * 2) |
| }); |
| |
| testWidgets('Scrollable rotated 90 degrees', (WidgetTester tester) async { |
| final ScrollController controller = ScrollController(); |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Transform.rotate( |
| angle: math.pi / 2, |
| child: Center( |
| child: Container( |
| width: 200, |
| child: ListView.builder( |
| controller: controller, |
| cacheExtent: 0.0, |
| itemBuilder: (BuildContext context, int index) { |
| return Container( |
| height: 100.0, |
| color: index % 2 == 0 ? Colors.blue : Colors.red, |
| child: Text('Tile $index'), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ), |
| ), |
| ); |
| expect(controller.offset, 0.0); |
| |
| await tester.drag(find.byType(ListView), const Offset(100.0, 0.0)); |
| await tester.pump(); |
| expect(controller.offset, 100.0); |
| |
| await tester.drag(find.byType(ListView), const Offset(0.0, -100.0)); |
| await tester.pump(); |
| expect(controller.offset, 100.0); |
| |
| await tester.drag(find.byType(ListView), const Offset(-70.0, -50.0)); |
| await tester.pump(); |
| expect(controller.offset, 30.0); // 100.0 - 70.0 |
| }); |
| |
| testWidgets('Perspective transform on scrollable', (WidgetTester tester) async { |
| final ScrollController controller = ScrollController(); |
| await tester.pumpWidget( |
| MaterialApp( |
| home: Transform( |
| transform: Matrix4.identity() |
| ..setEntry(3, 2, 0.001) |
| ..rotateX(math.pi / 4), |
| child: Center( |
| child: Container( |
| width: 200, |
| child: ListView.builder( |
| controller: controller, |
| cacheExtent: 0.0, |
| itemBuilder: (BuildContext context, int index) { |
| return Container( |
| height: 100.0, |
| color: index % 2 == 0 ? Colors.blue : Colors.red, |
| child: Text('Tile $index'), |
| ); |
| }, |
| ), |
| ), |
| ), |
| ), |
| ), |
| ); |
| expect(controller.offset, 0.0); |
| |
| // We want to test that the point in the ListView that the finger touches |
| // on the screen stays under the finger as the finger scrolls the ListView |
| // in vertical direction. For this, we pick a point in the ListView (here |
| // the center of Tile 5) and calculate its position in the coordinate space |
| // of the screen. We then place our finger on that point and drag that |
| // point up in vertical direction. After the scroll activity is done, |
| // we verify that - in the coordinate space of the screen (!) - the point |
| // has moved the same distance as the finger. Due to the perspective |
| // transform the point will have moved more distance in the *local* |
| // coordinate system of the ListView. |
| |
| // Calculate where the center of Tile 5 is located in the coordinate |
| // space of the screen. We cannot use `tester.getCenter` because it |
| // does not properly remove the perspective component from the transform |
| // to give us the place on the screen at which we need to touch the screen |
| // to have the center of Tile 5 directly under our finger. |
| final RenderBox tile5 = tester.renderObject(find.text('Tile 5')); |
| final Offset pointOnScreenStart = MatrixUtils.transformPoint( |
| PointerEvent.removePerspectiveTransform(tile5.getTransformTo(null)), |
| tile5.size.center(Offset.zero), |
| ); |
| |
| // Place the finger on the tracked point and move the finger upwards for |
| // 50 pixels to scroll the ListView (the ListView's scroll offset will |
| // move more then 50 pixels due to the perspective transform). |
| await tester.dragFrom(pointOnScreenStart, const Offset(0.0, -50.0)); |
| await tester.pump(); |
| |
| // Get the new position of the tracked point in the screen's coordinate |
| // system. |
| final Offset pointOnScreenEnd = MatrixUtils.transformPoint( |
| PointerEvent.removePerspectiveTransform(tile5.getTransformTo(null)), |
| tile5.size.center(Offset.zero), |
| ); |
| |
| // The tracked point (in the coordinate space of the screen) and the finger |
| // should have moved the same vertical distance over the screen. |
| expect( |
| pointOnScreenStart.dy - pointOnScreenEnd.dy, |
| within(distance: 0.00001, from: 50.0), |
| ); |
| |
| // While the point traveled the same distance as the finger in the |
| // coordinate space of the screen, the scroll view actually moved far more |
| // pixels in its local coordinate system due to the perspective transform. |
| expect(controller.offset, greaterThan(100)); |
| }); |
| } |