Skip to content

fix(a11y): stop sliders from freezing the desktop accessibility tree - #279

Merged
Porthorion merged 1 commit into
release/1.5from
fix/slider-semantics-orphan
Nov 5, 2026
Merged

Porthorion merged 1 commit into
release/1.5from
fix/slider-semantics-orphan

Conversation

@Porthorion

Copy link
Copy Markdown
Owner

Problem

Part of #267. On macOS, the app could crash after a settings or player screen with a slider was opened. Flutter 3.47.5's material Slider sends the engine a semantics node that no parent lists (flutter/flutter#190357). The macOS and Windows accessibility bridges reject that update and stay broken. They freeze the native accessibility tree on stale nodes, and a later pointer move can read a freed node and crash (flutter/flutter#193588).

Related issue: #267. The batched enqueue and the count-and-size confirmation follow in a separate PR.

Approach

NavisSlider wraps Slider in its own Overlay.wrap(alwaysSizeToContent: true, clipBehavior: Clip.none). That keeps the value indicator's semantics inside the slider's own subtree. All 17 slider sites use it, and an architecture test forbids a bare Slider/RangeSlider in lib/. The wrap can go once a stable Flutter ships the framework fix (flutter/flutter#193372).

Validation

  • Focused tests: navis_slider_test (a bare Slider sends 1 orphan node in a pushed route, NavisSlider sends 0), the architecture tests, and the volume, seek bar, EQ, downloads and settings tests all pass. Golden diffs on macOS match the parent commit exactly; those goldens are Linux-rendered.
  • ./tool/check.sh: not run locally (it can't run on this machine); CI is the gate.
  • Running app (macOS): the same scripted tour across slider screens. Before the fix: 94 Failed to update ui::AXTree errors. After: 0 errors and 0 orphan nodes in 300+ updates, and navigation works.

Evidence

Evidence: none, no visible change in normal use (see Risks for the value bubble).

Risks

  • The value bubble on labelled, divided sliders now paints in the slider's own overlay. An ancestor clip can now cut it off, for example when the slider is scrolled to within about 50 px under the app bar, and so can a later translucent sibling. Every affected slider also shows its value in text next to it.
  • Windows Narrator was not tested.
  • The engine use-after-free still exists for any other source of orphan nodes.

Checklist

  • I read and can explain the whole diff
  • One concern, no unrelated cleanup
  • Evidence included, or none needed
  • Targets dev (or release/X.Y for a release fix)
  • Documentation updated

AI Disclosure

  • Harness: Claude Code
  • Model(s): Claude (Opus coordinator and root cause, Sonnet fix, a fresh Sonnet adversarial review)
  • Involvement: Fully AI-generated, human verified
  • Adversarial review: a fresh reviewer rendered bare and wrapped sliders to images (bubble clipping, overlay sizing, sibling painting), compared every forwarded parameter with Slider's API, and probed the source-guard regex and the orphan recorder in a dialog, a bottom sheet and a pushed route. Nothing blocking. The bubble clipping is accepted and listed above.

Changelog

Fixed a macOS crash, and frozen screen-reader support on macOS and Windows, after opening a screen with a slider.

Material's Slider always wraps its value indicator in an OverlayPortal. In
a pushed route Flutter 3.47 then sends the engine a semantics node that no
parent lists as a child (flutter/flutter#190357). The macOS and Windows
accessibility bridges reject it, stay failed, and freeze their native tree
on stale nodes; a later pointer move can then read a freed node and crash
(flutter/flutter#193588). Part of #267.

NavisSlider wraps Slider in its own Overlay.wrap(alwaysSizeToContent,
Clip.none), which keeps the portal's semantics inside the slider. All 17
Slider sites use it and an architecture test forbids a bare Slider in lib.
The wrap goes away once a stable Flutter ships flutter/flutter#193372.

Co-Authored-By: Claude Sonnet <[email protected]>
@Porthorion Porthorion mentioned this pull request Nov 5, 2026
@Porthorion
Porthorion merged commit 3f41572 into release/1.5 Nov 5, 2026
8 checks passed
@Porthorion
Porthorion deleted the fix/slider-semantics-orphan branch November 5, 2026 00:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants