Toggle whether label or hint contribute to text field semantics when unfocused/focused (#19790)
This commit is contained in:
parent
6dc18525c2
commit
ffdd6e1bf1
@ -540,12 +540,14 @@ class _RenderDecoration extends RenderBox {
|
||||
@required _Decoration decoration,
|
||||
@required TextDirection textDirection,
|
||||
@required TextBaseline textBaseline,
|
||||
@required bool isFocused,
|
||||
}) : assert(decoration != null),
|
||||
assert(textDirection != null),
|
||||
assert(textBaseline != null),
|
||||
_decoration = decoration,
|
||||
_textDirection = textDirection,
|
||||
_textBaseline = textBaseline;
|
||||
_textBaseline = textBaseline,
|
||||
_isFocused = isFocused;
|
||||
|
||||
final Map<_DecorationSlot, RenderBox> slotToChild = <_DecorationSlot, RenderBox>{};
|
||||
final Map<RenderBox, _DecorationSlot> childToSlot = <RenderBox, _DecorationSlot>{};
|
||||
@ -686,6 +688,16 @@ class _RenderDecoration extends RenderBox {
|
||||
markNeedsLayout();
|
||||
}
|
||||
|
||||
bool get isFocused => _isFocused;
|
||||
bool _isFocused;
|
||||
set isFocused(bool value) {
|
||||
assert(value != null);
|
||||
if (_isFocused == value)
|
||||
return;
|
||||
_isFocused = value;
|
||||
markNeedsSemanticsUpdate();
|
||||
}
|
||||
|
||||
@override
|
||||
void attach(PipelineOwner owner) {
|
||||
super.attach(owner);
|
||||
@ -710,6 +722,35 @@ class _RenderDecoration extends RenderBox {
|
||||
_children.forEach(visitor);
|
||||
}
|
||||
|
||||
@override
|
||||
void visitChildrenForSemantics(RenderObjectVisitor visitor) {
|
||||
if (icon != null)
|
||||
visitor(icon);
|
||||
if (prefix != null)
|
||||
visitor(prefix);
|
||||
if (prefixIcon != null)
|
||||
visitor(prefixIcon);
|
||||
if (isFocused && hint != null) {
|
||||
// Bypass opacity to always read hint when focused. This prevents the
|
||||
// label from changing when text is entered.
|
||||
final RenderProxyBox typedHint = hint;
|
||||
visitor(typedHint.child);
|
||||
} else if (!isFocused && label != null)
|
||||
visitor(label);
|
||||
if (input != null)
|
||||
visitor(input);
|
||||
if (suffixIcon != null)
|
||||
visitor(suffixIcon);
|
||||
if (suffix != null)
|
||||
visitor(suffix);
|
||||
if (container != null)
|
||||
visitor(container);
|
||||
if (helperError != null)
|
||||
visitor(helperError);
|
||||
if (counter != null)
|
||||
visitor(counter);
|
||||
}
|
||||
|
||||
@override
|
||||
List<DiagnosticsNode> debugDescribeChildren() {
|
||||
final List<DiagnosticsNode> value = <DiagnosticsNode>[];
|
||||
@ -1301,6 +1342,7 @@ class _Decorator extends RenderObjectWidget {
|
||||
@required this.decoration,
|
||||
@required this.textDirection,
|
||||
@required this.textBaseline,
|
||||
@required this.isFocused,
|
||||
}) : assert(decoration != null),
|
||||
assert(textDirection != null),
|
||||
assert(textBaseline != null),
|
||||
@ -1309,6 +1351,7 @@ class _Decorator extends RenderObjectWidget {
|
||||
final _Decoration decoration;
|
||||
final TextDirection textDirection;
|
||||
final TextBaseline textBaseline;
|
||||
final bool isFocused;
|
||||
|
||||
@override
|
||||
_RenderDecorationElement createElement() => new _RenderDecorationElement(this);
|
||||
@ -1319,6 +1362,7 @@ class _Decorator extends RenderObjectWidget {
|
||||
decoration: decoration,
|
||||
textDirection: textDirection,
|
||||
textBaseline: textBaseline,
|
||||
isFocused: isFocused,
|
||||
);
|
||||
}
|
||||
|
||||
@ -1327,7 +1371,8 @@ class _Decorator extends RenderObjectWidget {
|
||||
renderObject
|
||||
..decoration = decoration
|
||||
..textDirection = textDirection
|
||||
..textBaseline = textBaseline;
|
||||
..textBaseline = textBaseline
|
||||
..isFocused = isFocused;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1784,6 +1829,7 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
? const EdgeInsets.fromLTRB(12.0, 20.0, 12.0, 12.0)
|
||||
: const EdgeInsets.fromLTRB(12.0, 24.0, 12.0, 16.0));
|
||||
}
|
||||
|
||||
return new _Decorator(
|
||||
decoration: new _Decoration(
|
||||
contentPadding: contentPadding,
|
||||
@ -1803,9 +1849,10 @@ class _InputDecoratorState extends State<InputDecorator> with TickerProviderStat
|
||||
helperError: helperError,
|
||||
counter: counter,
|
||||
container: container,
|
||||
),
|
||||
textDirection: textDirection,
|
||||
textBaseline: textBaseline,
|
||||
),
|
||||
textDirection: textDirection,
|
||||
textBaseline: textBaseline,
|
||||
isFocused: isFocused,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -2250,4 +2250,68 @@ void main() {
|
||||
expect(focusNode.hasFocus, isFalse);
|
||||
});
|
||||
|
||||
testWidgets('TextField semantics', (WidgetTester tester) async {
|
||||
final SemanticsTester semantics = new SemanticsTester(tester);
|
||||
final TextEditingController controller = new TextEditingController();
|
||||
final Key key = new UniqueKey();
|
||||
|
||||
await tester.pumpWidget(
|
||||
overlay(
|
||||
child: new TextField(
|
||||
key: key,
|
||||
controller: controller,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'label',
|
||||
hintText: 'hint',
|
||||
helperText: 'helper',
|
||||
counterText: 'counter',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(semantics, hasSemantics(new TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
label: 'label\nhelper\ncounter',
|
||||
id: 1,
|
||||
textDirection: TextDirection.ltr,
|
||||
actions: <SemanticsAction>[
|
||||
SemanticsAction.tap,
|
||||
],
|
||||
flags: <SemanticsFlag>[
|
||||
SemanticsFlag.isTextField,
|
||||
],
|
||||
),
|
||||
],
|
||||
), ignoreTransform: true, ignoreRect: true));
|
||||
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pump();
|
||||
|
||||
expect(semantics, hasSemantics(new TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
label: 'hint\nhelper\ncounter',
|
||||
id: 1,
|
||||
textDirection: TextDirection.ltr,
|
||||
textSelection: const TextSelection(baseOffset: 0, extentOffset: 0),
|
||||
actions: <SemanticsAction>[
|
||||
SemanticsAction.tap,
|
||||
SemanticsAction.setSelection,
|
||||
SemanticsAction.paste,
|
||||
],
|
||||
flags: <SemanticsFlag>[
|
||||
SemanticsFlag.isTextField,
|
||||
SemanticsFlag.isFocused,
|
||||
],
|
||||
),
|
||||
],
|
||||
), ignoreTransform: true, ignoreRect: true));
|
||||
|
||||
controller.text = 'hello';
|
||||
await tester.pump();
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user