Reverts "Use live region in error text input decorator for Android (#165531)" (#168848)

<!-- start_original_pr_link -->
Reverts: flutter/flutter#165531
<!-- end_original_pr_link -->
<!-- start_initiating_author -->
Initiated by: matanlurey
<!-- end_initiating_author -->
<!-- start_revert_reason -->
Reason for reverting: Breaks google client tests
<!-- end_revert_reason -->
<!-- start_original_pr_author -->
Original PR Author: ash2moon
<!-- end_original_pr_author -->

<!-- start_reviewers -->
Reviewed By: {chunhtai, reidbaker, hannah-hyj}
<!-- end_reviewers -->

<!-- start_revert_body -->
This change reverts the following previous change:
Resolves partly https://github.com/flutter/flutter/issues/165510


**Context:** This issue originates from
https://github.com/flutter/flutter/issues/99715, where it was reported
that `liveRegion` alone was insufficient for announcing form validation
errors. While `liveRegion` announces the first error encountered,
subsequent submissions with the same error message on Android would not
trigger a re-announcement.

**Original Solution:** Pull request
https://github.com/flutter/flutter/pull/123373 addressed this by
implementing the `announce` event to ensure error messages were
consistently announced, even for repeated submissions.

**Native Android Behavior (Jetpack Compose):** In native Android
development using Jetpack Compose, setting the `isError` property of a
`TextField` to `true` triggers Talkback to announce "Error invalid
input." This announcement occurs *only* on the initial change to the
error state. Subsequent errors, even if the `isError` property remains
`true`, are not re-announced. This behavior closely mirrors the
functionality of `liveRegion`, with the key difference being that
`liveRegion` also announces the specific error text, in addition to the
general error state. Testing in a native Jetpack Compose application
confirms this behavior and provides a valuable comparison point against
the current Flutter form example.

**Suggested Action:** **Fork** the behavior in
https://github.com/flutter/flutter/pull/123373. Reinstate the use of
`liveRegion` for error announcements within `widgets/Form` for Android
and keep other platforms the same.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] 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

<!-- end_revert_body -->

Co-authored-by: auto-submit[bot] <flutter-engprod-team@google.com>
This commit is contained in:
auto-submit[bot] 2025-05-14 17:47:38 +00:00 committed by GitHub
parent 494b08b420
commit 606bb06c0f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 135 additions and 371 deletions

View File

@ -931,7 +931,6 @@ class AccessibilityFeatures {
static const int _kReduceMotionIndex = 1 << 4; static const int _kReduceMotionIndex = 1 << 4;
static const int _kHighContrastIndex = 1 << 5; static const int _kHighContrastIndex = 1 << 5;
static const int _kOnOffSwitchLabelsIndex = 1 << 6; static const int _kOnOffSwitchLabelsIndex = 1 << 6;
static const int _kNoAnnounceIndex = 1 << 7;
// A bitfield which represents each enabled feature. // A bitfield which represents each enabled feature.
final int _index; final int _index;
@ -969,20 +968,6 @@ class AccessibilityFeatures {
/// Only supported on iOS. /// Only supported on iOS.
bool get onOffSwitchLabels => _kOnOffSwitchLabelsIndex & _index != 0; bool get onOffSwitchLabels => _kOnOffSwitchLabelsIndex & _index != 0;
/// Whether accessibility announcements (like [SemanticsService.announce])
/// are allowed on the current platform.
///
/// Returns `false` on Android, where platform announcements are deprecated
/// by the underlying platform.
///
/// Returns `true` on all other platforms (iOS, web, desktop) where such
/// announcements are generally supported without discouragement.
///
/// Use this flag to conditionally avoid making announcements on Android.
// This is an inverted check on _index since there are many more platforms
// that support announce whereas don't.
bool get announce => _kNoAnnounceIndex & _index == 0;
@override @override
String toString() { String toString() {
final List<String> features = <String>[]; final List<String> features = <String>[];
@ -1007,9 +992,6 @@ class AccessibilityFeatures {
if (onOffSwitchLabels) { if (onOffSwitchLabels) {
features.add('onOffSwitchLabels'); features.add('onOffSwitchLabels');
} }
if (announce) {
features.add('announce');
}
return 'AccessibilityFeatures$features'; return 'AccessibilityFeatures$features';
} }

View File

@ -48,7 +48,6 @@ enum class AccessibilityFeatureFlag : int32_t {
kReduceMotion = 1 << 4, kReduceMotion = 1 << 4,
kHighContrast = 1 << 5, kHighContrast = 1 << 5,
kOnOffSwitchLabels = 1 << 6, kOnOffSwitchLabels = 1 << 6,
kNoAnnounce = 1 << 7,
}; };
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------

View File

@ -54,7 +54,6 @@ class EngineAccessibilityFeatures implements ui.AccessibilityFeatures {
static const int _kReduceMotionIndex = 1 << 4; static const int _kReduceMotionIndex = 1 << 4;
static const int _kHighContrastIndex = 1 << 5; static const int _kHighContrastIndex = 1 << 5;
static const int _kOnOffSwitchLabelsIndex = 1 << 6; static const int _kOnOffSwitchLabelsIndex = 1 << 6;
static const int _kNoAnnounceIndex = 1 << 7;
// A bitfield which represents each enabled feature. // A bitfield which represents each enabled feature.
final int _index; final int _index;
@ -73,10 +72,6 @@ class EngineAccessibilityFeatures implements ui.AccessibilityFeatures {
bool get highContrast => _kHighContrastIndex & _index != 0; bool get highContrast => _kHighContrastIndex & _index != 0;
@override @override
bool get onOffSwitchLabels => _kOnOffSwitchLabelsIndex & _index != 0; bool get onOffSwitchLabels => _kOnOffSwitchLabelsIndex & _index != 0;
// This is an inverted check on _index since there are many more platforms
// that support announce whereas don't.
@override
bool get announce => _kNoAnnounceIndex & _index == 0;
@override @override
String toString() { String toString() {
@ -102,9 +97,6 @@ class EngineAccessibilityFeatures implements ui.AccessibilityFeatures {
if (onOffSwitchLabels) { if (onOffSwitchLabels) {
features.add('onOffSwitchLabels'); features.add('onOffSwitchLabels');
} }
if (announce) {
features.add('announce');
}
return 'AccessibilityFeatures$features'; return 'AccessibilityFeatures$features';
} }
@ -127,7 +119,6 @@ class EngineAccessibilityFeatures implements ui.AccessibilityFeatures {
bool? reduceMotion, bool? reduceMotion,
bool? highContrast, bool? highContrast,
bool? onOffSwitchLabels, bool? onOffSwitchLabels,
bool? announce,
}) { }) {
final EngineAccessibilityFeaturesBuilder builder = EngineAccessibilityFeaturesBuilder(0); final EngineAccessibilityFeaturesBuilder builder = EngineAccessibilityFeaturesBuilder(0);
@ -138,7 +129,6 @@ class EngineAccessibilityFeatures implements ui.AccessibilityFeatures {
builder.reduceMotion = reduceMotion ?? this.reduceMotion; builder.reduceMotion = reduceMotion ?? this.reduceMotion;
builder.highContrast = highContrast ?? this.highContrast; builder.highContrast = highContrast ?? this.highContrast;
builder.onOffSwitchLabels = onOffSwitchLabels ?? this.onOffSwitchLabels; builder.onOffSwitchLabels = onOffSwitchLabels ?? this.onOffSwitchLabels;
builder.announce = announce ?? this.announce;
return builder.build(); return builder.build();
} }
@ -156,9 +146,6 @@ class EngineAccessibilityFeaturesBuilder {
bool get reduceMotion => EngineAccessibilityFeatures._kReduceMotionIndex & _index != 0; bool get reduceMotion => EngineAccessibilityFeatures._kReduceMotionIndex & _index != 0;
bool get highContrast => EngineAccessibilityFeatures._kHighContrastIndex & _index != 0; bool get highContrast => EngineAccessibilityFeatures._kHighContrastIndex & _index != 0;
bool get onOffSwitchLabels => EngineAccessibilityFeatures._kOnOffSwitchLabelsIndex & _index != 0; bool get onOffSwitchLabels => EngineAccessibilityFeatures._kOnOffSwitchLabelsIndex & _index != 0;
// This is an inverted check on _index since there are many more platforms
// that support announce whereas don't.
bool get announce => EngineAccessibilityFeatures._kNoAnnounceIndex & _index == 0;
set accessibleNavigation(bool value) { set accessibleNavigation(bool value) {
const int accessibleNavigation = EngineAccessibilityFeatures._kAccessibleNavigation; const int accessibleNavigation = EngineAccessibilityFeatures._kAccessibleNavigation;
@ -195,12 +182,6 @@ class EngineAccessibilityFeaturesBuilder {
_index = value ? _index | onOffSwitchLabels : _index & ~onOffSwitchLabels; _index = value ? _index | onOffSwitchLabels : _index & ~onOffSwitchLabels;
} }
set announce(bool value) {
const int noAnnounce = EngineAccessibilityFeatures._kNoAnnounceIndex;
// Since we are using noAnnounce for the embedder, we need to flip the value.
_index = !value ? _index | noAnnounce : _index & ~noAnnounce;
}
/// Creates and returns an instance of EngineAccessibilityFeatures based on the value of _index /// Creates and returns an instance of EngineAccessibilityFeatures based on the value of _index
EngineAccessibilityFeatures build() { EngineAccessibilityFeatures build() {
return EngineAccessibilityFeatures(_index); return EngineAccessibilityFeatures(_index);

View File

@ -115,7 +115,6 @@ abstract class AccessibilityFeatures {
bool get reduceMotion; bool get reduceMotion;
bool get highContrast; bool get highContrast;
bool get onOffSwitchLabels; bool get onOffSwitchLabels;
bool get announce;
} }
enum Brightness { dark, light } enum Brightness { dark, light }

View File

@ -299,14 +299,6 @@ void _testEngineAccessibilityBuilder() {
expect(features.onOffSwitchLabels, isTrue); expect(features.onOffSwitchLabels, isTrue);
}); });
test('announce', () {
// By default this starts off true, see EngineAccessibilityFeatures.announce
expect(features.announce, isTrue);
builder.announce = false;
features = builder.build();
expect(features.announce, isFalse);
});
test('reduce motion', () { test('reduce motion', () {
expect(features.reduceMotion, isFalse); expect(features.reduceMotion, isFalse);
builder.reduceMotion = true; builder.reduceMotion = true;
@ -399,10 +391,7 @@ void _testEngineSemanticsOwner() {
}); });
test('accessibilityFeatures copyWith function works', () { test('accessibilityFeatures copyWith function works', () {
// Announce is an inverted check, see EngineAccessibilityFeatures.announce. const EngineAccessibilityFeatures original = EngineAccessibilityFeatures(0);
// Therefore, we need to ensure that the original copy starts with false (1 << 7).
const EngineAccessibilityFeatures original = EngineAccessibilityFeatures(0 | 1 << 7);
EngineAccessibilityFeatures copy = original.copyWith(accessibleNavigation: true); EngineAccessibilityFeatures copy = original.copyWith(accessibleNavigation: true);
expect(copy.accessibleNavigation, true); expect(copy.accessibleNavigation, true);
expect(copy.boldText, false); expect(copy.boldText, false);
@ -410,7 +399,6 @@ void _testEngineSemanticsOwner() {
expect(copy.highContrast, false); expect(copy.highContrast, false);
expect(copy.invertColors, false); expect(copy.invertColors, false);
expect(copy.onOffSwitchLabels, false); expect(copy.onOffSwitchLabels, false);
expect(copy.announce, false);
expect(copy.reduceMotion, false); expect(copy.reduceMotion, false);
copy = original.copyWith(boldText: true); copy = original.copyWith(boldText: true);
@ -429,7 +417,6 @@ void _testEngineSemanticsOwner() {
expect(copy.highContrast, false); expect(copy.highContrast, false);
expect(copy.invertColors, false); expect(copy.invertColors, false);
expect(copy.onOffSwitchLabels, false); expect(copy.onOffSwitchLabels, false);
expect(copy.announce, false);
expect(copy.reduceMotion, false); expect(copy.reduceMotion, false);
copy = original.copyWith(highContrast: true); copy = original.copyWith(highContrast: true);
@ -439,7 +426,6 @@ void _testEngineSemanticsOwner() {
expect(copy.highContrast, true); expect(copy.highContrast, true);
expect(copy.invertColors, false); expect(copy.invertColors, false);
expect(copy.onOffSwitchLabels, false); expect(copy.onOffSwitchLabels, false);
expect(copy.announce, false);
expect(copy.reduceMotion, false); expect(copy.reduceMotion, false);
copy = original.copyWith(invertColors: true); copy = original.copyWith(invertColors: true);
@ -449,7 +435,6 @@ void _testEngineSemanticsOwner() {
expect(copy.highContrast, false); expect(copy.highContrast, false);
expect(copy.invertColors, true); expect(copy.invertColors, true);
expect(copy.onOffSwitchLabels, false); expect(copy.onOffSwitchLabels, false);
expect(copy.announce, false);
expect(copy.reduceMotion, false); expect(copy.reduceMotion, false);
copy = original.copyWith(onOffSwitchLabels: true); copy = original.copyWith(onOffSwitchLabels: true);
@ -461,16 +446,6 @@ void _testEngineSemanticsOwner() {
expect(copy.onOffSwitchLabels, true); expect(copy.onOffSwitchLabels, true);
expect(copy.reduceMotion, false); expect(copy.reduceMotion, false);
copy = original.copyWith(announce: true);
expect(copy.accessibleNavigation, false);
expect(copy.boldText, false);
expect(copy.disableAnimations, false);
expect(copy.highContrast, false);
expect(copy.invertColors, false);
expect(copy.onOffSwitchLabels, false);
expect(copy.announce, true);
expect(copy.reduceMotion, false);
copy = original.copyWith(reduceMotion: true); copy = original.copyWith(reduceMotion: true);
expect(copy.accessibleNavigation, false); expect(copy.accessibleNavigation, false);
expect(copy.boldText, false); expect(copy.boldText, false);
@ -478,7 +453,6 @@ void _testEngineSemanticsOwner() {
expect(copy.highContrast, false); expect(copy.highContrast, false);
expect(copy.invertColors, false); expect(copy.invertColors, false);
expect(copy.onOffSwitchLabels, false); expect(copy.onOffSwitchLabels, false);
expect(copy.announce, false);
expect(copy.reduceMotion, true); expect(copy.reduceMotion, true);
}); });

View File

@ -490,7 +490,6 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
this.accessibilityManager.addTouchExplorationStateChangeListener( this.accessibilityManager.addTouchExplorationStateChangeListener(
touchExplorationStateChangeListener); touchExplorationStateChangeListener);
accessibilityFeatureFlags |= AccessibilityFeature.NO_ANNOUNCE.value;
// Tell Flutter whether animations should initially be enabled or disabled. Then register a // Tell Flutter whether animations should initially be enabled or disabled. Then register a
// listener to be notified of changes in the future. // listener to be notified of changes in the future.
animationScaleObserver.onChange(false); animationScaleObserver.onChange(false);
@ -2171,8 +2170,7 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
BOLD_TEXT(1 << 3), // NOT SUPPORTED BOLD_TEXT(1 << 3), // NOT SUPPORTED
REDUCE_MOTION(1 << 4), // NOT SUPPORTED REDUCE_MOTION(1 << 4), // NOT SUPPORTED
HIGH_CONTRAST(1 << 5), // NOT SUPPORTED HIGH_CONTRAST(1 << 5), // NOT SUPPORTED
ON_OFF_SWITCH_LABELS(1 << 6), // NOT SUPPORTED ON_OFF_SWITCH_LABELS(1 << 6); // NOT SUPPORTED
NO_ANNOUNCE(1 << 7);
final int value; final int value;

View File

@ -66,11 +66,6 @@ import org.robolectric.annotation.Config;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class AccessibilityBridgeTest { public class AccessibilityBridgeTest {
private static final int ACCESSIBILITY_FEATURE_NAVIGATION = 1 << 0;
private static final int ACCESSIBILITY_FEATURE_DISABLE_ANIMATIONS = 1 << 2;
private static final int ACCESSIBILITY_FEATURE_BOLD_TEXT = 1 << 3;
private static final int ACCESSIBILITY_FEATURE_NO_ANNOUNCE = 1 << 7;
@Test @Test
public void itDescribesNonTextFieldsWithAContentDescription() { public void itDescribesNonTextFieldsWithAContentDescription() {
AccessibilityBridge accessibilityBridge = setUpBridge(); AccessibilityBridge accessibilityBridge = setUpBridge();
@ -140,26 +135,6 @@ public class AccessibilityBridgeTest {
assertEquals(position, outBoundsInScreen.top); assertEquals(position, outBoundsInScreen.top);
} }
@Test
public void itSetsNoAnnounceAccessibleFlagByDefault() {
AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
AccessibilityManager mockManager = mock(AccessibilityManager.class);
View mockRootView = mock(View.class);
Context context = mock(Context.class);
when(mockRootView.getContext()).thenReturn(context);
when(context.getPackageName()).thenReturn("test");
when(mockManager.isTouchExplorationEnabled()).thenReturn(false);
setUpBridge(
/*rootAccessibilityView=*/ mockRootView,
/*accessibilityChannel=*/ mockChannel,
/*accessibilityManager=*/ mockManager,
/*contentResolver=*/ null,
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
/*platformViewsAccessibilityDelegate=*/ null);
verify(mockChannel).setAccessibilityFeatures(ACCESSIBILITY_FEATURE_NO_ANNOUNCE);
}
@Test @Test
public void itSetsAccessibleNavigation() { public void itSetsAccessibleNavigation() {
AccessibilityChannel mockChannel = mock(AccessibilityChannel.class); AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
@ -183,20 +158,18 @@ public class AccessibilityBridgeTest {
verify(mockManager).addTouchExplorationStateChangeListener(listenerCaptor.capture()); verify(mockManager).addTouchExplorationStateChangeListener(listenerCaptor.capture());
assertEquals(accessibilityBridge.getAccessibleNavigation(), false); assertEquals(accessibilityBridge.getAccessibleNavigation(), false);
verify(mockChannel).setAccessibilityFeatures(ACCESSIBILITY_FEATURE_NO_ANNOUNCE); verify(mockChannel).setAccessibilityFeatures(0);
reset(mockChannel); reset(mockChannel);
// Simulate assistive technology accessing accessibility tree. // Simulate assistive technology accessing accessibility tree.
accessibilityBridge.createAccessibilityNodeInfo(0); accessibilityBridge.createAccessibilityNodeInfo(0);
verify(mockChannel) verify(mockChannel).setAccessibilityFeatures(1);
.setAccessibilityFeatures(
ACCESSIBILITY_FEATURE_NAVIGATION | ACCESSIBILITY_FEATURE_NO_ANNOUNCE);
assertEquals(accessibilityBridge.getAccessibleNavigation(), true); assertEquals(accessibilityBridge.getAccessibleNavigation(), true);
// Simulate turning off TalkBack. // Simulate turning off TalkBack.
reset(mockChannel); reset(mockChannel);
listenerCaptor.getValue().onTouchExplorationStateChanged(false); listenerCaptor.getValue().onTouchExplorationStateChanged(false);
verify(mockChannel).setAccessibilityFeatures(ACCESSIBILITY_FEATURE_NO_ANNOUNCE); verify(mockChannel).setAccessibilityFeatures(0);
assertEquals(accessibilityBridge.getAccessibleNavigation(), false); assertEquals(accessibilityBridge.getAccessibleNavigation(), false);
} }
@ -1184,9 +1157,7 @@ public class AccessibilityBridgeTest {
/*accessibilityViewEmbedder=*/ mockViewEmbedder, /*accessibilityViewEmbedder=*/ mockViewEmbedder,
/*platformViewsAccessibilityDelegate=*/ null); /*platformViewsAccessibilityDelegate=*/ null);
verify(mockChannel) verify(mockChannel).setAccessibilityFeatures(1 << 3);
.setAccessibilityFeatures(
ACCESSIBILITY_FEATURE_BOLD_TEXT | ACCESSIBILITY_FEATURE_NO_ANNOUNCE);
reset(mockChannel); reset(mockChannel);
// Now verify that clearing the BOLD_TEXT flag doesn't touch any of the other flags. // Now verify that clearing the BOLD_TEXT flag doesn't touch any of the other flags.
@ -1208,9 +1179,7 @@ public class AccessibilityBridgeTest {
// constructor, verify that the latest argument is correct // constructor, verify that the latest argument is correct
ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class); ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
verify(mockChannel, atLeastOnce()).setAccessibilityFeatures(captor.capture()); verify(mockChannel, atLeastOnce()).setAccessibilityFeatures(captor.capture());
assertEquals( assertEquals(1 << 2 /* DISABLE_ANIMATION */, captor.getValue().intValue());
ACCESSIBILITY_FEATURE_DISABLE_ANIMATIONS | ACCESSIBILITY_FEATURE_NO_ANNOUNCE,
captor.getValue().intValue());
// Set back to default // Set back to default
Settings.Global.putFloat(null, "transition_animation_scale", 1.0f); Settings.Global.putFloat(null, "transition_animation_scale", 1.0f);
@ -1904,21 +1873,19 @@ public class AccessibilityBridgeTest {
ContentObserver observer = observerCaptor.getValue(); ContentObserver observer = observerCaptor.getValue();
// Initial state // Initial state
verify(mockChannel).setAccessibilityFeatures(ACCESSIBILITY_FEATURE_NO_ANNOUNCE); verify(mockChannel).setAccessibilityFeatures(0);
reset(mockChannel); reset(mockChannel);
// Animations are disabled // Animations are disabled
Settings.Global.putFloat(mockContentResolver, "transition_animation_scale", 0.0f); Settings.Global.putFloat(mockContentResolver, "transition_animation_scale", 0.0f);
observer.onChange(false); observer.onChange(false);
verify(mockChannel) verify(mockChannel).setAccessibilityFeatures(1 << 2);
.setAccessibilityFeatures(
ACCESSIBILITY_FEATURE_DISABLE_ANIMATIONS | ACCESSIBILITY_FEATURE_NO_ANNOUNCE);
reset(mockChannel); reset(mockChannel);
// Animations are enabled // Animations are enabled
Settings.Global.putFloat(mockContentResolver, "transition_animation_scale", 1.0f); Settings.Global.putFloat(mockContentResolver, "transition_animation_scale", 1.0f);
observer.onChange(false); observer.onChange(false);
verify(mockChannel).setAccessibilityFeatures(ACCESSIBILITY_FEATURE_NO_ANNOUNCE); verify(mockChannel).setAccessibilityFeatures(0);
} }
@Test @Test

View File

@ -105,8 +105,6 @@ typedef enum {
kFlutterAccessibilityFeatureHighContrast = 1 << 5, kFlutterAccessibilityFeatureHighContrast = 1 << 5,
/// Request to show on/off labels inside switches. /// Request to show on/off labels inside switches.
kFlutterAccessibilityFeatureOnOffSwitchLabels = 1 << 6, kFlutterAccessibilityFeatureOnOffSwitchLabels = 1 << 6,
/// Indicate the platform does not support announcements.
kFlutterAccessibilityFeatureNoAnnounce = 1 << 7,
} FlutterAccessibilityFeature; } FlutterAccessibilityFeature;
/// The set of possible actions that can be conveyed to a semantics node. /// The set of possible actions that can be conveyed to a semantics node.

View File

@ -322,7 +322,6 @@ class _HelperErrorState extends State<_HelperError> with SingleTickerProviderSta
void initState() { void initState() {
super.initState(); super.initState();
_controller = AnimationController(duration: _kTransitionDuration, vsync: this); _controller = AnimationController(duration: _kTransitionDuration, vsync: this);
// TODO(ash2moon): https://github.com/flutter/flutter/issues/168022
if (_hasError) { if (_hasError) {
_error = _buildError(); _error = _buildError();
_controller.value = 1.0; _controller.value = 1.0;
@ -400,31 +399,26 @@ class _HelperErrorState extends State<_HelperError> with SingleTickerProviderSta
Widget _buildError() { Widget _buildError() {
assert(widget.error != null || widget.errorText != null); assert(widget.error != null || widget.errorText != null);
return Builder( return Semantics(
builder: (BuildContext context) { container: true,
return Semantics( child: FadeTransition(
container: true, opacity: _controller,
liveRegion: !MediaQuery.announceOf(context), child: FractionalTranslation(
child: FadeTransition( translation: Tween<Offset>(
opacity: _controller, begin: const Offset(0.0, -0.25),
child: FractionalTranslation( end: Offset.zero,
translation: Tween<Offset>( ).evaluate(_controller.view),
begin: const Offset(0.0, -0.25), child:
end: Offset.zero, widget.error ??
).evaluate(_controller.view), Text(
child: widget.errorText!,
widget.error ?? style: widget.errorStyle,
Text( textAlign: widget.textAlign,
widget.errorText!, overflow: TextOverflow.ellipsis,
style: widget.errorStyle, maxLines: widget.errorMaxLines,
textAlign: widget.textAlign, ),
overflow: TextOverflow.ellipsis, ),
maxLines: widget.errorMaxLines, ),
),
),
),
);
},
); );
} }
@ -3933,7 +3927,6 @@ class InputDecoration {
bool? alignLabelWithHint, bool? alignLabelWithHint,
BoxConstraints? constraints, BoxConstraints? constraints,
VisualDensity? visualDensity, VisualDensity? visualDensity,
SemanticsService? semanticsService,
}) { }) {
return InputDecoration( return InputDecoration(
icon: icon ?? this.icon, icon: icon ?? this.icon,

View File

@ -7,6 +7,7 @@ library;
import 'dart:ui' show TextDirection; import 'dart:ui' show TextDirection;
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart' show SystemChannels; import 'package:flutter/services.dart' show SystemChannels;
import 'semantics_event.dart' show AnnounceSemanticsEvent, Assertiveness, TooltipSemanticsEvent; import 'semantics_event.dart' show AnnounceSemanticsEvent, Assertiveness, TooltipSemanticsEvent;
@ -33,8 +34,8 @@ abstract final class SemanticsService {
/// Currently, this is only supported by the web engine and has no effect on /// Currently, this is only supported by the web engine and has no effect on
/// other platforms. The default mode is [Assertiveness.polite]. /// other platforms. The default mode is [Assertiveness.polite].
/// ///
/// Not all platforms support announcements. Check to see if it is supported using /// Not all platforms support announcements. Check to see if
/// [MediaQuery.announceOf] before calling this method. /// [isAnnounceSupported] before calling this method.
/// ///
/// ### Android /// ### Android
/// Android has [deprecated announcement events][1] due to its disruptive /// Android has [deprecated announcement events][1] due to its disruptive
@ -65,4 +66,12 @@ abstract final class SemanticsService {
final TooltipSemanticsEvent event = TooltipSemanticsEvent(message); final TooltipSemanticsEvent event = TooltipSemanticsEvent(message);
await SystemChannels.accessibility.send(event.toMap()); await SystemChannels.accessibility.send(event.toMap());
} }
/// Checks if announce is supported on the given platform.
///
/// On Android the announce method is deprecated, therefore will return false.
/// On other platforms, this will return true.
static bool isAnnounceSupported() {
return defaultTargetPlatform != TargetPlatform.android;
}
} }

View File

@ -16,7 +16,6 @@ import 'binding.dart';
import 'focus_manager.dart'; import 'focus_manager.dart';
import 'focus_scope.dart'; import 'focus_scope.dart';
import 'framework.dart'; import 'framework.dart';
import 'media_query.dart';
import 'navigator.dart'; import 'navigator.dart';
import 'pop_scope.dart'; import 'pop_scope.dart';
import 'restoration.dart'; import 'restoration.dart';
@ -370,7 +369,7 @@ class FormState extends State<Form> {
} }
} }
if (errorMessage.isNotEmpty && MediaQuery.announceOf(context)) { if (errorMessage.isNotEmpty) {
final TextDirection directionality = Directionality.of(context); final TextDirection directionality = Directionality.of(context);
if (defaultTargetPlatform == TargetPlatform.iOS) { if (defaultTargetPlatform == TargetPlatform.iOS) {
unawaited( unawaited(

View File

@ -103,9 +103,6 @@ enum _MediaQueryAspect {
/// Specifies the aspect corresponding to [MediaQueryData.boldText]. /// Specifies the aspect corresponding to [MediaQueryData.boldText].
boldText, boldText,
/// Specifies the aspect corresponding to [MediaQueryData.announce].
announce,
/// Specifies the aspect corresponding to [MediaQueryData.navigationMode]. /// Specifies the aspect corresponding to [MediaQueryData.navigationMode].
navigationMode, navigationMode,
@ -213,7 +210,6 @@ class MediaQueryData {
this.onOffSwitchLabels = false, this.onOffSwitchLabels = false,
this.disableAnimations = false, this.disableAnimations = false,
this.boldText = false, this.boldText = false,
this.announce = false,
this.navigationMode = NavigationMode.traditional, this.navigationMode = NavigationMode.traditional,
this.gestureSettings = const DeviceGestureSettings(touchSlop: kTouchSlop), this.gestureSettings = const DeviceGestureSettings(touchSlop: kTouchSlop),
this.displayFeatures = const <ui.DisplayFeature>[], this.displayFeatures = const <ui.DisplayFeature>[],
@ -299,7 +295,6 @@ class MediaQueryData {
platformData?.disableAnimations ?? platformData?.disableAnimations ??
view.platformDispatcher.accessibilityFeatures.disableAnimations, view.platformDispatcher.accessibilityFeatures.disableAnimations,
boldText = platformData?.boldText ?? view.platformDispatcher.accessibilityFeatures.boldText, boldText = platformData?.boldText ?? view.platformDispatcher.accessibilityFeatures.boldText,
announce = platformData?.announce ?? view.platformDispatcher.accessibilityFeatures.announce,
highContrast = highContrast =
platformData?.highContrast ?? view.platformDispatcher.accessibilityFeatures.highContrast, platformData?.highContrast ?? view.platformDispatcher.accessibilityFeatures.highContrast,
onOffSwitchLabels = onOffSwitchLabels =
@ -589,23 +584,6 @@ class MediaQueryData {
/// originates. /// originates.
final bool boldText; final bool boldText;
/// Whether accessibility announcements (like [SemanticsService.announce])
/// are allowed on the current platform.
///
/// Returns `false` on Android, where platform announcements are deprecated
/// by the underlying platform.
///
/// Returns `true` on all other platforms (iOS, web, desktop) where such
/// announcements are generally supported without discouragement.
///
/// Use this flag to conditionally avoid making announcements on Android.
///
/// See also:
///
/// * [dart:ui.PlatformDispatcher.accessibilityFeatures], where the setting
/// originates.
final bool announce;
/// Describes the navigation mode requested by the platform. /// Describes the navigation mode requested by the platform.
/// ///
/// Some user interfaces are better navigated using a directional pad (DPAD) /// Some user interfaces are better navigated using a directional pad (DPAD)
@ -687,7 +665,6 @@ class MediaQueryData {
bool? invertColors, bool? invertColors,
bool? accessibleNavigation, bool? accessibleNavigation,
bool? boldText, bool? boldText,
bool? announce,
NavigationMode? navigationMode, NavigationMode? navigationMode,
DeviceGestureSettings? gestureSettings, DeviceGestureSettings? gestureSettings,
List<ui.DisplayFeature>? displayFeatures, List<ui.DisplayFeature>? displayFeatures,
@ -713,7 +690,6 @@ class MediaQueryData {
disableAnimations: disableAnimations ?? this.disableAnimations, disableAnimations: disableAnimations ?? this.disableAnimations,
accessibleNavigation: accessibleNavigation ?? this.accessibleNavigation, accessibleNavigation: accessibleNavigation ?? this.accessibleNavigation,
boldText: boldText ?? this.boldText, boldText: boldText ?? this.boldText,
announce: announce ?? this.announce,
navigationMode: navigationMode ?? this.navigationMode, navigationMode: navigationMode ?? this.navigationMode,
gestureSettings: gestureSettings ?? this.gestureSettings, gestureSettings: gestureSettings ?? this.gestureSettings,
displayFeatures: displayFeatures ?? this.displayFeatures, displayFeatures: displayFeatures ?? this.displayFeatures,
@ -916,7 +892,6 @@ class MediaQueryData {
other.invertColors == invertColors && other.invertColors == invertColors &&
other.accessibleNavigation == accessibleNavigation && other.accessibleNavigation == accessibleNavigation &&
other.boldText == boldText && other.boldText == boldText &&
other.announce == announce &&
other.navigationMode == navigationMode && other.navigationMode == navigationMode &&
other.gestureSettings == gestureSettings && other.gestureSettings == gestureSettings &&
listEquals(other.displayFeatures, displayFeatures) && listEquals(other.displayFeatures, displayFeatures) &&
@ -1738,27 +1713,6 @@ class MediaQuery extends InheritedModel<_MediaQueryAspect> {
static bool? maybeBoldTextOf(BuildContext context) => static bool? maybeBoldTextOf(BuildContext context) =>
_maybeOf(context, _MediaQueryAspect.boldText)?.boldText; _maybeOf(context, _MediaQueryAspect.boldText)?.boldText;
/// Returns the [MediaQueryData.announce] accessibility setting for the
/// nearest [MediaQuery] ancestor or false, if no such ancestor exists.
///
/// Use of this method will cause the given [context] to rebuild any time that
/// the [MediaQueryData.announce] property of the ancestor [MediaQuery]
/// changes.
///
/// {@macro flutter.widgets.media_query.MediaQuery.dontUseOf}
static bool announceOf(BuildContext context) => maybeAnnounceOf(context) ?? false;
/// Returns the [MediaQueryData.announce] accessibility setting for the
/// nearest [MediaQuery] ancestor or null, if no such ancestor exists.
///
/// Use of this method will cause the given [context] to rebuild any time that
/// the [MediaQueryData.announce] property of the ancestor [MediaQuery]
/// changes.
///
/// {@macro flutter.widgets.media_query.MediaQuery.dontUseMaybeOf}
static bool? maybeAnnounceOf(BuildContext context) =>
_maybeOf(context, _MediaQueryAspect.announce)?.announce;
/// Returns [MediaQueryData.navigationMode] for the nearest [MediaQuery] /// Returns [MediaQueryData.navigationMode] for the nearest [MediaQuery]
/// ancestor or throws an exception, if no such ancestor exists. /// ancestor or throws an exception, if no such ancestor exists.
/// ///
@ -1889,7 +1843,6 @@ class MediaQuery extends InheritedModel<_MediaQueryAspect> {
_MediaQueryAspect.disableAnimations => _MediaQueryAspect.disableAnimations =>
data.disableAnimations != oldWidget.data.disableAnimations, data.disableAnimations != oldWidget.data.disableAnimations,
_MediaQueryAspect.boldText => data.boldText != oldWidget.data.boldText, _MediaQueryAspect.boldText => data.boldText != oldWidget.data.boldText,
_MediaQueryAspect.announce => data.announce != oldWidget.data.announce,
_MediaQueryAspect.navigationMode => _MediaQueryAspect.navigationMode =>
data.navigationMode != oldWidget.data.navigationMode, data.navigationMode != oldWidget.data.navigationMode,
_MediaQueryAspect.gestureSettings => _MediaQueryAspect.gestureSettings =>

View File

@ -8945,67 +8945,55 @@ void main() {
semantics.dispose(); semantics.dispose();
}); });
for (final bool announce in <bool>[true, false]) { testWidgets('InputDecoration errorText semantics', (WidgetTester tester) async {
testWidgets('InputDecoration errorText semantics (announce=$announce)', ( final SemanticsTester semantics = SemanticsTester(tester);
WidgetTester tester, final TextEditingController controller = _textEditingController();
) async { final Key key = UniqueKey();
final SemanticsTester semantics = SemanticsTester(tester);
final TextEditingController controller = _textEditingController();
final Key key = UniqueKey();
await tester.pumpWidget( await tester.pumpWidget(
overlay( overlay(
child: MediaQuery( child: TextField(
data: MediaQueryData(announce: announce), key: key,
child: TextField( controller: controller,
key: key, decoration: const InputDecoration(
controller: controller, labelText: 'label',
decoration: const InputDecoration( hintText: 'hint',
labelText: 'label', errorText: 'oh no!',
hintText: 'hint', ),
errorText: 'oh no!', ),
), ),
);
expect(
semantics,
hasSemantics(
TestSemantics.root(
children: <TestSemantics>[
TestSemantics.rootChild(
label: 'label',
textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
flags: <SemanticsFlag>[
SemanticsFlag.isTextField,
SemanticsFlag.hasEnabledState,
SemanticsFlag.isEnabled,
],
inputType: ui.SemanticsInputType.text,
currentValueLength: 0,
children: <TestSemantics>[
TestSemantics(label: 'oh no!', textDirection: TextDirection.ltr),
],
), ),
), ],
), ),
); ignoreTransform: true,
ignoreRect: true,
ignoreId: true,
),
);
expect( semantics.dispose();
semantics, });
hasSemantics(
TestSemantics.root(
children: <TestSemantics>[
TestSemantics.rootChild(
label: 'label',
textDirection: TextDirection.ltr,
actions: <SemanticsAction>[SemanticsAction.tap, SemanticsAction.focus],
flags: <SemanticsFlag>[
SemanticsFlag.isTextField,
SemanticsFlag.hasEnabledState,
SemanticsFlag.isEnabled,
],
inputType: ui.SemanticsInputType.text,
currentValueLength: 0,
children: <TestSemantics>[
TestSemantics(
label: 'oh no!',
textDirection: TextDirection.ltr,
flags: <SemanticsFlag>[if (!announce) SemanticsFlag.isLiveRegion],
),
],
),
],
),
ignoreTransform: true,
ignoreRect: true,
ignoreId: true,
),
);
semantics.dispose();
debugDefaultTargetPlatformOverride = null;
});
}
testWidgets('floating label does not overlap with value at large textScaleFactors', ( testWidgets('floating label does not overlap with value at large textScaleFactors', (
WidgetTester tester, WidgetTester tester,

View File

@ -2,8 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:flutter/foundation.dart';
import 'package:flutter/semantics.dart'; import 'package:flutter/semantics.dart';
import 'package:flutter/services.dart' show SystemChannels; import 'package:flutter/services.dart' show SystemChannels, TargetPlatform;
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
void main() { void main() {
@ -44,4 +45,12 @@ void main() {
]), ]),
); );
}); });
for (final TargetPlatform platform in TargetPlatform.values) {
test('Announce not supported on Android. (platform=$platform)', () {
debugDefaultTargetPlatformOverride = platform;
expect(SemanticsService.isAnnounceSupported(), platform != TargetPlatform.android);
debugDefaultTargetPlatformOverride = null;
});
}
} }

View File

@ -169,67 +169,50 @@ void main() {
await checkErrorText(''); await checkErrorText('');
}); });
for (final _PlatformAnnounceScenario test in <_PlatformAnnounceScenario>[ testWidgets('Should announce only the first error message when validate returns errors', (
_PlatformAnnounceScenario( WidgetTester tester,
announce: false, ) async {
testName: final GlobalKey<FormState> formKey = GlobalKey<FormState>();
'Should announce only the first error message when validate returns errors and announce = false', await tester.pumpWidget(
), MaterialApp(
_PlatformAnnounceScenario( home: MediaQuery(
announce: true, data: const MediaQueryData(),
testName: child: Directionality(
'Should not announce error message when validate returns errors and announce = true', textDirection: TextDirection.ltr,
), child: Center(
]) { child: Material(
testWidgets(test.testName, (WidgetTester tester) async { child: Form(
final GlobalKey<FormState> formKey = GlobalKey<FormState>(); key: formKey,
await tester.pumpWidget( child: Column(
MaterialApp( children: <Widget>[
home: MediaQuery( TextFormField(validator: (_) => 'First error message'),
data: MediaQueryData(announce: test.announce), TextFormField(validator: (_) => 'Second error message'),
child: Directionality( ],
textDirection: TextDirection.ltr,
child: Center(
child: Material(
child: Form(
key: formKey,
child: Column(
children: <Widget>[
TextFormField(validator: (_) => 'First error message'),
TextFormField(validator: (_) => 'Second error message'),
],
),
), ),
), ),
), ),
), ),
), ),
), ),
); ),
formKey.currentState!.reset(); );
await tester.enterText(find.byType(TextFormField).first, ''); formKey.currentState!.reset();
await tester.pump(); await tester.enterText(find.byType(TextFormField).first, '');
await tester.pump();
// Manually validate. // Manually validate.
expect(find.text('First error message'), findsNothing); expect(find.text('First error message'), findsNothing);
expect(find.text('Second error message'), findsNothing); expect(find.text('Second error message'), findsNothing);
formKey.currentState!.validate(); formKey.currentState!.validate();
await tester.pump(); await tester.pump();
expect(find.text('First error message'), findsOneWidget); expect(find.text('First error message'), findsOneWidget);
expect(find.text('Second error message'), findsOneWidget); expect(find.text('Second error message'), findsOneWidget);
if (test.announce) { final CapturedAccessibilityAnnouncement announcement = tester.takeAnnouncements().single;
final CapturedAccessibilityAnnouncement announcement = tester.takeAnnouncements().single; expect(announcement.message, 'First error message');
expect(announcement.message, 'First error message'); expect(announcement.textDirection, TextDirection.ltr);
expect(announcement.textDirection, TextDirection.ltr); expect(announcement.assertiveness, Assertiveness.assertive);
expect(announcement.assertiveness, Assertiveness.assertive); });
} else {
final CapturedAccessibilityAnnouncement? announcement =
tester.takeAnnouncements().firstOrNull;
expect(announcement, null);
}
});
}
testWidgets('isValid returns true when a field is valid', (WidgetTester tester) async { testWidgets('isValid returns true when a field is valid', (WidgetTester tester) async {
final GlobalKey<FormFieldState<String>> fieldKey1 = GlobalKey<FormFieldState<String>>(); final GlobalKey<FormFieldState<String>> fieldKey1 = GlobalKey<FormFieldState<String>>();
@ -405,7 +388,7 @@ void main() {
Widget builder() { Widget builder() {
return MaterialApp( return MaterialApp(
home: MediaQuery( home: MediaQuery(
data: const MediaQueryData(announce: true), data: const MediaQueryData(),
child: Directionality( child: Directionality(
textDirection: TextDirection.ltr, textDirection: TextDirection.ltr,
child: Center( child: Center(
@ -1632,9 +1615,3 @@ void main() {
); );
}); });
} }
class _PlatformAnnounceScenario {
_PlatformAnnounceScenario({required this.announce, required this.testName});
final bool announce;
final String testName;
}

View File

@ -144,7 +144,6 @@ void main() {
}); });
testWidgets('MediaQueryData.fromView is sane', (WidgetTester tester) async { testWidgets('MediaQueryData.fromView is sane', (WidgetTester tester) async {
tester.platformDispatcher.accessibilityFeaturesTestValue = const FakeAccessibilityFeatures();
final MediaQueryData data = MediaQueryData.fromView(tester.view); final MediaQueryData data = MediaQueryData.fromView(tester.view);
expect(data, hasOneLineDescription); expect(data, hasOneLineDescription);
expect(data.hashCode, equals(data.copyWith().hashCode)); expect(data.hashCode, equals(data.copyWith().hashCode));
@ -155,7 +154,6 @@ void main() {
expect(data.boldText, false); expect(data.boldText, false);
expect(data.highContrast, false); expect(data.highContrast, false);
expect(data.onOffSwitchLabels, false); expect(data.onOffSwitchLabels, false);
expect(data.announce, false);
expect(data.platformBrightness, Brightness.light); expect(data.platformBrightness, Brightness.light);
expect(data.gestureSettings.touchSlop, null); expect(data.gestureSettings.touchSlop, null);
expect(data.displayFeatures, isEmpty); expect(data.displayFeatures, isEmpty);
@ -171,7 +169,6 @@ void main() {
boldText: true, boldText: true,
highContrast: true, highContrast: true,
onOffSwitchLabels: true, onOffSwitchLabels: true,
announce: true,
alwaysUse24HourFormat: true, alwaysUse24HourFormat: true,
navigationMode: NavigationMode.directional, navigationMode: NavigationMode.directional,
); );
@ -205,7 +202,6 @@ void main() {
expect(data.boldText, platformData.boldText); expect(data.boldText, platformData.boldText);
expect(data.highContrast, platformData.highContrast); expect(data.highContrast, platformData.highContrast);
expect(data.onOffSwitchLabels, platformData.onOffSwitchLabels); expect(data.onOffSwitchLabels, platformData.onOffSwitchLabels);
expect(data.announce, platformData.announce);
expect(data.alwaysUse24HourFormat, platformData.alwaysUse24HourFormat); expect(data.alwaysUse24HourFormat, platformData.alwaysUse24HourFormat);
expect(data.navigationMode, platformData.navigationMode); expect(data.navigationMode, platformData.navigationMode);
expect(data.gestureSettings, DeviceGestureSettings.fromView(tester.view)); expect(data.gestureSettings, DeviceGestureSettings.fromView(tester.view));
@ -259,7 +255,6 @@ void main() {
data.onOffSwitchLabels, data.onOffSwitchLabels,
tester.platformDispatcher.accessibilityFeatures.onOffSwitchLabels, tester.platformDispatcher.accessibilityFeatures.onOffSwitchLabels,
); );
expect(data.announce, tester.platformDispatcher.accessibilityFeatures.announce);
expect(data.alwaysUse24HourFormat, tester.platformDispatcher.alwaysUse24HourFormat); expect(data.alwaysUse24HourFormat, tester.platformDispatcher.alwaysUse24HourFormat);
expect(data.navigationMode, NavigationMode.traditional); expect(data.navigationMode, NavigationMode.traditional);
expect(data.gestureSettings, DeviceGestureSettings.fromView(tester.view)); expect(data.gestureSettings, DeviceGestureSettings.fromView(tester.view));
@ -279,7 +274,6 @@ void main() {
boldText: true, boldText: true,
highContrast: true, highContrast: true,
onOffSwitchLabels: true, onOffSwitchLabels: true,
announce: true,
alwaysUse24HourFormat: true, alwaysUse24HourFormat: true,
navigationMode: NavigationMode.directional, navigationMode: NavigationMode.directional,
); );
@ -327,7 +321,6 @@ void main() {
expect(data.boldText, platformData.boldText); expect(data.boldText, platformData.boldText);
expect(data.highContrast, platformData.highContrast); expect(data.highContrast, platformData.highContrast);
expect(data.onOffSwitchLabels, platformData.onOffSwitchLabels); expect(data.onOffSwitchLabels, platformData.onOffSwitchLabels);
expect(data.announce, platformData.announce);
expect(data.alwaysUse24HourFormat, platformData.alwaysUse24HourFormat); expect(data.alwaysUse24HourFormat, platformData.alwaysUse24HourFormat);
expect(data.navigationMode, platformData.navigationMode); expect(data.navigationMode, platformData.navigationMode);
expect(data.gestureSettings, DeviceGestureSettings.fromView(tester.view)); expect(data.gestureSettings, DeviceGestureSettings.fromView(tester.view));
@ -400,7 +393,6 @@ void main() {
data.onOffSwitchLabels, data.onOffSwitchLabels,
tester.platformDispatcher.accessibilityFeatures.onOffSwitchLabels, tester.platformDispatcher.accessibilityFeatures.onOffSwitchLabels,
); );
expect(data.announce, tester.platformDispatcher.accessibilityFeatures.announce);
expect(data.alwaysUse24HourFormat, tester.platformDispatcher.alwaysUse24HourFormat); expect(data.alwaysUse24HourFormat, tester.platformDispatcher.alwaysUse24HourFormat);
expect(data.navigationMode, NavigationMode.traditional); expect(data.navigationMode, NavigationMode.traditional);
expect(data.gestureSettings, DeviceGestureSettings.fromView(tester.view)); expect(data.gestureSettings, DeviceGestureSettings.fromView(tester.view));
@ -585,7 +577,6 @@ void main() {
expect(copied.boldText, data.boldText); expect(copied.boldText, data.boldText);
expect(copied.highContrast, data.highContrast); expect(copied.highContrast, data.highContrast);
expect(copied.onOffSwitchLabels, data.onOffSwitchLabels); expect(copied.onOffSwitchLabels, data.onOffSwitchLabels);
expect(copied.announce, data.announce);
expect(copied.platformBrightness, data.platformBrightness); expect(copied.platformBrightness, data.platformBrightness);
expect(copied.gestureSettings, data.gestureSettings); expect(copied.gestureSettings, data.gestureSettings);
expect(copied.displayFeatures, data.displayFeatures); expect(copied.displayFeatures, data.displayFeatures);
@ -626,7 +617,6 @@ void main() {
boldText: true, boldText: true,
highContrast: true, highContrast: true,
onOffSwitchLabels: true, onOffSwitchLabels: true,
announce: true,
platformBrightness: Brightness.dark, platformBrightness: Brightness.dark,
navigationMode: NavigationMode.directional, navigationMode: NavigationMode.directional,
gestureSettings: gestureSettings, gestureSettings: gestureSettings,
@ -646,7 +636,6 @@ void main() {
expect(copied.boldText, true); expect(copied.boldText, true);
expect(copied.highContrast, true); expect(copied.highContrast, true);
expect(copied.onOffSwitchLabels, true); expect(copied.onOffSwitchLabels, true);
expect(copied.announce, true);
expect(copied.platformBrightness, Brightness.dark); expect(copied.platformBrightness, Brightness.dark);
expect(copied.navigationMode, NavigationMode.directional); expect(copied.navigationMode, NavigationMode.directional);
expect(copied.gestureSettings, gestureSettings); expect(copied.gestureSettings, gestureSettings);
@ -685,7 +674,6 @@ void main() {
boldText: true, boldText: true,
highContrast: true, highContrast: true,
onOffSwitchLabels: true, onOffSwitchLabels: true,
announce: true,
navigationMode: NavigationMode.directional, navigationMode: NavigationMode.directional,
displayFeatures: displayFeatures, displayFeatures: displayFeatures,
), ),
@ -722,7 +710,6 @@ void main() {
expect(unpadded.boldText, true); expect(unpadded.boldText, true);
expect(unpadded.highContrast, true); expect(unpadded.highContrast, true);
expect(unpadded.onOffSwitchLabels, true); expect(unpadded.onOffSwitchLabels, true);
expect(unpadded.announce, true);
expect(unpadded.navigationMode, NavigationMode.directional); expect(unpadded.navigationMode, NavigationMode.directional);
expect(unpadded.displayFeatures, displayFeatures); expect(unpadded.displayFeatures, displayFeatures);
}); });
@ -761,7 +748,6 @@ void main() {
boldText: true, boldText: true,
highContrast: true, highContrast: true,
onOffSwitchLabels: true, onOffSwitchLabels: true,
announce: true,
navigationMode: NavigationMode.directional, navigationMode: NavigationMode.directional,
displayFeatures: displayFeatures, displayFeatures: displayFeatures,
), ),
@ -795,7 +781,6 @@ void main() {
expect(unpadded.boldText, true); expect(unpadded.boldText, true);
expect(unpadded.highContrast, true); expect(unpadded.highContrast, true);
expect(unpadded.onOffSwitchLabels, true); expect(unpadded.onOffSwitchLabels, true);
expect(unpadded.announce, true);
expect(unpadded.navigationMode, NavigationMode.directional); expect(unpadded.navigationMode, NavigationMode.directional);
expect(unpadded.displayFeatures, displayFeatures); expect(unpadded.displayFeatures, displayFeatures);
}); });
@ -834,7 +819,6 @@ void main() {
boldText: true, boldText: true,
highContrast: true, highContrast: true,
onOffSwitchLabels: true, onOffSwitchLabels: true,
announce: true,
navigationMode: NavigationMode.directional, navigationMode: NavigationMode.directional,
displayFeatures: displayFeatures, displayFeatures: displayFeatures,
), ),
@ -871,7 +855,6 @@ void main() {
expect(unpadded.boldText, true); expect(unpadded.boldText, true);
expect(unpadded.highContrast, true); expect(unpadded.highContrast, true);
expect(unpadded.onOffSwitchLabels, true); expect(unpadded.onOffSwitchLabels, true);
expect(unpadded.announce, true);
expect(unpadded.navigationMode, NavigationMode.directional); expect(unpadded.navigationMode, NavigationMode.directional);
expect(unpadded.displayFeatures, displayFeatures); expect(unpadded.displayFeatures, displayFeatures);
}); });
@ -910,7 +893,6 @@ void main() {
boldText: true, boldText: true,
highContrast: true, highContrast: true,
onOffSwitchLabels: true, onOffSwitchLabels: true,
announce: true,
navigationMode: NavigationMode.directional, navigationMode: NavigationMode.directional,
displayFeatures: displayFeatures, displayFeatures: displayFeatures,
), ),
@ -944,7 +926,6 @@ void main() {
expect(unpadded.boldText, true); expect(unpadded.boldText, true);
expect(unpadded.highContrast, true); expect(unpadded.highContrast, true);
expect(unpadded.onOffSwitchLabels, true); expect(unpadded.onOffSwitchLabels, true);
expect(unpadded.announce, true);
expect(unpadded.navigationMode, NavigationMode.directional); expect(unpadded.navigationMode, NavigationMode.directional);
expect(unpadded.displayFeatures, displayFeatures); expect(unpadded.displayFeatures, displayFeatures);
}); });
@ -983,7 +964,6 @@ void main() {
boldText: true, boldText: true,
highContrast: true, highContrast: true,
onOffSwitchLabels: true, onOffSwitchLabels: true,
announce: true,
navigationMode: NavigationMode.directional, navigationMode: NavigationMode.directional,
displayFeatures: displayFeatures, displayFeatures: displayFeatures,
), ),
@ -1020,7 +1000,6 @@ void main() {
expect(unpadded.boldText, true); expect(unpadded.boldText, true);
expect(unpadded.highContrast, true); expect(unpadded.highContrast, true);
expect(unpadded.onOffSwitchLabels, true); expect(unpadded.onOffSwitchLabels, true);
expect(unpadded.announce, true);
expect(unpadded.navigationMode, NavigationMode.directional); expect(unpadded.navigationMode, NavigationMode.directional);
expect(unpadded.displayFeatures, displayFeatures); expect(unpadded.displayFeatures, displayFeatures);
}); });
@ -1059,7 +1038,6 @@ void main() {
boldText: true, boldText: true,
highContrast: true, highContrast: true,
onOffSwitchLabels: true, onOffSwitchLabels: true,
announce: true,
navigationMode: NavigationMode.directional, navigationMode: NavigationMode.directional,
displayFeatures: displayFeatures, displayFeatures: displayFeatures,
), ),
@ -1093,7 +1071,6 @@ void main() {
expect(unpadded.boldText, true); expect(unpadded.boldText, true);
expect(unpadded.highContrast, true); expect(unpadded.highContrast, true);
expect(unpadded.onOffSwitchLabels, true); expect(unpadded.onOffSwitchLabels, true);
expect(unpadded.announce, true);
expect(unpadded.navigationMode, NavigationMode.directional); expect(unpadded.navigationMode, NavigationMode.directional);
expect(unpadded.displayFeatures, displayFeatures); expect(unpadded.displayFeatures, displayFeatures);
}); });
@ -1198,32 +1175,6 @@ void main() {
expect(insideOnOffSwitchLabels, true); expect(insideOnOffSwitchLabels, true);
}); });
testWidgets('MediaQuery.announce', (WidgetTester tester) async {
late bool outsideAnnounce;
late bool insideAnnounce;
tester.platformDispatcher.accessibilityFeaturesTestValue = const FakeAccessibilityFeatures();
await tester.pumpWidget(
Builder(
builder: (BuildContext context) {
outsideAnnounce = MediaQuery.announceOf(context);
return MediaQuery(
data: const MediaQueryData(announce: true),
child: Builder(
builder: (BuildContext context) {
insideAnnounce = MediaQuery.announceOf(context);
return Container();
},
),
);
},
),
);
expect(outsideAnnounce, false);
expect(insideAnnounce, true);
});
testWidgets('MediaQuery.boldTextOf', (WidgetTester tester) async { testWidgets('MediaQuery.boldTextOf', (WidgetTester tester) async {
late bool outsideBoldTextOverride; late bool outsideBoldTextOverride;
late bool insideBoldTextOverride; late bool insideBoldTextOverride;
@ -1363,7 +1314,6 @@ void main() {
boldText: true, boldText: true,
highContrast: true, highContrast: true,
onOffSwitchLabels: true, onOffSwitchLabels: true,
announce: true,
displayFeatures: displayFeatures, displayFeatures: displayFeatures,
), ),
child: Builder( child: Builder(
@ -1395,7 +1345,6 @@ void main() {
expect(subScreenMediaQuery.boldText, true); expect(subScreenMediaQuery.boldText, true);
expect(subScreenMediaQuery.highContrast, true); expect(subScreenMediaQuery.highContrast, true);
expect(subScreenMediaQuery.onOffSwitchLabels, true); expect(subScreenMediaQuery.onOffSwitchLabels, true);
expect(subScreenMediaQuery.announce, true);
expect(subScreenMediaQuery.displayFeatures, isEmpty); expect(subScreenMediaQuery.displayFeatures, isEmpty);
}); });
@ -1442,7 +1391,6 @@ void main() {
boldText: true, boldText: true,
highContrast: true, highContrast: true,
onOffSwitchLabels: true, onOffSwitchLabels: true,
announce: true,
displayFeatures: displayFeatures, displayFeatures: displayFeatures,
), ),
child: Builder( child: Builder(
@ -1480,7 +1428,6 @@ void main() {
expect(subScreenMediaQuery.boldText, true); expect(subScreenMediaQuery.boldText, true);
expect(subScreenMediaQuery.highContrast, true); expect(subScreenMediaQuery.highContrast, true);
expect(subScreenMediaQuery.onOffSwitchLabels, true); expect(subScreenMediaQuery.onOffSwitchLabels, true);
expect(subScreenMediaQuery.announce, true);
expect(subScreenMediaQuery.displayFeatures, <DisplayFeature>[cutoutDisplayFeature]); expect(subScreenMediaQuery.displayFeatures, <DisplayFeature>[cutoutDisplayFeature]);
}); });
@ -1745,8 +1692,6 @@ void main() {
MediaQuery.maybeOnOffSwitchLabelsOf, MediaQuery.maybeOnOffSwitchLabelsOf,
MediaQueryData(onOffSwitchLabels: true), MediaQueryData(onOffSwitchLabels: true),
), ),
const _MediaQueryAspectCase(MediaQuery.announceOf, MediaQueryData(announce: true)),
const _MediaQueryAspectCase(MediaQuery.maybeAnnounceOf, MediaQueryData(announce: true)),
const _MediaQueryAspectCase( const _MediaQueryAspectCase(
MediaQuery.disableAnimationsOf, MediaQuery.disableAnimationsOf,
MediaQueryData(disableAnimations: true), MediaQueryData(disableAnimations: true),

View File

@ -32,7 +32,6 @@ class FakeAccessibilityFeatures implements AccessibilityFeatures {
this.reduceMotion = false, this.reduceMotion = false,
this.highContrast = false, this.highContrast = false,
this.onOffSwitchLabels = false, this.onOffSwitchLabels = false,
this.announce = false,
}); });
/// An instance of [AccessibilityFeatures] where all the features are enabled. /// An instance of [AccessibilityFeatures] where all the features are enabled.
@ -44,7 +43,6 @@ class FakeAccessibilityFeatures implements AccessibilityFeatures {
reduceMotion: true, reduceMotion: true,
highContrast: true, highContrast: true,
onOffSwitchLabels: true, onOffSwitchLabels: true,
announce: true,
); );
@override @override
@ -68,9 +66,6 @@ class FakeAccessibilityFeatures implements AccessibilityFeatures {
@override @override
final bool onOffSwitchLabels; final bool onOffSwitchLabels;
@override
final bool announce;
@override @override
bool operator ==(Object other) { bool operator ==(Object other) {
if (other.runtimeType != runtimeType) { if (other.runtimeType != runtimeType) {
@ -83,8 +78,7 @@ class FakeAccessibilityFeatures implements AccessibilityFeatures {
other.boldText == boldText && other.boldText == boldText &&
other.reduceMotion == reduceMotion && other.reduceMotion == reduceMotion &&
other.highContrast == highContrast && other.highContrast == highContrast &&
other.onOffSwitchLabels == onOffSwitchLabels && other.onOffSwitchLabels == onOffSwitchLabels;
other.announce == announce;
} }
@override @override
@ -97,7 +91,6 @@ class FakeAccessibilityFeatures implements AccessibilityFeatures {
reduceMotion, reduceMotion,
highContrast, highContrast,
onOffSwitchLabels, onOffSwitchLabels,
announce,
); );
} }