-
Notifications
You must be signed in to change notification settings - Fork 29.1k
Snackbar a11y fix #173686
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Snackbar a11y fix #173686
Conversation
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request introduces an accessibility improvement for SnackBars by explicitly announcing their content using SemanticsService.announce
. This is achieved by wrapping the showSnackBar
logic in a new showAccessibleSnackBar
helper method. The changes also include updates to the corresponding tests.
My review identifies a few areas for improvement:
- The use of
Future.delayed
for the semantics announcement could be fragile. I've suggested a more robust alternative. - The test file contains duplicate imports that should be cleaned up.
- A useful assertion was removed from a test, and I recommend re-adding it to improve test robustness.
Overall, this is a good step towards better accessibility. Addressing the feedback will make the implementation more solid.
Future<void>.delayed(const Duration(milliseconds: 100), () { | ||
debugPrint('Announcing: $message'); | ||
SemanticsService.announce(message, TextDirection.ltr); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of Future.delayed
with a fixed duration can be fragile. The announcement might occur before the SnackBar is fully visible, especially on slower devices or if animations are disabled. For more robustness, consider using WidgetsBinding.instance.addPostFrameCallback
to schedule the announcement after the current frame has been rendered. This ensures the SnackBar is on screen when the announcement is made.
WidgetsBinding.instance.addPostFrameCallback((_) {
debugPrint('Announcing: $message');
SemanticsService.announce(message, TextDirection.ltr);
});
import 'package:a11y_assessments/use_cases/snack_bar.dart'; | ||
import 'package:flutter_test/flutter_test.dart'; | ||
|
||
import 'test_utils.dart'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import 'package:flutter_test/flutter_test.dart'; | ||
|
||
import 'test_utils.dart'; | ||
|
||
void main() { | ||
testWidgets('snack bar can run', (WidgetTester tester) async { | ||
await pumpsUseCase(tester, SnackBarUseCase()); | ||
const String snackBarText = 'Awesome Snackbar!'; | ||
expect(find.text(snackBarText), findsNothing); | ||
await tester.tap(find.text('Show Snackbar')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assertion expect(find.text(snackBarText), findsNothing);
was removed from this test. This check is valuable because it verifies that the SnackBar is not visible immediately after the tap event and before tester.pump()
is called. Restoring this assertion would make the test more thorough by explicitly checking the state transition.
await tester.tap(find.text('Show Snackbar'));
expect(find.text(snackBarText), findsNothing);
); | ||
|
||
Future<void>.delayed(const Duration(milliseconds: 100), () { | ||
debugPrint('Announcing: $message'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let's remove print statement
), | ||
); | ||
|
||
Future<void>.delayed(const Duration(milliseconds: 100), () { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should only do this for MediaQueryData.supportsAnnounce is true, you can get this from MediaQuery.of(context).
await tester.pumpAndSettle(); | ||
final Finder findHeadingLevelOnes = find.bySemanticsLabel('SnackBar Demo'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a expect that the message is announced? See this file for example
https://github.com/flutter/flutter/blob/master/packages/flutter/test/semantics/semantics_service_test.dart
…et tests to verify Snackbar visibility and announcement.
dev/a11y_assessments/pubspec.yaml
Outdated
@@ -5,8 +5,6 @@ version: 5.0.0+5 | |||
environment: | |||
sdk: ^3.8.0-0 | |||
|
|||
resolution: workspace |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why this change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line was removed because it was not required for this package to work correctly, and it was causing resolution issues during local development. Let me know if you'd prefer to keep it.
@@ -1,8 +1,8 @@ | |||
// Copyright 2014 The Flutter Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style license that can be | |||
// found in the LICENSE file. | |||
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we revert this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure
@@ -1,27 +1,78 @@ | |||
// Copyright 2014 The Flutter Authors. All rights reserved. | |||
// Use of this source code is governed by a BSD-style license that can be | |||
// found in the LICENSE file. | |||
// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay
|
||
// Verify the message was announced for accessibility | ||
await tester.pumpAndSettle(); | ||
// Note: In test environment, SemanticsService.announce may not work the same way |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In test environment the entire widgets binding are mocked, and we should have api to catch any calls to SystemChannel.accessibility, which is where the announcement is sent to
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing that out! I've updated the test to intercept SystemChannels.accessibility and verify that the correct announcement message is sent. Let me know if you have any suggestions on improving the implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems not addressed. Do you forget to push the change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @chunhtai, thanks for the review! I just pushed the update that intercepts SystemChannels.accessibility and verifies the announcement message in the latest commit. Could you please check if the change is now visible on your end? Let me know if anything else needs to be addressed. Thanks!
@@ -29,6 +29,25 @@ class MainWidget extends StatefulWidget { | |||
class MainWidgetState extends State<MainWidget> { | |||
String pageTitle = getUseCaseName(SnackBarUseCase()); | |||
|
|||
/// Shows a SnackBar with accessibility announcement support. | |||
/// This method checks if the device supports announcements and announces |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
/// This method checks if the device supports announcements and announces | |
/// | |
/// This method checks if the device supports announcements and announces |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a new line after the first paragraph
|
||
// Verify the message was announced for accessibility | ||
await tester.pumpAndSettle(); | ||
// Note: In test environment, SemanticsService.announce may not work the same way |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems not addressed. Do you forget to push the change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@nancyflutter Could you sign the CLA? |
also, looks like you forgot to sign the cla |
Co-authored-by: Loïc Sharma <737941+loic-sharma@users.noreply.github.com>
Replace this paragraph with a description of what this PR is changing or adding, and why. Consider including before/after screenshots.
List which issues are fixed by this PR. You must list at least one issue. An issue is not required if the PR fixes something trivial like a typo.
If you had to change anything in the flutter/tests repo, include a link to the migration guide as per the breaking change policy.
Pre-launch Checklist
///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.
Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the
gemini-code-assist
bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.