Adds dialog and alertdialog role (#162692)
<!-- Thanks for filing a pull request! Reviewers are typically assigned within a week of filing a request. To learn more about code review, see our documentation on Tree Hygiene: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md --> fixes https://github.com/flutter/flutter/issues/162124 fixes https://github.com/flutter/flutter/issues/157207 fixes https://github.com/flutter/flutter/issues/157204 ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [ ] I signed the [CLA]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
parent
04cbda2b1a
commit
21471aa236
@ -362,6 +362,12 @@ enum SemanticsRole {
|
|||||||
|
|
||||||
/// The main display for a tab.
|
/// The main display for a tab.
|
||||||
tabPanel,
|
tabPanel,
|
||||||
|
|
||||||
|
/// A pop up dialog.
|
||||||
|
dialog,
|
||||||
|
|
||||||
|
/// An alert dialog.
|
||||||
|
alertDialog,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A Boolean value that can be associated with a semantics node.
|
/// A Boolean value that can be associated with a semantics node.
|
||||||
|
@ -256,7 +256,7 @@ class SemanticsFlag {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mirrors engine/src/flutter/lib/ui/semantics.dart
|
// Mirrors engine/src/flutter/lib/ui/semantics.dart
|
||||||
enum SemanticsRole { none, tab, tabBar, tabPanel }
|
enum SemanticsRole { none, tab, tabBar, tabPanel, dialog, alertDialog }
|
||||||
|
|
||||||
// When adding a new StringAttributeType, the classes in these file must be
|
// When adding a new StringAttributeType, the classes in these file must be
|
||||||
// updated as well.
|
// updated as well.
|
||||||
|
@ -6,17 +6,8 @@ import '../dom.dart';
|
|||||||
import '../semantics.dart';
|
import '../semantics.dart';
|
||||||
import '../util.dart';
|
import '../util.dart';
|
||||||
|
|
||||||
/// Denotes that all descendant nodes are inside a route.
|
class SemanticRouteBase extends SemanticRole {
|
||||||
///
|
SemanticRouteBase(super.kind, super.object) : super.blank() {
|
||||||
/// Routes can include dialogs, pop-up menus, sub-screens, and more.
|
|
||||||
///
|
|
||||||
/// See also:
|
|
||||||
///
|
|
||||||
/// * [RouteName], which provides a description for this route in the absense
|
|
||||||
/// of an explicit route label set on the route itself.
|
|
||||||
class SemanticRoute extends SemanticRole {
|
|
||||||
SemanticRoute(SemanticsObject semanticsObject)
|
|
||||||
: super.blank(EngineSemanticsRole.route, semanticsObject) {
|
|
||||||
// The following behaviors can coexist with the route. Generic `RouteName`
|
// The following behaviors can coexist with the route. Generic `RouteName`
|
||||||
// and `LabelAndValue` are not used by this role because when the route
|
// and `LabelAndValue` are not used by this role because when the route
|
||||||
// names its own route an `aria-label` is used instead of
|
// names its own route an `aria-label` is used instead of
|
||||||
@ -42,13 +33,6 @@ class SemanticRoute extends SemanticRole {
|
|||||||
// Case 2: nothing requested explicit focus. Focus on the first descendant.
|
// Case 2: nothing requested explicit focus. Focus on the first descendant.
|
||||||
_setDefaultFocus();
|
_setDefaultFocus();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Lacking any more specific information, ARIA role "dialog" is the
|
|
||||||
// closest thing to Flutter's route. This can be revisited if better
|
|
||||||
// options become available, especially if the framework volunteers more
|
|
||||||
// specific information about the route. Other attributes in the vicinity
|
|
||||||
// of routes include: "alertdialog", `aria-modal`, "menu", "tooltip".
|
|
||||||
setAriaRole('dialog');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _setDefaultFocus() {
|
void _setDefaultFocus() {
|
||||||
@ -109,6 +93,63 @@ class SemanticRoute extends SemanticRole {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Denotes that all descendant nodes are inside a route.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [RouteName], which provides a description for this route in the absence
|
||||||
|
/// of an explicit route label set on the route itself.
|
||||||
|
class SemanticRoute extends SemanticRouteBase {
|
||||||
|
SemanticRoute(SemanticsObject object) : super(EngineSemanticsRole.route, object) {
|
||||||
|
// Lacking any more specific information, ARIA role "dialog" is the
|
||||||
|
// closest thing to Flutter's route. This can be revisited if better
|
||||||
|
// options become available, especially if the framework volunteers more
|
||||||
|
// specific information about the route. Other attributes in the vicinity
|
||||||
|
// of routes include: "alertdialog", `aria-modal`, "menu", "tooltip".
|
||||||
|
setAriaRole('dialog');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indicates the container as a pop dialog.
|
||||||
|
///
|
||||||
|
/// Uses aria dialog role to convey this semantic information to the element.
|
||||||
|
///
|
||||||
|
/// Setting this role will also set aria-modal to true, which helps screen
|
||||||
|
/// reader better understand this section of screen.
|
||||||
|
///
|
||||||
|
/// Screen-readers take advantage of "aria-label" to describe the visual.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [RouteName], which provides a description for this route in the absence
|
||||||
|
/// of an explicit route label set on the route itself.
|
||||||
|
class SemanticDialog extends SemanticRouteBase {
|
||||||
|
SemanticDialog(SemanticsObject object) : super(EngineSemanticsRole.dialog, object) {
|
||||||
|
setAriaRole('dialog');
|
||||||
|
setAttribute('aria-modal', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indicates the container as an alert dialog.
|
||||||
|
///
|
||||||
|
/// Uses aria alertdialog role to convey this semantic information to the element.
|
||||||
|
///
|
||||||
|
/// Setting this role will also set aria-modal to true, which helps screen
|
||||||
|
/// reader better understand this section of screen.
|
||||||
|
///
|
||||||
|
/// Screen-readers takes advantage of "aria-label" to describe the visual.
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [RouteName], which provides a description for this route in the absence
|
||||||
|
/// of an explicit route label set on the route itself.
|
||||||
|
class SemanticAlertDialog extends SemanticRouteBase {
|
||||||
|
SemanticAlertDialog(SemanticsObject object) : super(EngineSemanticsRole.alertDialog, object) {
|
||||||
|
setAriaRole('alertdialog');
|
||||||
|
setAttribute('aria-modal', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Supplies a description for the nearest ancestor [SemanticRoute].
|
/// Supplies a description for the nearest ancestor [SemanticRoute].
|
||||||
///
|
///
|
||||||
/// This role is assigned to nodes that have `namesRoute` set but not
|
/// This role is assigned to nodes that have `namesRoute` set but not
|
||||||
@ -121,7 +162,7 @@ class SemanticRoute extends SemanticRole {
|
|||||||
class RouteName extends SemanticBehavior {
|
class RouteName extends SemanticBehavior {
|
||||||
RouteName(super.semanticsObject, super.owner);
|
RouteName(super.semanticsObject, super.owner);
|
||||||
|
|
||||||
SemanticRoute? _route;
|
SemanticRouteBase? _route;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void update() {
|
void update() {
|
||||||
@ -139,7 +180,7 @@ class RouteName extends SemanticBehavior {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (semanticsObject.isLabelDirty) {
|
if (semanticsObject.isLabelDirty) {
|
||||||
final SemanticRoute? route = _route;
|
final SemanticRouteBase? route = _route;
|
||||||
if (route != null) {
|
if (route != null) {
|
||||||
// Already attached to a route, just update the description.
|
// Already attached to a route, just update the description.
|
||||||
route.describeBy(this);
|
route.describeBy(this);
|
||||||
@ -158,11 +199,11 @@ class RouteName extends SemanticBehavior {
|
|||||||
|
|
||||||
void _lookUpNearestAncestorRoute() {
|
void _lookUpNearestAncestorRoute() {
|
||||||
SemanticsObject? parent = semanticsObject.parent;
|
SemanticsObject? parent = semanticsObject.parent;
|
||||||
while (parent != null && parent.semanticRole?.kind != EngineSemanticsRole.route) {
|
while (parent != null && (parent.semanticRole is! SemanticRouteBase)) {
|
||||||
parent = parent.parent;
|
parent = parent.parent;
|
||||||
}
|
}
|
||||||
if (parent != null && parent.semanticRole?.kind == EngineSemanticsRole.route) {
|
if (parent != null) {
|
||||||
_route = parent.semanticRole! as SemanticRoute;
|
_route = parent.semanticRole! as SemanticRouteBase;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -416,6 +416,12 @@ enum EngineSemanticsRole {
|
|||||||
/// A main content for a tab.
|
/// A main content for a tab.
|
||||||
tabPanel,
|
tabPanel,
|
||||||
|
|
||||||
|
/// A popup dialog.
|
||||||
|
dialog,
|
||||||
|
|
||||||
|
/// An alert dialog.
|
||||||
|
alertDialog,
|
||||||
|
|
||||||
/// A role used when a more specific role cannot be assigend to
|
/// A role used when a more specific role cannot be assigend to
|
||||||
/// a [SemanticsObject].
|
/// a [SemanticsObject].
|
||||||
///
|
///
|
||||||
@ -1745,6 +1751,10 @@ class SemanticsObject {
|
|||||||
return EngineSemanticsRole.tabPanel;
|
return EngineSemanticsRole.tabPanel;
|
||||||
case ui.SemanticsRole.tabBar:
|
case ui.SemanticsRole.tabBar:
|
||||||
return EngineSemanticsRole.tabList;
|
return EngineSemanticsRole.tabList;
|
||||||
|
case ui.SemanticsRole.dialog:
|
||||||
|
return EngineSemanticsRole.dialog;
|
||||||
|
case ui.SemanticsRole.alertDialog:
|
||||||
|
return EngineSemanticsRole.alertDialog;
|
||||||
case ui.SemanticsRole.none:
|
case ui.SemanticsRole.none:
|
||||||
// fallback to checking semantics properties.
|
// fallback to checking semantics properties.
|
||||||
}
|
}
|
||||||
@ -1794,6 +1804,8 @@ class SemanticsObject {
|
|||||||
EngineSemanticsRole.tab => SemanticTab(this),
|
EngineSemanticsRole.tab => SemanticTab(this),
|
||||||
EngineSemanticsRole.tabList => SemanticTabList(this),
|
EngineSemanticsRole.tabList => SemanticTabList(this),
|
||||||
EngineSemanticsRole.tabPanel => SemanticTabPanel(this),
|
EngineSemanticsRole.tabPanel => SemanticTabPanel(this),
|
||||||
|
EngineSemanticsRole.dialog => SemanticDialog(this),
|
||||||
|
EngineSemanticsRole.alertDialog => SemanticAlertDialog(this),
|
||||||
EngineSemanticsRole.generic => GenericRole(this),
|
EngineSemanticsRole.generic => GenericRole(this),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -112,6 +112,7 @@ void runSemanticsTests() {
|
|||||||
});
|
});
|
||||||
group('route', () {
|
group('route', () {
|
||||||
_testRoute();
|
_testRoute();
|
||||||
|
_testDialogs();
|
||||||
});
|
});
|
||||||
group('focusable', () {
|
group('focusable', () {
|
||||||
_testFocusable();
|
_testFocusable();
|
||||||
@ -3352,6 +3353,99 @@ void _testRoute() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _testDialogs() {
|
||||||
|
test('nodes with dialog role', () {
|
||||||
|
semantics()
|
||||||
|
..debugOverrideTimestampFunction(() => _testTime)
|
||||||
|
..semanticsEnabled = true;
|
||||||
|
|
||||||
|
SemanticsObject pumpSemantics() {
|
||||||
|
final SemanticsTester tester = SemanticsTester(owner());
|
||||||
|
tester.updateNode(
|
||||||
|
id: 0,
|
||||||
|
role: ui.SemanticsRole.dialog,
|
||||||
|
rect: const ui.Rect.fromLTRB(0, 0, 100, 50),
|
||||||
|
);
|
||||||
|
tester.apply();
|
||||||
|
return tester.getSemanticsObject(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
final SemanticsObject object = pumpSemantics();
|
||||||
|
expect(object.semanticRole?.kind, EngineSemanticsRole.dialog);
|
||||||
|
expect(object.element.getAttribute('role'), 'dialog');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('nodes with alertdialog role', () {
|
||||||
|
semantics()
|
||||||
|
..debugOverrideTimestampFunction(() => _testTime)
|
||||||
|
..semanticsEnabled = true;
|
||||||
|
|
||||||
|
SemanticsObject pumpSemantics() {
|
||||||
|
final SemanticsTester tester = SemanticsTester(owner());
|
||||||
|
tester.updateNode(
|
||||||
|
id: 0,
|
||||||
|
role: ui.SemanticsRole.alertDialog,
|
||||||
|
rect: const ui.Rect.fromLTRB(0, 0, 100, 50),
|
||||||
|
);
|
||||||
|
tester.apply();
|
||||||
|
return tester.getSemanticsObject(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
final SemanticsObject object = pumpSemantics();
|
||||||
|
expect(object.semanticRole?.kind, EngineSemanticsRole.alertDialog);
|
||||||
|
expect(object.element.getAttribute('role'), 'alertdialog');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('dialog can be described by a descendant', () {
|
||||||
|
semantics()
|
||||||
|
..debugOverrideTimestampFunction(() => _testTime)
|
||||||
|
..semanticsEnabled = true;
|
||||||
|
|
||||||
|
void pumpSemantics({required String label}) {
|
||||||
|
final SemanticsTester tester = SemanticsTester(owner());
|
||||||
|
tester.updateNode(
|
||||||
|
id: 0,
|
||||||
|
role: ui.SemanticsRole.dialog,
|
||||||
|
transform: Matrix4.identity().toFloat64(),
|
||||||
|
children: <SemanticsNodeUpdate>[
|
||||||
|
tester.updateNode(
|
||||||
|
id: 1,
|
||||||
|
children: <SemanticsNodeUpdate>[
|
||||||
|
tester.updateNode(id: 2, namesRoute: true, label: label),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
tester.apply();
|
||||||
|
|
||||||
|
expectSemanticsTree(owner(), '''
|
||||||
|
<sem role="dialog" aria-describedby="flt-semantic-node-2">
|
||||||
|
<sem-c>
|
||||||
|
<sem>
|
||||||
|
<sem-c>
|
||||||
|
<sem><span>$label</span></sem>
|
||||||
|
</sem-c>
|
||||||
|
</sem>
|
||||||
|
</sem-c>
|
||||||
|
</sem>
|
||||||
|
''');
|
||||||
|
}
|
||||||
|
|
||||||
|
pumpSemantics(label: 'Route label');
|
||||||
|
|
||||||
|
expect(owner().debugSemanticsTree![0]!.semanticRole?.kind, EngineSemanticsRole.dialog);
|
||||||
|
expect(owner().debugSemanticsTree![2]!.semanticRole?.kind, EngineSemanticsRole.generic);
|
||||||
|
expect(
|
||||||
|
owner().debugSemanticsTree![2]!.semanticRole?.debugSemanticBehaviorTypes,
|
||||||
|
contains(RouteName),
|
||||||
|
);
|
||||||
|
|
||||||
|
pumpSemantics(label: 'Updated route label');
|
||||||
|
|
||||||
|
semantics().semanticsEnabled = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
typedef CapturedAction = (int nodeId, ui.SemanticsAction action, Object? args);
|
typedef CapturedAction = (int nodeId, ui.SemanticsAction action, Object? args);
|
||||||
|
|
||||||
void _testFocusable() {
|
void _testFocusable() {
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
library;
|
library;
|
||||||
|
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
import 'dart:ui' show ImageFilter, lerpDouble;
|
import 'dart:ui' show ImageFilter, SemanticsRole, lerpDouble;
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
@ -463,6 +463,7 @@ class _CupertinoAlertDialogState extends State<CupertinoAlertDialog> {
|
|||||||
child: CupertinoPopupSurface(
|
child: CupertinoPopupSurface(
|
||||||
isSurfacePainted: false,
|
isSurfacePainted: false,
|
||||||
child: Semantics(
|
child: Semantics(
|
||||||
|
role: SemanticsRole.alertDialog,
|
||||||
namesRoute: true,
|
namesRoute: true,
|
||||||
scopesRoute: true,
|
scopesRoute: true,
|
||||||
explicitChildNodes: true,
|
explicitChildNodes: true,
|
||||||
@ -1332,6 +1333,7 @@ class _CupertinoActionSheetState extends State<CupertinoActionSheet> {
|
|||||||
namesRoute: true,
|
namesRoute: true,
|
||||||
scopesRoute: true,
|
scopesRoute: true,
|
||||||
explicitChildNodes: true,
|
explicitChildNodes: true,
|
||||||
|
role: SemanticsRole.dialog,
|
||||||
label: 'Alert',
|
label: 'Alert',
|
||||||
child: CupertinoUserInterfaceLevel(
|
child: CupertinoUserInterfaceLevel(
|
||||||
data: CupertinoUserInterfaceLevelData.elevated,
|
data: CupertinoUserInterfaceLevelData.elevated,
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
/// @docImport 'text_button.dart';
|
/// @docImport 'text_button.dart';
|
||||||
library;
|
library;
|
||||||
|
|
||||||
import 'dart:ui' show clampDouble, lerpDouble;
|
import 'dart:ui' show SemanticsRole, clampDouble, lerpDouble;
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
|
|
||||||
@ -67,6 +67,7 @@ class Dialog extends StatelessWidget {
|
|||||||
this.shape,
|
this.shape,
|
||||||
this.alignment,
|
this.alignment,
|
||||||
this.child,
|
this.child,
|
||||||
|
this.semanticsRole = SemanticsRole.dialog,
|
||||||
}) : assert(elevation == null || elevation >= 0.0),
|
}) : assert(elevation == null || elevation >= 0.0),
|
||||||
_fullscreen = false;
|
_fullscreen = false;
|
||||||
|
|
||||||
@ -79,6 +80,7 @@ class Dialog extends StatelessWidget {
|
|||||||
this.insetAnimationDuration = Duration.zero,
|
this.insetAnimationDuration = Duration.zero,
|
||||||
this.insetAnimationCurve = Curves.decelerate,
|
this.insetAnimationCurve = Curves.decelerate,
|
||||||
this.child,
|
this.child,
|
||||||
|
this.semanticsRole = SemanticsRole.dialog,
|
||||||
}) : elevation = 0,
|
}) : elevation = 0,
|
||||||
shadowColor = null,
|
shadowColor = null,
|
||||||
surfaceTintColor = null,
|
surfaceTintColor = null,
|
||||||
@ -229,6 +231,11 @@ class Dialog extends StatelessWidget {
|
|||||||
/// This value is used to determine if this is a fullscreen dialog.
|
/// This value is used to determine if this is a fullscreen dialog.
|
||||||
final bool _fullscreen;
|
final bool _fullscreen;
|
||||||
|
|
||||||
|
/// The role this dialog represent in assist technologies.
|
||||||
|
///
|
||||||
|
/// Defaults to [SemanticsRole.dialog].
|
||||||
|
final SemanticsRole semanticsRole;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final ThemeData theme = Theme.of(context);
|
final ThemeData theme = Theme.of(context);
|
||||||
@ -268,7 +275,9 @@ class Dialog extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return AnimatedPadding(
|
return Semantics(
|
||||||
|
role: semanticsRole,
|
||||||
|
child: AnimatedPadding(
|
||||||
padding: effectivePadding,
|
padding: effectivePadding,
|
||||||
duration: insetAnimationDuration,
|
duration: insetAnimationDuration,
|
||||||
curve: insetAnimationCurve,
|
curve: insetAnimationCurve,
|
||||||
@ -280,6 +289,7 @@ class Dialog extends StatelessWidget {
|
|||||||
context: context,
|
context: context,
|
||||||
child: dialogChild,
|
child: dialogChild,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -918,6 +928,7 @@ class AlertDialog extends StatelessWidget {
|
|||||||
clipBehavior: clipBehavior,
|
clipBehavior: clipBehavior,
|
||||||
shape: shape,
|
shape: shape,
|
||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
|
semanticsRole: SemanticsRole.alertDialog,
|
||||||
child: dialogChild,
|
child: dialogChild,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,8 @@ final int _kUnblockedUserActions =
|
|||||||
/// A static class to conduct semantics role checks.
|
/// A static class to conduct semantics role checks.
|
||||||
sealed class _DebugSemanticsRoleChecks {
|
sealed class _DebugSemanticsRoleChecks {
|
||||||
static FlutterError? _checkSemanticsData(SemanticsNode node) => switch (node.role) {
|
static FlutterError? _checkSemanticsData(SemanticsNode node) => switch (node.role) {
|
||||||
|
SemanticsRole.alertDialog => _noCheckRequired,
|
||||||
|
SemanticsRole.dialog => _noCheckRequired,
|
||||||
SemanticsRole.none => _noCheckRequired,
|
SemanticsRole.none => _noCheckRequired,
|
||||||
SemanticsRole.tab => _semanticsTab,
|
SemanticsRole.tab => _semanticsTab,
|
||||||
SemanticsRole.tabBar => _semanticsTabBar,
|
SemanticsRole.tabBar => _semanticsTabBar,
|
||||||
|
@ -7,9 +7,10 @@
|
|||||||
@Tags(<String>['reduced-test-set'])
|
@Tags(<String>['reduced-test-set'])
|
||||||
library;
|
library;
|
||||||
|
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
@ -1760,6 +1761,7 @@ void main() {
|
|||||||
TestSemantics(
|
TestSemantics(
|
||||||
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute, SemanticsFlag.namesRoute],
|
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute, SemanticsFlag.namesRoute],
|
||||||
label: 'Alert',
|
label: 'Alert',
|
||||||
|
role: SemanticsRole.dialog,
|
||||||
children: <TestSemantics>[
|
children: <TestSemantics>[
|
||||||
TestSemantics(
|
TestSemantics(
|
||||||
flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
|
flags: <SemanticsFlag>[SemanticsFlag.hasImplicitScrolling],
|
||||||
|
@ -711,6 +711,7 @@ void main() {
|
|||||||
SemanticsFlag.scopesRoute,
|
SemanticsFlag.scopesRoute,
|
||||||
SemanticsFlag.namesRoute,
|
SemanticsFlag.namesRoute,
|
||||||
],
|
],
|
||||||
|
role: SemanticsRole.alertDialog,
|
||||||
label: 'Alert',
|
label: 'Alert',
|
||||||
children: <TestSemantics>[
|
children: <TestSemantics>[
|
||||||
TestSemantics(
|
TestSemantics(
|
||||||
|
@ -1616,6 +1616,7 @@ void main() {
|
|||||||
children: <TestSemantics>[
|
children: <TestSemantics>[
|
||||||
TestSemantics(
|
TestSemantics(
|
||||||
id: 4,
|
id: 4,
|
||||||
|
role: SemanticsRole.alertDialog,
|
||||||
children: <TestSemantics>[
|
children: <TestSemantics>[
|
||||||
TestSemantics(id: 5, label: 'title', textDirection: TextDirection.ltr),
|
TestSemantics(id: 5, label: 'title', textDirection: TextDirection.ltr),
|
||||||
// The content semantics does not merge into the semantics
|
// The content semantics does not merge into the semantics
|
||||||
@ -1803,6 +1804,7 @@ void main() {
|
|||||||
children: <TestSemantics>[
|
children: <TestSemantics>[
|
||||||
TestSemantics(
|
TestSemantics(
|
||||||
id: 4,
|
id: 4,
|
||||||
|
role: SemanticsRole.dialog,
|
||||||
children: <TestSemantics>[
|
children: <TestSemantics>[
|
||||||
// Title semantics does not merge into the semantics
|
// Title semantics does not merge into the semantics
|
||||||
// node 4.
|
// node 4.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user