Unverified Commit 74ea7132 authored by nbayati's avatar nbayati Committed by GitHub

Add optional flag to determine assertiveness level in aria announcement for flutter web (#107568)

parent e5055293
...@@ -8,6 +8,21 @@ import 'package:flutter/painting.dart'; ...@@ -8,6 +8,21 @@ import 'package:flutter/painting.dart';
export 'dart:ui' show TextDirection; export 'dart:ui' show TextDirection;
/// Determines the assertiveness level of the accessibility announcement.
///
/// It is used by [AnnounceSemanticsEvent] to determine the priority with which
/// assistive technology should treat announcements.
enum Assertiveness {
/// The assistive technology will speak changes whenever the user is idle.
polite,
/// The assistive technology will interrupt any announcement that it is
/// currently making to notify the user about the change.
///
/// It should only be used for time-sensitive/critical notifications.
assertive,
}
/// An event sent by the application to notify interested listeners that /// An event sent by the application to notify interested listeners that
/// something happened to the user interface (e.g. a view scrolled). /// something happened to the user interface (e.g. a view scrolled).
/// ///
...@@ -71,7 +86,7 @@ abstract class SemanticsEvent { ...@@ -71,7 +86,7 @@ abstract class SemanticsEvent {
class AnnounceSemanticsEvent extends SemanticsEvent { class AnnounceSemanticsEvent extends SemanticsEvent {
/// Constructs an event that triggers an announcement by the platform. /// Constructs an event that triggers an announcement by the platform.
const AnnounceSemanticsEvent(this.message, this.textDirection) const AnnounceSemanticsEvent(this.message, this.textDirection, {this.assertiveness = Assertiveness.polite})
: assert(message != null), : assert(message != null),
assert(textDirection != null), assert(textDirection != null),
super('announce'); super('announce');
...@@ -86,11 +101,20 @@ class AnnounceSemanticsEvent extends SemanticsEvent { ...@@ -86,11 +101,20 @@ class AnnounceSemanticsEvent extends SemanticsEvent {
/// This property must not be null. /// This property must not be null.
final TextDirection textDirection; final TextDirection textDirection;
/// Determines whether the announcement should interrupt any existing announcement,
/// or queue after it.
///
/// On the web this option uses the aria-live level to set the assertiveness
/// of the announcement. On iOS, Android, Windows, Linux, macOS, and Fuchsia
/// this option currently has no effect.
final Assertiveness assertiveness;
@override @override
Map<String, dynamic> getDataMap() { Map<String, dynamic> getDataMap() {
return <String, dynamic>{ return <String, dynamic>{
'message': message, 'message': message,
'textDirection': textDirection.index, 'textDirection': textDirection.index,
'assertiveness': assertiveness.index,
}; };
} }
} }
......
...@@ -6,7 +6,7 @@ import 'dart:ui' show TextDirection; ...@@ -6,7 +6,7 @@ import 'dart:ui' show TextDirection;
import 'package:flutter/services.dart' show SystemChannels; import 'package:flutter/services.dart' show SystemChannels;
import 'semantics_event.dart' show AnnounceSemanticsEvent, TooltipSemanticsEvent; import 'semantics_event.dart' show AnnounceSemanticsEvent, Assertiveness, TooltipSemanticsEvent;
export 'dart:ui' show TextDirection; export 'dart:ui' show TextDirection;
...@@ -29,8 +29,12 @@ class SemanticsService { ...@@ -29,8 +29,12 @@ class SemanticsService {
/// ///
/// For example a camera application can use this method to make accessibility /// For example a camera application can use this method to make accessibility
/// announcements regarding objects in the viewfinder. /// announcements regarding objects in the viewfinder.
static Future<void> announce(String message, TextDirection textDirection) async { ///
final AnnounceSemanticsEvent event = AnnounceSemanticsEvent(message, textDirection); /// The assertiveness level of the announcement is determined by [assertiveness].
/// Currently, this is only supported by the web engine and has no effect on
/// other platforms. The default mode is [Assertiveness.polite].
static Future<void> announce(String message, TextDirection textDirection, {Assertiveness assertiveness = Assertiveness.polite}) async {
final AnnounceSemanticsEvent event = AnnounceSemanticsEvent(message, textDirection, assertiveness: assertiveness);
await SystemChannels.accessibility.send(event.toMap()); await SystemChannels.accessibility.send(event.toMap());
} }
......
...@@ -20,11 +20,10 @@ void main() { ...@@ -20,11 +20,10 @@ void main() {
TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockDecodedMessageHandler<dynamic>(SystemChannels.accessibility, handleMessage); TestDefaultBinaryMessengerBinding.instance!.defaultBinaryMessenger.setMockDecodedMessageHandler<dynamic>(SystemChannels.accessibility, handleMessage);
await SemanticsService.announce('announcement 1', TextDirection.ltr); await SemanticsService.announce('announcement 1', TextDirection.ltr);
await SemanticsService.announce('announcement 2', TextDirection.rtl); await SemanticsService.announce('announcement 2', TextDirection.rtl, assertiveness: Assertiveness.assertive);
expect(log, equals(<Map<String, dynamic>>[ expect(log, equals(<Map<String, dynamic>>[
<String, dynamic>{'type': 'announce', 'data': <String, dynamic>{'message': 'announcement 1', 'textDirection': 1}}, <String, dynamic>{'type': 'announce', 'data': <String, dynamic>{'message': 'announcement 1', 'textDirection': 1, 'assertiveness': 0}},
<String, dynamic>{'type': 'announce', 'data': <String, dynamic>{'message': 'announcement 2', 'textDirection': 0}}, <String, dynamic>{'type': 'announce', 'data': <String, dynamic>{'message': 'announcement 2', 'textDirection': 0, 'assertiveness': 1}},
])); ]));
}); });
} }
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