mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Support SemanticsFlag for Header (#15255)
This commit is contained in:
parent
96ce9d64ac
commit
3a40d0ee4e
@ -152,7 +152,10 @@ class GalleryHomeState extends State<GalleryHome> with SingleTickerProviderState
|
||||
child: new SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: new Text(galleryItem.category, style: headerStyle),
|
||||
child: new Semantics(
|
||||
header: true,
|
||||
child: new Text(galleryItem.category, style: headerStyle),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -817,6 +817,21 @@ class RenderCustomPaint extends RenderProxyBox {
|
||||
if (properties.button != null) {
|
||||
config.isButton = properties.button;
|
||||
}
|
||||
if (properties.textField != null) {
|
||||
config.isTextField = properties.textField;
|
||||
}
|
||||
if (properties.focused != null) {
|
||||
config.isFocused = properties.focused;
|
||||
}
|
||||
if (properties.enabled != null) {
|
||||
config.isEnabled = properties.enabled;
|
||||
}
|
||||
if (properties.inMutuallyExclusiveGroup != null) {
|
||||
config.isInMutuallyExclusiveGroup = properties.inMutuallyExclusiveGroup;
|
||||
}
|
||||
if (properties.header != null) {
|
||||
config.isHeader = properties.header;
|
||||
}
|
||||
if (properties.label != null) {
|
||||
config.label = properties.label;
|
||||
}
|
||||
|
@ -3015,6 +3015,10 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
|
||||
bool checked,
|
||||
bool selected,
|
||||
bool button,
|
||||
bool header,
|
||||
bool textField,
|
||||
bool focused,
|
||||
bool inMutuallyExclusiveGroup,
|
||||
String label,
|
||||
String value,
|
||||
String increasedValue,
|
||||
@ -3045,6 +3049,10 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
|
||||
_checked = checked,
|
||||
_selected = selected,
|
||||
_button = button,
|
||||
_header = header,
|
||||
_textField = textField,
|
||||
_focused = focused,
|
||||
_inMutuallyExclusiveGroup = inMutuallyExclusiveGroup,
|
||||
_label = label,
|
||||
_value = value,
|
||||
_increasedValue = increasedValue,
|
||||
@ -3152,6 +3160,47 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
|
||||
markNeedsSemanticsUpdate();
|
||||
}
|
||||
|
||||
/// If non-null, sets the [SemanticsNode.isHeader] semantic to the given value.
|
||||
bool get header => _header;
|
||||
bool _header;
|
||||
set header(bool value) {
|
||||
if (header == value)
|
||||
return;
|
||||
_header = value;
|
||||
markNeedsSemanticsUpdate();
|
||||
}
|
||||
|
||||
/// If non-null, sets the [SemanticsNode.isTextField] semantic to the given value.
|
||||
bool get textField => _textField;
|
||||
bool _textField;
|
||||
set textField(bool value) {
|
||||
if (textField == value)
|
||||
return;
|
||||
_textField = value;
|
||||
markNeedsSemanticsUpdate();
|
||||
}
|
||||
|
||||
/// If non-null, sets the [SemanticsNode.isFocused] semantic to the given value.
|
||||
bool get focused => _focused;
|
||||
bool _focused;
|
||||
set focused(bool value) {
|
||||
if (focused == value)
|
||||
return;
|
||||
_focused = value;
|
||||
markNeedsSemanticsUpdate();
|
||||
}
|
||||
|
||||
/// If non-null, sets the [SemanticsNode.isInMutuallyExclusiveGroup] semantic
|
||||
/// to the given value.
|
||||
bool get inMutuallyExclusiveGroup => _inMutuallyExclusiveGroup;
|
||||
bool _inMutuallyExclusiveGroup;
|
||||
set inMutuallyExclusiveGroup(bool value) {
|
||||
if (inMutuallyExclusiveGroup == value)
|
||||
return;
|
||||
_inMutuallyExclusiveGroup = value;
|
||||
markNeedsSemanticsUpdate();
|
||||
}
|
||||
|
||||
/// If non-null, sets the [SemanticsNode.label] semantic to the given value.
|
||||
///
|
||||
/// The reading direction is given by [textDirection].
|
||||
@ -3581,6 +3630,14 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
|
||||
config.isSelected = selected;
|
||||
if (button != null)
|
||||
config.isButton = button;
|
||||
if (header != null)
|
||||
config.isHeader = header;
|
||||
if (textField != null)
|
||||
config.isTextField = textField;
|
||||
if (focused != null)
|
||||
config.isFocused = focused;
|
||||
if (inMutuallyExclusiveGroup != null)
|
||||
config.isInMutuallyExclusiveGroup = inMutuallyExclusiveGroup;
|
||||
if (label != null)
|
||||
config.label = label;
|
||||
if (value != null)
|
||||
|
@ -315,6 +315,10 @@ class SemanticsProperties extends DiagnosticableTree {
|
||||
this.checked,
|
||||
this.selected,
|
||||
this.button,
|
||||
this.header,
|
||||
this.textField,
|
||||
this.focused,
|
||||
this.inMutuallyExclusiveGroup,
|
||||
this.label,
|
||||
this.value,
|
||||
this.increasedValue,
|
||||
@ -366,6 +370,35 @@ class SemanticsProperties extends DiagnosticableTree {
|
||||
/// is focused.
|
||||
final bool button;
|
||||
|
||||
/// If non-null, indicates that this subtree represents a header.
|
||||
///
|
||||
/// A header divides into sections. For example, an address book application
|
||||
/// might define headers A, B, C, etc. to divide the list of alphabetically
|
||||
/// sorted contacts into sections.
|
||||
final bool header;
|
||||
|
||||
/// If non-null, indicates that this subtree represents a text field.
|
||||
///
|
||||
/// TalkBack/VoiceOver provide special affordances to enter text into a
|
||||
/// text field.
|
||||
final bool textField;
|
||||
|
||||
/// If non-null, whether the node currently holds input focus.
|
||||
///
|
||||
/// At most one node in the tree should hold input focus at any point in time.
|
||||
///
|
||||
/// Input focus (indicates that the node will receive keyboard events) is not
|
||||
/// to be confused with accessibility focus. Accessibility focus is the
|
||||
/// green/black rectangular that TalkBack/VoiceOver on the screen and is
|
||||
/// separate from input focus.
|
||||
final bool focused;
|
||||
|
||||
/// If non-null, whether a semantic node is in a mutually exclusive group.
|
||||
///
|
||||
/// For example, a radio button is in a mutually exclusive group because only
|
||||
/// one radio button in that group can be marked as [checked].
|
||||
final bool inMutuallyExclusiveGroup;
|
||||
|
||||
/// Provides a textual description of the widget.
|
||||
///
|
||||
/// If a label is provided, there must either by an ambient [Directionality]
|
||||
@ -2365,6 +2398,12 @@ class SemanticsConfiguration {
|
||||
_setFlag(SemanticsFlag.isButton, value);
|
||||
}
|
||||
|
||||
/// Whether the owning [RenderObject] is a header (true) or not (false).
|
||||
bool get isHeader => _hasFlag(SemanticsFlag.isHeader);
|
||||
set isHeader(bool value) {
|
||||
_setFlag(SemanticsFlag.isHeader, value);
|
||||
}
|
||||
|
||||
/// Whether the owning [RenderObject] is a text field.
|
||||
bool get isTextField => _hasFlag(SemanticsFlag.isTextField);
|
||||
set isTextField(bool value) {
|
||||
|
@ -4847,6 +4847,10 @@ class Semantics extends SingleChildRenderObjectWidget {
|
||||
bool checked,
|
||||
bool selected,
|
||||
bool button,
|
||||
bool header,
|
||||
bool textField,
|
||||
bool focused,
|
||||
bool inMutuallyExclusiveGroup,
|
||||
String label,
|
||||
String value,
|
||||
String increasedValue,
|
||||
@ -4881,6 +4885,10 @@ class Semantics extends SingleChildRenderObjectWidget {
|
||||
checked: checked,
|
||||
selected: selected,
|
||||
button: button,
|
||||
header: header,
|
||||
textField: textField,
|
||||
focused: focused,
|
||||
inMutuallyExclusiveGroup: inMutuallyExclusiveGroup,
|
||||
label: label,
|
||||
value: value,
|
||||
increasedValue: increasedValue,
|
||||
@ -4960,6 +4968,10 @@ class Semantics extends SingleChildRenderObjectWidget {
|
||||
checked: properties.checked,
|
||||
selected: properties.selected,
|
||||
button: properties.button,
|
||||
header: properties.header,
|
||||
textField: properties.textField,
|
||||
focused: properties.focused,
|
||||
inMutuallyExclusiveGroup: properties.inMutuallyExclusiveGroup,
|
||||
label: properties.label,
|
||||
value: properties.value,
|
||||
increasedValue: properties.increasedValue,
|
||||
|
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
@ -397,6 +398,52 @@ void _defineTests() {
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
testWidgets('Supports all flags', (WidgetTester tester) async {
|
||||
final SemanticsTester semantics = new SemanticsTester(tester);
|
||||
|
||||
await tester.pumpWidget(new CustomPaint(
|
||||
painter: new _PainterWithSemantics(
|
||||
semantics: new CustomPainterSemantics(
|
||||
key: const ValueKey<int>(1),
|
||||
rect: new Rect.fromLTRB(1.0, 2.0, 3.0, 4.0),
|
||||
properties: const SemanticsProperties(
|
||||
enabled: true,
|
||||
checked: true,
|
||||
selected: true,
|
||||
button: true,
|
||||
textField: true,
|
||||
focused: true,
|
||||
inMutuallyExclusiveGroup: true,
|
||||
header: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
const int expectedId = 2;
|
||||
final TestSemantics expectedSemantics = new TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
id: 1,
|
||||
previousNodeId: -1,
|
||||
nextNodeId: expectedId,
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
id: expectedId,
|
||||
rect: TestSemantics.fullScreen,
|
||||
flags: SemanticsFlag.values.values.toList(),
|
||||
previousNodeId: 1,
|
||||
nextNodeId: -1,
|
||||
),
|
||||
]
|
||||
),
|
||||
],
|
||||
);
|
||||
expect(semantics, hasSemantics(expectedSemantics, ignoreRect: true, ignoreTransform: true));
|
||||
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
group('diffing', () {
|
||||
testWidgets('complains about duplicate keys', (WidgetTester tester) async {
|
||||
final SemanticsTester semanticsTester = new SemanticsTester(tester);
|
||||
|
@ -449,6 +449,39 @@ void main() {
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
testWidgets('Semantics widget supports all flags', (WidgetTester tester) async {
|
||||
final SemanticsTester semantics = new SemanticsTester(tester);
|
||||
|
||||
await tester.pumpWidget(
|
||||
new Semantics(
|
||||
container: true,
|
||||
// flags
|
||||
enabled: true,
|
||||
checked: true,
|
||||
selected: true,
|
||||
button: true,
|
||||
textField: true,
|
||||
focused: true,
|
||||
inMutuallyExclusiveGroup: true,
|
||||
header: true,
|
||||
)
|
||||
);
|
||||
|
||||
final TestSemantics expectedSemantics = new TestSemantics.root(
|
||||
children: <TestSemantics>[
|
||||
new TestSemantics.rootChild(
|
||||
rect: TestSemantics.fullScreen,
|
||||
flags: SemanticsFlag.values.values.toList(),
|
||||
previousNodeId: -1,
|
||||
nextNodeId: -1,
|
||||
),
|
||||
],
|
||||
);
|
||||
expect(semantics, hasSemantics(expectedSemantics, ignoreId: true));
|
||||
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
testWidgets('Actions can be replaced without triggering semantics update', (WidgetTester tester) async {
|
||||
final SemanticsTester semantics = new SemanticsTester(tester);
|
||||
int semanticsUpdateCount = 0;
|
||||
|
Loading…
Reference in New Issue
Block a user