Actions
Trigger an action or represent an on/off state.
Headless Flutter primitives with semantics, keyboard support, focus, overlays, and observable interaction state—without a single opinion about how your interface should look.
NakedButton(
onPressed: onSave,
builder: (context, state, child) {
final color = state.when(
pressed: const Color(0xFF1D4ED8),
hovered: const Color(0xFF2563EB),
orElse: const Color(0xFF3B82F6),
);
return AnimatedContainer(
duration: const Duration(milliseconds: 160),
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 12,
),
decoration: BoxDecoration(
color: color,
borderRadius: BorderRadius.circular(12),
),
child: const Text('Save changes'),
);
},
)A component library should make behavior boring without making your product generic. Naked UI separates the two.
Naked UI gives each control the behavior users expect, then exposes the state your own widgets need to render it well.
Screen-reader roles and control state live beside the behavior instead of being rebuilt in every visual component.
Builders receive typed snapshots for hover, focus, press, drag, selection, and disabled states—ready for your styles and animations.
Activation, arrow-key navigation, roving focus, and traversal behavior follow the control you chose.
Dialogs, menus, selects, tooltips, and popovers handle anchoring, dismissal, and focus lifecycle.
Return ordinary Flutter widgets from the builder. Use your tokens, painters, animations, or styling system.
Every primitive uses the same builder-first idea, from a button to a focus-trapped dialog. Open any component to see its complete API and accessibility guidance.
Trigger an action or represent an on/off state.
Single, multiple, and continuous-value selection.
Native text editing behavior with a custom visual shell.
Organize and reveal content with managed selection and focus.
Anchored and modal surfaces with lifecycle behavior built in.
Your design-system widget remains an ordinary Flutter widget. Naked UI sits at the interaction boundary and keeps behavior, semantics, and state in one predictable place.
class DesignSystemButton extends StatelessWidget {
const DesignSystemButton({
super.key,
required this.label,
required this.onPressed,
});
final String label;
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return NakedButton(
onPressed: onPressed,
builder: (context, state, child) {
return AppButtonSurface(
label: label,
hovered: state.isHovered,
pressed: state.isPressed,
focused: state.isFocused,
);
},
);
}
}No. The primitives own interaction behavior, state, semantics, focus, and overlay mechanics. Their builders return the Flutter widgets that define your colors, spacing, typography, shape, motion, and layout.
No. Naked UI depends on Flutter and works with ordinary widgets, Theme extensions, custom painters, Mix, or any other styling approach you already use.
Each primitive supplies control-appropriate semantics and interaction behavior such as keyboard activation, roving focus, focus trapping, or screen-reader state. You still own accessible labels, readable contrast, touch-target sizing, and the meaning of your custom content.
Use Switch for an immediate on/off setting, Checkbox for independent choices, Radio for one choice in a group, and Toggle for a pressable selected state or toggle group. The component docs include selection and semantics guidance for each.
Naked UI is currently published as a beta. Pin the version you adopt, review the changelog when upgrading, and cover your design-system wrappers with widget tests while the API continues to mature.
Yes. Naked UI is open source under the BSD 3-Clause license.
Add Naked UI, wrap one control you already own, and stop rebuilding the interaction layer every time the visuals change.
flutter pub add naked_ui