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

https://github.com/flutter/flutter/issues/11992
This commit is contained in:
amirh 2017-10-23 14:52:41 -07:00 committed by GitHub
parent 52815b2224
commit ce4a45779d
7 changed files with 77 additions and 7 deletions

View file

@ -330,7 +330,7 @@ class _MaterialButtonState extends State<MaterialButton> {
child: new Center(
widthFactor: 1.0,
heightFactor: 1.0,
child: widget.child
child: new Semantics(button: true, child: widget.child),
)
)
)

View file

@ -3175,6 +3175,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
bool explicitChildNodes,
bool checked,
bool selected,
bool button,
String label,
TextDirection textDirection,
}) : assert(container != null),
@ -3182,6 +3183,7 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
_explicitChildNodes = explicitChildNodes,
_checked = checked,
_selected = selected,
_button = button,
_label = label,
_textDirection = textDirection,
super(child);
@ -3260,6 +3262,17 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
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.
///
/// This must not be null if [label] is not null.
@ -3286,6 +3299,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
config.label = label;
if (textDirection != null)
config.textDirection = textDirection;
if (button != null)
config.isButton = button;
}
}

View file

@ -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('isSelected', value: _hasFlag(SemanticsFlags.isSelected), ifTrue: 'selected'));
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));
}
@ -1045,6 +1046,11 @@ class SemanticsConfiguration {
_setFlag(SemanticsFlags.isChecked, value);
}
/// Whether the owning [RenderObject] is a button (true) or not (false).
set isButton(bool value) {
_setFlag(SemanticsFlags.isButton, value);
}
// TAGS
Iterable<SemanticsTag> get tagsForChildren => _tagsForChildren;

View file

@ -4451,6 +4451,7 @@ class Semantics extends SingleChildRenderObjectWidget {
this.explicitChildNodes: false,
this.checked,
this.selected,
this.button,
this.label,
this.textDirection,
}) : assert(container != null),
@ -4491,6 +4492,12 @@ class Semantics extends SingleChildRenderObjectWidget {
/// all other tabs are unselected.
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.
///
/// If a label is provided, there must either by an ambient [Directionality]
@ -4514,6 +4521,7 @@ class Semantics extends SingleChildRenderObjectWidget {
checked: checked,
selected: selected,
label: label,
button: button,
textDirection: _getTextDirection(context),
);
}

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' show SemanticsFlags;
import 'package:flutter/rendering.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
@ -31,14 +33,49 @@ void main() {
new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics.rootChild(
id: 1,
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)
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();

View file

@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' show SemanticsFlags;
import 'package:flutter/rendering.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
@ -47,6 +49,7 @@ void main() {
label: 'Button',
textDirection: TextDirection.ltr,
actions: SemanticsAction.tap.index,
flags: SemanticsFlags.isButton.index,
),
],
),

View file

@ -198,7 +198,7 @@ void main() {
expect(
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()
@ -208,6 +208,7 @@ void main() {
..addAction(SemanticsAction.showOnScreen, () { })
..isChecked = false
..isSelected = true
..isButton = true
..label = 'Use all the properties'
..textDirection = TextDirection.rtl;
final SemanticsNode allProperties = new SemanticsNode()
@ -217,11 +218,11 @@ void main() {
..updateWith(config: config, childrenInInversePaintOrder: null);
expect(
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(
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()