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(
|
child: new SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
bottom: 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) {
|
if (properties.button != null) {
|
||||||
config.isButton = properties.button;
|
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) {
|
if (properties.label != null) {
|
||||||
config.label = properties.label;
|
config.label = properties.label;
|
||||||
}
|
}
|
||||||
|
@ -3015,6 +3015,10 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
|
|||||||
bool checked,
|
bool checked,
|
||||||
bool selected,
|
bool selected,
|
||||||
bool button,
|
bool button,
|
||||||
|
bool header,
|
||||||
|
bool textField,
|
||||||
|
bool focused,
|
||||||
|
bool inMutuallyExclusiveGroup,
|
||||||
String label,
|
String label,
|
||||||
String value,
|
String value,
|
||||||
String increasedValue,
|
String increasedValue,
|
||||||
@ -3045,6 +3049,10 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
|
|||||||
_checked = checked,
|
_checked = checked,
|
||||||
_selected = selected,
|
_selected = selected,
|
||||||
_button = button,
|
_button = button,
|
||||||
|
_header = header,
|
||||||
|
_textField = textField,
|
||||||
|
_focused = focused,
|
||||||
|
_inMutuallyExclusiveGroup = inMutuallyExclusiveGroup,
|
||||||
_label = label,
|
_label = label,
|
||||||
_value = value,
|
_value = value,
|
||||||
_increasedValue = increasedValue,
|
_increasedValue = increasedValue,
|
||||||
@ -3152,6 +3160,47 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
|
|||||||
markNeedsSemanticsUpdate();
|
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.
|
/// If non-null, sets the [SemanticsNode.label] semantic to the given value.
|
||||||
///
|
///
|
||||||
/// The reading direction is given by [textDirection].
|
/// The reading direction is given by [textDirection].
|
||||||
@ -3581,6 +3630,14 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
|
|||||||
config.isSelected = selected;
|
config.isSelected = selected;
|
||||||
if (button != null)
|
if (button != null)
|
||||||
config.isButton = button;
|
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)
|
if (label != null)
|
||||||
config.label = label;
|
config.label = label;
|
||||||
if (value != null)
|
if (value != null)
|
||||||
|
@ -315,6 +315,10 @@ class SemanticsProperties extends DiagnosticableTree {
|
|||||||
this.checked,
|
this.checked,
|
||||||
this.selected,
|
this.selected,
|
||||||
this.button,
|
this.button,
|
||||||
|
this.header,
|
||||||
|
this.textField,
|
||||||
|
this.focused,
|
||||||
|
this.inMutuallyExclusiveGroup,
|
||||||
this.label,
|
this.label,
|
||||||
this.value,
|
this.value,
|
||||||
this.increasedValue,
|
this.increasedValue,
|
||||||
@ -366,6 +370,35 @@ class SemanticsProperties extends DiagnosticableTree {
|
|||||||
/// is focused.
|
/// is focused.
|
||||||
final bool button;
|
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.
|
/// 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]
|
||||||
@ -2365,6 +2398,12 @@ class SemanticsConfiguration {
|
|||||||
_setFlag(SemanticsFlag.isButton, value);
|
_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.
|
/// Whether the owning [RenderObject] is a text field.
|
||||||
bool get isTextField => _hasFlag(SemanticsFlag.isTextField);
|
bool get isTextField => _hasFlag(SemanticsFlag.isTextField);
|
||||||
set isTextField(bool value) {
|
set isTextField(bool value) {
|
||||||
|
@ -4847,6 +4847,10 @@ class Semantics extends SingleChildRenderObjectWidget {
|
|||||||
bool checked,
|
bool checked,
|
||||||
bool selected,
|
bool selected,
|
||||||
bool button,
|
bool button,
|
||||||
|
bool header,
|
||||||
|
bool textField,
|
||||||
|
bool focused,
|
||||||
|
bool inMutuallyExclusiveGroup,
|
||||||
String label,
|
String label,
|
||||||
String value,
|
String value,
|
||||||
String increasedValue,
|
String increasedValue,
|
||||||
@ -4881,6 +4885,10 @@ class Semantics extends SingleChildRenderObjectWidget {
|
|||||||
checked: checked,
|
checked: checked,
|
||||||
selected: selected,
|
selected: selected,
|
||||||
button: button,
|
button: button,
|
||||||
|
header: header,
|
||||||
|
textField: textField,
|
||||||
|
focused: focused,
|
||||||
|
inMutuallyExclusiveGroup: inMutuallyExclusiveGroup,
|
||||||
label: label,
|
label: label,
|
||||||
value: value,
|
value: value,
|
||||||
increasedValue: increasedValue,
|
increasedValue: increasedValue,
|
||||||
@ -4960,6 +4968,10 @@ class Semantics extends SingleChildRenderObjectWidget {
|
|||||||
checked: properties.checked,
|
checked: properties.checked,
|
||||||
selected: properties.selected,
|
selected: properties.selected,
|
||||||
button: properties.button,
|
button: properties.button,
|
||||||
|
header: properties.header,
|
||||||
|
textField: properties.textField,
|
||||||
|
focused: properties.focused,
|
||||||
|
inMutuallyExclusiveGroup: properties.inMutuallyExclusiveGroup,
|
||||||
label: properties.label,
|
label: properties.label,
|
||||||
value: properties.value,
|
value: properties.value,
|
||||||
increasedValue: properties.increasedValue,
|
increasedValue: properties.increasedValue,
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
@ -397,6 +398,52 @@ void _defineTests() {
|
|||||||
semantics.dispose();
|
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', () {
|
group('diffing', () {
|
||||||
testWidgets('complains about duplicate keys', (WidgetTester tester) async {
|
testWidgets('complains about duplicate keys', (WidgetTester tester) async {
|
||||||
final SemanticsTester semanticsTester = new SemanticsTester(tester);
|
final SemanticsTester semanticsTester = new SemanticsTester(tester);
|
||||||
|
@ -449,6 +449,39 @@ void main() {
|
|||||||
semantics.dispose();
|
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 {
|
testWidgets('Actions can be replaced without triggering semantics update', (WidgetTester tester) async {
|
||||||
final SemanticsTester semantics = new SemanticsTester(tester);
|
final SemanticsTester semantics = new SemanticsTester(tester);
|
||||||
int semanticsUpdateCount = 0;
|
int semanticsUpdateCount = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user