Unverified Commit 8059aea3 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

TextFields should only have one SemanticsNode (#14219)

* scrolling node eleminated

* remove second node

* fix ids
parent 773902c5
......@@ -648,6 +648,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
FocusScope.of(context).reparentIfNeeded(widget.focusNode);
super.build(context); // See AutomaticKeepAliveClientMixin.
return new Scrollable(
excludeFromSemantics: true,
axisDirection: _isMultiline ? AxisDirection.down : AxisDirection.right,
controller: _scrollController,
physics: const ClampingScrollPhysics(),
......
......@@ -79,8 +79,10 @@ class Scrollable extends StatefulWidget {
this.controller,
this.physics,
@required this.viewportBuilder,
this.excludeFromSemantics: false,
}) : assert(axisDirection != null),
assert(viewportBuilder != null),
assert(excludeFromSemantics != null),
super (key: key);
/// The direction in which this widget scrolls.
......@@ -147,6 +149,19 @@ class Scrollable extends StatefulWidget {
/// slivers and sizes itself based on the size of the slivers.
final ViewportBuilder viewportBuilder;
/// Whether the scroll actions introduced by this [Scrollable] are exposed
/// in the semantics tree.
///
/// Text fields with an overflow are usually scrollable to make sure that the
/// user can get to the beginning/end of the entered text. However, these
/// scrolling actions are generally not exposed to the semantics layer.
///
/// See also:
///
/// * [GestureDetector.excludeFromSemantics], which is used to accomplish the
/// exclusion.
final bool excludeFromSemantics;
/// The axis along which the scroll view scrolls.
///
/// Determined by the [axisDirection].
......@@ -490,27 +505,33 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
Widget build(BuildContext context) {
assert(position != null);
// TODO(ianh): Having all these global keys is sad.
final Widget result = new _ExcludableScrollSemantics(
key: _excludableScrollSemanticsKey,
child: new RawGestureDetector(
key: _gestureDetectorKey,
gestures: _gestureRecognizers,
behavior: HitTestBehavior.opaque,
child: new Semantics(
explicitChildNodes: true,
child: new IgnorePointer(
key: _ignorePointerKey,
ignoring: _shouldIgnorePointer,
ignoringSemantics: false,
child: new _ScrollableScope(
scrollable: this,
position: position,
child: widget.viewportBuilder(context, position),
),
Widget result = new RawGestureDetector(
key: _gestureDetectorKey,
gestures: _gestureRecognizers,
behavior: HitTestBehavior.opaque,
excludeFromSemantics: widget.excludeFromSemantics,
child: new Semantics(
explicitChildNodes: !widget.excludeFromSemantics,
child: new IgnorePointer(
key: _ignorePointerKey,
ignoring: _shouldIgnorePointer,
ignoringSemantics: false,
child: new _ScrollableScope(
scrollable: this,
position: position,
child: widget.viewportBuilder(context, position),
),
),
),
);
if (!widget.excludeFromSemantics) {
result = new _ExcludableScrollSemantics(
key: _excludableScrollSemanticsKey,
child: result,
);
}
return _configuration.buildViewportChrome(context, result, widget.axisDirection);
}
......
......@@ -164,6 +164,10 @@ void main() {
return endpoints[0].point + const Offset(0.0, -2.0);
}
setUp(() {
debugResetSemanticsIdCounter();
});
testWidgets('TextField has consistent size', (WidgetTester tester) async {
final Key textFieldKey = new UniqueKey();
String textFieldValue;
......@@ -1747,4 +1751,120 @@ void main() {
expect(tester.getBottomLeft(find.byKey(keyB)).dy, rowBottomY);
});
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,
)
),
);
expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
id: 2,
textDirection: TextDirection.ltr,
actions: <SemanticsAction>[
SemanticsAction.tap,
],
flags: <SemanticsFlag>[
SemanticsFlag.isTextField,
],
),
],
), ignoreTransform: true, ignoreRect: true));
controller.text = 'Guten Tag';
await tester.pump();
expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
id: 2,
textDirection: TextDirection.ltr,
value: 'Guten Tag',
actions: <SemanticsAction>[
SemanticsAction.tap,
],
flags: <SemanticsFlag>[
SemanticsFlag.isTextField,
],
),
],
), ignoreTransform: true, ignoreRect: true));
await tester.tap(find.byKey(key));
await tester.pump();
expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
id: 2,
textDirection: TextDirection.ltr,
value: 'Guten Tag',
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.moveCursorBackwardByCharacter,
],
flags: <SemanticsFlag>[
SemanticsFlag.isTextField,
SemanticsFlag.isFocused,
],
),
],
), ignoreTransform: true, ignoreRect: true));
controller.selection = const TextSelection.collapsed(offset: 4);
await tester.pump();
expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
id: 2,
textDirection: TextDirection.ltr,
value: 'Guten Tag',
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.moveCursorBackwardByCharacter,
SemanticsAction.moveCursorForwardByCharacter,
],
flags: <SemanticsFlag>[
SemanticsFlag.isTextField,
SemanticsFlag.isFocused,
],
),
],
), ignoreTransform: true, ignoreRect: true));
controller.text = 'Schönen Feierabend';
controller.selection = const TextSelection.collapsed(offset: 0);
await tester.pump();
expect(semantics, hasSemantics(new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
id: 2,
textDirection: TextDirection.ltr,
value: 'Schönen Feierabend',
actions: <SemanticsAction>[
SemanticsAction.tap,
SemanticsAction.moveCursorForwardByCharacter,
],
flags: <SemanticsFlag>[
SemanticsFlag.isTextField,
SemanticsFlag.isFocused,
],
),
],
), ignoreTransform: true, ignoreRect: true));
semantics.dispose();
});
}
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