Repository navigation
Description
Reproduced on Flutter 3.47.4 stable (9584c67), Chrome on macOS. The code involved is unchanged on master 697012a (links below).
Steps to reproduce
- Run
flutter build webon the code sample below, serve the build and open it in Chrome. - Turn semantics on: run
document.querySelector('flt-semantics-placeholder').click()in the console, or use a screen reader. - Inspect the checkbox's
flt-semanticselement, or the checkbox in Chrome's accessibility tree.
Expected results
The checkbox has aria-checked="mixed", so a screen reader announces it as "mixed" or "partially checked".
Actual results
The checkbox has aria-checked="false", so a screen reader announces it as not checked.
The framework side is correct: the semantics node for the same checkbox has isChecked == CheckedState.mixed (see the widget test under Logs). The value is lost in the web engine.
Code sample
Code sample
import 'package:flutter/material.dart';
void main() => runApp(
MaterialApp(
home: Scaffold(
body: Checkbox(tristate: true, value: null, onChanged: (_) {}, semanticLabel: 'Team'),
),
),
);Screenshots or Video
Not applicable: the logs below are the evidence.
Logs
Logs
The checkbox's DOM element and its node in Chrome's accessibility tree:
DOM <flt-semantics id="flt-semantic-node-4" tabindex="0" aria-label="Team" flt-tappable="" role="checkbox" aria-checked="false" …>
AX checkbox "Team" checked="false"
The widget test below, run on the same code sample, prints:
checkbox: isChecked=CheckedState.mixed
Widget test showing the framework sends CheckedState.mixed
package:repro/mixed_checkbox.dart is the code sample above.
import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:repro/mixed_checkbox.dart' as mixed;
void main() {
testWidgets('tristate checkbox', (tester) async {
final h = tester.ensureSemantics();
mixed.main();
await tester.pump();
final d = find.semantics.byLabel('Team').evaluate().single.getSemanticsData();
debugPrint('checkbox: isChecked=${d.flagsCollection.isChecked}');
h.dispose();
});
}Cause
SemanticCheckable.updatewritesaria-checkedasisChecked == CheckedState.isTrue || isToggled == Tristate.isTrue ? 'true' : 'false'. It has no branch for the mixed state. The same code is on master: checkable.dart#L103-L109.- The separate
Checkablebehavior already writes'mixed'whensemanticsObject.isMixedis true. It is attached only throughaddCheckedBehavior(), which onlySemanticMenuItemCheckbox(menus.dart#L177) andSemanticMenuItemRadio(menus.dart#L198) call.SemanticCheckable, which rendersrole="checkbox", never does. On master these are checkable.dart#L195-L214, semantics.dart#L2517, and menus.dart#L125 and #L146.
Suggested fix
In SemanticCheckable.update, write 'mixed' when semanticsObject.isMixed is true and _kind == _CheckableKind.checkbox. Alternatively, call addCheckedBehavior() from the SemanticCheckable constructor and drop the inline aria-checked write. For radio and switch, ARIA allows only true and false, so keep the current two-way value for those.
Related issues
- Expose the tri-state checkbox "indeterminate" state in the semantics data #20763 ("Expose the tri-state checkbox 'indeterminate' state in the semantics data", open, 2018) is the old framework-side proposal. It has since been done: the framework now sends
CheckedState.mixed. It could be closed or pointed at this issue. - Semantics for
Checkboxshould react to the three-state mixed state #110107 ("Semantics forCheckboxshould react to the three-state mixed state", closed) was fixed on the framework side by Use tristate checkbox engine changes #111032. A comment after that fix reported that NVDA in Chrome still did not read the half-checked state. That issue is locked, and this issue is the web half of it.
Flutter Doctor output
Doctor output
[✓] Flutter (Channel stable, 3.47.4, on macOS 27.0.1 26A434 darwin-arm64, locale en-GB)
• Flutter version 3.47.4 on channel stable at ~/Development/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 9584c6713b (5 weeks ago), 2026-09-10 15:25:10 -0700
• Engine revision 06a2e2a110
• Dart version 3.13.3
• DevTools version 2.60.0
• Feature flags: enable-web, enable-linux-desktop, enable-macos-desktop, enable-windows-desktop, enable-android, enable-ios, cli-animations, enable-native-assets, enable-record-use, enable-swift-package-manager, omit-legacy-version-file, enable-lldb-debugging, enable-uiscene-migration
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Connected device (2 available)
• macOS (desktop) • macos • darwin-arm64 • macOS 27.0.1 26A434 darwin-arm64
• Chrome (web) • chrome • web-javascript • Google Chrome 155.0.8059.82
[✓] Network resources
• All expected network resources are available.
(Android and Xcode checks timed out on this machine and are left out; they do not apply to these web/framework reports.)
Activity
puneetkukreja98 commentedon Oct 16, 2026
Thanks for reporting this. I was able to reproduce this on the latest stable channel on Windows.
Findings:
Screen-shot

flutter doctor -v:
flutter-zl commentedon Oct 21, 2026
Checkboxwithvalue: null.document.querySelector('flt-semantics[role=checkbox]').getAttribute('aria-checked')in the console."false". Fixed:"mixed".[web] Expose mixed tristate checkboxes as aria-checked="mixed" (flutt…
github-actions commentedon Nov 12, 2026
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.