feat(switch): Switch primitive (shadcn v4) + gallery showcase#52
Merged
Conversation
added 5 commits
June 15, 2026 14:57
Controlled animated toggle (TDD): switch.dart + switch_behavior_test.dart. All 12 behavior tests pass: tap toggles on/off, off=input/on=primary track color, md wider than sm, animation settles, Space/Enter keyboard toggle, disabled dimmed + non-interactive, >=48px hit target (MinTapTarget), toggled semantics + Wi-Fi label, RTL no overflow, dark reskin. SDK deviation: SemanticsFlags.isToggled is Tristate in Flutter 3.41.9 (no hasToggledState getter); test uses Tristate.isTrue comparison.
…cus-ring test (review)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Switch — the canonical interactive + animated toggle (5th primitive)
Ships
Switchatapps/gallery/lib/components/ui/switch.dart— a Material-free, themeable, shadcn-v4-faithful toggle. It's the repo's first animated component: it mirrors Button's interaction scaffolding (FocusableActionDetector+ tap detector, Space/Enter activation, focus-visiblering,MinTapTarget,Semantics) and adds a dependency-free sliding-thumb animation. It also unblocks the structure layer'sThemeToggle.shadcn v4 class mapping
rounded-full border-transparent,checked:bg-primary/unchecked:bg-input.tw.bg(<lerped>).roundedFull.border(1, color: Color(0x00000000)), color =Color.lerp(input, primary, t)bg-background size-4(default) /size-3(sm).tw.size(4/3).bg(background).roundedFulltranslate-x-0↔translate-x-[calc(100%-2px)]AlignlerpingAlignmentDirectional.centerStart↔centerEndinside a.px(0.5)insetdata-[size=default]/data-[size=sm]SwitchSize { md, sm }(md = shadcndefault)focus-visible:ring-[3px] ring-ring/50.ring(3, color: ring.withValues(alpha: 0.5))when focus-visibledisabled:opacity-50 pointer-events-none.opacity(0.5)+ non-interactiveDesign decisions
value; tap/Space/Enter callsonChanged(!value). The widget holds no value state, only the toggle animation.onChanged == null⇒ disabled (the Flutter idiom).easeInOutAnimationControllerdrives onetthat lerps both the track color and the thumb position via a singleAnimatedBuilder(the static thumb is passed as the builder'schildso it isn't rebuilt per frame). Noflutter_animate— a state toggle is a built-in implicit animation, so a copiedSwitchdrags in nothing.sm/md(shadcn parity), via an exhaustiveswitchgeometry record.Semantics(toggled: value, enabled:, label:)(a switch role announcing on/off);MinTapTargetgives a ≥48px touch area around the ~18px visual (mobile-first).Faithful deviations (documented, not bugs)
shadow-xsand the dark-only refinements (dark:…unchecked:bg-input/80, the per-state dark thumb colors) are dropped — the thumb isbackgroundin both brightnesses (faithful in light; the "on" state stays high-contrast in dark). Same precedent as Badge's/Input's droppeddark:refinements. Note: Switch's focus ring isring-[3px]with no ring-offset (matching its shadcn class) — unlike Button, whose class hasring-offset-2.Tests
MinTapTarget;toggledsemantics (Tristate) + label; RTL; dark reskin.apps/gallery(a small stateful Switches demo) + smoke-tested. Charter §8 progress note.Executed via writing-plans → subagent-driven-development (implementer + spec-review + code-quality-review; review findings triaged — dispose the
CurvedAnimation,AnimatedBuilderchildidiom, a focus-ring test added; the reviewer's ring-offset andexcludeSemanticssuggestions were verified against the shadcn class + the no-text-child structure and correctly not applied).🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com