Skip to content

Add non-text color contrast evaluation for a11y#183569

Merged
auto-submit[bot] merged 8 commits into
flutter:masterfrom
QuncCccccc:non_text_contrast_evaluation
Mar 20, 2026
Merged

Add non-text color contrast evaluation for a11y#183569
auto-submit[bot] merged 8 commits into
flutter:masterfrom
QuncCccccc:non_text_contrast_evaluation

Conversation

@QuncCccccc

Copy link
Copy Markdown
Contributor

Fixes #170687

This PR is to add an evaluation to ensure non-text controls have enough color contrast.

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • I followed the [breaking change policy] and added [Data Driven Fixes] where supported.
  • All existing and new tests are passing.

@github-actions github-actions Bot added framework flutter/packages/flutter repository. See also f: labels. a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) labels Mar 12, 2026
@QuncCccccc QuncCccccc marked this pull request as ready for review March 12, 2026 23:40
@QuncCccccc QuncCccccc requested a review from chunhtai March 12, 2026 23:40

@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 introduces a new accessibility evaluation for non-text color contrast, which is a valuable addition. The implementation is generally well-structured, and the accompanying tests provide good coverage. However, I've identified a few critical issues related to coordinate system calculations and a potential null pointer exception that need to be addressed to ensure correctness. I've also included some minor suggestions to improve code clarity and maintainability.

Comment thread packages/flutter/lib/src/widgets/_accessibility_evaluations.dart Outdated
Comment on lines +590 to +595
final logicalBounds = Rect.fromLTRB(
nodeBounds.left / devicePixelRatio,
nodeBounds.top / devicePixelRatio,
nodeBounds.right / devicePixelRatio,
nodeBounds.bottom / devicePixelRatio,
);

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.

high

The nodeBounds are calculated in logical pixels. However, they are then incorrectly divided by devicePixelRatio. Since the image capture and color analysis are performed in logical pixels, logicalBounds should be assigned directly from nodeBounds.

    final Rect logicalBounds = nodeBounds;

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.

The transformation chain seem contain layout scaling instructions

Comment on lines +654 to +660
bool _isNodeOffScreen(Rect paintBounds, ui.FlutterView window) {
final Size windowPhysicalSize = window.physicalSize * window.devicePixelRatio;
return paintBounds.top < -50.0 ||
paintBounds.left < -50.0 ||
paintBounds.bottom > windowPhysicalSize.height + 50.0 ||
paintBounds.right > windowPhysicalSize.width + 50.0;
}

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.

high

The paintBounds (in logical pixels) are being compared against a size derived from window.physicalSize * window.devicePixelRatio, which is incorrect. The comparison should be against the window's logical size, which is window.physicalSize / window.devicePixelRatio. This seems to be a recurring pattern in this file and should be corrected for accuracy.

Suggested change
bool _isNodeOffScreen(Rect paintBounds, ui.FlutterView window) {
final Size windowPhysicalSize = window.physicalSize * window.devicePixelRatio;
return paintBounds.top < -50.0 ||
paintBounds.left < -50.0 ||
paintBounds.bottom > windowPhysicalSize.height + 50.0 ||
paintBounds.right > windowPhysicalSize.width + 50.0;
}
bool _isNodeOffScreen(Rect paintBounds, ui.FlutterView window) {
final Size windowLogicalSize = window.physicalSize / window.devicePixelRatio;
return paintBounds.top < -50.0 ||
paintBounds.left < -50.0 ||
paintBounds.bottom > windowLogicalSize.height + 50.0 ||
paintBounds.right > windowLogicalSize.width + 50.0;
}

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.

I changed it to use xxxLogicalSize in the base class. @chunhtai

Comment thread packages/flutter/lib/src/widgets/_accessibility_evaluations.dart Outdated
Comment on lines +648 to +651
if (!isControl) {
return true;
}
return false;

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.

medium

This logic can be simplified to a single return statement, which improves readability and conciseness.

Suggested change
if (!isControl) {
return true;
}
return false;
return !isControl;

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.

I think the current implementation is easier to understand.

final ByteData? byteData = await image.toByteData();

violations.addAll(await _evaluateNode(root, image, byteData!, renderView));
if (byteData != null) {

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.

in what case will this be null?

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.

Is it possible that await image.toByteData() might return null? Just wanted to give a explicit null guard because I saw the type is ByteSata?, Or maybe we should change the type to ByteData.

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.

I will change the line 252 to
final ByteData byteData = await image.toByteData()!;

unless there is a use case that it can be null

Comment thread packages/flutter/lib/src/widgets/_accessibility_evaluations.dart Outdated
Comment thread packages/flutter/lib/src/widgets/_accessibility_evaluations.dart Outdated
Comment thread packages/flutter/lib/src/widgets/_accessibility_evaluations.dart Outdated
@QuncCccccc QuncCccccc force-pushed the non_text_contrast_evaluation branch from ed5f391 to a58a17d Compare March 17, 2026 00:03
@QuncCccccc QuncCccccc requested a review from chunhtai March 17, 2026 19:39
@QuncCccccc QuncCccccc force-pushed the non_text_contrast_evaluation branch from 9e539f8 to d0343e9 Compare March 17, 2026 19:40
@QuncCccccc QuncCccccc added the CICD Run CI/CD label Mar 17, 2026
@github-actions github-actions Bot removed the CICD Run CI/CD label Mar 17, 2026
@QuncCccccc QuncCccccc added the CICD Run CI/CD label Mar 17, 2026
chunhtai
chunhtai previously approved these changes Mar 18, 2026

@chunhtai chunhtai 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.

just some nit, otherwise, LGTM

final ByteData? byteData = await image.toByteData();

violations.addAll(await _evaluateNode(root, image, byteData!, renderView));
if (byteData != null) {

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.

I will change the line 252 to
final ByteData byteData = await image.toByteData()!;

unless there is a use case that it can be null

/// The minimum contrast ratio for non-text controls.
///
/// Defined by http://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html
static const double kMinimumRatioNonText = 3.0;

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.

can this be kept private?

@github-actions github-actions Bot removed the CICD Run CI/CD label Mar 19, 2026
@QuncCccccc QuncCccccc added the CICD Run CI/CD label Mar 19, 2026
chunhtai
chunhtai previously approved these changes Mar 19, 2026

@chunhtai chunhtai 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.

LGTM

@QuncCccccc QuncCccccc added the autosubmit Merge PR when tree becomes green via auto submit App label Mar 19, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Mar 19, 2026
@auto-submit

auto-submit Bot commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

autosubmit label was removed for flutter/flutter/183569, because - The status or check suite Linux web_canvaskit_tests_5 has failed. Please fix the issues identified (or deflake) before re-applying this label.

@QuncCccccc QuncCccccc force-pushed the non_text_contrast_evaluation branch from 4c1de26 to 374b6b9 Compare March 19, 2026 20:14
@github-actions github-actions Bot removed the CICD Run CI/CD label Mar 19, 2026
@QuncCccccc QuncCccccc added the CICD Run CI/CD label Mar 19, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 21, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 22, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 22, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 23, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 23, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 23, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 23, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 24, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 24, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 24, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 24, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 24, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 24, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 25, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 25, 2026
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 25, 2026
mboetger pushed a commit to mboetger/flutter that referenced this pull request Mar 26, 2026
Fixes flutter#170687

This PR is to add an evaluation to ensure non-text controls have enough
color contrast.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [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.
ahmedsameha1 pushed a commit to ahmedsameha1/flutter that referenced this pull request Apr 14, 2026
Fixes flutter#170687

This PR is to add an evaluation to ensure non-text controls have enough
color contrast.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) CICD Run CI/CD framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[A11y] non-text control color contrast

3 participants