| // 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:async'; |
| |
| import 'package:flutter/material.dart'; |
| import 'package:flutter/rendering.dart'; |
| import 'package:flutter_test/flutter_test.dart'; |
| |
| import '../rendering/mock_canvas.dart'; |
| |
| Widget buildInputDecorator({ |
| InputDecoration decoration = const InputDecoration(), |
| InputDecorationTheme? inputDecorationTheme, |
| TextDirection textDirection = TextDirection.ltr, |
| bool expands = false, |
| bool isEmpty = false, |
| bool isFocused = false, |
| bool isHovering = false, |
| TextStyle? baseStyle, |
| TextAlignVertical? textAlignVertical, |
| VisualDensity? visualDensity, |
| bool fixTextFieldOutlineLabel = false, |
| Widget child = const Text( |
| 'text', |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 16.0), |
| ), |
| }) { |
| return MaterialApp( |
| home: Material( |
| child: Builder( |
| builder: (BuildContext context) { |
| return Theme( |
| data: Theme.of(context)!.copyWith( |
| inputDecorationTheme: inputDecorationTheme, |
| visualDensity: visualDensity, |
| fixTextFieldOutlineLabel: fixTextFieldOutlineLabel, |
| ), |
| child: Align( |
| alignment: Alignment.topLeft, |
| child: Directionality( |
| textDirection: textDirection, |
| child: InputDecorator( |
| expands: expands, |
| decoration: decoration, |
| isEmpty: isEmpty, |
| isFocused: isFocused, |
| isHovering: isHovering, |
| baseStyle: baseStyle, |
| textAlignVertical: textAlignVertical, |
| child: child, |
| ), |
| ), |
| ), |
| ); |
| }, |
| ), |
| ), |
| ); |
| } |
| |
| Finder findBorderPainter() { |
| return find.descendant( |
| of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_BorderContainer'), |
| matching: find.byWidgetPredicate((Widget w) => w is CustomPaint), |
| ); |
| } |
| |
| double getBorderBottom(WidgetTester tester) { |
| final RenderBox box = InputDecorator.containerOf(tester.element(findBorderPainter()))!; |
| return box.size.height; |
| } |
| |
| Finder findLabel() { |
| return find.descendant( |
| of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_Shaker'), |
| matching: find.byWidgetPredicate((Widget w) => w is Text), |
| ); |
| } |
| |
| Rect getLabelRect(WidgetTester tester) { |
| return tester.getRect(findLabel()); |
| } |
| |
| InputBorder? getBorder(WidgetTester tester) { |
| if (!tester.any(findBorderPainter())) |
| return null; |
| final CustomPaint customPaint = tester.widget(findBorderPainter()); |
| final dynamic/*_InputBorderPainter*/ inputBorderPainter = customPaint.foregroundPainter; |
| final dynamic/*_InputBorderTween*/ inputBorderTween = inputBorderPainter.border; |
| final Animation<double> animation = inputBorderPainter.borderAnimation as Animation<double>; |
| final InputBorder border = inputBorderTween.evaluate(animation) as InputBorder; |
| return border; |
| } |
| |
| BorderSide? getBorderSide(WidgetTester tester) { |
| return getBorder(tester)!.borderSide; |
| } |
| |
| BorderRadius? getBorderRadius(WidgetTester tester) { |
| final InputBorder border = getBorder(tester)!; |
| if (border is UnderlineInputBorder) { |
| return border.borderRadius; |
| } |
| return null; |
| } |
| |
| double getBorderWeight(WidgetTester tester) => getBorderSide(tester)!.width; |
| |
| Color getBorderColor(WidgetTester tester) => getBorderSide(tester)!.color; |
| |
| Color getContainerColor(WidgetTester tester) { |
| final CustomPaint customPaint = tester.widget(findBorderPainter()); |
| final dynamic/*_InputBorderPainter*/ inputBorderPainter = customPaint.foregroundPainter; |
| return inputBorderPainter.blendedColor as Color; |
| } |
| |
| double getOpacity(WidgetTester tester, String textValue) { |
| final FadeTransition opacityWidget = tester.widget<FadeTransition>( |
| find.ancestor( |
| of: find.text(textValue), |
| matching: find.byType(FadeTransition), |
| ).first, |
| ); |
| return opacityWidget.opacity.value; |
| } |
| |
| void main() { |
| testWidgets('InputDecorator input/label layout', (WidgetTester tester) async { |
| // The label appears above the input text |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| labelText: 'label', |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| |
| // Overall height for this InputDecorator is 56dps: |
| // 12 - top padding |
| // 12 - floating label (ahem font size 16dps * 0.75 = 12) |
| // 4 - floating label / input text gap |
| // 16 - input text (ahem font size 16dps) |
| // 12 - bottom padding |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 28.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 44.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 12.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 24.0); |
| expect(getBorderBottom(tester), 56.0); |
| expect(getBorderWeight(tester), 1.0); |
| |
| // The label appears within the input when there is no text content |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| labelText: 'label', |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| |
| expect(tester.getTopLeft(find.text('label')).dy, 20.0); |
| |
| // The label appears above the input text when there is no content and floatingLabelBehavior is always |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| labelText: 'label', |
| floatingLabelBehavior: FloatingLabelBehavior.always |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| |
| expect(tester.getTopLeft(find.text('label')).dy, 12.0); |
| |
| // The label appears within the input text when there is content and floatingLabelBehavior is never |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: false, |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| labelText: 'label', |
| floatingLabelBehavior: FloatingLabelBehavior.never |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| |
| expect(tester.getTopLeft(find.text('label')).dy, 20.0); |
| |
| // Overall height for this InputDecorator is 56dps: |
| // 12 - top padding |
| // 12 - floating label (ahem font size 16dps * 0.75 = 12) |
| // 4 - floating label / input text gap |
| // 16 - input text (ahem font size 16dps) |
| // 12 - bottom padding |
| |
| expect(tester.getTopLeft(find.text('label')).dy, 20.0); |
| |
| // isFocused: true increases the border's weight from 1.0 to 2.0 |
| // but does not change the overall height. |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| isFocused: true, |
| decoration: const InputDecoration( |
| labelText: 'label', |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 28.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 44.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 12.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 24.0); |
| expect(getBorderBottom(tester), 56.0); |
| expect(getBorderWeight(tester), 2.0); |
| |
| // isEmpty: true causes the label to be aligned with the input text |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| isFocused: false, |
| decoration: const InputDecoration( |
| labelText: 'label', |
| ), |
| ), |
| ); |
| |
| // The label animates downwards from it's initial position |
| // above the input text. The animation's duration is 200ms. |
| { |
| await tester.pump(const Duration(milliseconds: 50)); |
| final double labelY50ms = tester.getTopLeft(find.text('label')).dy; |
| expect(labelY50ms, inExclusiveRange(12.0, 20.0)); |
| await tester.pump(const Duration(milliseconds: 50)); |
| final double labelY100ms = tester.getTopLeft(find.text('label')).dy; |
| expect(labelY100ms, inExclusiveRange(labelY50ms, 20.0)); |
| } |
| await tester.pumpAndSettle(); |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 28.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 44.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 20.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 36.0); |
| expect(getBorderBottom(tester), 56.0); |
| expect(getBorderWeight(tester), 1.0); |
| |
| // isFocused: true causes the label to move back up above the input text. |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| isFocused: true, |
| decoration: const InputDecoration( |
| labelText: 'label', |
| ), |
| ), |
| ); |
| |
| // The label animates upwards from it's initial position |
| // above the input text. The animation's duration is 200ms. |
| { |
| await tester.pump(const Duration(milliseconds: 50)); |
| final double labelY50ms = tester.getTopLeft(find.text('label')).dy; |
| expect(labelY50ms, inExclusiveRange(12.0, 28.0)); |
| await tester.pump(const Duration(milliseconds: 50)); |
| final double labelY100ms = tester.getTopLeft(find.text('label')).dy; |
| expect(labelY100ms, inExclusiveRange(12.0, labelY50ms)); |
| } |
| |
| await tester.pumpAndSettle(); |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 28.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 44.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 12.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 24.0); |
| expect(getBorderBottom(tester), 56.0); |
| expect(getBorderWeight(tester), 2.0); |
| |
| // enabled: false produces a hairline border if filled: false (the default) |
| // The widget's size and layout is the same as for enabled: true. |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| isFocused: false, |
| decoration: const InputDecoration( |
| labelText: 'label', |
| enabled: false, |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 28.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 44.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 20.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 36.0); |
| expect(getBorderWeight(tester), 0.0); |
| |
| // enabled: false produces a transparent border if filled: true. |
| // The widget's size and layout is the same as for enabled: true. |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| isFocused: false, |
| decoration: const InputDecoration( |
| labelText: 'label', |
| enabled: false, |
| filled: true, |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 28.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 44.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 20.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 36.0); |
| expect(getBorderColor(tester), Colors.transparent); |
| |
| // alignLabelWithHint: true positions the label at the text baseline, |
| // aligned with the hint. |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| isFocused: false, |
| decoration: const InputDecoration( |
| labelText: 'label', |
| alignLabelWithHint: true, |
| hintText: 'hint', |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0)); |
| expect(tester.getTopLeft(find.text('label')).dy, tester.getTopLeft(find.text('hint')).dy); |
| expect(tester.getBottomLeft(find.text('label')).dy, tester.getBottomLeft(find.text('hint')).dy); |
| }); |
| |
| group('alignLabelWithHint', () { |
| group('expands false', () { |
| testWidgets('multiline TextField no-strut', (WidgetTester tester) async { |
| const String text = 'text'; |
| final FocusNode focusNode = FocusNode(); |
| final TextEditingController controller = TextEditingController(); |
| Widget buildFrame(bool alignLabelWithHint) { |
| return MaterialApp( |
| home: Material( |
| child: Directionality( |
| textDirection: TextDirection.ltr, |
| child: TextField( |
| controller: controller, |
| focusNode: focusNode, |
| maxLines: 8, |
| decoration: InputDecoration( |
| labelText: 'label', |
| alignLabelWithHint: alignLabelWithHint, |
| hintText: 'hint', |
| ), |
| strutStyle: StrutStyle.disabled, |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| // alignLabelWithHint: false centers the label in the TextField. |
| await tester.pumpWidget(buildFrame(false)); |
| await tester.pumpAndSettle(); |
| expect(tester.getTopLeft(find.text('label')).dy, 76.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 92.0); |
| |
| // Entering text still happens at the top. |
| await tester.enterText(find.byType(TextField), text); |
| expect(tester.getTopLeft(find.text(text)).dy, 28.0); |
| controller.clear(); |
| focusNode.unfocus(); |
| |
| // alignLabelWithHint: true aligns the label with the hint. |
| await tester.pumpWidget(buildFrame(true)); |
| await tester.pumpAndSettle(); |
| expect(tester.getTopLeft(find.text('label')).dy, tester.getTopLeft(find.text('hint')).dy); |
| expect(tester.getBottomLeft(find.text('label')).dy, tester.getBottomLeft(find.text('hint')).dy); |
| |
| // Entering text still happens at the top. |
| await tester.enterText(find.byType(TextField), text); |
| expect(tester.getTopLeft(find.text(text)).dy, 28.0); |
| controller.clear(); |
| focusNode.unfocus(); |
| }); |
| |
| testWidgets('multiline TextField', (WidgetTester tester) async { |
| const String text = 'text'; |
| final FocusNode focusNode = FocusNode(); |
| final TextEditingController controller = TextEditingController(); |
| Widget buildFrame(bool alignLabelWithHint) { |
| return MaterialApp( |
| home: Material( |
| child: Directionality( |
| textDirection: TextDirection.ltr, |
| child: TextField( |
| controller: controller, |
| focusNode: focusNode, |
| maxLines: 8, |
| decoration: InputDecoration( |
| labelText: 'label', |
| alignLabelWithHint: alignLabelWithHint, |
| hintText: 'hint', |
| ), |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| // alignLabelWithHint: false centers the label in the TextField. |
| await tester.pumpWidget(buildFrame(false)); |
| await tester.pumpAndSettle(); |
| expect(tester.getTopLeft(find.text('label')).dy, 76.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 92.0); |
| |
| // Entering text still happens at the top. |
| await tester.enterText(find.byType(InputDecorator), text); |
| expect(tester.getTopLeft(find.text(text)).dy, 28.0); |
| controller.clear(); |
| focusNode.unfocus(); |
| |
| // alignLabelWithHint: true aligns the label with the hint. |
| await tester.pumpWidget(buildFrame(true)); |
| await tester.pumpAndSettle(); |
| expect(tester.getTopLeft(find.text('label')).dy, tester.getTopLeft(find.text('hint')).dy); |
| expect(tester.getBottomLeft(find.text('label')).dy, tester.getBottomLeft(find.text('hint')).dy); |
| |
| // Entering text still happens at the top. |
| await tester.enterText(find.byType(InputDecorator), text); |
| expect(tester.getTopLeft(find.text(text)).dy, 28.0); |
| controller.clear(); |
| focusNode.unfocus(); |
| }); |
| }); |
| |
| group('expands true', () { |
| testWidgets('multiline TextField', (WidgetTester tester) async { |
| const String text = 'text'; |
| final FocusNode focusNode = FocusNode(); |
| final TextEditingController controller = TextEditingController(); |
| Widget buildFrame(bool alignLabelWithHint) { |
| return MaterialApp( |
| home: Material( |
| child: Directionality( |
| textDirection: TextDirection.ltr, |
| child: TextField( |
| controller: controller, |
| focusNode: focusNode, |
| maxLines: null, |
| expands: true, |
| decoration: InputDecoration( |
| labelText: 'label', |
| alignLabelWithHint: alignLabelWithHint, |
| hintText: 'hint', |
| ), |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| // alignLabelWithHint: false centers the label in the TextField. |
| await tester.pumpWidget(buildFrame(false)); |
| await tester.pumpAndSettle(); |
| expect(tester.getTopLeft(find.text('label')).dy, 292.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 308.0); |
| |
| // Entering text still happens at the top. |
| await tester.enterText(find.byType(InputDecorator), text); |
| expect(tester.getTopLeft(find.text(text)).dy, 28.0); |
| controller.clear(); |
| focusNode.unfocus(); |
| |
| // alignLabelWithHint: true aligns the label with the hint at the top. |
| await tester.pumpWidget(buildFrame(true)); |
| await tester.pumpAndSettle(); |
| expect(tester.getTopLeft(find.text('label')).dy, 28.0); |
| expect(tester.getTopLeft(find.text('label')).dy, tester.getTopLeft(find.text('hint')).dy); |
| expect(tester.getBottomLeft(find.text('label')).dy, tester.getBottomLeft(find.text('hint')).dy); |
| |
| // Entering text still happens at the top. |
| await tester.enterText(find.byType(InputDecorator), text); |
| expect(tester.getTopLeft(find.text(text)).dy, 28.0); |
| controller.clear(); |
| focusNode.unfocus(); |
| }); |
| |
| testWidgets('multiline TextField with outline border', (WidgetTester tester) async { |
| const String text = 'text'; |
| final FocusNode focusNode = FocusNode(); |
| final TextEditingController controller = TextEditingController(); |
| Widget buildFrame(bool alignLabelWithHint) { |
| return MaterialApp( |
| home: Material( |
| child: Directionality( |
| textDirection: TextDirection.ltr, |
| child: TextField( |
| controller: controller, |
| focusNode: focusNode, |
| maxLines: null, |
| expands: true, |
| decoration: InputDecoration( |
| labelText: 'label', |
| alignLabelWithHint: alignLabelWithHint, |
| hintText: 'hint', |
| border: OutlineInputBorder( |
| borderSide: const BorderSide(width: 1, color: Colors.black, style: BorderStyle.solid), |
| borderRadius: BorderRadius.circular(0), |
| ), |
| ), |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| // alignLabelWithHint: false centers the label in the TextField. |
| await tester.pumpWidget(buildFrame(false)); |
| await tester.pumpAndSettle(); |
| expect(tester.getTopLeft(find.text('label')).dy, 292.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 308.0); |
| |
| // Entering text happens in the center as well. |
| await tester.enterText(find.byType(InputDecorator), text); |
| expect(tester.getTopLeft(find.text(text)).dy, 291.0); |
| controller.clear(); |
| focusNode.unfocus(); |
| |
| // alignLabelWithHint: true aligns keeps the label in the center because |
| // that's where the hint is. |
| await tester.pumpWidget(buildFrame(true)); |
| await tester.pumpAndSettle(); |
| expect(tester.getTopLeft(find.text('label')).dy, 291.0); |
| expect(tester.getTopLeft(find.text('label')).dy, tester.getTopLeft(find.text('hint')).dy); |
| expect(tester.getBottomLeft(find.text('label')).dy, tester.getBottomLeft(find.text('hint')).dy); |
| |
| // Entering text still happens in the center. |
| await tester.enterText(find.byType(InputDecorator), text); |
| expect(tester.getTopLeft(find.text(text)).dy, 291.0); |
| controller.clear(); |
| focusNode.unfocus(); |
| }); |
| }); |
| }); |
| |
| // Overall height for this InputDecorator is 40.0dps |
| // 12 - top padding |
| // 16 - input text (ahem font size 16dps) |
| // 12 - bottom padding |
| testWidgets('InputDecorator input/hint layout', (WidgetTester tester) async { |
| // The hint aligns with the input text |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| hintText: 'hint', |
| ), |
| ), |
| ); |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, kMinInteractiveDimension)); |
| expect(tester.getTopLeft(find.text('text')).dy, 16.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 32.0); |
| expect(tester.getTopLeft(find.text('hint')).dy, 16.0); |
| expect(tester.getBottomLeft(find.text('hint')).dy, 32.0); |
| expect(getBorderBottom(tester), 48.0); |
| expect(getBorderWeight(tester), 1.0); |
| |
| expect(tester.getSize(find.text('hint')).width, tester.getSize(find.text('text')).width); |
| }); |
| |
| testWidgets('InputDecorator input/label/hint layout', (WidgetTester tester) async { |
| // Label is visible, hint is not (opacity 0.0). |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| labelText: 'label', |
| hintText: 'hint', |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is 56dps. When the |
| // label is "floating" (empty input or no focus) the layout is: |
| // |
| // 12 - top padding |
| // 12 - floating label (ahem font size 16dps * 0.75 = 12) |
| // 4 - floating label / input text gap |
| // 16 - input text (ahem font size 16dps) |
| // 12 - bottom padding |
| // |
| // When the label is not floating, it's vertically centered. |
| // |
| // 20 - top padding |
| // 16 - label (ahem font size 16dps) |
| // 20 - bottom padding (empty input text still appears here) |
| |
| |
| // The label is not floating so it's vertically centered. |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 28.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 44.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 20.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 36.0); |
| expect(getOpacity(tester, 'hint'), 0.0); |
| expect(getBorderBottom(tester), 56.0); |
| expect(getBorderWeight(tester), 1.0); |
| |
| // Label moves upwards, hint is visible (opacity 1.0). |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| isFocused: true, |
| decoration: const InputDecoration( |
| labelText: 'label', |
| hintText: 'hint', |
| ), |
| ), |
| ); |
| |
| // The hint's opacity animates from 0.0 to 1.0. |
| // The animation's duration is 200ms. |
| { |
| await tester.pump(const Duration(milliseconds: 50)); |
| final double hintOpacity50ms = getOpacity(tester, 'hint'); |
| expect(hintOpacity50ms, inExclusiveRange(0.0, 1.0)); |
| await tester.pump(const Duration(milliseconds: 50)); |
| final double hintOpacity100ms = getOpacity(tester, 'hint'); |
| expect(hintOpacity100ms, inExclusiveRange(hintOpacity50ms, 1.0)); |
| } |
| |
| await tester.pumpAndSettle(); |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 28.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 44.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 12.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 24.0); |
| expect(tester.getTopLeft(find.text('hint')).dy, 28.0); |
| expect(tester.getBottomLeft(find.text('hint')).dy, 44.0); |
| expect(getOpacity(tester, 'hint'), 1.0); |
| expect(getBorderBottom(tester), 56.0); |
| expect(getBorderWeight(tester), 2.0); |
| |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: false, |
| isFocused: true, |
| decoration: const InputDecoration( |
| labelText: 'label', |
| hintText: 'hint', |
| ), |
| ), |
| ); |
| |
| // The hint's opacity animates from 1.0 to 0.0. |
| // The animation's duration is 200ms. |
| { |
| await tester.pump(const Duration(milliseconds: 50)); |
| final double hintOpacity50ms = getOpacity(tester, 'hint'); |
| expect(hintOpacity50ms, inExclusiveRange(0.0, 1.0)); |
| await tester.pump(const Duration(milliseconds: 50)); |
| final double hintOpacity100ms = getOpacity(tester, 'hint'); |
| expect(hintOpacity100ms, inExclusiveRange(0.0, hintOpacity50ms)); |
| } |
| |
| await tester.pumpAndSettle(); |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 28.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 44.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 12.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 24.0); |
| expect(tester.getTopLeft(find.text('hint')).dy, 28.0); |
| expect(tester.getBottomLeft(find.text('hint')).dy, 44.0); |
| expect(getOpacity(tester, 'hint'), 0.0); |
| expect(getBorderBottom(tester), 56.0); |
| expect(getBorderWeight(tester), 2.0); |
| }); |
| |
| testWidgets('InputDecorator input/label/hint dense layout', (WidgetTester tester) async { |
| // Label is visible, hint is not (opacity 0.0). |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| labelText: 'label', |
| hintText: 'hint', |
| isDense: true, |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is 48dps. When the |
| // label is "floating" (empty input or no focus) the layout is: |
| // |
| // 8 - top padding |
| // 12 - floating label (ahem font size 16dps * 0.75 = 12) |
| // 4 - floating label / input text gap |
| // 16 - input text (ahem font size 16dps) |
| // 8 - bottom padding |
| // |
| // When the label is not floating, it's vertically centered. |
| // |
| // 16 - top padding |
| // 16 - label (ahem font size 16dps) |
| // 16 - bottom padding (empty input text still appears here) |
| |
| // The label is not floating so it's vertically centered. |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 48.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 24.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 40.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 16.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 32.0); |
| expect(getOpacity(tester, 'hint'), 0.0); |
| expect(getBorderBottom(tester), 48.0); |
| expect(getBorderWeight(tester), 1.0); |
| |
| // Label is visible, hint is not (opacity 0.0). |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| isFocused: true, |
| decoration: const InputDecoration( |
| labelText: 'label', |
| hintText: 'hint', |
| isDense: true, |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 48.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 24.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 40.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 8.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 20.0); |
| expect(getOpacity(tester, 'hint'), 1.0); |
| expect(getBorderBottom(tester), 48.0); |
| expect(getBorderWeight(tester), 2.0); |
| }); |
| |
| testWidgets('InputDecorator with no input border', (WidgetTester tester) async { |
| // Label is visible, hint is not (opacity 0.0). |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| border: InputBorder.none, |
| ), |
| ), |
| ); |
| expect(getBorderWeight(tester), 0.0); |
| }); |
| |
| testWidgets('InputDecorator error/helper/counter layout', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| labelText: 'label', |
| helperText: 'helper', |
| counterText: 'counter', |
| filled: true, |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is 76dps. When the label is |
| // floating the layout is: |
| // |
| // 12 - top padding |
| // 12 - floating label (ahem font size 16dps * 0.75 = 12) |
| // 4 - floating label / input text gap |
| // 16 - input text (ahem font size 16dps) |
| // 12 - bottom padding |
| // 8 - below the border padding |
| // 12 - help/error/counter text (ahem font size 12dps) |
| // |
| // When the label is not floating, it's vertically centered in the space |
| // above the subtext: |
| // |
| // 20 - top padding |
| // 16 - label (ahem font size 16dps) |
| // 20 - bottom padding (empty input text still appears here) |
| // 8 - below the border padding |
| // 12 - help/error/counter text (ahem font size 12dps) |
| |
| // isEmpty: true, the label is not floating |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 76.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 28.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 44.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 20.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 36.0); |
| expect(getBorderBottom(tester), 56.0); |
| expect(getBorderWeight(tester), 1.0); |
| expect(tester.getTopLeft(find.text('helper')), const Offset(12.0, 64.0)); |
| expect(tester.getTopRight(find.text('counter')), const Offset(788.0, 64.0)); |
| |
| // If errorText is specified then the helperText isn't shown |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| labelText: 'label', |
| errorText: 'error', |
| helperText: 'helper', |
| counterText: 'counter', |
| filled: true, |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| |
| // isEmpty: false, the label _is_ floating |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 76.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 28.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 44.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 12.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 24.0); |
| expect(getBorderBottom(tester), 56.0); |
| expect(getBorderWeight(tester), 1.0); |
| expect(tester.getTopLeft(find.text('error')), const Offset(12.0, 64.0)); |
| expect(tester.getTopRight(find.text('counter')), const Offset(788.0, 64.0)); |
| expect(find.text('helper'), findsNothing); |
| |
| // Overall height for this dense layout InputDecorator is 68dps. When the |
| // label is floating the layout is: |
| // |
| // 8 - top padding |
| // 12 - floating label (ahem font size 16dps * 0.75 = 12) |
| // 4 - floating label / input text gap |
| // 16 - input text (ahem font size 16dps) |
| // 8 - bottom padding |
| // 8 - below the border padding |
| // 12 - help/error/counter text (ahem font size 12dps) |
| // |
| // When the label is not floating, it's vertically centered in the space |
| // above the subtext: |
| // |
| // 16 - top padding |
| // 16 - label (ahem font size 16dps) |
| // 16 - bottom padding (empty input text still appears here) |
| // 8 - below the border padding |
| // 12 - help/error/counter text (ahem font size 12dps) |
| // The layout of the error/helper/counter subtext doesn't change for dense layout. |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| isDense: true, |
| labelText: 'label', |
| errorText: 'error', |
| helperText: 'helper', |
| counterText: 'counter', |
| filled: true, |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| |
| // isEmpty: false, the label _is_ floating |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 68.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 24.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 40.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 8.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 20.0); |
| expect(getBorderBottom(tester), 48.0); |
| expect(getBorderWeight(tester), 1.0); |
| expect(tester.getTopLeft(find.text('error')), const Offset(12.0, 56.0)); |
| expect(tester.getTopRight(find.text('counter')), const Offset(788.0, 56.0)); |
| |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| isDense: true, |
| labelText: 'label', |
| errorText: 'error', |
| helperText: 'helper', |
| counterText: 'counter', |
| filled: true, |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| |
| // isEmpty: false, the label is not floating |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 68.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 24.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 40.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 16.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 32.0); |
| expect(getBorderBottom(tester), 48.0); |
| expect(getBorderWeight(tester), 1.0); |
| expect(tester.getTopLeft(find.text('error')), const Offset(12.0, 56.0)); |
| expect(tester.getTopRight(find.text('counter')), const Offset(788.0, 56.0)); |
| }); |
| |
| testWidgets('InputDecorator counter text, widget, and null', (WidgetTester tester) async { |
| Widget buildFrame({ |
| InputCounterWidgetBuilder? buildCounter, |
| String? counterText, |
| Widget? counter, |
| int? maxLength, |
| }) { |
| return MaterialApp( |
| home: Scaffold( |
| body: Center( |
| child: Column( |
| mainAxisAlignment: MainAxisAlignment.center, |
| children: <Widget>[ |
| TextFormField( |
| buildCounter: buildCounter, |
| maxLength: maxLength, |
| decoration: InputDecoration( |
| counterText: counterText, |
| counter: counter, |
| ), |
| ), |
| ], |
| ), |
| ), |
| ), |
| ); |
| } |
| |
| // When counter, counterText, and buildCounter are null, defaults to showing |
| // the built-in counter. |
| int? maxLength = 10; |
| await tester.pumpWidget(buildFrame(maxLength: maxLength)); |
| Finder counterFinder = find.byType(Text); |
| expect(counterFinder, findsOneWidget); |
| final Text counterWidget = tester.widget(counterFinder); |
| expect(counterWidget.data, '0/${maxLength.toString()}'); |
| |
| // When counter, counterText, and buildCounter are set, shows the counter |
| // widget. |
| final Key counterKey = UniqueKey(); |
| final Key buildCounterKey = UniqueKey(); |
| const String counterText = 'I show instead of count'; |
| final Widget counter = Text('hello', key: counterKey); |
| final InputCounterWidgetBuilder buildCounter = ( |
| BuildContext context, { |
| required int currentLength, |
| required int? maxLength, |
| required bool isFocused, |
| }) { |
| return Text( |
| '${currentLength.toString()} of ${maxLength.toString()}', |
| key: buildCounterKey, |
| ); |
| }; |
| |
| await tester.pumpWidget(buildFrame( |
| counterText: counterText, |
| counter: counter, |
| buildCounter: buildCounter, |
| maxLength: maxLength, |
| )); |
| counterFinder = find.byKey(counterKey); |
| expect(counterFinder, findsOneWidget); |
| expect(find.text(counterText), findsNothing); |
| expect(find.byKey(buildCounterKey), findsNothing); |
| |
| // When counter is null but counterText and buildCounter are set, shows the |
| // counterText. |
| await tester.pumpWidget(buildFrame( |
| counterText: counterText, |
| buildCounter: buildCounter, |
| maxLength: maxLength, |
| )); |
| expect(find.text(counterText), findsOneWidget); |
| counterFinder = find.byKey(counterKey); |
| expect(counterFinder, findsNothing); |
| expect(find.byKey(buildCounterKey), findsNothing); |
| |
| // When counter and counterText are null but buildCounter is set, shows the |
| // generated widget. |
| await tester.pumpWidget(buildFrame( |
| buildCounter: buildCounter, |
| maxLength: maxLength, |
| )); |
| expect(find.byKey(buildCounterKey), findsOneWidget); |
| expect(counterFinder, findsNothing); |
| expect(find.text(counterText), findsNothing); |
| |
| // When counterText is empty string and counter and buildCounter are null, |
| // shows nothing. |
| await tester.pumpWidget(buildFrame(counterText: '', maxLength: maxLength)); |
| expect(find.byType(Text), findsNothing); |
| |
| // When no maxLength, can still show a counter |
| maxLength = null; |
| await tester.pumpWidget(buildFrame( |
| buildCounter: buildCounter, |
| maxLength: maxLength, |
| )); |
| expect(find.byKey(buildCounterKey), findsOneWidget); |
| }); |
| |
| testWidgets('InputDecoration errorMaxLines', (WidgetTester tester) async { |
| const String kError1 = 'e0'; |
| const String kError2 = 'e0\ne1'; |
| const String kError3 = 'e0\ne1\ne2'; |
| |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| labelText: 'label', |
| helperText: 'helper', |
| errorText: kError3, |
| errorMaxLines: 3, |
| filled: true, |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is 100dps: |
| // |
| // 12 - top padding |
| // 12 - floating label (ahem font size 16dps * 0.75 = 12) |
| // 4 - floating label / input text gap |
| // 16 - input text (ahem font size 16dps) |
| // 12 - bottom padding |
| // 8 - below the border padding |
| // 36 - error text (3 lines, ahem font size 12dps) |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 100.0)); |
| expect(tester.getTopLeft(find.text(kError3)), const Offset(12.0, 64.0)); |
| expect(tester.getBottomLeft(find.text(kError3)), const Offset(12.0, 100.0)); |
| |
| // Overall height for this InputDecorator is 12 less than the first |
| // one, 88dps, because errorText only occupies two lines. |
| |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| labelText: 'label', |
| helperText: 'helper', |
| errorText: kError2, |
| errorMaxLines: 3, |
| filled: true, |
| ), |
| ), |
| ); |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 88.0)); |
| expect(tester.getTopLeft(find.text(kError2)), const Offset(12.0, 64.0)); |
| expect(tester.getBottomLeft(find.text(kError2)), const Offset(12.0, 88.0)); |
| |
| // Overall height for this InputDecorator is 24 less than the first |
| // one, 88dps, because errorText only occupies one line. |
| |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| labelText: 'label', |
| helperText: 'helper', |
| errorText: kError1, |
| errorMaxLines: 3, |
| filled: true, |
| ), |
| ), |
| ); |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 76.0)); |
| expect(tester.getTopLeft(find.text(kError1)), const Offset(12.0, 64.0)); |
| expect(tester.getBottomLeft(find.text(kError1)), const Offset(12.0, 76.0)); |
| }); |
| |
| testWidgets('InputDecoration helperMaxLines', (WidgetTester tester) async { |
| const String kHelper1 = 'e0'; |
| const String kHelper2 = 'e0\ne1'; |
| const String kHelper3 = 'e0\ne1\ne2'; |
| |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| labelText: 'label', |
| helperText: kHelper3, |
| helperMaxLines: 3, |
| errorText: null, |
| filled: true, |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is 100dps: |
| // |
| // 12 - top padding |
| // 12 - floating label (ahem font size 16dps * 0.75 = 12) |
| // 4 - floating label / input text gap |
| // 16 - input text (ahem font size 16dps) |
| // 12 - bottom padding |
| // 8 - below the border padding |
| // 36 - helper text (3 lines, ahem font size 12dps) |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 100.0)); |
| expect(tester.getTopLeft(find.text(kHelper3)), const Offset(12.0, 64.0)); |
| expect(tester.getBottomLeft(find.text(kHelper3)), const Offset(12.0, 100.0)); |
| |
| // Overall height for this InputDecorator is 12 less than the first |
| // one, 88dps, because helperText only occupies two lines. |
| |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| labelText: 'label', |
| helperText: kHelper3, |
| helperMaxLines: 2, |
| errorText: null, |
| filled: true, |
| ), |
| ), |
| ); |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 88.0)); |
| expect(tester.getTopLeft(find.text(kHelper3)), const Offset(12.0, 64.0)); |
| expect(tester.getBottomLeft(find.text(kHelper3)), const Offset(12.0, 88.0)); |
| |
| // Overall height for this InputDecorator is 12 less than the first |
| // one, 88dps, because helperText only occupies two lines. |
| |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| labelText: 'label', |
| helperText: kHelper2, |
| helperMaxLines: 3, |
| errorText: null, |
| filled: true, |
| ), |
| ), |
| ); |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 88.0)); |
| expect(tester.getTopLeft(find.text(kHelper2)), const Offset(12.0, 64.0)); |
| expect(tester.getBottomLeft(find.text(kHelper2)), const Offset(12.0, 88.0)); |
| |
| // Overall height for this InputDecorator is 24 less than the first |
| // one, 88dps, because helperText only occupies one line. |
| |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| labelText: 'label', |
| helperText: kHelper1, |
| helperMaxLines: 3, |
| errorText: null, |
| filled: true, |
| ), |
| ), |
| ); |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 76.0)); |
| expect(tester.getTopLeft(find.text(kHelper1)), const Offset(12.0, 64.0)); |
| expect(tester.getBottomLeft(find.text(kHelper1)), const Offset(12.0, 76.0)); |
| }); |
| |
| testWidgets('InputDecorator prefix/suffix texts', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| prefixText: 'p', |
| suffixText: 's', |
| filled: true, |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is 40dps: |
| // 12 - top padding |
| // 16 - input text (ahem font size 16dps) |
| // 12 - bottom padding |
| // |
| // The prefix and suffix wrap the input text and are left and right justified |
| // respectively. They should have the same height as the input text (16). |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, kMinInteractiveDimension)); |
| expect(tester.getSize(find.text('text')).height, 16.0); |
| expect(tester.getSize(find.text('p')).height, 16.0); |
| expect(tester.getSize(find.text('s')).height, 16.0); |
| expect(tester.getTopLeft(find.text('text')).dy, 16.0); |
| expect(tester.getTopLeft(find.text('p')).dy, 16.0); |
| expect(tester.getTopLeft(find.text('p')).dx, 12.0); |
| expect(tester.getTopLeft(find.text('s')).dy, 16.0); |
| expect(tester.getTopRight(find.text('s')).dx, 788.0); |
| |
| // layout is a row: [p text s] |
| expect(tester.getTopLeft(find.text('p')).dx, 12.0); |
| expect(tester.getTopRight(find.text('p')).dx, lessThanOrEqualTo(tester.getTopLeft(find.text('text')).dx)); |
| expect(tester.getTopRight(find.text('text')).dx, lessThanOrEqualTo(tester.getTopLeft(find.text('s')).dx)); |
| }); |
| |
| testWidgets('InputDecorator icon/prefix/suffix', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| prefixText: 'p', |
| suffixText: 's', |
| icon: Icon(Icons.android), |
| filled: true, |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is 40dps: |
| // 12 - top padding |
| // 16 - input text (ahem font size 16dps) |
| // 12 - bottom padding |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, kMinInteractiveDimension)); |
| expect(tester.getSize(find.text('text')).height, 16.0); |
| expect(tester.getSize(find.text('p')).height, 16.0); |
| expect(tester.getSize(find.text('s')).height, 16.0); |
| expect(tester.getTopLeft(find.text('text')).dy, 16.0); |
| expect(tester.getTopLeft(find.text('p')).dy, 16.0); |
| expect(tester.getTopLeft(find.text('s')).dy, 16.0); |
| expect(tester.getTopRight(find.text('s')).dx, 788.0); |
| expect(tester.getSize(find.byType(Icon)).height, 24.0); |
| |
| // The 24dps high icon is centered on the 16dps high input line |
| expect(tester.getTopLeft(find.byType(Icon)).dy, 12.0); |
| |
| // layout is a row: [icon, p text s] |
| expect(tester.getTopLeft(find.byType(Icon)).dx, 0.0); |
| expect(tester.getTopRight(find.byType(Icon)).dx, lessThanOrEqualTo(tester.getTopLeft(find.text('p')).dx)); |
| expect(tester.getTopRight(find.text('p')).dx, lessThanOrEqualTo(tester.getTopLeft(find.text('text')).dx)); |
| expect(tester.getTopRight(find.text('text')).dx, lessThanOrEqualTo(tester.getTopLeft(find.text('s')).dx)); |
| }); |
| |
| testWidgets('InputDecorator prefix/suffix widgets', (WidgetTester tester) async { |
| const Key pKey = Key('p'); |
| const Key sKey = Key('s'); |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| prefix: Padding( |
| key: pKey, |
| padding: EdgeInsets.all(4.0), |
| child: Text('p'), |
| ), |
| suffix: Padding( |
| key: sKey, |
| padding: EdgeInsets.all(4.0), |
| child: Text('s'), |
| ), |
| filled: true, |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is 48dps because |
| // the prefix and the suffix widget is surrounded with padding: |
| // 12 - top padding |
| // 4 - top prefix/suffix padding |
| // 16 - input text (ahem font size 16dps) |
| // 4 - bottom prefix/suffix padding |
| // 12 - bottom padding |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 48.0)); |
| expect(tester.getSize(find.text('text')).height, 16.0); |
| expect(tester.getSize(find.byKey(pKey)).height, 24.0); |
| expect(tester.getSize(find.text('p')).height, 16.0); |
| expect(tester.getSize(find.byKey(sKey)).height, 24.0); |
| expect(tester.getSize(find.text('s')).height, 16.0); |
| expect(tester.getTopLeft(find.text('text')).dy, 16.0); |
| expect(tester.getTopLeft(find.byKey(pKey)).dy, 12.0); |
| expect(tester.getTopLeft(find.text('p')).dy, 16.0); |
| expect(tester.getTopLeft(find.byKey(sKey)).dy, 12.0); |
| expect(tester.getTopLeft(find.text('s')).dy, 16.0); |
| expect(tester.getTopRight(find.byKey(sKey)).dx, 788.0); |
| expect(tester.getTopRight(find.text('s')).dx, 784.0); |
| |
| // layout is a row: [prefix text suffix] |
| expect(tester.getTopLeft(find.byKey(pKey)).dx, 12.0); |
| expect(tester.getTopRight(find.byKey(pKey)).dx, tester.getTopLeft(find.text('text')).dx); |
| expect(tester.getTopRight(find.text('text')).dx, lessThanOrEqualTo(tester.getTopRight(find.byKey(sKey)).dx)); |
| }); |
| |
| testWidgets('InputDecorator tall prefix', (WidgetTester tester) async { |
| const Key pKey = Key('p'); |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: InputDecoration( |
| prefix: Container( |
| key: pKey, |
| height: 100, |
| width: 10, |
| ), |
| filled: true, |
| ), |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| 'text', |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is ~127.2dps because |
| // the prefix is 100dps tall, but it aligns with the input's baseline, |
| // overlapping the input a bit. |
| // 12 - top padding |
| // 100 - total height of prefix |
| // -16 - input prefix overlap (distance input top to baseline, not exact) |
| // 20 - input text (ahem font size 16dps) |
| // 0 - bottom prefix/suffix padding |
| // 12 - bottom padding |
| |
| expect(tester.getSize(find.byType(InputDecorator)).width, 800.0); |
| expect(tester.getSize(find.byType(InputDecorator)).height, moreOrLessEquals(128.0, epsilon: .0001)); |
| expect(tester.getSize(find.text('text')).height, 20.0); |
| expect(tester.getSize(find.byKey(pKey)).height, 100.0); |
| expect(tester.getTopLeft(find.text('text')).dy, moreOrLessEquals(96, epsilon: .0001)); // 12 + 100 - 16 |
| expect(tester.getTopLeft(find.byKey(pKey)).dy, 12.0); |
| |
| // layout is a row: [prefix text suffix] |
| expect(tester.getTopLeft(find.byKey(pKey)).dx, 12.0); |
| expect(tester.getTopRight(find.byKey(pKey)).dx, tester.getTopLeft(find.text('text')).dx); |
| }); |
| |
| testWidgets('InputDecorator tall prefix with border', (WidgetTester tester) async { |
| const Key pKey = Key('p'); |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: InputDecoration( |
| border: const OutlineInputBorder(), |
| prefix: Container( |
| key: pKey, |
| height: 100, |
| width: 10, |
| ), |
| filled: true, |
| ), |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| 'text', |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is ~127.2dps because |
| // the prefix is 100dps tall, but it aligns with the input's baseline, |
| // overlapping the input a bit. |
| // 24 - top padding |
| // 100 - total height of prefix |
| // -16 - input prefix overlap (distance input top to baseline, not exact) |
| // 20 - input text (ahem font size 16dps) |
| // 0 - bottom prefix/suffix padding |
| // 16 - bottom padding |
| // When a border is present, the input text and prefix/suffix are centered |
| // within the input. Here, that will be content of height 106, including 2 |
| // extra pixels of space, centered within an input of height 144. That gives |
| // 19 pixels of space on each side of the content, so the prefix is |
| // positioned at 19, and the text is at 19+100-16=103. |
| |
| expect(tester.getSize(find.byType(InputDecorator)).width, 800.0); |
| expect(tester.getSize(find.byType(InputDecorator)).height, moreOrLessEquals(144, epsilon: .0001)); |
| expect(tester.getSize(find.text('text')).height, 20.0); |
| expect(tester.getSize(find.byKey(pKey)).height, 100.0); |
| expect(tester.getTopLeft(find.text('text')).dy, moreOrLessEquals(103, epsilon: .0001)); |
| expect(tester.getTopLeft(find.byKey(pKey)).dy, 19.0); |
| |
| // layout is a row: [prefix text suffix] |
| expect(tester.getTopLeft(find.byKey(pKey)).dx, 12.0); |
| expect(tester.getTopRight(find.byKey(pKey)).dx, tester.getTopLeft(find.text('text')).dx); |
| }); |
| |
| testWidgets('InputDecorator prefixIcon/suffixIcon', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| prefixIcon: Icon(Icons.pages), |
| suffixIcon: Icon(Icons.satellite), |
| filled: true, |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is 48dps because the prefix icon's minimum size |
| // is 48x48 and the rest of the elements only require 40dps: |
| // 12 - top padding |
| // 16 - input text (ahem font size 16dps) |
| // 12 - bottom padding |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 48.0)); |
| expect(tester.getSize(find.text('text')).height, 16.0); |
| expect(tester.getSize(find.byIcon(Icons.pages)).height, 48.0); |
| expect(tester.getSize(find.byIcon(Icons.satellite)).height, 48.0); |
| expect(tester.getTopLeft(find.text('text')).dy, 12.0); |
| expect(tester.getTopLeft(find.byIcon(Icons.pages)).dy, 0.0); |
| expect(tester.getTopLeft(find.byIcon(Icons.satellite)).dy, 0.0); |
| expect(tester.getTopRight(find.byIcon(Icons.satellite)).dx, 800.0); |
| |
| |
| // layout is a row: [icon text icon] |
| expect(tester.getTopLeft(find.byIcon(Icons.pages)).dx, 0.0); |
| expect(tester.getTopRight(find.byIcon(Icons.pages)).dx, lessThanOrEqualTo(tester.getTopLeft(find.text('text')).dx)); |
| expect(tester.getTopRight(find.text('text')).dx, lessThanOrEqualTo(tester.getTopLeft(find.byIcon(Icons.satellite)).dx)); |
| }); |
| |
| testWidgets('InputDecorator prefixIconConstraints/suffixIconConstraints', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| prefixIcon: Icon(Icons.pages), |
| prefixIconConstraints: BoxConstraints( |
| minWidth: 32, |
| minHeight: 32, |
| ), |
| suffixIcon: Icon(Icons.satellite), |
| suffixIconConstraints: BoxConstraints( |
| minWidth: 25, |
| minHeight: 25, |
| ), |
| isDense: true, // has to be true to go below 48px height |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is 32px because the prefix icon |
| // is now a custom value |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 32.0)); |
| expect(tester.getSize(find.text('text')).height, 16.0); |
| expect(tester.getSize(find.byIcon(Icons.pages)).height, 32.0); |
| expect(tester.getSize(find.byIcon(Icons.satellite)).height, 25.0); |
| |
| // (InputDecorator height - Text widget height) / 2 |
| expect(tester.getTopLeft(find.text('text')).dy, (32.0 - 16.0) / 2); |
| // prefixIcon should take up the entire height of InputDecorator |
| expect(tester.getTopLeft(find.byIcon(Icons.pages)).dy, 0.0); |
| // (InputDecorator height - suffixIcon height) / 2 |
| expect(tester.getTopLeft(find.byIcon(Icons.satellite)).dy, (32.0 - 25.0) / 2); |
| expect(tester.getTopRight(find.byIcon(Icons.satellite)).dx, 800.0); |
| }); |
| |
| testWidgets('prefix/suffix icons are centered when smaller than 48 by 48', (WidgetTester tester) async { |
| const Key prefixKey = Key('prefix'); |
| await tester.pumpWidget( |
| buildInputDecorator( |
| decoration: const InputDecoration( |
| prefixIcon: Padding( |
| padding: EdgeInsets.all(16.0), |
| child: SizedBox(width: 8.0, height: 8.0, key: prefixKey), |
| ), |
| filled: true, |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is 48dps because the prefix icon's minimum size |
| // is 48x48 and the rest of the elements only require 40dps: |
| // 12 - top padding |
| // 16 - input text (ahem font size 16dps) |
| // 12 - bottom padding |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 48.0)); |
| expect(tester.getSize(find.byKey(prefixKey)).height, 16.0); |
| expect(tester.getTopLeft(find.byKey(prefixKey)).dy, 16.0); |
| }); |
| |
| testWidgets('InputDecorator respects reduced theme visualDensity', (WidgetTester tester) async { |
| // Label is visible, hint is not (opacity 0.0). |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| visualDensity: const VisualDensity(horizontal: -2.0, vertical: -2.0), |
| decoration: const InputDecoration( |
| labelText: 'label', |
| hintText: 'hint', |
| ), |
| ), |
| ); |
| |
| // The label is not floating so it's vertically centered. |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 48.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 24.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 40.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 16.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 32.0); |
| expect(getOpacity(tester, 'hint'), 0.0); |
| expect(getBorderBottom(tester), 48.0); |
| expect(getBorderWeight(tester), 1.0); |
| |
| // Label moves upwards, hint is visible (opacity 1.0). |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| isFocused: true, |
| visualDensity: const VisualDensity(horizontal: -2.0, vertical: -2.0), |
| decoration: const InputDecoration( |
| labelText: 'label', |
| hintText: 'hint', |
| ), |
| ), |
| ); |
| |
| // The hint's opacity animates from 0.0 to 1.0. |
| // The animation's duration is 200ms. |
| { |
| await tester.pump(const Duration(milliseconds: 50)); |
| final double hintOpacity50ms = getOpacity(tester, 'hint'); |
| expect(hintOpacity50ms, inExclusiveRange(0.0, 1.0)); |
| await tester.pump(const Duration(milliseconds: 50)); |
| final double hintOpacity100ms = getOpacity(tester, 'hint'); |
| expect(hintOpacity100ms, inExclusiveRange(hintOpacity50ms, 1.0)); |
| } |
| |
| await tester.pumpAndSettle(); |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 48.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 24.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 40.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 12.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 24.0); |
| expect(tester.getTopLeft(find.text('hint')).dy, 24.0); |
| expect(tester.getBottomLeft(find.text('hint')).dy, 40.0); |
| expect(getOpacity(tester, 'hint'), 1.0); |
| expect(getBorderBottom(tester), 48.0); |
| expect(getBorderWeight(tester), 2.0); |
| |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: false, |
| isFocused: true, |
| visualDensity: const VisualDensity(horizontal: -2.0, vertical: -2.0), |
| decoration: const InputDecoration( |
| labelText: 'label', |
| hintText: 'hint', |
| ), |
| ), |
| ); |
| |
| // The hint's opacity animates from 1.0 to 0.0. |
| // The animation's duration is 200ms. |
| { |
| await tester.pump(const Duration(milliseconds: 50)); |
| final double hintOpacity50ms = getOpacity(tester, 'hint'); |
| expect(hintOpacity50ms, inExclusiveRange(0.0, 1.0)); |
| await tester.pump(const Duration(milliseconds: 50)); |
| final double hintOpacity100ms = getOpacity(tester, 'hint'); |
| expect(hintOpacity100ms, inExclusiveRange(0.0, hintOpacity50ms)); |
| } |
| |
| await tester.pumpAndSettle(); |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 48.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 24.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 40.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 12.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 24.0); |
| expect(tester.getTopLeft(find.text('hint')).dy, 24.0); |
| expect(tester.getBottomLeft(find.text('hint')).dy, 40.0); |
| expect(getOpacity(tester, 'hint'), 0.0); |
| expect(getBorderBottom(tester), 48.0); |
| expect(getBorderWeight(tester), 2.0); |
| }); |
| |
| testWidgets('InputDecorator respects increased theme visualDensity', (WidgetTester tester) async { |
| // Label is visible, hint is not (opacity 0.0). |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| visualDensity: const VisualDensity(horizontal: 2.0, vertical: 2.0), |
| decoration: const InputDecoration( |
| labelText: 'label', |
| hintText: 'hint', |
| ), |
| ), |
| ); |
| |
| // The label is not floating so it's vertically centered. |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 64.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 32.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 48.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 24.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 40.0); |
| expect(getOpacity(tester, 'hint'), 0.0); |
| expect(getBorderBottom(tester), 64.0); |
| expect(getBorderWeight(tester), 1.0); |
| |
| // Label moves upwards, hint is visible (opacity 1.0). |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| isFocused: true, |
| visualDensity: const VisualDensity(horizontal: 2.0, vertical: 2.0), |
| decoration: const InputDecoration( |
| labelText: 'label', |
| hintText: 'hint', |
| ), |
| ), |
| ); |
| |
| // The hint's opacity animates from 0.0 to 1.0. |
| // The animation's duration is 200ms. |
| { |
| await tester.pump(const Duration(milliseconds: 50)); |
| final double hintOpacity50ms = getOpacity(tester, 'hint'); |
| expect(hintOpacity50ms, inExclusiveRange(0.0, 1.0)); |
| await tester.pump(const Duration(milliseconds: 50)); |
| final double hintOpacity100ms = getOpacity(tester, 'hint'); |
| expect(hintOpacity100ms, inExclusiveRange(hintOpacity50ms, 1.0)); |
| } |
| |
| await tester.pumpAndSettle(); |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 64.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 32.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 48.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 12.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 24.0); |
| expect(tester.getTopLeft(find.text('hint')).dy, 32.0); |
| expect(tester.getBottomLeft(find.text('hint')).dy, 48.0); |
| expect(getOpacity(tester, 'hint'), 1.0); |
| expect(getBorderBottom(tester), 64.0); |
| expect(getBorderWeight(tester), 2.0); |
| |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: false, |
| isFocused: true, |
| visualDensity: const VisualDensity(horizontal: 2.0, vertical: 2.0), |
| decoration: const InputDecoration( |
| labelText: 'label', |
| hintText: 'hint', |
| ), |
| ), |
| ); |
| |
| // The hint's opacity animates from 1.0 to 0.0. |
| // The animation's duration is 200ms. |
| { |
| await tester.pump(const Duration(milliseconds: 50)); |
| final double hintOpacity50ms = getOpacity(tester, 'hint'); |
| expect(hintOpacity50ms, inExclusiveRange(0.0, 1.0)); |
| await tester.pump(const Duration(milliseconds: 50)); |
| final double hintOpacity100ms = getOpacity(tester, 'hint'); |
| expect(hintOpacity100ms, inExclusiveRange(0.0, hintOpacity50ms)); |
| } |
| |
| await tester.pumpAndSettle(); |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 64.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 32.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 48.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 12.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 24.0); |
| expect(tester.getTopLeft(find.text('hint')).dy, 32.0); |
| expect(tester.getBottomLeft(find.text('hint')).dy, 48.0); |
| expect(getOpacity(tester, 'hint'), 0.0); |
| expect(getBorderBottom(tester), 64.0); |
| expect(getBorderWeight(tester), 2.0); |
| }); |
| |
| testWidgets('prefix/suffix icons increase height of decoration when larger than 48 by 48', (WidgetTester tester) async { |
| const Key prefixKey = Key('prefix'); |
| await tester.pumpWidget( |
| buildInputDecorator( |
| decoration: const InputDecoration( |
| prefixIcon: SizedBox(width: 100.0, height: 100.0, key: prefixKey), |
| filled: true, |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is 100dps because the prefix icon's size |
| // is 100x100 and the rest of the elements only require 40dps: |
| // 12 - top padding |
| // 16 - input text (ahem font size 16dps) |
| // 12 - bottom padding |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 100.0)); |
| expect(tester.getSize(find.byKey(prefixKey)).height, 100.0); |
| expect(tester.getTopLeft(find.byKey(prefixKey)).dy, 0.0); |
| }); |
| |
| group('textAlignVertical position', () { |
| group('simple case', () { |
| testWidgets('align top (default)', (WidgetTester tester) async { |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| expands: true, // so we have a tall input where align can vary |
| decoration: const InputDecoration( |
| filled: true, |
| ), |
| textAlignVertical: TextAlignVertical.top, // default when no border |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // Same as the default case above. |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(12.0, epsilon: .0001)); |
| }); |
| |
| testWidgets('align center', (WidgetTester tester) async { |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| expands: true, |
| decoration: const InputDecoration( |
| filled: true, |
| ), |
| textAlignVertical: TextAlignVertical.center, |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // Below the top aligned case. |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(290.0, epsilon: .0001)); |
| }); |
| |
| testWidgets('align bottom', (WidgetTester tester) async { |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| expands: true, |
| decoration: const InputDecoration( |
| filled: true, |
| ), |
| textAlignVertical: TextAlignVertical.bottom, |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // Below the center aligned case. |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(568.0, epsilon: .0001)); |
| }); |
| |
| testWidgets('align as a double', (WidgetTester tester) async { |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| expands: true, |
| decoration: const InputDecoration( |
| filled: true, |
| ), |
| textAlignVertical: const TextAlignVertical(y: 0.75), |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // In between the center and bottom aligned cases. |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(498.5, epsilon: .0001)); |
| }); |
| }); |
| |
| group('outline border', () { |
| testWidgets('align top', (WidgetTester tester) async { |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| expands: true, // so we have a tall input where align can vary |
| decoration: const InputDecoration( |
| filled: true, |
| border: OutlineInputBorder(), |
| ), |
| textAlignVertical: TextAlignVertical.top, |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // Similar to the case without a border, but with a little extra room at |
| // the top to make room for the border. |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(24.0, epsilon: .0001)); |
| }); |
| |
| testWidgets('align center (default)', (WidgetTester tester) async { |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| expands: true, |
| decoration: const InputDecoration( |
| filled: true, |
| border: OutlineInputBorder(), |
| ), |
| textAlignVertical: TextAlignVertical.center, // default when border |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // Below the top aligned case. |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(289.0, epsilon: .0001)); |
| }); |
| |
| testWidgets('align bottom', (WidgetTester tester) async { |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| expands: true, |
| decoration: const InputDecoration( |
| filled: true, |
| border: OutlineInputBorder(), |
| ), |
| textAlignVertical: TextAlignVertical.bottom, |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // Below the center aligned case. |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(564.0, epsilon: .0001)); |
| }); |
| }); |
| |
| group('prefix', () { |
| testWidgets('InputDecorator tall prefix align top', (WidgetTester tester) async { |
| const Key pKey = Key('p'); |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: InputDecoration( |
| prefix: Container( |
| key: pKey, |
| height: 100, |
| width: 10, |
| ), |
| filled: true, |
| ), |
| textAlignVertical: TextAlignVertical.top, // default when no border |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // Same as the default case above. |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(96, epsilon: .0001)); |
| expect(tester.getTopLeft(find.byKey(pKey)).dy, 12.0); |
| }); |
| |
| testWidgets('InputDecorator tall prefix align center', (WidgetTester tester) async { |
| const Key pKey = Key('p'); |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: InputDecoration( |
| prefix: Container( |
| key: pKey, |
| height: 100, |
| width: 10, |
| ), |
| filled: true, |
| ), |
| textAlignVertical: TextAlignVertical.center, |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // Same as the default case above. |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(96.0, epsilon: .0001)); |
| expect(tester.getTopLeft(find.byKey(pKey)).dy, 12.0); |
| }); |
| |
| testWidgets('InputDecorator tall prefix align bottom', (WidgetTester tester) async { |
| const Key pKey = Key('p'); |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: InputDecoration( |
| prefix: Container( |
| key: pKey, |
| height: 100, |
| width: 10, |
| ), |
| filled: true, |
| ), |
| textAlignVertical: TextAlignVertical.bottom, |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // Top of the input + 100 prefix height - overlap |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(96.0, epsilon: .0001)); |
| expect(tester.getTopLeft(find.byKey(pKey)).dy, 12.0); |
| }); |
| }); |
| |
| group('outline border and prefix', () { |
| testWidgets('InputDecorator tall prefix align center', (WidgetTester tester) async { |
| const Key pKey = Key('p'); |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| expands: true, |
| decoration: InputDecoration( |
| border: const OutlineInputBorder(), |
| prefix: Container( |
| key: pKey, |
| height: 100, |
| width: 10, |
| ), |
| filled: true, |
| ), |
| textAlignVertical: TextAlignVertical.center, // default when border |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // In the middle of the expanded InputDecorator. |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(331.0, epsilon: .0001)); |
| expect(tester.getTopLeft(find.byKey(pKey)).dy, moreOrLessEquals(247.0, epsilon: .0001)); |
| }); |
| |
| testWidgets('InputDecorator tall prefix with border align top', (WidgetTester tester) async { |
| const Key pKey = Key('p'); |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| expands: true, |
| decoration: InputDecoration( |
| border: const OutlineInputBorder(), |
| prefix: Container( |
| key: pKey, |
| height: 100, |
| width: 10, |
| ), |
| filled: true, |
| ), |
| textAlignVertical: TextAlignVertical.top, |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // Above the center example. |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(108.0, epsilon: .0001)); |
| // The prefix is positioned at the top of the input, so this value is |
| // the same as the top aligned test without a prefix. |
| expect(tester.getTopLeft(find.byKey(pKey)).dy, 24.0); |
| }); |
| |
| testWidgets('InputDecorator tall prefix with border align bottom', (WidgetTester tester) async { |
| const Key pKey = Key('p'); |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| expands: true, |
| decoration: InputDecoration( |
| border: const OutlineInputBorder(), |
| prefix: Container( |
| key: pKey, |
| height: 100, |
| width: 10, |
| ), |
| filled: true, |
| ), |
| textAlignVertical: TextAlignVertical.bottom, |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // Below the center example. |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(564.0, epsilon: .0001)); |
| expect(tester.getTopLeft(find.byKey(pKey)).dy, moreOrLessEquals(480.0, epsilon: .0001)); |
| }); |
| |
| testWidgets('InputDecorator tall prefix with border align double', (WidgetTester tester) async { |
| const Key pKey = Key('p'); |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| expands: true, |
| decoration: InputDecoration( |
| border: const OutlineInputBorder(), |
| prefix: Container( |
| key: pKey, |
| height: 100, |
| width: 10, |
| ), |
| filled: true, |
| ), |
| textAlignVertical: const TextAlignVertical(y: 0.1), |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // Between the top and center examples. |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(354.3, epsilon: .0001)); |
| expect(tester.getTopLeft(find.byKey(pKey)).dy, moreOrLessEquals(270.3, epsilon: .0001)); |
| }); |
| }); |
| |
| group('label', () { |
| testWidgets('align top (default)', (WidgetTester tester) async { |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| expands: true, // so we have a tall input where align can vary |
| decoration: const InputDecoration( |
| labelText: 'label', |
| filled: true, |
| ), |
| textAlignVertical: TextAlignVertical.top, // default |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // The label causes the text to start slightly lower than it would |
| // otherwise. |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(28.0, epsilon: .0001)); |
| }); |
| |
| testWidgets('align center', (WidgetTester tester) async { |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| expands: true, // so we have a tall input where align can vary |
| decoration: const InputDecoration( |
| labelText: 'label', |
| filled: true, |
| ), |
| textAlignVertical: TextAlignVertical.center, |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // The label reduces the amount of space available for text, so the |
| // center is slightly lower. |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(298.0, epsilon: .0001)); |
| }); |
| |
| testWidgets('align bottom', (WidgetTester tester) async { |
| const String text = 'text'; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| expands: true, // so we have a tall input where align can vary |
| decoration: const InputDecoration( |
| labelText: 'label', |
| filled: true, |
| ), |
| textAlignVertical: TextAlignVertical.bottom, |
| // Set the fontSize so that everything works out to whole numbers. |
| child: const Text( |
| text, |
| style: TextStyle(fontFamily: 'Ahem', fontSize: 20.0), |
| ), |
| ), |
| ); |
| |
| // The label reduces the amount of space available for text, but the |
| // bottom line is still in the same place. |
| expect(tester.getTopLeft(find.text(text)).dy, moreOrLessEquals(568.0, epsilon: .0001)); |
| }); |
| }); |
| }); |
| |
| group('OutlineInputBorder', () { |
| group('default alignment', () { |
| testWidgets('Centers when border', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| decoration: const InputDecoration( |
| border: OutlineInputBorder(), |
| ), |
| ), |
| ); |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 19.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 35.0); |
| expect(getBorderBottom(tester), 56.0); |
| expect(getBorderWeight(tester), 1.0); |
| }); |
| |
| testWidgets('Centers when border and label', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| decoration: const InputDecoration( |
| labelText: 'label', |
| border: OutlineInputBorder(), |
| ), |
| ), |
| ); |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 19.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 35.0); |
| expect(getBorderBottom(tester), 56.0); |
| expect(getBorderWeight(tester), 1.0); |
| }); |
| |
| testWidgets('Centers when border and contentPadding', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| decoration: const InputDecoration( |
| border: OutlineInputBorder(), |
| contentPadding: EdgeInsets.fromLTRB( |
| 12.0, 14.0, |
| 8.0, 14.0, |
| ), |
| ), |
| ), |
| ); |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 48.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 15.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 31.0); |
| expect(getBorderBottom(tester), 48.0); |
| expect(getBorderWeight(tester), 1.0); |
| }); |
| |
| testWidgets('Centers when border and contentPadding and label', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| decoration: const InputDecoration( |
| labelText: 'label', |
| border: OutlineInputBorder(), |
| contentPadding: EdgeInsets.fromLTRB( |
| 12.0, 14.0, |
| 8.0, 14.0, |
| ), |
| ), |
| ), |
| ); |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, kMinInteractiveDimension)); |
| expect(tester.getTopLeft(find.text('text')).dy, 15.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 31.0); |
| expect(getBorderBottom(tester), 48.0); |
| expect(getBorderWeight(tester), 1.0); |
| }); |
| |
| testWidgets('Centers when border and lopsided contentPadding and label', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| decoration: const InputDecoration( |
| labelText: 'label', |
| border: OutlineInputBorder(), |
| contentPadding: EdgeInsets.fromLTRB( |
| 12.0, 104.0, |
| 8.0, 0.0, |
| ), |
| ), |
| ), |
| ); |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 120.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 51.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 67.0); |
| expect(getBorderBottom(tester), 120.0); |
| expect(getBorderWeight(tester), 1.0); |
| }); |
| }); |
| |
| group('3 point interpolation alignment', () { |
| testWidgets('top align includes padding', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| expands: true, |
| textAlignVertical: TextAlignVertical.top, |
| decoration: const InputDecoration( |
| border: OutlineInputBorder(), |
| contentPadding: EdgeInsets.fromLTRB( |
| 12.0, 24.0, |
| 8.0, 2.0, |
| ), |
| ), |
| ), |
| ); |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 600.0)); |
| // Aligned to the top including the 24px padding. |
| expect(tester.getTopLeft(find.text('text')).dy, 24.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 40.0); |
| expect(getBorderBottom(tester), 600.0); |
| expect(getBorderWeight(tester), 1.0); |
| }); |
| |
| testWidgets('center align ignores padding', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| expands: true, |
| textAlignVertical: TextAlignVertical.center, |
| decoration: const InputDecoration( |
| border: OutlineInputBorder(), |
| contentPadding: EdgeInsets.fromLTRB( |
| 12.0, 24.0, |
| 8.0, 2.0, |
| ), |
| ), |
| ), |
| ); |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 600.0)); |
| // Baseline is on the center of the 600px high input. |
| expect(tester.getTopLeft(find.text('text')).dy, 291.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 307.0); |
| expect(getBorderBottom(tester), 600.0); |
| expect(getBorderWeight(tester), 1.0); |
| }); |
| |
| testWidgets('bottom align includes padding', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| expands: true, |
| textAlignVertical: TextAlignVertical.bottom, |
| decoration: const InputDecoration( |
| border: OutlineInputBorder(), |
| contentPadding: EdgeInsets.fromLTRB( |
| 12.0, 24.0, |
| 8.0, 2.0, |
| ), |
| ), |
| ), |
| ); |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 600.0)); |
| // Includes bottom padding of 2px. |
| expect(tester.getTopLeft(find.text('text')).dy, 582.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 598.0); |
| expect(getBorderBottom(tester), 600.0); |
| expect(getBorderWeight(tester), 1.0); |
| }); |
| |
| testWidgets('padding exceeds middle keeps top at middle', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| expands: true, |
| textAlignVertical: TextAlignVertical.top, |
| decoration: const InputDecoration( |
| border: OutlineInputBorder(), |
| contentPadding: EdgeInsets.fromLTRB( |
| 12.0, 504.0, |
| 8.0, 0.0, |
| ), |
| ), |
| ), |
| ); |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 600.0)); |
| // Same position as the center example above. |
| expect(tester.getTopLeft(find.text('text')).dy, 291.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 307.0); |
| expect(getBorderBottom(tester), 600.0); |
| expect(getBorderWeight(tester), 1.0); |
| }); |
| }); |
| }); |
| |
| testWidgets('counter text has correct right margin - LTR, not dense', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| counterText: 'test', |
| filled: true, |
| ), |
| ), |
| ); |
| |
| // Margin for text decoration is 12 when filled |
| // (dx) - 12 = (text offset)x. |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 68.0)); |
| final double dx = tester.getRect(find.byType(InputDecorator)).right; |
| expect(tester.getRect(find.text('test')).right, dx - 12.0); |
| }); |
| |
| testWidgets('counter text has correct right margin - RTL, not dense', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| textDirection: TextDirection.rtl, |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| counterText: 'test', |
| filled: true, |
| ), |
| ), |
| ); |
| |
| // Margin for text decoration is 12 when filled and top left offset is (0, 0) |
| // 0 + 12 = 12. |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 68.0)); |
| expect(tester.getRect(find.text('test')).left, 12.0); |
| }); |
| |
| testWidgets('counter text has correct right margin - LTR, dense', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| counterText: 'test', |
| filled: true, |
| isDense: true, |
| ), |
| ), |
| ); |
| |
| // Margin for text decoration is 12 when filled |
| // (dx) - 12 = (text offset)x. |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 52.0)); |
| final double dx = tester.getRect(find.byType(InputDecorator)).right; |
| expect(tester.getRect(find.text('test')).right, dx - 12.0); |
| }); |
| |
| testWidgets('counter text has correct right margin - RTL, dense', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| textDirection: TextDirection.rtl, |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| counterText: 'test', |
| filled: true, |
| isDense: true, |
| ), |
| ), |
| ); |
| |
| // Margin for text decoration is 12 when filled and top left offset is (0, 0) |
| // 0 + 12 = 12. |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 52.0)); |
| expect(tester.getRect(find.text('test')).left, 12.0); |
| }); |
| |
| testWidgets('InputDecorator error/helper/counter RTL layout', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| textDirection: TextDirection.rtl, |
| decoration: const InputDecoration( |
| labelText: 'label', |
| helperText: 'helper', |
| counterText: 'counter', |
| filled: true, |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is 76dps: |
| // 12 - top padding |
| // 12 - floating label (ahem font size 16dps * 0.75 = 12) |
| // 4 - floating label / input text gap |
| // 16 - input text (ahem font size 16dps) |
| // 12 - bottom padding |
| // 8 - below the border padding |
| // 12 - [counter helper/error] (ahem font size 12dps) |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 76.0)); |
| expect(tester.getTopLeft(find.text('text')).dy, 28.0); |
| expect(tester.getBottomLeft(find.text('text')).dy, 44.0); |
| expect(tester.getTopLeft(find.text('label')).dy, 12.0); |
| expect(tester.getBottomLeft(find.text('label')).dy, 24.0); |
| expect(getBorderBottom(tester), 56.0); |
| expect(getBorderWeight(tester), 1.0); |
| expect(tester.getTopLeft(find.text('counter')), const Offset(12.0, 64.0)); |
| expect(tester.getTopRight(find.text('helper')), const Offset(788.0, 64.0)); |
| |
| // If both error and helper are specified, show the error |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| textDirection: TextDirection.rtl, |
| decoration: const InputDecoration( |
| labelText: 'label', |
| helperText: 'helper', |
| errorText: 'error', |
| counterText: 'counter', |
| filled: true, |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| expect(tester.getTopLeft(find.text('counter')), const Offset(12.0, 64.0)); |
| expect(tester.getTopRight(find.text('error')), const Offset(788.0, 64.0)); |
| expect(find.text('helper'), findsNothing); |
| }); |
| |
| testWidgets('InputDecorator prefix/suffix RTL', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| textDirection: TextDirection.rtl, |
| decoration: const InputDecoration( |
| prefixText: 'p', |
| suffixText: 's', |
| filled: true, |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is 40dps: |
| // 12 - top padding |
| // 16 - input text (ahem font size 16dps) |
| // 12 - bottom padding |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, kMinInteractiveDimension)); // 40 bumped up to minimum. |
| expect(tester.getSize(find.text('text')).height, 16.0); |
| expect(tester.getSize(find.text('p')).height, 16.0); |
| expect(tester.getSize(find.text('s')).height, 16.0); |
| expect(tester.getTopLeft(find.text('text')).dy, 16.0); |
| expect(tester.getTopLeft(find.text('p')).dy, 16.0); |
| expect(tester.getTopLeft(find.text('s')).dy, 16.0); |
| |
| // layout is a row: [s text p] |
| expect(tester.getTopLeft(find.text('s')).dx, 12.0); |
| expect(tester.getTopRight(find.text('s')).dx, lessThanOrEqualTo(tester.getTopLeft(find.text('text')).dx)); |
| expect(tester.getTopRight(find.text('text')).dx, lessThanOrEqualTo(tester.getTopLeft(find.text('p')).dx)); |
| }); |
| |
| testWidgets('InputDecorator contentPadding RTL layout', (WidgetTester tester) async { |
| // LTR: content left edge is contentPadding.start: 40.0 |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| // isFocused: false (default) |
| textDirection: TextDirection.ltr, |
| decoration: const InputDecoration( |
| contentPadding: EdgeInsetsDirectional.only(start: 40.0, top: 12.0, bottom: 12.0), |
| labelText: 'label', |
| hintText: 'hint', |
| filled: true, |
| ), |
| ), |
| ); |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0)); |
| expect(tester.getTopLeft(find.text('text')).dx, 40.0); |
| expect(tester.getTopLeft(find.text('label')).dx, 40.0); |
| expect(tester.getTopLeft(find.text('hint')).dx, 40.0); |
| |
| // RTL: content right edge is 800 - contentPadding.start: 760.0. |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| isFocused: true, // label is floating, still adjusted for contentPadding |
| textDirection: TextDirection.rtl, |
| decoration: const InputDecoration( |
| contentPadding: EdgeInsetsDirectional.only(start: 40.0, top: 12.0, bottom: 12.0), |
| labelText: 'label', |
| hintText: 'hint', |
| filled: true, |
| ), |
| ), |
| ); |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0)); |
| expect(tester.getTopRight(find.text('text')).dx, 760.0); |
| expect(tester.getTopRight(find.text('label')).dx, 760.0); |
| expect(tester.getTopRight(find.text('hint')).dx, 760.0); |
| }); |
| |
| testWidgets('InputDecorator prefix/suffix dense layout', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default) |
| isFocused: true, |
| decoration: const InputDecoration( |
| isDense: true, |
| prefixText: 'p', |
| suffixText: 's', |
| filled: true, |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is 32dps: |
| // 8 - top padding |
| // 16 - input text (ahem font size 16dps) |
| // 8 - bottom padding |
| // |
| // The only difference from normal layout for this case is that the |
| // padding above and below the prefix, input text, suffix, is 8 instead of 12. |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 32.0)); |
| expect(tester.getSize(find.text('text')).height, 16.0); |
| expect(tester.getSize(find.text('p')).height, 16.0); |
| expect(tester.getSize(find.text('s')).height, 16.0); |
| expect(tester.getTopLeft(find.text('text')).dy, 8.0); |
| expect(tester.getTopLeft(find.text('p')).dy, 8.0); |
| expect(tester.getTopLeft(find.text('p')).dx, 12.0); |
| expect(tester.getTopLeft(find.text('s')).dy, 8.0); |
| expect(tester.getTopRight(find.text('s')).dx, 788.0); |
| |
| // layout is a row: [p text s] |
| expect(tester.getTopLeft(find.text('p')).dx, 12.0); |
| expect(tester.getTopRight(find.text('p')).dx, lessThanOrEqualTo(tester.getTopLeft(find.text('text')).dx)); |
| expect(tester.getTopRight(find.text('text')).dx, lessThanOrEqualTo(tester.getTopLeft(find.text('s')).dx)); |
| |
| expect(getBorderBottom(tester), 32.0); |
| expect(getBorderWeight(tester), 2.0); |
| }); |
| |
| testWidgets('InputDecorator with empty InputDecoration', (WidgetTester tester) async { |
| await tester.pumpWidget(buildInputDecorator()); |
| |
| // Overall height for this InputDecorator is 40dps: |
| // 12 - top padding |
| // 16 - input text (ahem font size 16dps) |
| // 12 - bottom padding |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, kMinInteractiveDimension)); // 40 bumped up to minimum. |
| expect(tester.getSize(find.text('text')).height, 16.0); |
| expect(tester.getTopLeft(find.text('text')).dy, 16.0); |
| expect(getBorderBottom(tester), kMinInteractiveDimension); // 40 bumped up to minimum. |
| expect(getBorderWeight(tester), 1.0); |
| }); |
| |
| testWidgets('contentPadding smaller than kMinInteractiveDimension', (WidgetTester tester) async { |
| // Regression test for https://github.com/flutter/flutter/issues/42449 |
| const double verticalPadding = 1.0; |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default), |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| hintText: 'hint', |
| contentPadding: EdgeInsets.symmetric(vertical: verticalPadding), |
| isDense: true, |
| ), |
| ), |
| ); |
| |
| // The overall height is 18dps. This is shorter than |
| // kMinInteractiveDimension, but because isDense is true, the minimum is |
| // ignored. |
| // 16 - input text (ahem font size 16dps) |
| // 2 - total vertical padding |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 18.0)); |
| expect(tester.getSize(find.text('text')).height, 16.0); |
| expect(tester.getTopLeft(find.text('text')).dy, 1.0); |
| expect(getOpacity(tester, 'hint'), 0.0); |
| expect(getBorderWeight(tester), 1.0); |
| |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default), |
| // isFocused: false (default) |
| decoration: const InputDecoration.collapsed( |
| hintText: 'hint', |
| // InputDecoration.collapsed does not support contentPadding |
| ), |
| ), |
| ); |
| |
| // The overall height is 16dps. This is shorter than |
| // kMinInteractiveDimension, but because isCollapsed is true, the minimum is |
| // ignored. There is no padding at all, because isCollapsed doesn't support |
| // contentPadding. |
| // 16 - input text (ahem font size 16dps) |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 16.0)); |
| expect(tester.getSize(find.text('text')).height, 16.0); |
| expect(tester.getTopLeft(find.text('text')).dy, 0.0); |
| expect(getOpacity(tester, 'hint'), 0.0); |
| expect(getBorderWeight(tester), 1.0); |
| |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default), |
| // isFocused: false (default) |
| decoration: const InputDecoration( |
| hintText: 'hint', |
| contentPadding: EdgeInsets.symmetric(vertical: verticalPadding), |
| ), |
| ), |
| ); |
| |
| // The requested overall height is 18dps, however the minimum height is |
| // kMinInteractiveDimension because neither isDense or isCollapsed are true. |
| // 16 - input text (ahem font size 16dps) |
| // 2 - total vertical padding |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, kMinInteractiveDimension)); |
| expect(tester.getSize(find.text('text')).height, 16.0); |
| expect(tester.getTopLeft(find.text('text')).dy, 16.0); |
| expect(getOpacity(tester, 'hint'), 0.0); |
| expect(getBorderWeight(tester), 0.0); |
| }); |
| |
| testWidgets('InputDecorator.collapsed', (WidgetTester tester) async { |
| await tester.pumpWidget( |
| buildInputDecorator( |
| // isEmpty: false (default), |
| // isFocused: false (default) |
| decoration: const InputDecoration.collapsed( |
| hintText: 'hint', |
| ), |
| ), |
| ); |
| |
| // Overall height for this InputDecorator is 16dps. There is no minimum |
| // height when InputDecoration.collapsed is used. |
| // 16 - input text (ahem font size 16dps) |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 16.0)); |
| expect(tester.getSize(find.text('text')).height, 16.0); |
| expect(tester.getTopLeft(find.text('text')).dy, 0.0); |
| expect(getOpacity(tester, 'hint'), 0.0); |
| expect(getBorderWeight(tester), 0.0); |
| |
| // The hint should appear |
| await tester.pumpWidget( |
| buildInputDecorator( |
| isEmpty: true, |
| isFocused: true, |
| decoration: const InputDecoration.collapsed( |
| hintText: 'hint', |
| ), |
| ), |
| ); |
| await tester.pumpAndSettle(); |
| |
| expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 16.0)); |
| expect(tester.getSize(find.text('text')).height, 16.0); |
| expect(tester.getTopLeft(find.text('text')).dy, 0.0); |