// 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_test/flutter_test.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: SizedBox( width: 200, child: ListView.builder( controller: controller, cacheExtent: 0.0, itemBuilder: (BuildContext context, int index) { return Container( height: 100.0, color: index.isEven ? 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: SizedBox( width: 200, child: ListView.builder( controller: controller, cacheExtent: 0.0, itemBuilder: (BuildContext context, int index) { return Container( height: 100.0, color: index.isEven ? 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: SizedBox( width: 200, child: ListView.builder( controller: controller, cacheExtent: 0.0, itemBuilder: (BuildContext context, int index) { return Container( height: 100.0, color: index.isEven ? 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: SizedBox( width: 200, child: ListView.builder( controller: controller, cacheExtent: 0.0, itemBuilder: (BuildContext context, int index) { return Container( height: 100.0, color: index.isEven ? 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)); }); }