mirror of
https://github.com/flutter/flutter
synced 2024-10-13 19:52:53 +00:00
Adds prefix and suffix support to TextField, per Material Design spec. (#10675)
* Prefix and Suffix support for TextFields * Adding Tests * Removing spurious newline. * Fixing a small problem with the test * Review Changes
This commit is contained in:
parent
befe019896
commit
9f344b695d
|
@ -129,6 +129,7 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
||||||
icon: const Icon(Icons.phone),
|
icon: const Icon(Icons.phone),
|
||||||
hintText: 'Where can we reach you?',
|
hintText: 'Where can we reach you?',
|
||||||
labelText: 'Phone Number *',
|
labelText: 'Phone Number *',
|
||||||
|
prefixText: '+1'
|
||||||
),
|
),
|
||||||
keyboardType: TextInputType.phone,
|
keyboardType: TextInputType.phone,
|
||||||
onSaved: (String value) { person.phoneNumber = value; },
|
onSaved: (String value) { person.phoneNumber = value; },
|
||||||
|
@ -147,6 +148,16 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
||||||
),
|
),
|
||||||
maxLines: 3,
|
maxLines: 3,
|
||||||
),
|
),
|
||||||
|
new TextFormField(
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Salary',
|
||||||
|
prefixText: '\$',
|
||||||
|
suffixText: 'USD',
|
||||||
|
suffixStyle: const TextStyle(color: Colors.green)
|
||||||
|
),
|
||||||
|
maxLines: 1,
|
||||||
|
),
|
||||||
new Row(
|
new Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
|
|
|
@ -37,6 +37,10 @@ class InputDecoration {
|
||||||
this.errorStyle,
|
this.errorStyle,
|
||||||
this.isDense: false,
|
this.isDense: false,
|
||||||
this.hideDivider: false,
|
this.hideDivider: false,
|
||||||
|
this.prefixText,
|
||||||
|
this.prefixStyle,
|
||||||
|
this.suffixText,
|
||||||
|
this.suffixStyle,
|
||||||
}) : isCollapsed = false;
|
}) : isCollapsed = false;
|
||||||
|
|
||||||
/// Creates a decoration that is the same size as the input field.
|
/// Creates a decoration that is the same size as the input field.
|
||||||
|
@ -55,7 +59,11 @@ class InputDecoration {
|
||||||
errorStyle = null,
|
errorStyle = null,
|
||||||
isDense = false,
|
isDense = false,
|
||||||
isCollapsed = true,
|
isCollapsed = true,
|
||||||
hideDivider = true;
|
hideDivider = true,
|
||||||
|
prefixText = null,
|
||||||
|
prefixStyle = null,
|
||||||
|
suffixText = null,
|
||||||
|
suffixStyle = null;
|
||||||
|
|
||||||
/// An icon to show before the input field.
|
/// An icon to show before the input field.
|
||||||
///
|
///
|
||||||
|
@ -108,7 +116,7 @@ class InputDecoration {
|
||||||
/// If non-null the divider, that appears below the input field is red.
|
/// If non-null the divider, that appears below the input field is red.
|
||||||
final String errorText;
|
final String errorText;
|
||||||
|
|
||||||
/// The style to use for the [errorText.
|
/// The style to use for the [errorText].
|
||||||
///
|
///
|
||||||
/// If null, defaults of a value derived from the base [TextStyle] for the
|
/// If null, defaults of a value derived from the base [TextStyle] for the
|
||||||
/// input field and the current [Theme].
|
/// input field and the current [Theme].
|
||||||
|
@ -133,6 +141,28 @@ class InputDecoration {
|
||||||
/// Defaults to false.
|
/// Defaults to false.
|
||||||
final bool hideDivider;
|
final bool hideDivider;
|
||||||
|
|
||||||
|
/// Optional text prefix to place on the line before the input.
|
||||||
|
///
|
||||||
|
/// Uses the [prefixStyle]. Uses [hintStyle] if [prefixStyle] isn't
|
||||||
|
/// specified. Prefix is not returned as part of the input.
|
||||||
|
final String prefixText;
|
||||||
|
|
||||||
|
/// The style to use for the [prefixText].
|
||||||
|
///
|
||||||
|
/// If null, defaults to the [hintStyle].
|
||||||
|
final TextStyle prefixStyle;
|
||||||
|
|
||||||
|
/// Optional text suffix to place on the line after the input.
|
||||||
|
///
|
||||||
|
/// Uses the [suffixStyle]. Uses [hintStyle] if [suffixStyle] isn't
|
||||||
|
/// specified. Suffix is not returned as part of the input.
|
||||||
|
final String suffixText;
|
||||||
|
|
||||||
|
/// The style to use for the [suffixText].
|
||||||
|
///
|
||||||
|
/// If null, defaults to the [hintStyle].
|
||||||
|
final TextStyle suffixStyle;
|
||||||
|
|
||||||
/// Creates a copy of this input decoration but with the given fields replaced
|
/// Creates a copy of this input decoration but with the given fields replaced
|
||||||
/// with the new values.
|
/// with the new values.
|
||||||
///
|
///
|
||||||
|
@ -147,6 +177,10 @@ class InputDecoration {
|
||||||
TextStyle errorStyle,
|
TextStyle errorStyle,
|
||||||
bool isDense,
|
bool isDense,
|
||||||
bool hideDivider,
|
bool hideDivider,
|
||||||
|
String prefixText,
|
||||||
|
TextStyle prefixStyle,
|
||||||
|
String suffixText,
|
||||||
|
TextStyle suffixStyle,
|
||||||
}) {
|
}) {
|
||||||
return new InputDecoration(
|
return new InputDecoration(
|
||||||
icon: icon ?? this.icon,
|
icon: icon ?? this.icon,
|
||||||
|
@ -158,6 +192,10 @@ class InputDecoration {
|
||||||
errorStyle: errorStyle ?? this.errorStyle,
|
errorStyle: errorStyle ?? this.errorStyle,
|
||||||
isDense: isDense ?? this.isDense,
|
isDense: isDense ?? this.isDense,
|
||||||
hideDivider: hideDivider ?? this.hideDivider,
|
hideDivider: hideDivider ?? this.hideDivider,
|
||||||
|
prefixText: prefixText ?? this.prefixText,
|
||||||
|
prefixStyle: prefixStyle ?? this.prefixStyle,
|
||||||
|
suffixText: suffixText ?? this.suffixText,
|
||||||
|
suffixStyle: suffixStyle ?? this.suffixStyle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +215,11 @@ class InputDecoration {
|
||||||
&& typedOther.errorStyle == errorStyle
|
&& typedOther.errorStyle == errorStyle
|
||||||
&& typedOther.isDense == isDense
|
&& typedOther.isDense == isDense
|
||||||
&& typedOther.isCollapsed == isCollapsed
|
&& typedOther.isCollapsed == isCollapsed
|
||||||
&& typedOther.hideDivider == hideDivider;
|
&& typedOther.hideDivider == hideDivider
|
||||||
|
&& typedOther.prefixText == prefixText
|
||||||
|
&& typedOther.prefixStyle == prefixStyle
|
||||||
|
&& typedOther.suffixText == suffixText
|
||||||
|
&& typedOther.suffixStyle == suffixStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -193,6 +235,10 @@ class InputDecoration {
|
||||||
isDense,
|
isDense,
|
||||||
isCollapsed,
|
isCollapsed,
|
||||||
hideDivider,
|
hideDivider,
|
||||||
|
prefixText,
|
||||||
|
prefixStyle,
|
||||||
|
suffixText,
|
||||||
|
suffixStyle,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,6 +259,14 @@ class InputDecoration {
|
||||||
description.add('isCollapsed: $isCollapsed');
|
description.add('isCollapsed: $isCollapsed');
|
||||||
if (hideDivider)
|
if (hideDivider)
|
||||||
description.add('hideDivider: $hideDivider');
|
description.add('hideDivider: $hideDivider');
|
||||||
|
if (prefixText != null)
|
||||||
|
description.add('prefixText: $prefixText');
|
||||||
|
if (prefixStyle != null)
|
||||||
|
description.add('prefixStyle: $prefixStyle');
|
||||||
|
if (suffixText != null)
|
||||||
|
description.add('suffixText: $suffixText');
|
||||||
|
if (suffixStyle != null)
|
||||||
|
description.add('suffixStyle: $suffixStyle');
|
||||||
return 'InputDecoration(${description.join(', ')})';
|
return 'InputDecoration(${description.join(', ')})';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -293,7 +347,7 @@ class InputDecorator extends StatelessWidget {
|
||||||
return themeData.hintColor;
|
return themeData.hintColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildContent(Color borderColor, double topPadding, bool isDense) {
|
Widget _buildContent(Color borderColor, double topPadding, bool isDense, Widget inputChild) {
|
||||||
final double bottomPadding = isDense ? 8.0 : 1.0;
|
final double bottomPadding = isDense ? 8.0 : 1.0;
|
||||||
const double bottomBorder = 2.0;
|
const double bottomBorder = 2.0;
|
||||||
final double bottomHeight = isDense ? 14.0 : 18.0;
|
final double bottomHeight = isDense ? 14.0 : 18.0;
|
||||||
|
@ -305,7 +359,7 @@ class InputDecorator extends StatelessWidget {
|
||||||
return new Container(
|
return new Container(
|
||||||
margin: margin + const EdgeInsets.only(bottom: bottomBorder),
|
margin: margin + const EdgeInsets.only(bottom: bottomBorder),
|
||||||
padding: padding,
|
padding: padding,
|
||||||
child: child,
|
child: inputChild,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,7 +376,7 @@ class InputDecorator extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: child,
|
child: inputChild,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,7 +402,7 @@ class InputDecorator extends StatelessWidget {
|
||||||
|
|
||||||
final List<Widget> stackChildren = <Widget>[];
|
final List<Widget> stackChildren = <Widget>[];
|
||||||
|
|
||||||
// If we're not focused, there's not value, and labelText was provided,
|
// 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
|
// then the label appears where the hint would. And we will not show
|
||||||
// the hintText.
|
// the hintText.
|
||||||
final bool hasInlineLabel = !isFocused && labelText != null && isEmpty;
|
final bool hasInlineLabel = !isFocused && labelText != null && isEmpty;
|
||||||
|
@ -402,11 +456,33 @@ class InputDecorator extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget inputChild;
|
||||||
|
if (!hasInlineLabel && (!isEmpty || hintText == null) &&
|
||||||
|
(decoration?.prefixText != null || decoration?.suffixText != null)) {
|
||||||
|
final List<Widget> rowContents = <Widget>[];
|
||||||
|
if (decoration.prefixText != null) {
|
||||||
|
rowContents.add(
|
||||||
|
new Text(decoration.prefixText,
|
||||||
|
style: decoration.prefixStyle ?? hintStyle)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
rowContents.add(new Expanded(child: child));
|
||||||
|
if (decoration.suffixText != null) {
|
||||||
|
rowContents.add(
|
||||||
|
new Text(decoration.suffixText,
|
||||||
|
style: decoration.suffixStyle ?? hintStyle)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
inputChild = new Row(children: rowContents);
|
||||||
|
} else {
|
||||||
|
inputChild = child;
|
||||||
|
}
|
||||||
|
|
||||||
if (isCollapsed) {
|
if (isCollapsed) {
|
||||||
stackChildren.add(child);
|
stackChildren.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));
|
stackChildren.add(_buildContent(borderColor, topPadding, isDense, inputChild));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isDense && errorText != null) {
|
if (!isDense && errorText != null) {
|
||||||
|
|
|
@ -801,6 +801,248 @@ void main() {
|
||||||
expect(hintText.style, hintStyle);
|
expect(hintText.style, hintStyle);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('TextField with specified prefixStyle', (WidgetTester tester) async {
|
||||||
|
final TextStyle prefixStyle = new TextStyle(
|
||||||
|
color: Colors.pink[500],
|
||||||
|
fontSize: 10.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget builder() {
|
||||||
|
return new Center(
|
||||||
|
child: new Material(
|
||||||
|
child: new TextField(
|
||||||
|
decoration: new InputDecoration(
|
||||||
|
prefixText: 'Prefix:',
|
||||||
|
prefixStyle: prefixStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(builder());
|
||||||
|
|
||||||
|
final Text prefixText = tester.widget(find.text('Prefix:'));
|
||||||
|
expect(prefixText.style, prefixStyle);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('TextField with specified suffixStyle', (WidgetTester tester) async {
|
||||||
|
final TextStyle suffixStyle = new TextStyle(
|
||||||
|
color: Colors.pink[500],
|
||||||
|
fontSize: 10.0,
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget builder() {
|
||||||
|
return new Center(
|
||||||
|
child: new Material(
|
||||||
|
child: new TextField(
|
||||||
|
decoration: new InputDecoration(
|
||||||
|
suffixText: '.com',
|
||||||
|
suffixStyle: suffixStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await tester.pumpWidget(builder());
|
||||||
|
|
||||||
|
final Text suffixText = tester.widget(find.text('.com'));
|
||||||
|
expect(suffixText.style, suffixStyle);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('TextField prefix and suffix appear correctly with no hint or label',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
final Key secondKey = new UniqueKey();
|
||||||
|
|
||||||
|
Widget innerBuilder() {
|
||||||
|
return new Center(
|
||||||
|
child: new Material(
|
||||||
|
child: new Column(
|
||||||
|
children: <Widget>[
|
||||||
|
const TextField(
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'First',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
new TextField(
|
||||||
|
key: secondKey,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
prefixText: 'Prefix',
|
||||||
|
suffixText: 'Suffix',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Widget builder() => overlay(innerBuilder());
|
||||||
|
|
||||||
|
await tester.pumpWidget(builder());
|
||||||
|
|
||||||
|
expect(find.text('Prefix'), findsOneWidget);
|
||||||
|
expect(find.text('Suffix'), findsOneWidget);
|
||||||
|
|
||||||
|
// Focus the Input. The prefix should still display.
|
||||||
|
await tester.tap(find.byKey(secondKey));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(find.text('Prefix'), findsOneWidget);
|
||||||
|
expect(find.text('Suffix'), findsOneWidget);
|
||||||
|
|
||||||
|
// Enter some text, and the prefix should still display.
|
||||||
|
await tester.enterText(find.byKey(secondKey), "Hi");
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
|
||||||
|
expect(find.text('Prefix'), findsOneWidget);
|
||||||
|
expect(find.text('Suffix'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('TextField prefix and suffix appear correctly with hint text',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
final TextStyle hintStyle = new TextStyle(
|
||||||
|
color: Colors.pink[500],
|
||||||
|
fontSize: 10.0,
|
||||||
|
);
|
||||||
|
final Key secondKey = new UniqueKey();
|
||||||
|
|
||||||
|
Widget innerBuilder() {
|
||||||
|
return new Center(
|
||||||
|
child: new Material(
|
||||||
|
child: new Column(
|
||||||
|
children: <Widget>[
|
||||||
|
const TextField(
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'First',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
new TextField(
|
||||||
|
key: secondKey,
|
||||||
|
decoration: new InputDecoration(
|
||||||
|
hintText: 'Hint',
|
||||||
|
hintStyle: hintStyle,
|
||||||
|
prefixText: 'Prefix',
|
||||||
|
suffixText: 'Suffix',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Widget builder() => overlay(innerBuilder());
|
||||||
|
|
||||||
|
await tester.pumpWidget(builder());
|
||||||
|
|
||||||
|
// Neither the prefix or the suffix should initially be visible, only the hint.
|
||||||
|
expect(find.text('Prefix'), findsNothing);
|
||||||
|
expect(find.text('Suffix'), findsNothing);
|
||||||
|
expect(find.text('Hint'), findsOneWidget);
|
||||||
|
|
||||||
|
await tester.tap(find.byKey(secondKey));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
// Focus the Input. The hint should display, but not the prefix and suffix.
|
||||||
|
expect(find.text('Prefix'), findsNothing);
|
||||||
|
expect(find.text('Suffix'), findsNothing);
|
||||||
|
expect(find.text('Hint'), findsOneWidget);
|
||||||
|
|
||||||
|
// Enter some text, and the hint should disappear and the prefix and suffix
|
||||||
|
// should appear.
|
||||||
|
await tester.enterText(find.byKey(secondKey), "Hi");
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
|
||||||
|
expect(find.text('Prefix'), findsOneWidget);
|
||||||
|
expect(find.text('Suffix'), findsOneWidget);
|
||||||
|
|
||||||
|
// It's onstage, but animated to zero opacity.
|
||||||
|
expect(find.text('Hint'), findsOneWidget);
|
||||||
|
final Element target = tester.element(find.text('Hint'));
|
||||||
|
final Opacity opacity = target.ancestorWidgetOfExactType(Opacity);
|
||||||
|
expect(opacity, isNotNull);
|
||||||
|
expect(opacity.opacity, equals(0.0));
|
||||||
|
|
||||||
|
// Check and make sure that the right styles were applied.
|
||||||
|
final Text prefixText = tester.widget(find.text('Prefix'));
|
||||||
|
expect(prefixText.style, hintStyle);
|
||||||
|
final Text suffixText = tester.widget(find.text('Suffix'));
|
||||||
|
expect(suffixText.style, hintStyle);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('TextField prefix and suffix appear correctly with label text',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
final TextStyle prefixStyle = new TextStyle(
|
||||||
|
color: Colors.pink[500],
|
||||||
|
fontSize: 10.0,
|
||||||
|
);
|
||||||
|
final TextStyle suffixStyle = new TextStyle(
|
||||||
|
color: Colors.green[500],
|
||||||
|
fontSize: 12.0,
|
||||||
|
);
|
||||||
|
final Key secondKey = new UniqueKey();
|
||||||
|
|
||||||
|
Widget innerBuilder() {
|
||||||
|
return new Center(
|
||||||
|
child: new Material(
|
||||||
|
child: new Column(
|
||||||
|
children: <Widget>[
|
||||||
|
const TextField(
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'First',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
new TextField(
|
||||||
|
key: secondKey,
|
||||||
|
decoration: new InputDecoration(
|
||||||
|
labelText: 'Label',
|
||||||
|
prefixText: 'Prefix',
|
||||||
|
prefixStyle: prefixStyle,
|
||||||
|
suffixText: 'Suffix',
|
||||||
|
suffixStyle: suffixStyle,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Widget builder() => overlay(innerBuilder());
|
||||||
|
|
||||||
|
await tester.pumpWidget(builder());
|
||||||
|
|
||||||
|
// Not focused. The prefix should not display, but the label should.
|
||||||
|
expect(find.text('Prefix'), findsNothing);
|
||||||
|
expect(find.text('Suffix'), findsNothing);
|
||||||
|
expect(find.text('Label'), findsOneWidget);
|
||||||
|
|
||||||
|
await tester.tap(find.byKey(secondKey));
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
// Focus the input. The label should display, and also the prefix.
|
||||||
|
expect(find.text('Prefix'), findsOneWidget);
|
||||||
|
expect(find.text('Suffix'), findsOneWidget);
|
||||||
|
expect(find.text('Label'), findsOneWidget);
|
||||||
|
|
||||||
|
// Enter some text, and the label should stay and the prefix should
|
||||||
|
// remain.
|
||||||
|
await tester.enterText(find.byKey(secondKey), "Hi");
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(seconds: 1));
|
||||||
|
|
||||||
|
expect(find.text('Prefix'), findsOneWidget);
|
||||||
|
expect(find.text('Suffix'), findsOneWidget);
|
||||||
|
expect(find.text('Label'), findsOneWidget);
|
||||||
|
|
||||||
|
// Check and make sure that the right styles were applied.
|
||||||
|
final Text prefixText = tester.widget(find.text('Prefix'));
|
||||||
|
expect(prefixText.style, prefixStyle);
|
||||||
|
final Text suffixText = tester.widget(find.text('Suffix'));
|
||||||
|
expect(suffixText.style, suffixStyle);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('TextField label text animates', (WidgetTester tester) async {
|
testWidgets('TextField label text animates', (WidgetTester tester) async {
|
||||||
final Key secondKey = new UniqueKey();
|
final Key secondKey = new UniqueKey();
|
||||||
|
|
||||||
|
@ -1006,7 +1248,7 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'Cannot enter new lines onto single line TextField',
|
'Cannot enter new lines onto single line TextField',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
final TextEditingController textController = new TextEditingController();
|
final TextEditingController textController = new TextEditingController();
|
||||||
|
|
||||||
|
@ -1021,13 +1263,13 @@ void main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'Injected formatters are chained',
|
'Injected formatters are chained',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
final TextEditingController textController = new TextEditingController();
|
final TextEditingController textController = new TextEditingController();
|
||||||
|
|
||||||
await tester.pumpWidget(new Material(
|
await tester.pumpWidget(new Material(
|
||||||
child: new TextField(
|
child: new TextField(
|
||||||
controller: textController,
|
controller: textController,
|
||||||
decoration: null,
|
decoration: null,
|
||||||
inputFormatters: <TextInputFormatter> [
|
inputFormatters: <TextInputFormatter> [
|
||||||
new BlacklistingTextInputFormatter(
|
new BlacklistingTextInputFormatter(
|
||||||
|
@ -1045,13 +1287,13 @@ void main() {
|
||||||
);
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'Chained formatters are in sequence',
|
'Chained formatters are in sequence',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
final TextEditingController textController = new TextEditingController();
|
final TextEditingController textController = new TextEditingController();
|
||||||
|
|
||||||
await tester.pumpWidget(new Material(
|
await tester.pumpWidget(new Material(
|
||||||
child: new TextField(
|
child: new TextField(
|
||||||
controller: textController,
|
controller: textController,
|
||||||
decoration: null,
|
decoration: null,
|
||||||
maxLines: 2,
|
maxLines: 2,
|
||||||
inputFormatters: <TextInputFormatter> [
|
inputFormatters: <TextInputFormatter> [
|
||||||
|
@ -1067,15 +1309,15 @@ void main() {
|
||||||
await tester.enterText(find.byType(TextField), 'a1b2c3');
|
await tester.enterText(find.byType(TextField), 'a1b2c3');
|
||||||
// The first formatter turns it into
|
// The first formatter turns it into
|
||||||
// 12\n112\n212\n3
|
// 12\n112\n212\n3
|
||||||
// The second formatter turns it into
|
// The second formatter turns it into
|
||||||
// \n1\n2\n3
|
// \n1\n2\n3
|
||||||
// Multiline is allowed since maxLine != 1.
|
// Multiline is allowed since maxLine != 1.
|
||||||
expect(textController.text, '\n1\n2\n3');
|
expect(textController.text, '\n1\n2\n3');
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
testWidgets(
|
testWidgets(
|
||||||
'Pasted values are formatted',
|
'Pasted values are formatted',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
final TextEditingController textController = new TextEditingController();
|
final TextEditingController textController = new TextEditingController();
|
||||||
|
|
||||||
|
@ -1083,7 +1325,7 @@ void main() {
|
||||||
return overlay(new Center(
|
return overlay(new Center(
|
||||||
child: new Material(
|
child: new Material(
|
||||||
child: new TextField(
|
child: new TextField(
|
||||||
controller: textController,
|
controller: textController,
|
||||||
decoration: null,
|
decoration: null,
|
||||||
inputFormatters: <TextInputFormatter> [
|
inputFormatters: <TextInputFormatter> [
|
||||||
WhitelistingTextInputFormatter.digitsOnly,
|
WhitelistingTextInputFormatter.digitsOnly,
|
||||||
|
@ -1104,7 +1346,7 @@ void main() {
|
||||||
await tester.tapAt(textOffsetToPosition(tester, '123'.indexOf('2')));
|
await tester.tapAt(textOffsetToPosition(tester, '123'.indexOf('2')));
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
final RenderEditable renderEditable = findRenderEditable(tester);
|
final RenderEditable renderEditable = findRenderEditable(tester);
|
||||||
final List<TextSelectionPoint> endpoints =
|
final List<TextSelectionPoint> endpoints =
|
||||||
renderEditable.getEndpointsForSelection(textController.selection);
|
renderEditable.getEndpointsForSelection(textController.selection);
|
||||||
await tester.tapAt(endpoints[0].point + const Offset(1.0, 1.0));
|
await tester.tapAt(endpoints[0].point + const Offset(1.0, 1.0));
|
||||||
await tester.pumpWidget(builder());
|
await tester.pumpWidget(builder());
|
||||||
|
|
Loading…
Reference in a new issue