mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
<!-- 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:
parent
494b08b420
commit
606bb06c0f
@ -931,7 +931,6 @@ class AccessibilityFeatures {
|
||||
static const int _kReduceMotionIndex = 1 << 4;
|
||||
static const int _kHighContrastIndex = 1 << 5;
|
||||
static const int _kOnOffSwitchLabelsIndex = 1 << 6;
|
||||
static const int _kNoAnnounceIndex = 1 << 7;
|
||||
|
||||
// A bitfield which represents each enabled feature.
|
||||
final int _index;
|
||||
@ -969,20 +968,6 @@ class AccessibilityFeatures {
|
||||
/// Only supported on iOS.
|
||||
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
|
||||
String toString() {
|
||||
final List<String> features = <String>[];
|
||||
@ -1007,9 +992,6 @@ class AccessibilityFeatures {
|
||||
if (onOffSwitchLabels) {
|
||||
features.add('onOffSwitchLabels');
|
||||
}
|
||||
if (announce) {
|
||||
features.add('announce');
|
||||
}
|
||||
return 'AccessibilityFeatures$features';
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,6 @@ enum class AccessibilityFeatureFlag : int32_t {
|
||||
kReduceMotion = 1 << 4,
|
||||
kHighContrast = 1 << 5,
|
||||
kOnOffSwitchLabels = 1 << 6,
|
||||
kNoAnnounce = 1 << 7,
|
||||
};
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
@ -54,7 +54,6 @@ class EngineAccessibilityFeatures implements ui.AccessibilityFeatures {
|
||||
static const int _kReduceMotionIndex = 1 << 4;
|
||||
static const int _kHighContrastIndex = 1 << 5;
|
||||
static const int _kOnOffSwitchLabelsIndex = 1 << 6;
|
||||
static const int _kNoAnnounceIndex = 1 << 7;
|
||||
|
||||
// A bitfield which represents each enabled feature.
|
||||
final int _index;
|
||||
@ -73,10 +72,6 @@ class EngineAccessibilityFeatures implements ui.AccessibilityFeatures {
|
||||
bool get highContrast => _kHighContrastIndex & _index != 0;
|
||||
@override
|
||||
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
|
||||
String toString() {
|
||||
@ -102,9 +97,6 @@ class EngineAccessibilityFeatures implements ui.AccessibilityFeatures {
|
||||
if (onOffSwitchLabels) {
|
||||
features.add('onOffSwitchLabels');
|
||||
}
|
||||
if (announce) {
|
||||
features.add('announce');
|
||||
}
|
||||
return 'AccessibilityFeatures$features';
|
||||
}
|
||||
|
||||
@ -127,7 +119,6 @@ class EngineAccessibilityFeatures implements ui.AccessibilityFeatures {
|
||||
bool? reduceMotion,
|
||||
bool? highContrast,
|
||||
bool? onOffSwitchLabels,
|
||||
bool? announce,
|
||||
}) {
|
||||
final EngineAccessibilityFeaturesBuilder builder = EngineAccessibilityFeaturesBuilder(0);
|
||||
|
||||
@ -138,7 +129,6 @@ class EngineAccessibilityFeatures implements ui.AccessibilityFeatures {
|
||||
builder.reduceMotion = reduceMotion ?? this.reduceMotion;
|
||||
builder.highContrast = highContrast ?? this.highContrast;
|
||||
builder.onOffSwitchLabels = onOffSwitchLabels ?? this.onOffSwitchLabels;
|
||||
builder.announce = announce ?? this.announce;
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
@ -156,9 +146,6 @@ class EngineAccessibilityFeaturesBuilder {
|
||||
bool get reduceMotion => EngineAccessibilityFeatures._kReduceMotionIndex & _index != 0;
|
||||
bool get highContrast => EngineAccessibilityFeatures._kHighContrastIndex & _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) {
|
||||
const int accessibleNavigation = EngineAccessibilityFeatures._kAccessibleNavigation;
|
||||
@ -195,12 +182,6 @@ class EngineAccessibilityFeaturesBuilder {
|
||||
_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
|
||||
EngineAccessibilityFeatures build() {
|
||||
return EngineAccessibilityFeatures(_index);
|
||||
|
@ -115,7 +115,6 @@ abstract class AccessibilityFeatures {
|
||||
bool get reduceMotion;
|
||||
bool get highContrast;
|
||||
bool get onOffSwitchLabels;
|
||||
bool get announce;
|
||||
}
|
||||
|
||||
enum Brightness { dark, light }
|
||||
|
@ -299,14 +299,6 @@ void _testEngineAccessibilityBuilder() {
|
||||
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', () {
|
||||
expect(features.reduceMotion, isFalse);
|
||||
builder.reduceMotion = true;
|
||||
@ -399,10 +391,7 @@ void _testEngineSemanticsOwner() {
|
||||
});
|
||||
|
||||
test('accessibilityFeatures copyWith function works', () {
|
||||
// Announce is an inverted check, see EngineAccessibilityFeatures.announce.
|
||||
// Therefore, we need to ensure that the original copy starts with false (1 << 7).
|
||||
const EngineAccessibilityFeatures original = EngineAccessibilityFeatures(0 | 1 << 7);
|
||||
|
||||
const EngineAccessibilityFeatures original = EngineAccessibilityFeatures(0);
|
||||
EngineAccessibilityFeatures copy = original.copyWith(accessibleNavigation: true);
|
||||
expect(copy.accessibleNavigation, true);
|
||||
expect(copy.boldText, false);
|
||||
@ -410,7 +399,6 @@ void _testEngineSemanticsOwner() {
|
||||
expect(copy.highContrast, false);
|
||||
expect(copy.invertColors, false);
|
||||
expect(copy.onOffSwitchLabels, false);
|
||||
expect(copy.announce, false);
|
||||
expect(copy.reduceMotion, false);
|
||||
|
||||
copy = original.copyWith(boldText: true);
|
||||
@ -429,7 +417,6 @@ void _testEngineSemanticsOwner() {
|
||||
expect(copy.highContrast, false);
|
||||
expect(copy.invertColors, false);
|
||||
expect(copy.onOffSwitchLabels, false);
|
||||
expect(copy.announce, false);
|
||||
expect(copy.reduceMotion, false);
|
||||
|
||||
copy = original.copyWith(highContrast: true);
|
||||
@ -439,7 +426,6 @@ void _testEngineSemanticsOwner() {
|
||||
expect(copy.highContrast, true);
|
||||
expect(copy.invertColors, false);
|
||||
expect(copy.onOffSwitchLabels, false);
|
||||
expect(copy.announce, false);
|
||||
expect(copy.reduceMotion, false);
|
||||
|
||||
copy = original.copyWith(invertColors: true);
|
||||
@ -449,7 +435,6 @@ void _testEngineSemanticsOwner() {
|
||||
expect(copy.highContrast, false);
|
||||
expect(copy.invertColors, true);
|
||||
expect(copy.onOffSwitchLabels, false);
|
||||
expect(copy.announce, false);
|
||||
expect(copy.reduceMotion, false);
|
||||
|
||||
copy = original.copyWith(onOffSwitchLabels: true);
|
||||
@ -461,16 +446,6 @@ void _testEngineSemanticsOwner() {
|
||||
expect(copy.onOffSwitchLabels, true);
|
||||
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);
|
||||
expect(copy.accessibleNavigation, false);
|
||||
expect(copy.boldText, false);
|
||||
@ -478,7 +453,6 @@ void _testEngineSemanticsOwner() {
|
||||
expect(copy.highContrast, false);
|
||||
expect(copy.invertColors, false);
|
||||
expect(copy.onOffSwitchLabels, false);
|
||||
expect(copy.announce, false);
|
||||
expect(copy.reduceMotion, true);
|
||||
});
|
||||
|
||||
|
@ -490,7 +490,6 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
|
||||
this.accessibilityManager.addTouchExplorationStateChangeListener(
|
||||
touchExplorationStateChangeListener);
|
||||
|
||||
accessibilityFeatureFlags |= AccessibilityFeature.NO_ANNOUNCE.value;
|
||||
// Tell Flutter whether animations should initially be enabled or disabled. Then register a
|
||||
// listener to be notified of changes in the future.
|
||||
animationScaleObserver.onChange(false);
|
||||
@ -2171,8 +2170,7 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
|
||||
BOLD_TEXT(1 << 3), // NOT SUPPORTED
|
||||
REDUCE_MOTION(1 << 4), // NOT SUPPORTED
|
||||
HIGH_CONTRAST(1 << 5), // NOT SUPPORTED
|
||||
ON_OFF_SWITCH_LABELS(1 << 6), // NOT SUPPORTED
|
||||
NO_ANNOUNCE(1 << 7);
|
||||
ON_OFF_SWITCH_LABELS(1 << 6); // NOT SUPPORTED
|
||||
|
||||
final int value;
|
||||
|
||||
|
@ -66,11 +66,6 @@ import org.robolectric.annotation.Config;
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
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
|
||||
public void itDescribesNonTextFieldsWithAContentDescription() {
|
||||
AccessibilityBridge accessibilityBridge = setUpBridge();
|
||||
@ -140,26 +135,6 @@ public class AccessibilityBridgeTest {
|
||||
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
|
||||
public void itSetsAccessibleNavigation() {
|
||||
AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
|
||||
@ -183,20 +158,18 @@ public class AccessibilityBridgeTest {
|
||||
verify(mockManager).addTouchExplorationStateChangeListener(listenerCaptor.capture());
|
||||
|
||||
assertEquals(accessibilityBridge.getAccessibleNavigation(), false);
|
||||
verify(mockChannel).setAccessibilityFeatures(ACCESSIBILITY_FEATURE_NO_ANNOUNCE);
|
||||
verify(mockChannel).setAccessibilityFeatures(0);
|
||||
reset(mockChannel);
|
||||
|
||||
// Simulate assistive technology accessing accessibility tree.
|
||||
accessibilityBridge.createAccessibilityNodeInfo(0);
|
||||
verify(mockChannel)
|
||||
.setAccessibilityFeatures(
|
||||
ACCESSIBILITY_FEATURE_NAVIGATION | ACCESSIBILITY_FEATURE_NO_ANNOUNCE);
|
||||
verify(mockChannel).setAccessibilityFeatures(1);
|
||||
assertEquals(accessibilityBridge.getAccessibleNavigation(), true);
|
||||
|
||||
// Simulate turning off TalkBack.
|
||||
reset(mockChannel);
|
||||
listenerCaptor.getValue().onTouchExplorationStateChanged(false);
|
||||
verify(mockChannel).setAccessibilityFeatures(ACCESSIBILITY_FEATURE_NO_ANNOUNCE);
|
||||
verify(mockChannel).setAccessibilityFeatures(0);
|
||||
assertEquals(accessibilityBridge.getAccessibleNavigation(), false);
|
||||
}
|
||||
|
||||
@ -1184,9 +1157,7 @@ public class AccessibilityBridgeTest {
|
||||
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
|
||||
/*platformViewsAccessibilityDelegate=*/ null);
|
||||
|
||||
verify(mockChannel)
|
||||
.setAccessibilityFeatures(
|
||||
ACCESSIBILITY_FEATURE_BOLD_TEXT | ACCESSIBILITY_FEATURE_NO_ANNOUNCE);
|
||||
verify(mockChannel).setAccessibilityFeatures(1 << 3);
|
||||
reset(mockChannel);
|
||||
|
||||
// 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
|
||||
ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
|
||||
verify(mockChannel, atLeastOnce()).setAccessibilityFeatures(captor.capture());
|
||||
assertEquals(
|
||||
ACCESSIBILITY_FEATURE_DISABLE_ANIMATIONS | ACCESSIBILITY_FEATURE_NO_ANNOUNCE,
|
||||
captor.getValue().intValue());
|
||||
assertEquals(1 << 2 /* DISABLE_ANIMATION */, captor.getValue().intValue());
|
||||
|
||||
// Set back to default
|
||||
Settings.Global.putFloat(null, "transition_animation_scale", 1.0f);
|
||||
@ -1904,21 +1873,19 @@ public class AccessibilityBridgeTest {
|
||||
ContentObserver observer = observerCaptor.getValue();
|
||||
|
||||
// Initial state
|
||||
verify(mockChannel).setAccessibilityFeatures(ACCESSIBILITY_FEATURE_NO_ANNOUNCE);
|
||||
verify(mockChannel).setAccessibilityFeatures(0);
|
||||
reset(mockChannel);
|
||||
|
||||
// Animations are disabled
|
||||
Settings.Global.putFloat(mockContentResolver, "transition_animation_scale", 0.0f);
|
||||
observer.onChange(false);
|
||||
verify(mockChannel)
|
||||
.setAccessibilityFeatures(
|
||||
ACCESSIBILITY_FEATURE_DISABLE_ANIMATIONS | ACCESSIBILITY_FEATURE_NO_ANNOUNCE);
|
||||
verify(mockChannel).setAccessibilityFeatures(1 << 2);
|
||||
reset(mockChannel);
|
||||
|
||||
// Animations are enabled
|
||||
Settings.Global.putFloat(mockContentResolver, "transition_animation_scale", 1.0f);
|
||||
observer.onChange(false);
|
||||
verify(mockChannel).setAccessibilityFeatures(ACCESSIBILITY_FEATURE_NO_ANNOUNCE);
|
||||
verify(mockChannel).setAccessibilityFeatures(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -105,8 +105,6 @@ typedef enum {
|
||||
kFlutterAccessibilityFeatureHighContrast = 1 << 5,
|
||||
/// Request to show on/off labels inside switches.
|
||||
kFlutterAccessibilityFeatureOnOffSwitchLabels = 1 << 6,
|
||||
/// Indicate the platform does not support announcements.
|
||||
kFlutterAccessibilityFeatureNoAnnounce = 1 << 7,
|
||||
} FlutterAccessibilityFeature;
|
||||
|
||||
/// The set of possible actions that can be conveyed to a semantics node.
|
||||
|
@ -322,7 +322,6 @@ class _HelperErrorState extends State<_HelperError> with SingleTickerProviderSta
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = AnimationController(duration: _kTransitionDuration, vsync: this);
|
||||
// TODO(ash2moon): https://github.com/flutter/flutter/issues/168022
|
||||
if (_hasError) {
|
||||
_error = _buildError();
|
||||
_controller.value = 1.0;
|
||||
@ -400,31 +399,26 @@ class _HelperErrorState extends State<_HelperError> with SingleTickerProviderSta
|
||||
|
||||
Widget _buildError() {
|
||||
assert(widget.error != null || widget.errorText != null);
|
||||
return Builder(
|
||||
builder: (BuildContext context) {
|
||||
return Semantics(
|
||||
container: true,
|
||||
liveRegion: !MediaQuery.announceOf(context),
|
||||
child: FadeTransition(
|
||||
opacity: _controller,
|
||||
child: FractionalTranslation(
|
||||
translation: Tween<Offset>(
|
||||
begin: const Offset(0.0, -0.25),
|
||||
end: Offset.zero,
|
||||
).evaluate(_controller.view),
|
||||
child:
|
||||
widget.error ??
|
||||
Text(
|
||||
widget.errorText!,
|
||||
style: widget.errorStyle,
|
||||
textAlign: widget.textAlign,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: widget.errorMaxLines,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
return Semantics(
|
||||
container: true,
|
||||
child: FadeTransition(
|
||||
opacity: _controller,
|
||||
child: FractionalTranslation(
|
||||
translation: Tween<Offset>(
|
||||
begin: const Offset(0.0, -0.25),
|
||||
end: Offset.zero,
|
||||
).evaluate(_controller.view),
|
||||
child:
|
||||
widget.error ??
|
||||
Text(
|
||||
widget.errorText!,
|
||||
style: widget.errorStyle,
|
||||
textAlign: widget.textAlign,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: widget.errorMaxLines,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -3933,7 +3927,6 @@ class InputDecoration {
|
||||
bool? alignLabelWithHint,
|
||||
BoxConstraints? constraints,
|
||||
VisualDensity? visualDensity,
|
||||
SemanticsService? semanticsService,
|
||||
}) {
|
||||
return InputDecoration(
|
||||
icon: icon ?? this.icon,
|
||||
|
@ -7,6 +7,7 @@ library;
|
||||
|
||||
import 'dart:ui' show TextDirection;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart' show SystemChannels;
|
||||
|
||||
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
|
||||
/// other platforms. The default mode is [Assertiveness.polite].
|
||||
///
|
||||
/// Not all platforms support announcements. Check to see if it is supported using
|
||||
/// [MediaQuery.announceOf] before calling this method.
|
||||
/// Not all platforms support announcements. Check to see if
|
||||
/// [isAnnounceSupported] before calling this method.
|
||||
///
|
||||
/// ### Android
|
||||
/// Android has [deprecated announcement events][1] due to its disruptive
|
||||
@ -65,4 +66,12 @@ abstract final class SemanticsService {
|
||||
final TooltipSemanticsEvent event = TooltipSemanticsEvent(message);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ import 'binding.dart';
|
||||
import 'focus_manager.dart';
|
||||
import 'focus_scope.dart';
|
||||
import 'framework.dart';
|
||||
import 'media_query.dart';
|
||||
import 'navigator.dart';
|
||||
import 'pop_scope.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);
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS) {
|
||||
unawaited(
|
||||
|
@ -103,9 +103,6 @@ enum _MediaQueryAspect {
|
||||
/// Specifies the aspect corresponding to [MediaQueryData.boldText].
|
||||
boldText,
|
||||
|
||||
/// Specifies the aspect corresponding to [MediaQueryData.announce].
|
||||
announce,
|
||||
|
||||
/// Specifies the aspect corresponding to [MediaQueryData.navigationMode].
|
||||
navigationMode,
|
||||
|
||||
@ -213,7 +210,6 @@ class MediaQueryData {
|
||||
this.onOffSwitchLabels = false,
|
||||
this.disableAnimations = false,
|
||||
this.boldText = false,
|
||||
this.announce = false,
|
||||
this.navigationMode = NavigationMode.traditional,
|
||||
this.gestureSettings = const DeviceGestureSettings(touchSlop: kTouchSlop),
|
||||
this.displayFeatures = const <ui.DisplayFeature>[],
|
||||
@ -299,7 +295,6 @@ class MediaQueryData {
|
||||
platformData?.disableAnimations ??
|
||||
view.platformDispatcher.accessibilityFeatures.disableAnimations,
|
||||
boldText = platformData?.boldText ?? view.platformDispatcher.accessibilityFeatures.boldText,
|
||||
announce = platformData?.announce ?? view.platformDispatcher.accessibilityFeatures.announce,
|
||||
highContrast =
|
||||
platformData?.highContrast ?? view.platformDispatcher.accessibilityFeatures.highContrast,
|
||||
onOffSwitchLabels =
|
||||
@ -589,23 +584,6 @@ class MediaQueryData {
|
||||
/// originates.
|
||||
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.
|
||||
///
|
||||
/// Some user interfaces are better navigated using a directional pad (DPAD)
|
||||
@ -687,7 +665,6 @@ class MediaQueryData {
|
||||
bool? invertColors,
|
||||
bool? accessibleNavigation,
|
||||
bool? boldText,
|
||||
bool? announce,
|
||||
NavigationMode? navigationMode,
|
||||
DeviceGestureSettings? gestureSettings,
|
||||
List<ui.DisplayFeature>? displayFeatures,
|
||||
@ -713,7 +690,6 @@ class MediaQueryData {
|
||||
disableAnimations: disableAnimations ?? this.disableAnimations,
|
||||
accessibleNavigation: accessibleNavigation ?? this.accessibleNavigation,
|
||||
boldText: boldText ?? this.boldText,
|
||||
announce: announce ?? this.announce,
|
||||
navigationMode: navigationMode ?? this.navigationMode,
|
||||
gestureSettings: gestureSettings ?? this.gestureSettings,
|
||||
displayFeatures: displayFeatures ?? this.displayFeatures,
|
||||
@ -916,7 +892,6 @@ class MediaQueryData {
|
||||
other.invertColors == invertColors &&
|
||||
other.accessibleNavigation == accessibleNavigation &&
|
||||
other.boldText == boldText &&
|
||||
other.announce == announce &&
|
||||
other.navigationMode == navigationMode &&
|
||||
other.gestureSettings == gestureSettings &&
|
||||
listEquals(other.displayFeatures, displayFeatures) &&
|
||||
@ -1738,27 +1713,6 @@ class MediaQuery extends InheritedModel<_MediaQueryAspect> {
|
||||
static bool? maybeBoldTextOf(BuildContext context) =>
|
||||
_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]
|
||||
/// ancestor or throws an exception, if no such ancestor exists.
|
||||
///
|
||||
@ -1889,7 +1843,6 @@ class MediaQuery extends InheritedModel<_MediaQueryAspect> {
|
||||
_MediaQueryAspect.disableAnimations =>
|
||||
data.disableAnimations != oldWidget.data.disableAnimations,
|
||||
_MediaQueryAspect.boldText => data.boldText != oldWidget.data.boldText,
|
||||
_MediaQueryAspect.announce => data.announce != oldWidget.data.announce,
|
||||
_MediaQueryAspect.navigationMode =>
|
||||
data.navigationMode != oldWidget.data.navigationMode,
|
||||
_MediaQueryAspect.gestureSettings =>
|
||||
|
@ -8945,67 +8945,55 @@ void main() {
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
for (final bool announce in <bool>[true, false]) {
|
||||
testWidgets('InputDecoration errorText semantics (announce=$announce)', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final SemanticsTester semantics = SemanticsTester(tester);
|
||||
final TextEditingController controller = _textEditingController();
|
||||
final Key key = UniqueKey();
|
||||
testWidgets('InputDecoration errorText semantics', (WidgetTester tester) async {
|
||||
final SemanticsTester semantics = SemanticsTester(tester);
|
||||
final TextEditingController controller = _textEditingController();
|
||||
final Key key = UniqueKey();
|
||||
|
||||
await tester.pumpWidget(
|
||||
overlay(
|
||||
child: MediaQuery(
|
||||
data: MediaQueryData(announce: announce),
|
||||
child: TextField(
|
||||
key: key,
|
||||
controller: controller,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'label',
|
||||
hintText: 'hint',
|
||||
errorText: 'oh no!',
|
||||
),
|
||||
await tester.pumpWidget(
|
||||
overlay(
|
||||
child: TextField(
|
||||
key: key,
|
||||
controller: controller,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'label',
|
||||
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,
|
||||
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;
|
||||
});
|
||||
}
|
||||
semantics.dispose();
|
||||
});
|
||||
|
||||
testWidgets('floating label does not overlap with value at large textScaleFactors', (
|
||||
WidgetTester tester,
|
||||
|
@ -2,8 +2,9 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/foundation.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';
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -169,67 +169,50 @@ void main() {
|
||||
await checkErrorText('');
|
||||
});
|
||||
|
||||
for (final _PlatformAnnounceScenario test in <_PlatformAnnounceScenario>[
|
||||
_PlatformAnnounceScenario(
|
||||
announce: false,
|
||||
testName:
|
||||
'Should announce only the first error message when validate returns errors and announce = false',
|
||||
),
|
||||
_PlatformAnnounceScenario(
|
||||
announce: true,
|
||||
testName:
|
||||
'Should not announce error message when validate returns errors and announce = true',
|
||||
),
|
||||
]) {
|
||||
testWidgets(test.testName, (WidgetTester tester) async {
|
||||
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: MediaQueryData(announce: test.announce),
|
||||
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'),
|
||||
],
|
||||
),
|
||||
testWidgets('Should announce only the first error message when validate returns errors', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
final GlobalKey<FormState> formKey = GlobalKey<FormState>();
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: const MediaQueryData(),
|
||||
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, '');
|
||||
await tester.pump();
|
||||
),
|
||||
);
|
||||
formKey.currentState!.reset();
|
||||
await tester.enterText(find.byType(TextFormField).first, '');
|
||||
await tester.pump();
|
||||
|
||||
// Manually validate.
|
||||
expect(find.text('First error message'), findsNothing);
|
||||
expect(find.text('Second error message'), findsNothing);
|
||||
formKey.currentState!.validate();
|
||||
await tester.pump();
|
||||
expect(find.text('First error message'), findsOneWidget);
|
||||
expect(find.text('Second error message'), findsOneWidget);
|
||||
// Manually validate.
|
||||
expect(find.text('First error message'), findsNothing);
|
||||
expect(find.text('Second error message'), findsNothing);
|
||||
formKey.currentState!.validate();
|
||||
await tester.pump();
|
||||
expect(find.text('First error message'), findsOneWidget);
|
||||
expect(find.text('Second error message'), findsOneWidget);
|
||||
|
||||
if (test.announce) {
|
||||
final CapturedAccessibilityAnnouncement announcement = tester.takeAnnouncements().single;
|
||||
expect(announcement.message, 'First error message');
|
||||
expect(announcement.textDirection, TextDirection.ltr);
|
||||
expect(announcement.assertiveness, Assertiveness.assertive);
|
||||
} else {
|
||||
final CapturedAccessibilityAnnouncement? announcement =
|
||||
tester.takeAnnouncements().firstOrNull;
|
||||
expect(announcement, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
final CapturedAccessibilityAnnouncement announcement = tester.takeAnnouncements().single;
|
||||
expect(announcement.message, 'First error message');
|
||||
expect(announcement.textDirection, TextDirection.ltr);
|
||||
expect(announcement.assertiveness, Assertiveness.assertive);
|
||||
});
|
||||
|
||||
testWidgets('isValid returns true when a field is valid', (WidgetTester tester) async {
|
||||
final GlobalKey<FormFieldState<String>> fieldKey1 = GlobalKey<FormFieldState<String>>();
|
||||
@ -405,7 +388,7 @@ void main() {
|
||||
Widget builder() {
|
||||
return MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: const MediaQueryData(announce: true),
|
||||
data: const MediaQueryData(),
|
||||
child: Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: Center(
|
||||
@ -1632,9 +1615,3 @@ void main() {
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
class _PlatformAnnounceScenario {
|
||||
_PlatformAnnounceScenario({required this.announce, required this.testName});
|
||||
final bool announce;
|
||||
final String testName;
|
||||
}
|
||||
|
@ -144,7 +144,6 @@ void main() {
|
||||
});
|
||||
|
||||
testWidgets('MediaQueryData.fromView is sane', (WidgetTester tester) async {
|
||||
tester.platformDispatcher.accessibilityFeaturesTestValue = const FakeAccessibilityFeatures();
|
||||
final MediaQueryData data = MediaQueryData.fromView(tester.view);
|
||||
expect(data, hasOneLineDescription);
|
||||
expect(data.hashCode, equals(data.copyWith().hashCode));
|
||||
@ -155,7 +154,6 @@ void main() {
|
||||
expect(data.boldText, false);
|
||||
expect(data.highContrast, false);
|
||||
expect(data.onOffSwitchLabels, false);
|
||||
expect(data.announce, false);
|
||||
expect(data.platformBrightness, Brightness.light);
|
||||
expect(data.gestureSettings.touchSlop, null);
|
||||
expect(data.displayFeatures, isEmpty);
|
||||
@ -171,7 +169,6 @@ void main() {
|
||||
boldText: true,
|
||||
highContrast: true,
|
||||
onOffSwitchLabels: true,
|
||||
announce: true,
|
||||
alwaysUse24HourFormat: true,
|
||||
navigationMode: NavigationMode.directional,
|
||||
);
|
||||
@ -205,7 +202,6 @@ void main() {
|
||||
expect(data.boldText, platformData.boldText);
|
||||
expect(data.highContrast, platformData.highContrast);
|
||||
expect(data.onOffSwitchLabels, platformData.onOffSwitchLabels);
|
||||
expect(data.announce, platformData.announce);
|
||||
expect(data.alwaysUse24HourFormat, platformData.alwaysUse24HourFormat);
|
||||
expect(data.navigationMode, platformData.navigationMode);
|
||||
expect(data.gestureSettings, DeviceGestureSettings.fromView(tester.view));
|
||||
@ -259,7 +255,6 @@ void main() {
|
||||
data.onOffSwitchLabels,
|
||||
tester.platformDispatcher.accessibilityFeatures.onOffSwitchLabels,
|
||||
);
|
||||
expect(data.announce, tester.platformDispatcher.accessibilityFeatures.announce);
|
||||
expect(data.alwaysUse24HourFormat, tester.platformDispatcher.alwaysUse24HourFormat);
|
||||
expect(data.navigationMode, NavigationMode.traditional);
|
||||
expect(data.gestureSettings, DeviceGestureSettings.fromView(tester.view));
|
||||
@ -279,7 +274,6 @@ void main() {
|
||||
boldText: true,
|
||||
highContrast: true,
|
||||
onOffSwitchLabels: true,
|
||||
announce: true,
|
||||
alwaysUse24HourFormat: true,
|
||||
navigationMode: NavigationMode.directional,
|
||||
);
|
||||
@ -327,7 +321,6 @@ void main() {
|
||||
expect(data.boldText, platformData.boldText);
|
||||
expect(data.highContrast, platformData.highContrast);
|
||||
expect(data.onOffSwitchLabels, platformData.onOffSwitchLabels);
|
||||
expect(data.announce, platformData.announce);
|
||||
expect(data.alwaysUse24HourFormat, platformData.alwaysUse24HourFormat);
|
||||
expect(data.navigationMode, platformData.navigationMode);
|
||||
expect(data.gestureSettings, DeviceGestureSettings.fromView(tester.view));
|
||||
@ -400,7 +393,6 @@ void main() {
|
||||
data.onOffSwitchLabels,
|
||||
tester.platformDispatcher.accessibilityFeatures.onOffSwitchLabels,
|
||||
);
|
||||
expect(data.announce, tester.platformDispatcher.accessibilityFeatures.announce);
|
||||
expect(data.alwaysUse24HourFormat, tester.platformDispatcher.alwaysUse24HourFormat);
|
||||
expect(data.navigationMode, NavigationMode.traditional);
|
||||
expect(data.gestureSettings, DeviceGestureSettings.fromView(tester.view));
|
||||
@ -585,7 +577,6 @@ void main() {
|
||||
expect(copied.boldText, data.boldText);
|
||||
expect(copied.highContrast, data.highContrast);
|
||||
expect(copied.onOffSwitchLabels, data.onOffSwitchLabels);
|
||||
expect(copied.announce, data.announce);
|
||||
expect(copied.platformBrightness, data.platformBrightness);
|
||||
expect(copied.gestureSettings, data.gestureSettings);
|
||||
expect(copied.displayFeatures, data.displayFeatures);
|
||||
@ -626,7 +617,6 @@ void main() {
|
||||
boldText: true,
|
||||
highContrast: true,
|
||||
onOffSwitchLabels: true,
|
||||
announce: true,
|
||||
platformBrightness: Brightness.dark,
|
||||
navigationMode: NavigationMode.directional,
|
||||
gestureSettings: gestureSettings,
|
||||
@ -646,7 +636,6 @@ void main() {
|
||||
expect(copied.boldText, true);
|
||||
expect(copied.highContrast, true);
|
||||
expect(copied.onOffSwitchLabels, true);
|
||||
expect(copied.announce, true);
|
||||
expect(copied.platformBrightness, Brightness.dark);
|
||||
expect(copied.navigationMode, NavigationMode.directional);
|
||||
expect(copied.gestureSettings, gestureSettings);
|
||||
@ -685,7 +674,6 @@ void main() {
|
||||
boldText: true,
|
||||
highContrast: true,
|
||||
onOffSwitchLabels: true,
|
||||
announce: true,
|
||||
navigationMode: NavigationMode.directional,
|
||||
displayFeatures: displayFeatures,
|
||||
),
|
||||
@ -722,7 +710,6 @@ void main() {
|
||||
expect(unpadded.boldText, true);
|
||||
expect(unpadded.highContrast, true);
|
||||
expect(unpadded.onOffSwitchLabels, true);
|
||||
expect(unpadded.announce, true);
|
||||
expect(unpadded.navigationMode, NavigationMode.directional);
|
||||
expect(unpadded.displayFeatures, displayFeatures);
|
||||
});
|
||||
@ -761,7 +748,6 @@ void main() {
|
||||
boldText: true,
|
||||
highContrast: true,
|
||||
onOffSwitchLabels: true,
|
||||
announce: true,
|
||||
navigationMode: NavigationMode.directional,
|
||||
displayFeatures: displayFeatures,
|
||||
),
|
||||
@ -795,7 +781,6 @@ void main() {
|
||||
expect(unpadded.boldText, true);
|
||||
expect(unpadded.highContrast, true);
|
||||
expect(unpadded.onOffSwitchLabels, true);
|
||||
expect(unpadded.announce, true);
|
||||
expect(unpadded.navigationMode, NavigationMode.directional);
|
||||
expect(unpadded.displayFeatures, displayFeatures);
|
||||
});
|
||||
@ -834,7 +819,6 @@ void main() {
|
||||
boldText: true,
|
||||
highContrast: true,
|
||||
onOffSwitchLabels: true,
|
||||
announce: true,
|
||||
navigationMode: NavigationMode.directional,
|
||||
displayFeatures: displayFeatures,
|
||||
),
|
||||
@ -871,7 +855,6 @@ void main() {
|
||||
expect(unpadded.boldText, true);
|
||||
expect(unpadded.highContrast, true);
|
||||
expect(unpadded.onOffSwitchLabels, true);
|
||||
expect(unpadded.announce, true);
|
||||
expect(unpadded.navigationMode, NavigationMode.directional);
|
||||
expect(unpadded.displayFeatures, displayFeatures);
|
||||
});
|
||||
@ -910,7 +893,6 @@ void main() {
|
||||
boldText: true,
|
||||
highContrast: true,
|
||||
onOffSwitchLabels: true,
|
||||
announce: true,
|
||||
navigationMode: NavigationMode.directional,
|
||||
displayFeatures: displayFeatures,
|
||||
),
|
||||
@ -944,7 +926,6 @@ void main() {
|
||||
expect(unpadded.boldText, true);
|
||||
expect(unpadded.highContrast, true);
|
||||
expect(unpadded.onOffSwitchLabels, true);
|
||||
expect(unpadded.announce, true);
|
||||
expect(unpadded.navigationMode, NavigationMode.directional);
|
||||
expect(unpadded.displayFeatures, displayFeatures);
|
||||
});
|
||||
@ -983,7 +964,6 @@ void main() {
|
||||
boldText: true,
|
||||
highContrast: true,
|
||||
onOffSwitchLabels: true,
|
||||
announce: true,
|
||||
navigationMode: NavigationMode.directional,
|
||||
displayFeatures: displayFeatures,
|
||||
),
|
||||
@ -1020,7 +1000,6 @@ void main() {
|
||||
expect(unpadded.boldText, true);
|
||||
expect(unpadded.highContrast, true);
|
||||
expect(unpadded.onOffSwitchLabels, true);
|
||||
expect(unpadded.announce, true);
|
||||
expect(unpadded.navigationMode, NavigationMode.directional);
|
||||
expect(unpadded.displayFeatures, displayFeatures);
|
||||
});
|
||||
@ -1059,7 +1038,6 @@ void main() {
|
||||
boldText: true,
|
||||
highContrast: true,
|
||||
onOffSwitchLabels: true,
|
||||
announce: true,
|
||||
navigationMode: NavigationMode.directional,
|
||||
displayFeatures: displayFeatures,
|
||||
),
|
||||
@ -1093,7 +1071,6 @@ void main() {
|
||||
expect(unpadded.boldText, true);
|
||||
expect(unpadded.highContrast, true);
|
||||
expect(unpadded.onOffSwitchLabels, true);
|
||||
expect(unpadded.announce, true);
|
||||
expect(unpadded.navigationMode, NavigationMode.directional);
|
||||
expect(unpadded.displayFeatures, displayFeatures);
|
||||
});
|
||||
@ -1198,32 +1175,6 @@ void main() {
|
||||
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 {
|
||||
late bool outsideBoldTextOverride;
|
||||
late bool insideBoldTextOverride;
|
||||
@ -1363,7 +1314,6 @@ void main() {
|
||||
boldText: true,
|
||||
highContrast: true,
|
||||
onOffSwitchLabels: true,
|
||||
announce: true,
|
||||
displayFeatures: displayFeatures,
|
||||
),
|
||||
child: Builder(
|
||||
@ -1395,7 +1345,6 @@ void main() {
|
||||
expect(subScreenMediaQuery.boldText, true);
|
||||
expect(subScreenMediaQuery.highContrast, true);
|
||||
expect(subScreenMediaQuery.onOffSwitchLabels, true);
|
||||
expect(subScreenMediaQuery.announce, true);
|
||||
expect(subScreenMediaQuery.displayFeatures, isEmpty);
|
||||
});
|
||||
|
||||
@ -1442,7 +1391,6 @@ void main() {
|
||||
boldText: true,
|
||||
highContrast: true,
|
||||
onOffSwitchLabels: true,
|
||||
announce: true,
|
||||
displayFeatures: displayFeatures,
|
||||
),
|
||||
child: Builder(
|
||||
@ -1480,7 +1428,6 @@ void main() {
|
||||
expect(subScreenMediaQuery.boldText, true);
|
||||
expect(subScreenMediaQuery.highContrast, true);
|
||||
expect(subScreenMediaQuery.onOffSwitchLabels, true);
|
||||
expect(subScreenMediaQuery.announce, true);
|
||||
expect(subScreenMediaQuery.displayFeatures, <DisplayFeature>[cutoutDisplayFeature]);
|
||||
});
|
||||
|
||||
@ -1745,8 +1692,6 @@ void main() {
|
||||
MediaQuery.maybeOnOffSwitchLabelsOf,
|
||||
MediaQueryData(onOffSwitchLabels: true),
|
||||
),
|
||||
const _MediaQueryAspectCase(MediaQuery.announceOf, MediaQueryData(announce: true)),
|
||||
const _MediaQueryAspectCase(MediaQuery.maybeAnnounceOf, MediaQueryData(announce: true)),
|
||||
const _MediaQueryAspectCase(
|
||||
MediaQuery.disableAnimationsOf,
|
||||
MediaQueryData(disableAnimations: true),
|
||||
|
@ -32,7 +32,6 @@ class FakeAccessibilityFeatures implements AccessibilityFeatures {
|
||||
this.reduceMotion = false,
|
||||
this.highContrast = false,
|
||||
this.onOffSwitchLabels = false,
|
||||
this.announce = false,
|
||||
});
|
||||
|
||||
/// An instance of [AccessibilityFeatures] where all the features are enabled.
|
||||
@ -44,7 +43,6 @@ class FakeAccessibilityFeatures implements AccessibilityFeatures {
|
||||
reduceMotion: true,
|
||||
highContrast: true,
|
||||
onOffSwitchLabels: true,
|
||||
announce: true,
|
||||
);
|
||||
|
||||
@override
|
||||
@ -68,9 +66,6 @@ class FakeAccessibilityFeatures implements AccessibilityFeatures {
|
||||
@override
|
||||
final bool onOffSwitchLabels;
|
||||
|
||||
@override
|
||||
final bool announce;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (other.runtimeType != runtimeType) {
|
||||
@ -83,8 +78,7 @@ class FakeAccessibilityFeatures implements AccessibilityFeatures {
|
||||
other.boldText == boldText &&
|
||||
other.reduceMotion == reduceMotion &&
|
||||
other.highContrast == highContrast &&
|
||||
other.onOffSwitchLabels == onOffSwitchLabels &&
|
||||
other.announce == announce;
|
||||
other.onOffSwitchLabels == onOffSwitchLabels;
|
||||
}
|
||||
|
||||
@override
|
||||
@ -97,7 +91,6 @@ class FakeAccessibilityFeatures implements AccessibilityFeatures {
|
||||
reduceMotion,
|
||||
highContrast,
|
||||
onOffSwitchLabels,
|
||||
announce,
|
||||
);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user