Skip to content

Conversation

@loic-sharma
Copy link
Member

@loic-sharma loic-sharma commented Feb 25, 2025

This adds a new "radio group" accessibility role to dart:ui and the Flutter web engine.

This does not update existing widgets to use this new role. Currently, users must manually add a Semantics widget to use this accessibility role. See the example app below.

Part of: #162093

Example app

Example app that uses the radio group role...
import 'dart:ui';

import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart';

void main() {
  runApp(const RadioExampleApp());
  SemanticsBinding.instance.ensureSemantics();
}

class RadioExampleApp extends StatelessWidget {
  const RadioExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Radio Sample')),
        body: const Center(child: RadioExample()),
      ),
    );
  }
}

class RadioExample extends StatefulWidget {
  const RadioExample({super.key});

  @override
  State<RadioExample> createState() => _RadioExampleState();
}

class _RadioExampleState extends State<RadioExample> {
  int _groupValue = 0;

  @override
  Widget build(BuildContext context) {
    return Semantics(
      label: 'Radio group',
      role: SemanticsRole.radioGroup,
      explicitChildNodes: true,
      child: Column(
        children: <Widget>[
          ListTile(
            title: const Text('Foo'),
            leading: Radio<int>(
              value: 0,
              groupValue: _groupValue,
              onChanged: (int? value) => setState(() => _groupValue = value ?? 0),
            ),
          ),
          ListTile(
            title: const Text('Bar'),
            leading: Radio<int>(
              value: 1,
              groupValue: _groupValue,
              onChanged: (int? value) => setState(() => _groupValue = value ?? 0),
            ),
          ),
        ],
      ),
    );
  }
}
Accessibility tree...
SemanticsNode#0
 │ Rect.fromLTRB(0.0, 0.0, 1200.0, 924.0)
 │
 └─SemanticsNode#1
   │ Rect.fromLTRB(0.0, 0.0, 1200.0, 924.0)
   │ textDirection: ltr
   │
   └─SemanticsNode#2
     │ Rect.fromLTRB(0.0, 0.0, 1200.0, 924.0)
     │ sortKey: OrdinalSortKey#83a1d(order: 0.0)
     │
     └─SemanticsNode#3
       │ Rect.fromLTRB(0.0, 0.0, 1200.0, 924.0)
       │ flags: scopesRoute
       │
       ├─SemanticsNode#9
       │ │ Rect.fromLTRB(0.0, 0.0, 1200.0, 56.0)
       │ │
       │ └─SemanticsNode#10
       │     Rect.fromLTRB(532.6, 14.0, 667.4, 42.0)
       │     flags: isHeader
       │     label: "Radio Sample"
       │     textDirection: ltr
       │
       └─SemanticsNode#4
         │ Rect.fromLTRB(0.0, 56.0, 1200.0, 924.0)
         │ label: "Radio group"
         │ textDirection: ltr
         │ role: radioGroup
         │
         ├─SemanticsNode#5
         │ │ Rect.fromLTRB(0.0, 0.0, 1200.0, 48.0)
         │ │ flags: hasSelectedState, hasEnabledState, isEnabled
         │ │ label: "Foo"
         │ │ textDirection: ltr
         │ │
         │ └─SemanticsNode#6
         │     Rect.fromLTRB(16.0, 8.0, 48.0, 40.0)
         │     actions: focus, tap
         │     flags: hasCheckedState, hasSelectedState, hasEnabledState,
         │       isEnabled, isInMutuallyExclusiveGroup, isFocusable
         │
         └─SemanticsNode#7
           │ Rect.fromLTRB(0.0, 48.0, 1200.0, 96.0)
           │ flags: hasSelectedState, hasEnabledState, isEnabled
           │ label: "Bar"
           │ textDirection: ltr
           │
           └─SemanticsNode#8
               Rect.fromLTRB(16.0, 8.0, 48.0, 40.0)
               actions: focus, tap
               flags: hasCheckedState, isChecked, hasSelectedState, isSelected,
                 hasEnabledState, isEnabled, isInMutuallyExclusiveGroup,
                 isFocusable
HTML generated by Flutter web...
<html>
...

<body flt-embedding="full-page" flt-renderer="canvaskit" flt-build-mode="debug" spellcheck="false" style="...">
  ...
  <flt-announcement-host>
    <flt-announcement-polite aria-live="polite" style="...">
      <flt-announcement-assertive aria-live="assertive" style="...">
        <flutter-view style="...">
          <flt-glass-pane></flt-glass-pane>
          <flt-text-editing-host></flt-text-editing-host>
          <flt-semantics-host style="...">
            <flt-semantics id="flt-semantic-node-0" style="...">
              <flt-semantics-container style="...">
                <flt-semantics id="flt-semantic-node-1" style="...">
                  <flt-semantics-container style="...">
                    <flt-semantics id="flt-semantic-node-2" style="...">
                      <flt-semantics-container style="...">
                        <flt-semantics id="flt-semantic-node-3" role="dialog" style="...">
                          <flt-semantics-container style="...">
                            <flt-semantics id="flt-semantic-node-9" style="...">
                              <flt-semantics-container style="...">
                                <h2 id="flt-semantic-node-10" tabindex="-1" style="...">
                                  Radio Sample</h2>
                              </flt-semantics-container>
                            </flt-semantics>
                            <flt-semantics id="flt-semantic-node-4" role="radiogroup" aria-label="Radio group"
                              style="...">
                              <flt-semantics-container style="...">
                                <flt-semantics id="flt-semantic-node-5" role="group" aria-label="Foo"
                                  aria-selected="false" style="...">
                                  <flt-semantics-container style="...">
                                    <flt-semantics id="flt-semantic-node-6" tabindex="0" flt-tappable="" role="radio"
                                      aria-checked="false" style="...">
                                    </flt-semantics>
                                  </flt-semantics-container>
                                </flt-semantics>
                                <flt-semantics id="flt-semantic-node-7" role="group" aria-label="Bar"
                                  aria-selected="false" style="...">
                                  <flt-semantics-container style="...">
                                    <flt-semantics id="flt-semantic-node-8" tabindex="0" flt-tappable="" role="radio"
                                      aria-checked="true" style="...">
                                    </flt-semantics>
                                  </flt-semantics-container>
                                </flt-semantics>
                              </flt-semantics-container>
                            </flt-semantics>
                          </flt-semantics-container>
                        </flt-semantics>
                      </flt-semantics-container>
                    </flt-semantics>
                  </flt-semantics-container>
                </flt-semantics>
              </flt-semantics-container>
            </flt-semantics>
          </flt-semantics-host>
</body>

</html>

Pre-launch Checklist

If you need help, consider asking for advice on the #hackers-new channel on Discord.

@github-actions github-actions bot added framework flutter/packages/flutter repository. See also f: labels. engine flutter/engine related. See also e: labels. a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) platform-web Web applications specifically labels Feb 25, 2025
@loic-sharma loic-sharma marked this pull request as ready for review February 26, 2025 20:04
Copy link
Contributor

@chunhtai chunhtai left a comment

Choose a reason for hiding this comment

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

LGTM

Copy link
Contributor

@yjbanov yjbanov left a comment

Choose a reason for hiding this comment

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

lgtm

@loic-sharma loic-sharma added the autosubmit Merge PR when tree becomes green via auto submit App label Mar 3, 2025
@auto-submit auto-submit bot added this pull request to the merge queue Mar 3, 2025
Merged via the queue into flutter:master with commit 7d8c78c Mar 3, 2025
177 checks passed
@flutter-dashboard flutter-dashboard bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Mar 3, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 4, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 4, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 4, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 4, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 4, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 4, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 4, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 4, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 4, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 4, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 5, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 5, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 5, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 5, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 5, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 5, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request Mar 5, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 20, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 20, 2025
engine-flutter-autoroll added a commit to engine-flutter-autoroll/packages that referenced this pull request May 21, 2025
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) engine flutter/engine related. See also e: labels. framework flutter/packages/flutter repository. See also f: labels. platform-web Web applications specifically

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants