Commit 67cf7918 authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Fixes InputDecorator to work with textScaleFactor, fixes Material Design differences. (#12595)

Fixes InputDecorator to work with textScaleFactor, fixes Material Design differences.

There were a number of differences with the Material Design spec, including
several different padding values and underline thickness.  This corrects
that so that the decorator is in line with the Material Design spec now.

Also, the decorator properly handles changes to the textScaleFactor, where
before it would not re-layout when needed, painting the cursor and
underline incorrectly.

The decorator also now properly animates helper, error, and hint text when
the textScaleFactor or input decoration properties change.

Helper text is now properly displayed in dense mode, as the spec shows.
Before this change, it was never displayed in dense mode.

Fixes #12485
parent 7e09649c
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:math';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart' show debugDumpRenderTree; import 'package:flutter/rendering.dart' show debugDumpRenderTree;
...@@ -31,6 +33,11 @@ class CardCollectionState extends State<CardCollection> { ...@@ -31,6 +33,11 @@ class CardCollectionState extends State<CardCollection> {
static const double kCardMargins = 8.0; static const double kCardMargins = 8.0;
static const double kFixedCardHeight = 100.0; static const double kFixedCardHeight = 100.0;
static const List<double> _cardHeights = const <double>[
48.0, 63.0, 85.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0,
48.0, 63.0, 85.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0,
48.0, 63.0, 85.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0,
];
MaterialColor _primaryColor = Colors.deepPurple; MaterialColor _primaryColor = Colors.deepPurple;
List<CardModel> _cardModels; List<CardModel> _cardModels;
...@@ -41,15 +48,22 @@ class CardCollectionState extends State<CardCollection> { ...@@ -41,15 +48,22 @@ class CardCollectionState extends State<CardCollection> {
bool _sunshine = false; bool _sunshine = false;
bool _varyFontSizes = false; bool _varyFontSizes = false;
void _updateCardSizes() {
if (_fixedSizeCards)
return;
_cardModels = new List<CardModel>.generate(
_cardModels.length,
(int i) {
_cardModels[i].height = _editable ? max(_cardHeights[i], 60.0) : _cardHeights[i];
return _cardModels[i];
}
);
}
void _initVariableSizedCardModels() { void _initVariableSizedCardModels() {
final List<double> cardHeights = <double>[
48.0, 63.0, 82.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0,
48.0, 63.0, 82.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0,
48.0, 63.0, 82.0, 146.0, 60.0, 55.0, 84.0, 96.0, 50.0,
];
_cardModels = new List<CardModel>.generate( _cardModels = new List<CardModel>.generate(
cardHeights.length, _cardHeights.length,
(int i) => new CardModel(i, cardHeights[i]) (int i) => new CardModel(i, _editable ? max(_cardHeights[i], 60.0) : _cardHeights[i])
); );
} }
...@@ -126,6 +140,7 @@ class CardCollectionState extends State<CardCollection> { ...@@ -126,6 +140,7 @@ class CardCollectionState extends State<CardCollection> {
void _toggleEditable() { void _toggleEditable() {
setState(() { setState(() {
_editable = !_editable; _editable = !_editable;
_updateCardSizes();
}); });
} }
......
...@@ -371,6 +371,12 @@ class InputDecorator extends StatelessWidget { ...@@ -371,6 +371,12 @@ class InputDecorator extends StatelessWidget {
/// The widget below this widget in the tree. /// The widget below this widget in the tree.
final Widget child; final Widget child;
static const double _kBottomBorderHeight = 1.0;
static const double _kDensePadding = 4.0;
static const double _kNormalPadding = 8.0;
static const double _kDenseTopPadding = 8.0;
static const double _kNormalTopPadding = 16.0;
@override @override
void debugFillProperties(DiagnosticPropertiesBuilder description) { void debugFillProperties(DiagnosticPropertiesBuilder description) {
super.debugFillProperties(description); super.debugFillProperties(description);
...@@ -392,32 +398,23 @@ class InputDecorator extends StatelessWidget { ...@@ -392,32 +398,23 @@ class InputDecorator extends StatelessWidget {
return themeData.hintColor; return themeData.hintColor;
} }
Widget _buildContent(Color borderColor, double topPadding, bool isDense, Widget inputChild) { Widget _buildContent(Color borderColor, double topPadding, bool isDense, Widget inputChild, double subTextHeight) {
final double bottomPadding = isDense ? 8.0 : 1.0;
const double bottomBorder = 2.0;
final double bottomHeight = isDense ? 14.0 : 18.0;
final EdgeInsets padding = new EdgeInsets.only(top: topPadding, bottom: bottomPadding);
final EdgeInsets margin = new EdgeInsets.only(bottom: bottomHeight - (bottomPadding + bottomBorder));
if (decoration.hideDivider) { if (decoration.hideDivider) {
return new Container( return new Container(
margin: margin + const EdgeInsets.only(bottom: bottomBorder), padding: new EdgeInsets.only(top: topPadding, bottom: _kNormalPadding),
padding: padding,
child: inputChild, child: inputChild,
); );
} }
return new AnimatedContainer( return new AnimatedContainer(
margin: margin, padding: new EdgeInsets.only(top: topPadding, bottom: _kNormalPadding - _kBottomBorderHeight),
padding: padding,
duration: _kTransitionDuration, duration: _kTransitionDuration,
curve: _kTransitionCurve, curve: _kTransitionCurve,
decoration: new BoxDecoration( decoration: new BoxDecoration(
border: new Border( border: new Border(
bottom: new BorderSide( bottom: new BorderSide(
color: borderColor, color: borderColor,
width: bottomBorder, width: _kBottomBorderHeight,
), ),
), ),
), ),
...@@ -429,6 +426,7 @@ class InputDecorator extends StatelessWidget { ...@@ -429,6 +426,7 @@ class InputDecorator extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
assert(debugCheckHasMaterial(context)); assert(debugCheckHasMaterial(context));
final ThemeData themeData = Theme.of(context); final ThemeData themeData = Theme.of(context);
final double textScaleFactor = MediaQuery.of(context, nullOk: true)?.textScaleFactor ?? 1.0;
final bool isDense = decoration.isDense; final bool isDense = decoration.isDense;
final bool isCollapsed = decoration.isCollapsed; final bool isCollapsed = decoration.isCollapsed;
...@@ -439,34 +437,37 @@ class InputDecorator extends StatelessWidget { ...@@ -439,34 +437,37 @@ class InputDecorator extends StatelessWidget {
final String hintText = decoration.hintText; final String hintText = decoration.hintText;
final String errorText = decoration.errorText; final String errorText = decoration.errorText;
// If we're not focused, there's no value, and labelText was provided,
// then the label appears where the hint would. And we will not show
// the hintText.
final bool hasInlineLabel = !isFocused && labelText != null && isEmpty;
final Color activeColor = _getActiveColor(themeData);
final TextStyle baseStyle = this.baseStyle ?? themeData.textTheme.subhead; final TextStyle baseStyle = this.baseStyle ?? themeData.textTheme.subhead;
final TextStyle hintStyle = decoration.hintStyle ?? baseStyle.copyWith(color: themeData.hintColor); final TextStyle hintStyle = decoration.hintStyle ?? baseStyle.copyWith(color: themeData.hintColor);
final TextStyle subtextStyle = errorText != null
? decoration.errorStyle ?? themeData.textTheme.caption.copyWith(color: themeData.errorColor)
: decoration.helperStyle ?? themeData.textTheme.caption.copyWith(color: themeData.hintColor);
final Color activeColor = _getActiveColor(themeData); final double entryTextHeight = baseStyle.fontSize * textScaleFactor;
final double subTextHeight = subtextStyle.fontSize * textScaleFactor;
double topPadding = isCollapsed ? 0.0 : (isDense ? 12.0 : 16.0); double topPadding = isCollapsed ? 0.0 : (isDense ? _kDenseTopPadding : _kNormalTopPadding);
final List<Widget> stackChildren = <Widget>[]; final List<Widget> stackChildren = <Widget>[];
// If we're not focused, there's no value, and labelText was provided,
// then the label appears where the hint would. And we will not show
// the hintText.
final bool hasInlineLabel = !isFocused && labelText != null && isEmpty;
if (labelText != null) { if (labelText != null) {
assert(!isCollapsed); assert(!isCollapsed);
final TextStyle labelStyle = hasInlineLabel ? final TextStyle floatingLabelStyle = decoration.labelStyle ?? themeData.textTheme.caption.copyWith(color: activeColor);
hintStyle : (decoration.labelStyle ?? themeData.textTheme.caption.copyWith(color: activeColor)); final TextStyle labelStyle = hasInlineLabel ? hintStyle : floatingLabelStyle;
final double labelTextHeight = floatingLabelStyle.fontSize * textScaleFactor;
final double topPaddingIncrement = themeData.textTheme.caption.fontSize + (isDense ? 4.0 : 8.0);
double top = topPadding;
if (hasInlineLabel)
top += topPaddingIncrement + baseStyle.fontSize - labelStyle.fontSize;
final double topPaddingIncrement = labelTextHeight + (isDense ? _kDensePadding : _kNormalPadding);
stackChildren.add( stackChildren.add(
new AnimatedPositionedDirectional( new AnimatedPositionedDirectional(
start: 0.0, start: 0.0,
top: top, top: topPadding + (hasInlineLabel ? topPaddingIncrement : 0.0),
duration: _kTransitionDuration, duration: _kTransitionDuration,
curve: _kTransitionCurve, curve: _kTransitionCurve,
child: new _AnimatedLabel( child: new _AnimatedLabel(
...@@ -483,10 +484,12 @@ class InputDecorator extends StatelessWidget { ...@@ -483,10 +484,12 @@ class InputDecorator extends StatelessWidget {
if (hintText != null) { if (hintText != null) {
stackChildren.add( stackChildren.add(
new Positioned( new AnimatedPositionedDirectional(
left: 0.0, start: 0.0,
right: 0.0, end: 0.0,
top: topPadding + baseStyle.fontSize - hintStyle.fontSize, top: topPadding,
duration: _kTransitionDuration,
curve: _kTransitionCurve,
child: new AnimatedOpacity( child: new AnimatedOpacity(
opacity: (isEmpty && !hasInlineLabel) ? 1.0 : 0.0, opacity: (isEmpty && !hasInlineLabel) ? 1.0 : 0.0,
duration: _kTransitionDuration, duration: _kTransitionDuration,
...@@ -534,33 +537,43 @@ class InputDecorator extends StatelessWidget { ...@@ -534,33 +537,43 @@ class InputDecorator extends StatelessWidget {
inputChild = new Row(children: rowContents); inputChild = new Row(children: rowContents);
} }
// The inputChild and the helper/error text need to be in a column so that if the inputChild is
// a multiline input or a non-text widget, it lays out with the helper/error text below the
// inputChild.
final List<Widget> columnChildren = <Widget>[];
if (isCollapsed) { if (isCollapsed) {
stackChildren.add(inputChild); columnChildren.add(inputChild);
} else { } else {
final Color borderColor = errorText == null ? activeColor : themeData.errorColor; final Color borderColor = errorText == null ? activeColor : themeData.errorColor;
stackChildren.add(_buildContent(borderColor, topPadding, isDense, inputChild)); columnChildren.add(_buildContent(borderColor, topPadding, isDense, inputChild, subTextHeight));
} }
if (!isDense && (errorText != null || helperText != null)) { if (errorText != null || helperText != null) {
assert(!isCollapsed); assert(!isCollapsed);
final TextStyle captionStyle = themeData.textTheme.caption; final double linePadding = _kBottomBorderHeight + (isDense ? _kDensePadding : _kNormalPadding);
final TextStyle subtextStyle = errorText != null columnChildren.add(
? decoration.errorStyle ?? captionStyle.copyWith(color: themeData.errorColor) new AnimatedContainer(
: decoration.helperStyle ?? captionStyle.copyWith(color: themeData.hintColor); padding: new EdgeInsets.only(top: linePadding),
duration: _kTransitionDuration,
stackChildren.add(new Positioned( curve: _kTransitionCurve,
left: 0.0, child: new Text(
right: 0.0, errorText ?? helperText,
bottom: 0.0, style: subtextStyle,
child: new Text( textAlign: textAlign,
errorText ?? helperText, overflow: TextOverflow.ellipsis,
style: subtextStyle, ),
textAlign: textAlign,
overflow: TextOverflow.ellipsis,
), ),
)); );
} }
stackChildren.add(
new Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: columnChildren,
),
);
final Widget stack = new Stack( final Widget stack = new Stack(
fit: StackFit.passthrough, fit: StackFit.passthrough,
children: stackChildren children: stackChildren
...@@ -569,17 +582,19 @@ class InputDecorator extends StatelessWidget { ...@@ -569,17 +582,19 @@ class InputDecorator extends StatelessWidget {
if (decoration.icon != null) { if (decoration.icon != null) {
assert(!isCollapsed); assert(!isCollapsed);
final double iconSize = isDense ? 18.0 : 24.0; final double iconSize = isDense ? 18.0 : 24.0;
final double iconTop = topPadding + (baseStyle.fontSize - iconSize) / 2.0; final double iconTop = topPadding + (entryTextHeight - iconSize) / 2.0;
return new Row( return new Row(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[ children: <Widget>[
new Container( new AnimatedContainer(
margin: new EdgeInsets.only(top: iconTop), margin: new EdgeInsets.only(top: iconTop),
duration: _kTransitionDuration,
curve: _kTransitionCurve,
width: isDense ? 40.0 : 48.0, width: isDense ? 40.0 : 48.0,
child: IconTheme.merge( child: IconTheme.merge(
data: new IconThemeData( data: new IconThemeData(
color: isFocused ? activeColor : Colors.black45, color: isFocused ? activeColor : Colors.black45,
size: isDense ? 18.0 : 24.0, size: iconSize,
), ),
child: decoration.icon, child: decoration.icon,
), ),
...@@ -605,11 +620,15 @@ class _AnimatedLabel extends ImplicitlyAnimatedWidget { ...@@ -605,11 +620,15 @@ class _AnimatedLabel extends ImplicitlyAnimatedWidget {
@required this.style, @required this.style,
Curve curve: Curves.linear, Curve curve: Curves.linear,
@required Duration duration, @required Duration duration,
this.textAlign,
this.overflow,
}) : assert(style != null), }) : assert(style != null),
super(key: key, curve: curve, duration: duration); super(key: key, curve: curve, duration: duration);
final String text; final String text;
final TextStyle style; final TextStyle style;
final TextAlign textAlign;
final TextOverflow overflow;
@override @override
_AnimatedLabelState createState() => new _AnimatedLabelState(); _AnimatedLabelState createState() => new _AnimatedLabelState();
...@@ -646,6 +665,8 @@ class _AnimatedLabelState extends AnimatedWidgetBaseState<_AnimatedLabel> { ...@@ -646,6 +665,8 @@ class _AnimatedLabelState extends AnimatedWidgetBaseState<_AnimatedLabel> {
child: new Text( child: new Text(
widget.text, widget.text,
style: style, style: style,
textAlign: widget.textAlign,
overflow: widget.overflow,
), ),
); );
} }
......
...@@ -134,6 +134,7 @@ class TextPainter { ...@@ -134,6 +134,7 @@ class TextPainter {
return; return;
_textScaleFactor = value; _textScaleFactor = value;
_paragraph = null; _paragraph = null;
_layoutTemplate = null;
_needsLayout = true; _needsLayout = true;
} }
......
...@@ -1964,7 +1964,7 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im ...@@ -1964,7 +1964,7 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
// yet (that is, our layer might not have been detached yet), because the // yet (that is, our layer might not have been detached yet), because the
// same node that skipped us in layout is above us in the tree (obviously) // same node that skipped us in layout is above us in the tree (obviously)
// and therefore may not have had a chance to paint yet (since the tree // and therefore may not have had a chance to paint yet (since the tree
// paints in reverse order). In particular this will happen if they are have // paints in reverse order). In particular this will happen if they have
// a different layer, because there's a repaint boundary between us. // a different layer, because there's a repaint boundary between us.
if (_needsLayout) if (_needsLayout)
return; return;
......
...@@ -2,54 +2,343 @@ ...@@ -2,54 +2,343 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
testWidgets('InputDecorator always expands horizontally', (WidgetTester tester) async { Widget buildInputDecorator({InputDecoration decoration = const InputDecoration(), Widget child = const Text('Test')}) {
final Key key = new UniqueKey(); return new MaterialApp(
await tester.pumpWidget(new MaterialApp(
home: new Material( home: new Material(
child: new Center( child: new DefaultTextStyle(
child: new InputDecorator( style: const TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
decoration: const InputDecoration(), child: new Center(
child: new Container(key: key, width: 50.0, height: 60.0, color: Colors.blue), child: new InputDecorator(
decoration: decoration,
child: child,
),
), ),
), ),
), ),
)); );
}
Finder findInputDecoratorChildContainer() {
return find.byWidgetPredicate(
(Widget w) {
return w is AnimatedContainer && (w as dynamic).decoration != null;
});
}
double getBoxDecorationThickness(WidgetTester tester) {
final AnimatedContainer container = tester.widget(findInputDecoratorChildContainer());
final BoxDecoration decoration = container.decoration;
final Border border = decoration.border;
return border.bottom.width;
}
double getDividerY(WidgetTester tester) {
final Finder animatedContainerFinder = find.byWidgetPredicate(
(Widget w) {
return w is AnimatedContainer && (w as dynamic).decoration != null;
});
return tester.getRect(animatedContainerFinder).bottom;
}
double getDividerWidth(WidgetTester tester) {
final Finder animatedContainerFinder = find.byWidgetPredicate(
(Widget w) {
return w is AnimatedContainer && (w as dynamic).decoration != null;
});
return tester.getRect(animatedContainerFinder).size.width;
}
testWidgets('InputDecorator always expands horizontally', (WidgetTester tester) async {
final Key key = new UniqueKey();
await tester.pumpWidget(
buildInputDecorator(
child: new Container(key: key, width: 50.0, height: 60.0, color: Colors.blue),
),
);
expect(tester.element(find.byKey(key)).size, equals(const Size(800.0, 60.0))); expect(tester.element(find.byKey(key)).size, equals(const Size(800.0, 60.0)));
await tester.pumpWidget(new MaterialApp( await tester.pumpWidget(
home: new Material( buildInputDecorator(
child: new Center( decoration: const InputDecoration(
child: new InputDecorator( icon: const Icon(Icons.add_shopping_cart),
decoration: const InputDecoration(
icon: const Icon(Icons.add_shopping_cart),
),
child: new Container(key: key, width: 50.0, height: 60.0, color: Colors.blue),
),
), ),
child: new Container(key: key, width: 50.0, height: 60.0, color: Colors.blue),
), ),
)); );
expect(tester.element(find.byKey(key)).size, equals(const Size(752.0, 60.0))); expect(tester.element(find.byKey(key)).size, equals(const Size(752.0, 60.0)));
await tester.pumpWidget(
buildInputDecorator(
decoration: const InputDecoration.collapsed(
hintText: 'Hint text',
),
child: new Container(key: key, width: 50.0, height: 60.0, color: Colors.blue),
),
);
expect(tester.element(find.byKey(key)).size, equals(const Size(800.0, 60.0)));
});
testWidgets('InputDecorator draws the underline correctly in the right place.', (WidgetTester tester) async {
await tester.pumpWidget(
buildInputDecorator(
decoration: const InputDecoration(
hintText: 'Hint',
labelText: 'Label',
helperText: 'Helper',
),
),
);
expect(getBoxDecorationThickness(tester), equals(1.0));
expect(getDividerY(tester), equals(316.5));
expect(getDividerWidth(tester), equals(800.0));
});
testWidgets('InputDecorator draws the underline correctly in the right place for dense layout.', (WidgetTester tester) async {
await tester.pumpWidget(
buildInputDecorator(
decoration: const InputDecoration(
hintText: 'Hint',
labelText: 'Label',
helperText: 'Helper',
isDense: true,
),
),
);
expect(getBoxDecorationThickness(tester), equals(1.0));
expect(getDividerY(tester), equals(312.5));
expect(getDividerWidth(tester), equals(800.0));
});
testWidgets('InputDecorator does not draw the underline when hideDivider is true.', (WidgetTester tester) async {
await tester.pumpWidget(
buildInputDecorator(
decoration: const InputDecoration(
hintText: 'Hint',
labelText: 'Label',
helperText: 'Helper',
hideDivider: true,
),
),
);
expect(findInputDecoratorChildContainer(), findsNothing);
});
testWidgets('InputDecorator uses proper padding for dense mode', (WidgetTester tester) async {
await tester.pumpWidget(
buildInputDecorator(
decoration: const InputDecoration(
hintText: 'Hint',
labelText: 'Label',
helperText: 'Helper',
isDense: true,
),
),
);
// TODO(#12357): Update this test when the font metric bug is fixed to remove the anyOfs.
expect(
tester.getRect(find.text('Label')).size,
anyOf(<Size>[const Size(60.0, 12.0), const Size(61.0, 12.0)]),
);
expect(tester.getRect(find.text('Label')).left, equals(0.0));
expect(tester.getRect(find.text('Label')).top, equals(278.5));
expect(tester.getRect(find.text('Hint')).size, equals(const Size(800.0, 16.0)));
expect(tester.getRect(find.text('Hint')).left, equals(0.0));
expect(tester.getRect(find.text('Hint')).top, equals(294.5));
expect(tester.getRect(find.text('Helper')).size, equals(const Size(800.0, 12.0)));
expect(tester.getRect(find.text('Helper')).left, equals(0.0));
expect(tester.getRect(find.text('Helper')).top, equals(317.5));
});
testWidgets('InputDecorator uses proper padding', (WidgetTester tester) async {
await tester.pumpWidget(
buildInputDecorator(
decoration: const InputDecoration(
hintText: 'Hint',
labelText: 'Label',
helperText: 'Helper',
),
),
);
// TODO(#12357): Update this test when the font metric bug is fixed to remove the anyOfs.
expect(
tester.getRect(find.text('Label')).size,
anyOf(<Size>[const Size(60.0, 12.0), const Size(61.0, 12.0)]),
);
expect(tester.getRect(find.text('Label')).left, equals(0.0));
expect(tester.getRect(find.text('Label')).top, equals(278.5));
expect(tester.getRect(find.text('Hint')).size, equals(const Size(800.0, 16.0)));
expect(tester.getRect(find.text('Hint')).left, equals(0.0));
expect(tester.getRect(find.text('Hint')).top, equals(298.5));
expect(tester.getRect(find.text('Helper')).size, equals(const Size(800.0, 12.0)));
expect(tester.getRect(find.text('Helper')).left, equals(0.0));
expect(tester.getRect(find.text('Helper')).top, equals(325.5));
});
testWidgets('InputDecorator uses proper padding when error is set', (WidgetTester tester) async {
await tester.pumpWidget(
buildInputDecorator(
decoration: const InputDecoration(
hintText: 'Hint',
labelText: 'Label',
helperText: 'Helper',
errorText: 'Error',
),
),
);
// TODO(#12357): Update this test when the font metric bug is fixed to remove the anyOfs.
expect(
tester.getRect(find.text('Label')).size,
anyOf(<Size>[const Size(60.0, 12.0), const Size(61.0, 12.0)]),
);
expect(tester.getRect(find.text('Label')).left, equals(0.0));
expect(tester.getRect(find.text('Label')).top, equals(278.5));
expect(tester.getRect(find.text('Hint')).size, equals(const Size(800.0, 16.0)));
expect(tester.getRect(find.text('Hint')).left, equals(0.0));
expect(tester.getRect(find.text('Hint')).top, equals(298.5));
expect(tester.getRect(find.text('Error')).size, equals(const Size(800.0, 12.0)));
expect(tester.getRect(find.text('Error')).left, equals(0.0));
expect(tester.getRect(find.text('Error')).top, equals(325.5));
});
testWidgets('InputDecorator animates properly', (WidgetTester tester) async {
await tester.pumpWidget(new MaterialApp( await tester.pumpWidget(new MaterialApp(
home: new Material( home: const Material(
child: new Center( child: const DefaultTextStyle(
child: new InputDecorator( style: const TextStyle(fontFamily: 'Ahem', fontSize: 10.0),
decoration: const InputDecoration.collapsed( child: const Center(
hintText: 'Hint text', child: const TextField(
decoration: const InputDecoration(
suffixText: 'S',
prefixText: 'P',
hintText: 'Hint',
labelText: 'Label',
helperText: 'Helper',
),
), ),
child: new Container(key: key, width: 50.0, height: 60.0, color: Colors.blue),
), ),
), ),
), ),
)); ));
expect(tester.element(find.byKey(key)).size, equals(const Size(800.0, 60.0))); // TODO(#12357): Update this test when the font metric bug is fixed to remove the anyOfs.
expect(
tester.getRect(find.text('Label')).size,
anyOf(<Size>[const Size(80.0, 16.0), const Size(81.0, 16.0)]),
);
expect(tester.getRect(find.text('Label')).left, equals(0.0));
expect(tester.getRect(find.text('Label')).top, equals(295.5));
expect(tester.getRect(find.text('Hint')).size, equals(const Size(800.0, 16.0)));
expect(tester.getRect(find.text('Hint')).left, equals(0.0));
expect(tester.getRect(find.text('Hint')).top, equals(295.5));
expect(tester.getRect(find.text('Helper')).size, equals(const Size(800.0, 12.0)));
expect(tester.getRect(find.text('Helper')).left, equals(0.0));
expect(tester.getRect(find.text('Helper')).top, equals(328.5));
expect(find.text('P'), findsNothing);
expect(find.text('S'), findsNothing);
await tester.tap(find.byType(TextField));
await tester.pump(const Duration(milliseconds: 100));
expect(
tester.getRect(find.text('Label')).size,
anyOf(<Size>[const Size(60.0, 12.0), const Size(61.0, 12.0)]),
);
expect(tester.getRect(find.text('Label')).left, equals(0.0));
expect(tester.getRect(find.text('Label')).top, equals(295.5));
expect(tester.getRect(find.text('Hint')).size, equals(const Size(800.0, 16.0)));
expect(tester.getRect(find.text('Hint')).left, equals(0.0));
expect(tester.getRect(find.text('Hint')).top, equals(295.5));
expect(tester.getRect(find.text('Helper')).size, equals(const Size(800.0, 12.0)));
expect(tester.getRect(find.text('Helper')).left, equals(0.0));
expect(tester.getRect(find.text('Helper')).top, equals(328.5));
expect(find.text('P'), findsNothing);
expect(find.text('S'), findsNothing);
await tester.pump(const Duration(seconds: 1));
expect(
tester.getRect(find.text('Label')).size,
anyOf(<Size>[const Size(60.0, 12.0), const Size(61.0, 12.0)]),
);
expect(tester.getRect(find.text('Label')).left, equals(0.0));
expect(tester.getRect(find.text('Label')).top, equals(275.5));
expect(tester.getRect(find.text('Hint')).size, equals(const Size(800.0, 16.0)));
expect(tester.getRect(find.text('Hint')).left, equals(0.0));
expect(tester.getRect(find.text('Hint')).top, equals(295.5));
expect(tester.getRect(find.text('Helper')).size, equals(const Size(800.0, 12.0)));
expect(tester.getRect(find.text('Helper')).left, equals(0.0));
expect(tester.getRect(find.text('Helper')).top, equals(328.5));
expect(find.text('P'), findsNothing);
expect(find.text('S'), findsNothing);
await tester.enterText(find.byType(TextField), 'Test');
await tester.pump(const Duration(milliseconds: 100));
expect(
tester.getRect(find.text('Label')).size,
anyOf(<Size>[const Size(60.0, 12.0), const Size(61.0, 12.0)]),
);
expect(tester.getRect(find.text('Label')).left, equals(0.0));
expect(tester.getRect(find.text('Label')).top, equals(275.5));
expect(tester.getRect(find.text('Hint')).size, equals(const Size(800.0, 16.0)));
expect(tester.getRect(find.text('Hint')).left, equals(0.0));
expect(tester.getRect(find.text('Hint')).top, equals(295.5));
expect(tester.getRect(find.text('Helper')).size, equals(const Size(800.0, 12.0)));
expect(tester.getRect(find.text('Helper')).left, equals(0.0));
expect(tester.getRect(find.text('Helper')).top, equals(328.5));
expect(
tester.getRect(find.text('P')).size,
anyOf(<Size>[const Size(17.0, 16.0), const Size(16.0, 16.0)]),
);
expect(tester.getRect(find.text('P')).left, equals(0.0));
expect(tester.getRect(find.text('P')).top, equals(295.5));
expect(
tester.getRect(find.text('S')).size,
anyOf(<Size>[const Size(17.0, 16.0), const Size(16.0, 16.0)]),
);
expect(tester.getRect(find.text('S')).left, anyOf(783.0, 784.0));
expect(tester.getRect(find.text('S')).top, equals(295.5));
await tester.pump(const Duration(seconds: 1));
expect(
tester.getRect(find.text('Label')).size,
anyOf(<Size>[const Size(60.0, 12.0), const Size(61.0, 12.0)]),
);
expect(tester.getRect(find.text('Label')).left, equals(0.0));
expect(tester.getRect(find.text('Label')).top, equals(275.5));
expect(tester.getRect(find.text('Hint')).size, equals(const Size(800.0, 16.0)));
expect(tester.getRect(find.text('Hint')).left, equals(0.0));
expect(tester.getRect(find.text('Hint')).top, equals(295.5));
expect(tester.getRect(find.text('Helper')).size, equals(const Size(800.0, 12.0)));
expect(tester.getRect(find.text('Helper')).left, equals(0.0));
expect(tester.getRect(find.text('Helper')).top, equals(328.5));
expect(
tester.getRect(find.text('P')).size,
anyOf(<Size>[const Size(17.0, 16.0), const Size(16.0, 16.0)]),
);
expect(tester.getRect(find.text('P')).left, equals(0.0));
expect(tester.getRect(find.text('P')).top, equals(295.5));
expect(
tester.getRect(find.text('S')).size,
anyOf(<Size>[const Size(17.0, 16.0), const Size(16.0, 16.0)]),
);
expect(tester.getRect(find.text('S')).left, anyOf(783.0, 784.0));
expect(tester.getRect(find.text('S')).top, equals(295.5));
}); });
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment