1 | // Copyright 2014 The Flutter Authors. All rights reserved. |
---|---|
2 | // Use of this source code is governed by a BSD-style license that can be |
3 | // found in the LICENSE file. |
4 | |
5 | /// @docImport 'package:flutter/services.dart'; |
6 | /// @docImport 'package:flutter/widgets.dart'; |
7 | library; |
8 | |
9 | import 'package:flutter/foundation.dart'; |
10 | import 'package:flutter/painting.dart'; |
11 | |
12 | export 'dart:ui' show TextDirection; |
13 | |
14 | /// Determines the assertiveness level of the accessibility announcement. |
15 | /// |
16 | /// It is used by [AnnounceSemanticsEvent] to determine the priority with which |
17 | /// assistive technology should treat announcements. |
18 | enum Assertiveness { |
19 | /// The assistive technology will speak changes whenever the user is idle. |
20 | polite, |
21 | |
22 | /// The assistive technology will interrupt any announcement that it is |
23 | /// currently making to notify the user about the change. |
24 | /// |
25 | /// It should only be used for time-sensitive/critical notifications. |
26 | assertive, |
27 | } |
28 | |
29 | /// An event sent by the application to notify interested listeners that |
30 | /// something happened to the user interface (e.g. a view scrolled). |
31 | /// |
32 | /// These events are usually interpreted by assistive technologies to give the |
33 | /// user additional clues about the current state of the UI. |
34 | abstract class SemanticsEvent { |
35 | /// Initializes internal fields. |
36 | /// |
37 | /// [type] is a string that identifies this class of [SemanticsEvent]s. |
38 | const SemanticsEvent(this.type); |
39 | |
40 | /// The type of this event. |
41 | /// |
42 | /// The type is used by the engine to translate this event into the |
43 | /// appropriate native event (`UIAccessibility*Notification` on iOS and |
44 | /// `AccessibilityEvent` on Android). |
45 | final String type; |
46 | |
47 | /// Converts this event to a Map that can be encoded with |
48 | /// [StandardMessageCodec]. |
49 | /// |
50 | /// [nodeId] is the unique identifier of the semantics node associated with |
51 | /// the event, or null if the event is not associated with a semantics node. |
52 | Map<String, dynamic> toMap({int? nodeId}) { |
53 | final Map<String, dynamic> event = <String, dynamic>{'type': type, 'data': getDataMap()}; |
54 | if (nodeId != null) { |
55 | event['nodeId'] = nodeId; |
56 | } |
57 | |
58 | return event; |
59 | } |
60 | |
61 | /// Returns the event's data object. |
62 | Map<String, dynamic> getDataMap(); |
63 | |
64 | @override |
65 | String toString() { |
66 | final List<String> pairs = <String>[]; |
67 | final Map<String, dynamic> dataMap = getDataMap(); |
68 | final List<String> sortedKeys = dataMap.keys.toList()..sort(); |
69 | for (final String key in sortedKeys) { |
70 | pairs.add('$key :${dataMap[key]} '); |
71 | } |
72 | return '${objectRuntimeType(this, 'SemanticsEvent')} (${pairs.join( ', ')} )'; |
73 | } |
74 | } |
75 | |
76 | /// An event for a semantic announcement. |
77 | /// |
78 | /// This should be used for announcement that are not seamlessly announced by |
79 | /// the system as a result of a UI state change. |
80 | /// |
81 | /// For example a camera application can use this method to make accessibility |
82 | /// announcements regarding objects in the viewfinder. |
83 | /// |
84 | /// When possible, prefer using mechanisms like [Semantics] to implicitly |
85 | /// trigger announcements over using this event. |
86 | /// |
87 | /// ### Android |
88 | /// Android has [deprecated announcement events][1] due to its disruptive |
89 | /// behavior with TalkBack forcing it to clear its speech queue and speak the |
90 | /// provided text. Instead, use mechanisms like [Semantics] to implicitly |
91 | /// trigger announcements. |
92 | /// |
93 | /// [1]: https://developer.android.com/reference/android/view/View#announceForAccessibility(java.lang.CharSequence) |
94 | /// |
95 | class AnnounceSemanticsEvent extends SemanticsEvent { |
96 | /// Constructs an event that triggers an announcement by the platform. |
97 | const AnnounceSemanticsEvent( |
98 | this.message, |
99 | this.textDirection, { |
100 | this.assertiveness = Assertiveness.polite, |
101 | }) : super('announce'); |
102 | |
103 | /// The message to announce. |
104 | final String message; |
105 | |
106 | /// Text direction for [message]. |
107 | final TextDirection textDirection; |
108 | |
109 | /// Determines whether the announcement should interrupt any existing announcement, |
110 | /// or queue after it. |
111 | /// |
112 | /// On the web this option uses the aria-live level to set the assertiveness |
113 | /// of the announcement. On iOS, Android, Windows, Linux, macOS, and Fuchsia |
114 | /// this option currently has no effect. |
115 | final Assertiveness assertiveness; |
116 | |
117 | @override |
118 | Map<String, dynamic> getDataMap() { |
119 | return <String, dynamic>{ |
120 | 'message': message, |
121 | 'textDirection': textDirection.index, |
122 | if (assertiveness != Assertiveness.polite) 'assertiveness': assertiveness.index, |
123 | }; |
124 | } |
125 | } |
126 | |
127 | /// An event for a semantic announcement of a tooltip. |
128 | /// |
129 | /// This is only used by Android to announce tooltip values. |
130 | class TooltipSemanticsEvent extends SemanticsEvent { |
131 | /// Constructs an event that triggers a tooltip announcement by the platform. |
132 | const TooltipSemanticsEvent(this.message) : super('tooltip'); |
133 | |
134 | /// The text content of the tooltip. |
135 | final String message; |
136 | |
137 | @override |
138 | Map<String, dynamic> getDataMap() { |
139 | return <String, dynamic>{'message': message}; |
140 | } |
141 | } |
142 | |
143 | /// An event which triggers long press semantic feedback. |
144 | /// |
145 | /// Currently only honored on Android. Triggers a long-press specific sound |
146 | /// when TalkBack is enabled. |
147 | class LongPressSemanticsEvent extends SemanticsEvent { |
148 | /// Constructs an event that triggers a long-press semantic feedback by the platform. |
149 | const LongPressSemanticsEvent() : super('longPress'); |
150 | |
151 | @override |
152 | Map<String, dynamic> getDataMap() => const <String, dynamic>{}; |
153 | } |
154 | |
155 | /// An event which triggers tap semantic feedback. |
156 | /// |
157 | /// Currently only honored on Android. Triggers a tap specific sound when |
158 | /// TalkBack is enabled. |
159 | class TapSemanticEvent extends SemanticsEvent { |
160 | /// Constructs an event that triggers a long-press semantic feedback by the platform. |
161 | const TapSemanticEvent() : super('tap'); |
162 | |
163 | @override |
164 | Map<String, dynamic> getDataMap() => const <String, dynamic>{}; |
165 | } |
166 | |
167 | /// An event to move the accessibility focus. |
168 | /// |
169 | /// Using this API is generally not recommended, as it may break a users' expectation of |
170 | /// how a11y focus works and therefore should be used very carefully. |
171 | /// |
172 | /// One possible use case: |
173 | /// For example, the currently focused rendering object is replaced by another rendering |
174 | /// object. In general, such design should be avoided if possible. If not, one may want |
175 | /// to refocus the newly added rendering object. |
176 | /// |
177 | /// One example that is not recommended: |
178 | /// When a new popup or dropdown opens, moving the focus in these cases may confuse users |
179 | /// and make it less accessible. |
180 | /// |
181 | /// {@tool snippet} |
182 | /// |
183 | /// The following code snippet shows how one can request focus on a |
184 | /// certain widget. |
185 | /// |
186 | /// ```dart |
187 | /// class MyWidget extends StatefulWidget { |
188 | /// const MyWidget({super.key}); |
189 | /// |
190 | /// @override |
191 | /// State<MyWidget> createState() => _MyWidgetState(); |
192 | /// } |
193 | /// |
194 | /// class _MyWidgetState extends State<MyWidget> { |
195 | /// final GlobalKey mykey = GlobalKey(); |
196 | /// |
197 | /// @override |
198 | /// void initState() { |
199 | /// super.initState(); |
200 | /// // Using addPostFrameCallback because changing focus need to wait for the widget to finish rendering. |
201 | /// WidgetsBinding.instance.addPostFrameCallback((_) { |
202 | /// mykey.currentContext?.findRenderObject()?.sendSemanticsEvent(const FocusSemanticEvent()); |
203 | /// }); |
204 | /// } |
205 | /// |
206 | /// @override |
207 | /// Widget build(BuildContext context) { |
208 | /// return Scaffold( |
209 | /// appBar: AppBar( |
210 | /// title: const Text('example'), |
211 | /// ), |
212 | /// body: Column( |
213 | /// children: <Widget>[ |
214 | /// const Text('Hello World'), |
215 | /// const SizedBox(height: 50), |
216 | /// Text('set focus here', key: mykey), |
217 | /// ], |
218 | /// ), |
219 | /// ); |
220 | /// } |
221 | /// } |
222 | /// ``` |
223 | /// {@end-tool} |
224 | /// |
225 | /// This currently only supports Android and iOS. |
226 | class FocusSemanticEvent extends SemanticsEvent { |
227 | /// Constructs an event that triggers a focus change by the platform. |
228 | const FocusSemanticEvent() : super('focus'); |
229 | |
230 | @override |
231 | Map<String, dynamic> getDataMap() => const <String, dynamic>{}; |
232 | } |
233 |
Definitions
- Assertiveness
- SemanticsEvent
- SemanticsEvent
- toMap
- getDataMap
- toString
- AnnounceSemanticsEvent
- AnnounceSemanticsEvent
- getDataMap
- TooltipSemanticsEvent
- TooltipSemanticsEvent
- getDataMap
- LongPressSemanticsEvent
- LongPressSemanticsEvent
- getDataMap
- TapSemanticEvent
- TapSemanticEvent
- getDataMap
- FocusSemanticEvent
- FocusSemanticEvent
Learn more about Flutter for embedded and desktop on industrialflutter.com