Skip to content

Migrate CalendarDatePicker to declarative live regions for accessibility - #180131

Closed
Franklyn-R-Silva wants to merge 17 commits into
flutter:masterfrom
Franklyn-R-Silva:migrate-datepicker-announcements-to-live-region
Closed

Franklyn-R-Silva wants to merge 17 commits into
flutter:masterfrom
Franklyn-R-Silva:migrate-datepicker-announcements-to-live-region

Conversation

@Franklyn-R-Silva

Copy link
Copy Markdown
Contributor

Description

This PR migrates the CalendarDatePicker and _MonthPicker widgets from the deprecated SemanticsService.sendAnnouncement to a declarative approach using liveRegion: true.

As specified in #180096 and #177785, SemanticsService.sendAnnouncement is deprecated on Android as of API 36. By using a live region, we ensure that state change announcements (like month navigation and date selection) remain accessible and compliant with modern platform standards.

Changes:

  • Added _announcementText to _CalendarDatePickerState and _MonthPickerState to track accessibility messages.
  • Introduced an invisible Semantics widget with liveRegion: true in the build methods to process these announcements.
  • Updated calendar_date_picker_test.dart to verify the SemanticsFlag.isLiveRegion on the semantics tree instead of capturing imperatice announcements.

Related Issues

Fixes #177785
Relates to #180096

Tests

  • Updated packages/flutter/test/material/calendar_date_picker_test.dart to verify the new declarative announcement logic.

@github-actions github-actions Bot added framework flutter/packages/flutter repository. See also f: labels. p: material_ui material_ui package in flutter/packages labels Dec 19, 2025

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

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 successfully migrates CalendarDatePicker and _MonthPicker from the deprecated SemanticsService.sendAnnouncement to a declarative approach using Semantics with liveRegion: true. The changes are well-implemented, replacing imperative announcements with state updates that drive a live region for accessibility announcements.

My main feedback is on the test file calendar_date_picker_test.dart. While the test was correctly updated to reflect the new implementation, I found the logic for verifying semantics announcements to be brittle and contain a subtle bug. I've provided a suggestion to refactor the test for improved robustness and readability.

Overall, this is a solid improvement for accessibility compliance.

@hannah-hyj

hannah-hyj commented Dec 19, 2025 •

Copy link
Copy Markdown
Member

Hi @Franklyn-R-Silva,
Thank you for your attention to this a11y issue!
Not sure if you've had a chance to test this on Android yet. from the first glance, you're adding a live region outside of a SizedBox.shrink() and it will result in a zero-size node, which will cause it to be dropped from the semantics tree, and nothing will be announced.
To actually address this issue we need to wrap the semantics live region with a relevant widget and use accessiblityFocusBlockType to make sure focus traversal is still correct after adding the live region and test on an android device. Also we will only remove the a11y announcement for android but keep it on other platform.

…icker

- Wrap Semantics live region with SizedBox and Column to avoid zero-size nodes.
- Add accessiblityFocusBlockType to ensure correct focus traversal.
- Maintain imperative announcements for non-Android platforms in _handleDayChanged and _handleMonthPageChanged.
@Franklyn-R-Silva

Copy link
Copy Markdown
Contributor Author

Hi @Franklyn-R-Silva, Thank you for your attention to this a11y issue! Not sure if you've had a chance to test this on Android yet. from the first glance, you're adding a live region outside of a SizedBox.shrink() and it will result in a zero-size node, which will cause it to be dropped from the semantics tree, and nothing will be announced. To actually address this issue we need to wrap the semantics live region with a relevant widget and use accessiblityFocusBlockType to make sure focus traversal is still correct after adding the live region and test on an android device. Also we will only remove the a11y announcement for android but keep it on other platform.

Hi @hannah-hyj, thank you for the feedback!

I have updated the code to address the accessibility concerns:

Visibility: I wrapped the Semantics live regions with widgets that have physical dimensions (SizedBox and Column) to ensure they are not dropped from the semantics tree.

Focus Traversal: I added accessiblityFocusBlockType: AccessiblityFocusBlockType.none to maintain correct traversal after adding the live regions.

Platform Parity: I refactored the announcement logic to ensure Android uses the declarative approach while maintaining imperative announcements for other platforms.

Note: I am still working on updating the tests in calendar_date_picker_test.dart to reflect these changes and will push them shortly. I will ping you again once the tests are ready for review.

@Franklyn-R-Silva

Copy link
Copy Markdown
Contributor Author

Hi @Franklyn-R-Silva, Thank you for your attention to this a11y issue! Not sure if you've had a chance to test this on Android yet. from the first glance, you're adding a live region outside of a SizedBox.shrink() and it will result in a zero-size node, which will cause it to be dropped from the semantics tree, and nothing will be announced. To actually address this issue we need to wrap the semantics live region with a relevant widget and use accessiblityFocusBlockType to make sure focus traversal is still correct after adding the live region and test on an android device. Also we will only remove the a11y announcement for android but keep it on other platform.

Hi @hannah-hyj,

I’ve updated the PR to address your feedback:

Visibility: Wrapped Semantics in visible containers (SizedBox/Column) to prevent zero-size nodes.

Focus: Added accessiblityFocusBlockType to ensure correct traversal.

Platforms: Refactored logic to use declarative live regions on Android and maintain imperative announcements on other platforms.

Tests: Updated tests using the Android variant to verify the isLiveRegion flag.

Ready for another look!

@hannah-hyj
hannah-hyj self-requested a review January 6, 2026 21:21

@hannah-hyj hannah-hyj left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @Franklyn-R-Silva I just got back from the vacation and I'm taking a look at this PR again!

@Franklyn-R-Silva Franklyn-R-Silva left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

review

return Stack(
children: <Widget>[
SizedBox(height: _subHeaderHeight + scaledMaxDayPickerHeight, child: _buildPicker()),
Semantics(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should only add this Semantics widget here if platform == TargetPlatform.android.

'${_localizations.selectedDateLabel} ${widget.calendarDelegate.formatFullDate(_selectedDate!, _localizations)}$semanticLabelSuffix',
_textDirection,
);
case TargetPlatform.android:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like the previous behavior was to use sendAnnouncement only on Linux, macOS, and Windows, but not on Android or iOS. I'm not sure why that was the case. However, since the goal of this PR is to remove the deprecated sendAnnouncement usage on Android, I don't think we need to change anything in this code block (L313–L328).

@hannah-hyj

Copy link
Copy Markdown
Member

Hi @Franklyn-R-Silva ! this PR looks good to me over all and i just left a few more comments!

@hannah-hyj hannah-hyj added the a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) label Jan 16, 2026
@hannah-hyj

Copy link
Copy Markdown
Member

looks like some tests in calendar_date_picker_test.dart is failing, could you take a look?

@github-actions github-actions Bot removed the a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) label Jan 20, 2026
@Franklyn-R-Silva

Copy link
Copy Markdown
Contributor Author

looks like some tests in calendar_date_picker_test.dart is failing, could you take a look?

Yes, I'll check it out. I'm very busy at work and haven't had time to check everything yet.

@flutter-dashboard

Copy link
Copy Markdown

This pull request executed golden file tests, but it has not been updated in a while (20+ days). Test results from Gold expire after as many days, so this pull request will need to be updated with a fresh commit in order to get results from Gold.

For more guidance, visit Writing a golden file test for package:flutter.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@justinmc

Copy link
Copy Markdown
Contributor

@Franklyn-R-Silva Do you still plan to come back and finish this PR?

@hannah-hyj

Copy link
Copy Markdown
Member

hi @Franklyn-R-Silva, thank you for your attention to this issue. I will now mark this PR as inactive and close it, and create a new PR based on it.

@hannah-hyj hannah-hyj closed this Feb 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

framework flutter/packages/flutter repository. See also f: labels. p: material_ui material_ui package in flutter/packages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Remove ExpansionTile usage of Semantic Announcements

3 participants