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