Unverified Commit 8ebd3525 authored by Michael Klimushyn's avatar Michael Klimushyn Committed by GitHub

Add semantic routing to CupertinoAlertDialog (#21583)

Fixes #21250
parent 99810695
......@@ -10,6 +10,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'colors.dart';
import 'localizations.dart';
import 'scrollbar.dart';
// TODO(abarth): These constants probably belong somewhere more general.
......@@ -218,6 +219,8 @@ class CupertinoAlertDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
final CupertinoLocalizations localizations = CupertinoLocalizations.of(context)
?? const DefaultCupertinoLocalizations();
final bool isInAccessibilityMode = _isInAccessibilityMode(context);
final double textScaleFactor = MediaQuery.of(context).textScaleFactor;
return new MediaQuery(
......@@ -235,9 +238,15 @@ class CupertinoAlertDialog extends StatelessWidget {
: _kCupertinoDialogWidth,
child: new CupertinoPopupSurface(
isSurfacePainted: false,
child: new _CupertinoDialogRenderWidget(
contentSection: _buildContent(),
actionsSection: _buildActions(),
child: new Semantics(
namesRoute: true,
scopesRoute: true,
explicitChildNodes: true,
label: localizations.alertDialogLabel,
child: new _CupertinoDialogRenderWidget(
contentSection: _buildContent(),
actionsSection: _buildActions(),
),
),
),
),
......@@ -949,18 +958,21 @@ class _PressableActionButtonState extends State<_PressableActionButton> {
Widget build(BuildContext context) {
return new _ActionButtonParentDataWidget(
isPressed: _isPressed,
// TODO(mattcarroll): Button press dynamics need overhaul for iOS: https://github.com/flutter/flutter/issues/19786
child: new GestureDetector(
behavior: HitTestBehavior.opaque,
onTapDown: (TapDownDetails details) => setState(() {
_isPressed = true;
}),
onTapUp: (TapUpDetails details) => setState(() {
_isPressed = false;
}),
// TODO(mattcarroll): Cancel is currently triggered when user moves past slop instead of off button: https://github.com/flutter/flutter/issues/19783
onTapCancel: () => setState(() => _isPressed = false),
child: widget.child,
child: new MergeSemantics(
// TODO(mattcarroll): Button press dynamics need overhaul for iOS: https://github.com/flutter/flutter/issues/19786
child: new GestureDetector(
excludeFromSemantics: true,
behavior: HitTestBehavior.opaque,
onTapDown: (TapDownDetails details) => setState(() {
_isPressed = true;
}),
onTapUp: (TapUpDetails details) => setState(() {
_isPressed = false;
}),
// TODO(mattcarroll): Cancel is currently triggered when user moves past slop instead of off button: https://github.com/flutter/flutter/issues/19783
onTapCancel: () => setState(() => _isPressed = false),
child: widget.child,
),
),
);
}
......@@ -1091,12 +1103,16 @@ class CupertinoDialogAction extends StatelessWidget {
constraints: new BoxConstraints(
maxWidth: fontSizeRatio * (dialogWidth - (2 * padding)),
),
child: new DefaultTextStyle(
style: textStyle,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 1,
child: content,
child: new Semantics(
button: true,
onTap: onPressed,
child: new DefaultTextStyle(
style: textStyle,
textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
maxLines: 1,
child: content,
),
),
),
),
......@@ -1149,6 +1165,7 @@ class CupertinoDialogAction extends StatelessWidget {
);
return new GestureDetector(
excludeFromSemantics: true,
onTap: onPressed,
behavior: HitTestBehavior.opaque,
child: new ConstrainedBox(
......
......@@ -80,6 +80,9 @@ abstract class CupertinoLocalizations {
/// The abbreviation for post meridiem (after noon) shown in the time picker.
String get postMeridiemAbbreviation;
/// The term used by the system to announce dialog alerts.
String get alertDialogLabel;
/// Hour that is shown in [CupertinoCountdownTimerPicker] corresponding to
/// the given hour value.
///
......@@ -232,6 +235,9 @@ class DefaultCupertinoLocalizations implements CupertinoLocalizations {
@override
String get postMeridiemAbbreviation => 'PM';
@override
String get alertDialogLabel => 'Alert';
@override
String timerPickerHour(int hour) => hour.toString();
......
......@@ -10,6 +10,7 @@ import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import '../rendering/mock_canvas.dart';
import '../widgets/semantics_tester.dart';
void main() {
testWidgets('Alert dialog control test', (WidgetTester tester) async {
......@@ -63,6 +64,68 @@ void main() {
expect(widget.style.color.alpha, lessThan(255));
});
testWidgets('Has semantic annotations', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
await tester.pumpWidget(MaterialApp(home: const Material(
child: CupertinoAlertDialog(
title: Text('The Title'),
content: Text('Content'),
actions: <Widget>[
CupertinoDialogAction(child: Text('Cancel')),
CupertinoDialogAction(child: Text('OK')),
],
)
)));
expect(
semantics,
hasSemantics(
new TestSemantics.root(
children: <TestSemantics>[
new TestSemantics(
children: <TestSemantics>[
new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute],
children: <TestSemantics>[
new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.scopesRoute, SemanticsFlag.namesRoute],
label: 'Alert',
children: <TestSemantics>[
new TestSemantics(
children: <TestSemantics>[
new TestSemantics(label: 'The Title'),
new TestSemantics(label: 'Content'),
],
),
new TestSemantics(
children: <TestSemantics>[
new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton],
label: 'Cancel',
),
new TestSemantics(
flags: <SemanticsFlag>[SemanticsFlag.isButton],
label: 'OK',
),
],
),
],
),
],
),
],
),
],
),
ignoreId: true,
ignoreRect: true,
ignoreTransform: true,
)
);
semantics.dispose();
});
testWidgets('Dialog default action styles', (WidgetTester tester) async {
await tester.pumpWidget(boilerplate(const CupertinoDialogAction(
isDefaultAction: true,
......
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