| // 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. |
| |
| @Tags(<String>['reduced-test-set']) |
| |
| import 'package:flutter/cupertino.dart'; |
| import 'package:flutter/material.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| void main() { |
| final Offset basicOffset = Offset(CupertinoMagnifier.kDefaultSize.width / 2, |
| CupertinoMagnifier.kDefaultSize.height - CupertinoMagnifier.kMagnifierAboveFocalPoint); |
| const Rect reasonableTextField = Rect.fromLTRB(0, 100, 200, 200); |
| final MagnifierController magnifierController = MagnifierController(); |
| |
| // Make sure that your gesture in infoBearer is within the line in infoBearer, |
| // or else the magnifier status will stay hidden and this will not complete. |
| Future<void> showCupertinoMagnifier( |
| BuildContext context, |
| WidgetTester tester, |
| ValueNotifier<MagnifierOverlayInfoBearer> infoBearer, |
| ) async { |
| final Future<void> magnifierShown = magnifierController.show( |
| context: context, |
| builder: (_) => CupertinoTextMagnifier( |
| controller: magnifierController, |
| magnifierOverlayInfoBearer: infoBearer, |
| )); |
| |
| WidgetsBinding.instance.scheduleFrame(); |
| await tester.pumpAndSettle(); |
| |
| await magnifierShown; |
| } |
| |
| tearDown(() async { |
| magnifierController.removeFromOverlay(); |
| }); |
| |
| group('CupertinoTextEditingMagnifier', () { |
| group('position', () { |
| Offset getMagnifierPosition(WidgetTester tester) { |
| final AnimatedPositioned animatedPositioned = |
| tester.firstWidget(find.byType(AnimatedPositioned)); |
| return Offset( |
| animatedPositioned.left ?? 0, animatedPositioned.top ?? 0); |
| } |
| |
| testWidgets('should be at gesture position if does not violate any positioning rules', (WidgetTester tester) async { |
| final Key fakeTextFieldKey = UniqueKey(); |
| final Key outerKey = UniqueKey(); |
| |
| await tester.pumpWidget( |
| Container( |
| key: outerKey, |
| color: const Color.fromARGB(255, 0, 255, 179), |
| child: MaterialApp( |
| home: Center( |
| child: Container( |
| key: fakeTextFieldKey, |
| width: 10, |
| height: 10, |
| color: Colors.red, |
| child: const Placeholder(), |
| ), |
| ), |
| ), |
| ), |
| ); |
| final BuildContext context = tester.element(find.byType(Placeholder)); |
| |
| // Magnifier should be positioned directly over the red square. |
| final RenderBox tapPointRenderBox = |
| tester.firstRenderObject(find.byKey(fakeTextFieldKey)) as RenderBox; |
| final Rect fakeTextFieldRect = |
| tapPointRenderBox.localToGlobal(Offset.zero) & tapPointRenderBox.size; |
| |
| final ValueNotifier<MagnifierOverlayInfoBearer> magnifier = |
| ValueNotifier<MagnifierOverlayInfoBearer>( |
| MagnifierOverlayInfoBearer( |
| currentLineBoundaries: fakeTextFieldRect, |
| fieldBounds: fakeTextFieldRect, |
| caretRect: fakeTextFieldRect, |
| // The tap position is dragBelow units below the text field. |
| globalGesturePosition: fakeTextFieldRect.center, |
| ), |
| ); |
| |
| await showCupertinoMagnifier(context, tester, magnifier); |
| |
| // Should show two red squares; original, and one in the magnifier, |
| // directly ontop of one another. |
| await expectLater( |
| find.byKey(outerKey), |
| matchesGoldenFile('cupertino_magnifier.position.default.png'), |
| ); |
| }); |
| |
| testWidgets('should never horizontally be outside of Screen Padding', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| const MaterialApp( |
| color: Color.fromARGB(7, 0, 129, 90), |
| home: Placeholder(), |
| ), |
| ); |
| |
| final BuildContext context = tester.firstElement(find.byType(Placeholder)); |
| |
| await showCupertinoMagnifier( |
| context, |
| tester, |
| ValueNotifier<MagnifierOverlayInfoBearer>( |
| MagnifierOverlayInfoBearer( |
| currentLineBoundaries: reasonableTextField, |
| fieldBounds: reasonableTextField, |
| caretRect: reasonableTextField, |
| // The tap position is far out of the right side of the app. |
| globalGesturePosition: |
| Offset(MediaQuery.of(context).size.width + 100, 0), |
| ), |
| ), |
| ); |
| |
| // Should be less than the right edge, since we have padding. |
| expect(getMagnifierPosition(tester).dx, |
| lessThan(MediaQuery.of(context).size.width)); |
| }); |
| |
| testWidgets('should have some vertical drag', (WidgetTester tester) async { |
| final double dragPositionBelowTextField = reasonableTextField.center.dy + 30; |
| |
| await tester.pumpWidget( |
| const MaterialApp( |
| color: Color.fromARGB(7, 0, 129, 90), |
| home: Placeholder(), |
| ), |
| ); |
| |
| final BuildContext context = |
| tester.firstElement(find.byType(Placeholder)); |
| |
| await showCupertinoMagnifier( |
| context, |
| tester, |
| ValueNotifier<MagnifierOverlayInfoBearer>( |
| MagnifierOverlayInfoBearer( |
| currentLineBoundaries: reasonableTextField, |
| fieldBounds: reasonableTextField, |
| caretRect: reasonableTextField, |
| // The tap position is dragBelow units below the text field. |
| globalGesturePosition: Offset( |
| MediaQuery.of(context).size.width / 2, |
| dragPositionBelowTextField), |
| ), |
| ), |
| ); |
| |
| // The magnifier Y should be greater than the text field, since we "dragged" it down. |
| expect(getMagnifierPosition(tester).dy + basicOffset.dy, |
| greaterThan(reasonableTextField.center.dy)); |
| expect(getMagnifierPosition(tester).dy + basicOffset.dy, |
| lessThan(dragPositionBelowTextField)); |
| }); |
| }); |
| |
| group('status', () { |
| testWidgets('should hide if gesture is far below the text field', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| const MaterialApp( |
| color: Color.fromARGB(7, 0, 129, 90), |
| home: Placeholder(), |
| ), |
| ); |
| |
| final BuildContext context = |
| tester.firstElement(find.byType(Placeholder)); |
| |
| final ValueNotifier<MagnifierOverlayInfoBearer> magnifierinfo = |
| ValueNotifier<MagnifierOverlayInfoBearer>( |
| MagnifierOverlayInfoBearer( |
| currentLineBoundaries: reasonableTextField, |
| fieldBounds: reasonableTextField, |
| caretRect: reasonableTextField, |
| // The tap position is dragBelow units below the text field. |
| globalGesturePosition: Offset( |
| MediaQuery.of(context).size.width / 2, reasonableTextField.top), |
| ), |
| ); |
| |
| // Show the magnifier initally, so that we get it in a not hidden state. |
| await showCupertinoMagnifier(context, tester, magnifierinfo); |
| |
| // Move the gesture to one that should hide it. |
| magnifierinfo.value = MagnifierOverlayInfoBearer( |
| currentLineBoundaries: reasonableTextField, |
| fieldBounds: reasonableTextField, |
| caretRect: reasonableTextField, |
| globalGesturePosition: magnifierinfo.value.globalGesturePosition + const Offset(0, 100), |
| ); |
| await tester.pumpAndSettle(); |
| |
| expect(magnifierController.shown, false); |
| expect(magnifierController.overlayEntry, isNotNull); |
| }); |
| |
| testWidgets('should re-show if gesture moves back up', |
| (WidgetTester tester) async { |
| await tester.pumpWidget( |
| const MaterialApp( |
| color: Color.fromARGB(7, 0, 129, 90), |
| home: Placeholder(), |
| ), |
| ); |
| |
| final BuildContext context = |
| tester.firstElement(find.byType(Placeholder)); |
| |
| final ValueNotifier<MagnifierOverlayInfoBearer> magnifierInfo = |
| ValueNotifier<MagnifierOverlayInfoBearer>( |
| MagnifierOverlayInfoBearer( |
| currentLineBoundaries: reasonableTextField, |
| fieldBounds: reasonableTextField, |
| caretRect: reasonableTextField, |
| // The tap position is dragBelow units below the text field. |
| globalGesturePosition: Offset(MediaQuery.of(context).size.width / 2, reasonableTextField.top), |
| ), |
| ); |
| |
| // Show the magnifier initally, so that we get it in a not hidden state. |
| await showCupertinoMagnifier(context, tester, magnifierInfo); |
| |
| // Move the gesture to one that should hide it. |
| magnifierInfo.value = MagnifierOverlayInfoBearer( |
| currentLineBoundaries: reasonableTextField, |
| fieldBounds: reasonableTextField, |
| caretRect: reasonableTextField, |
| globalGesturePosition: |
| magnifierInfo.value.globalGesturePosition + const Offset(0, 100)); |
| await tester.pumpAndSettle(); |
| |
| expect(magnifierController.shown, false); |
| expect(magnifierController.overlayEntry, isNotNull); |
| |
| // Return the gesture to one that shows it. |
| magnifierInfo.value = MagnifierOverlayInfoBearer( |
| currentLineBoundaries: reasonableTextField, |
| fieldBounds: reasonableTextField, |
| caretRect: reasonableTextField, |
| globalGesturePosition: Offset(MediaQuery.of(context).size.width / 2, |
| reasonableTextField.top)); |
| await tester.pumpAndSettle(); |
| |
| expect(magnifierController.shown, true); |
| expect(magnifierController.overlayEntry, isNotNull); |
| }); |
| }); |
| }); |
| } |