Commit ce4a4577 authored by amirh's avatar amirh Committed by GitHub

Add an isButton flag to the Semantic widget,use it for MaterialButton (#12657)

https://github.com/flutter/flutter/issues/11992
parent 52815b22
...@@ -330,7 +330,7 @@ class _MaterialButtonState extends State<MaterialButton> { ...@@ -330,7 +330,7 @@ class _MaterialButtonState extends State<MaterialButton> {
child: new Center( child: new Center(
widthFactor: 1.0, widthFactor: 1.0,
heightFactor: 1.0, heightFactor: 1.0,
child: widget.child child: new Semantics(button: true, child: widget.child),
) )
) )
) )
......
...@@ -3175,6 +3175,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3175,6 +3175,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
bool explicitChildNodes, bool explicitChildNodes,
bool checked, bool checked,
bool selected, bool selected,
bool button,
String label, String label,
TextDirection textDirection, TextDirection textDirection,
}) : assert(container != null), }) : assert(container != null),
...@@ -3182,6 +3183,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3182,6 +3183,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
_explicitChildNodes = explicitChildNodes, _explicitChildNodes = explicitChildNodes,
_checked = checked, _checked = checked,
_selected = selected, _selected = selected,
_button = button,
_label = label, _label = label,
_textDirection = textDirection, _textDirection = textDirection,
super(child); super(child);
...@@ -3260,6 +3262,17 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3260,6 +3262,17 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
markNeedsSemanticsUpdate(onlyLocalUpdates: (value != null) == hadValue); markNeedsSemanticsUpdate(onlyLocalUpdates: (value != null) == hadValue);
} }
/// If non-null, sets the [SemanticsNode.isButton] semantic to the given value.
bool get button => _button;
bool _button;
set button(bool value) {
if (button == value)
return;
final bool hadValue = button != null;
_button = value;
markNeedsSemanticsUpdate(onlyLocalUpdates: (value != null) == hadValue);
}
/// If non-null, sets the [SemanticsNode.textDirection] semantic to the given value. /// If non-null, sets the [SemanticsNode.textDirection] semantic to the given value.
/// ///
/// This must not be null if [label] is not null. /// This must not be null if [label] is not null.
...@@ -3286,6 +3299,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox { ...@@ -3286,6 +3299,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
config.label = label; config.label = label;
if (textDirection != null) if (textDirection != null)
config.textDirection = textDirection; config.textDirection = textDirection;
if (button != null)
config.isButton = button;
} }
} }
......
...@@ -680,6 +680,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin { ...@@ -680,6 +680,7 @@ class SemanticsNode extends AbstractNode with DiagnosticableTreeMixin {
properties.add(new FlagProperty('isChecked', value: _hasFlag(SemanticsFlags.isChecked), ifTrue: 'checked', ifFalse: 'unchecked')); properties.add(new FlagProperty('isChecked', value: _hasFlag(SemanticsFlags.isChecked), ifTrue: 'checked', ifFalse: 'unchecked'));
properties.add(new FlagProperty('isSelected', value: _hasFlag(SemanticsFlags.isSelected), ifTrue: 'selected')); properties.add(new FlagProperty('isSelected', value: _hasFlag(SemanticsFlags.isSelected), ifTrue: 'selected'));
properties.add(new StringProperty('label', _label, defaultValue: '')); properties.add(new StringProperty('label', _label, defaultValue: ''));
properties.add(new FlagProperty('isButton', value: _hasFlag(SemanticsFlags.isButton), ifTrue: 'button'));
properties.add(new EnumProperty<TextDirection>('textDirection', _textDirection, defaultValue: null)); properties.add(new EnumProperty<TextDirection>('textDirection', _textDirection, defaultValue: null));
} }
...@@ -1045,6 +1046,11 @@ class SemanticsConfiguration { ...@@ -1045,6 +1046,11 @@ class SemanticsConfiguration {
_setFlag(SemanticsFlags.isChecked, value); _setFlag(SemanticsFlags.isChecked, value);
} }
/// Whether the owning [RenderObject] is a button (true) or not (false).
set isButton(bool value) {
_setFlag(SemanticsFlags.isButton, value);
}
// TAGS // TAGS
Iterable<SemanticsTag> get tagsForChildren => _tagsForChildren; Iterable<SemanticsTag> get tagsForChildren => _tagsForChildren;
......
...@@ -4451,6 +4451,7 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -4451,6 +4451,7 @@ class Semantics extends SingleChildRenderObjectWidget {
this.explicitChildNodes: false, this.explicitChildNodes: false,
this.checked, this.checked,
this.selected, this.selected,
this.button,
this.label, this.label,
this.textDirection, this.textDirection,
}) : assert(container != null), }) : assert(container != null),
...@@ -4491,6 +4492,12 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -4491,6 +4492,12 @@ class Semantics extends SingleChildRenderObjectWidget {
/// all other tabs are unselected. /// all other tabs are unselected.
final bool selected; final bool selected;
/// If non-null, indicates that this subtree represents a button.
///
/// TalkBack/VoiceOver provides users with the hint "button" when a button
/// is focused.
final bool button;
/// Provides a textual description of the widget. /// Provides a textual description of the widget.
/// ///
/// If a label is provided, there must either by an ambient [Directionality] /// If a label is provided, there must either by an ambient [Directionality]
...@@ -4514,6 +4521,7 @@ class Semantics extends SingleChildRenderObjectWidget { ...@@ -4514,6 +4521,7 @@ class Semantics extends SingleChildRenderObjectWidget {
checked: checked, checked: checked,
selected: selected, selected: selected,
label: label, label: label,
button: button,
textDirection: _getTextDirection(context), textDirection: _getTextDirection(context),
); );
} }
......
...@@ -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:ui' show SemanticsFlags;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
...@@ -31,14 +33,49 @@ void main() { ...@@ -31,14 +33,49 @@ void main() {
new TestSemantics.root( new TestSemantics.root(
children: <TestSemantics>[ children: <TestSemantics>[
new TestSemantics.rootChild( new TestSemantics.rootChild(
id: 1,
actions: SemanticsAction.tap.index, actions: SemanticsAction.tap.index,
label: 'ABC', label: 'ABC',
rect: new Rect.fromLTRB(0.0, 0.0, 88.0, 36.0), rect: new Rect.fromLTRB(0.0, 0.0, 88.0, 36.0),
transform: new Matrix4.translationValues(356.0, 282.0, 0.0) transform: new Matrix4.translationValues(356.0, 282.0, 0.0),
flags: SemanticsFlags.isButton.index,
)
],
),
ignoreId: true,
));
semantics.dispose();
});
testWidgets('Does RaisedButton contribute semantics', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(
new Directionality(
textDirection: TextDirection.ltr,
child: new Material(
child: new Center(
child: new RaisedButton(
onPressed: () { },
child: const Text('ABC')
),
),
),
),
);
expect(semantics, hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
actions: SemanticsAction.tap.index,
label: 'ABC',
rect: new Rect.fromLTRB(0.0, 0.0, 88.0, 36.0),
transform: new Matrix4.translationValues(356.0, 282.0, 0.0),
flags: SemanticsFlags.isButton.index,
) )
] ]
) ),
ignoreId: true,
)); ));
semantics.dispose(); semantics.dispose();
......
...@@ -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:ui' show SemanticsFlags;
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
...@@ -47,6 +49,7 @@ void main() { ...@@ -47,6 +49,7 @@ void main() {
label: 'Button', label: 'Button',
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
actions: SemanticsAction.tap.index, actions: SemanticsAction.tap.index,
flags: SemanticsFlags.isButton.index,
), ),
], ],
), ),
......
...@@ -198,7 +198,7 @@ void main() { ...@@ -198,7 +198,7 @@ void main() {
expect( expect(
minimalProperties.toStringDeep(minLevel: DiagnosticLevel.hidden), minimalProperties.toStringDeep(minLevel: DiagnosticLevel.hidden),
'SemanticsNode#16(owner: null, isPartOfNodeMerging: false, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0), wasAffectedByClip: false, actions: [], isSelected: false, label: "", textDirection: null)\n', 'SemanticsNode#16(owner: null, isPartOfNodeMerging: false, Rect.fromLTRB(0.0, 0.0, 0.0, 0.0), wasAffectedByClip: false, actions: [], isSelected: false, label: "", isButton: false, textDirection: null)\n',
); );
final SemanticsConfiguration config = new SemanticsConfiguration() final SemanticsConfiguration config = new SemanticsConfiguration()
...@@ -208,6 +208,7 @@ void main() { ...@@ -208,6 +208,7 @@ void main() {
..addAction(SemanticsAction.showOnScreen, () { }) ..addAction(SemanticsAction.showOnScreen, () { })
..isChecked = false ..isChecked = false
..isSelected = true ..isSelected = true
..isButton = true
..label = 'Use all the properties' ..label = 'Use all the properties'
..textDirection = TextDirection.rtl; ..textDirection = TextDirection.rtl;
final SemanticsNode allProperties = new SemanticsNode() final SemanticsNode allProperties = new SemanticsNode()
...@@ -217,11 +218,11 @@ void main() { ...@@ -217,11 +218,11 @@ void main() {
..updateWith(config: config, childrenInInversePaintOrder: null); ..updateWith(config: config, childrenInInversePaintOrder: null);
expect( expect(
allProperties.toStringDeep(), allProperties.toStringDeep(),
'SemanticsNode#17(STALE, owner: null, leaf merge, Rect.fromLTRB(60.0, 20.0, 80.0, 50.0), clipped, actions: [longPress, scrollUp, showOnScreen], unchecked, selected, label: "Use all the properties", textDirection: rtl)\n', 'SemanticsNode#17(STALE, owner: null, leaf merge, Rect.fromLTRB(60.0, 20.0, 80.0, 50.0), clipped, actions: [longPress, scrollUp, showOnScreen], unchecked, selected, label: "Use all the properties", button, textDirection: rtl)\n',
); );
expect( expect(
allProperties.getSemanticsData().toString(), allProperties.getSemanticsData().toString(),
'SemanticsData(Rect.fromLTRB(50.0, 10.0, 70.0, 40.0), [1.0,0.0,0.0,10.0; 0.0,1.0,0.0,10.0; 0.0,0.0,1.0,0.0; 0.0,0.0,0.0,1.0], actions: [longPress, scrollUp, showOnScreen], flags: [hasCheckedState, isSelected], label: "Use all the properties", textDirection: rtl)', 'SemanticsData(Rect.fromLTRB(50.0, 10.0, 70.0, 40.0), [1.0,0.0,0.0,10.0; 0.0,1.0,0.0,10.0; 0.0,0.0,1.0,0.0; 0.0,0.0,0.0,1.0], actions: [longPress, scrollUp, showOnScreen], flags: [hasCheckedState, isSelected, isButton], label: "Use all the properties", textDirection: rtl)',
); );
final SemanticsNode scaled = new SemanticsNode() final SemanticsNode scaled = new SemanticsNode()
......
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