Skip to content

fix(naked_ui): honor reduced motion in dialogs, tooltips, disclosures, and toasts - #330

Open
leoafarias wants to merge 2 commits into
mainfrom
fix/naked-ui-reduced-motion
Open

leoafarias wants to merge 2 commits into
mainfrom
fix/naked-ui-reduced-motion

Conversation

@leoafarias

Copy link
Copy Markdown
Collaborator

Description

showNakedDialog, showNakedAlertDialog, NakedTooltip, NakedDisclosure, and NakedToastScope did not honor the user's reduced-motion preference: dialogs and tooltips ignored it, and disclosures and toasts read only MediaQuery.disableAnimations. iOS Reduce Motion sets only AccessibilityFeatures.reduceMotion, not disableAnimations, and MediaQuery does not notify on it, so those widgets kept animating on iOS. Remix delegates to all five.

Each now skips its transitions when either MediaQuery.disableAnimations or the platform reduceMotion flag is set. Layout and semantics do not change.

  • Dialog: showNakedDialog pushes a private RawDialogRoute subclass whose duration is zero while reduced, so showNakedAlertDialog and showRemixDialog/showRemixAlertDialog inherit it. The setting is read from the caller at open and from the dialog's subtree at dismissal (TransitionRoute fixes its durations at install); a dialog that opened without motion also closes without it.
  • Tooltip: observes didChangeDependencies and didChangeAccessibilityFeatures and feeds zero durations to RawTooltip through the existing animation-style sync, including the deprecated 1.1.0 path.
  • Disclosure: observes didChangeAccessibilityFeatures like the tooltip, so a running transition completes when the setting turns on.
  • Toast: reads the setting when it enters and again when it exits, so a change while visible skips the exit.
  • Barrier semantics: a modal NakedDialog shown without a transition hid the dismissible barrier's "Dismiss" node from screen readers (Flutter's BlockSemantics behaves differently when the page is visible on the first frame). showNakedDialog now wraps the page in a semantics container; this adds one container node to every naked dialog's semantics tree.

Reviewable decision (D8): reduced motion is MediaQuery.disableAnimations OR the platform reduceMotion flag, read through one internal helper (reducedMotionOf, not exported). Remix's RemixReducedMotionBuilder (#327) uses the same definition.

Limitations: a dialog or tooltip transition already in flight when the setting changes finishes at its old pace (a disclosure completes at once); a tooltip not yet shown when the setting turns off may show once without motion (RawTooltip creates its controller lazily), documented. The Remix wrappers inherit this with the next naked_ui release; remix's naked_ui floor is raised in the release PR.

Validation

  • New tests parameterized over MediaQuery.disableAnimations, the platform disableAnimations flag, and the platform reduceMotion flag: default animates; instant open/close, enter/exit, show/hide; a request arriving while open or visible; withdrawal; unchanged layout; barrier semantics under reduced motion; observer lifecycle and rebuild counts. 31 fail on main; removing each review fix on its own fails exactly its tests.
  • naked_ui suite (1008 passed, 3 skipped), remix suite (2831), registry_source suite (1390), dart analyze (no new issues), root dart format, melos run ci:checks (includes docs:check).

Related Issues

Supersedes the closed #258 for naked_ui. Companion: #327 (Remix components).


Checklist

  • My PR includes unit or integration tests for all changed/updated/fixed behaviors.
  • I have updated or added relevant documentation (doc comments with ///).
  • I am prepared to follow up on review comments in a timely manner.

Breaking Change

  • Yes, this is a breaking change.
  • No, this is not a breaking change.

showNakedDialog, showNakedAlertDialog, and NakedTooltip now show and hide
at once when MediaQuery.disableAnimations is set or the platform reports
AccessibilityFeatures.reduceMotion, which iOS Reduce Motion sets without
setting disableAnimations. A dialog reads the setting when it opens and when
it is dismissed; a tooltip follows changes while mounted.
NakedDisclosure and NakedToastScope read only MediaQuery.disableAnimations,
so iOS Reduce Motion, which sets only the platform reduceMotion flag, still
animated them. Both now use the same definition as dialogs and tooltips. The
disclosure observes platform accessibility changes like the tooltip; a toast
reads the setting again when it exits.

A dialog that opened without motion now also closes without it, so a
reduced-motion request at push survives a MediaQuery override between the
caller and the route. showNakedDialog wraps the page in a semantics container
so a modal NakedDialog no longer hides the dismissible barrier's node when it
opens without a transition. Tooltip docs note that a tooltip not yet shown may
show once without motion after the setting is turned off.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-27T01:46:08.606593Z 2386961 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

This branch has not been deployed

No deployments
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.

1 participant