FxButton
A pre-styled, responsive button component with multiple variants, fluent modifiers, and loading support.
FxButton
FxButton is a high-level wrapper around Flutter's Material buttons, enhanced with atomic styling and built-in reactivity.
Usage
Fx.button("Click Me")
.primary
.onTap(() => print("Clicked"))Variants
Fluxy provides semantic variants that automatically adjust colors and interactions.

Fx.button("Primary").primary
Fx.button("Secondary").secondary
Fx.button("Danger").danger
Fx.button("Outline").outline
Fx.button("Ghost").ghost
Fx.button("Text").text
Fx.button("Custom").none // Clean-slate for unstyled interactionThe .none Variant (Interaction-Only)
The .none variant is the "Clean-Slate" primitive. It strips away all hardcoded theme colors (like the default white text contrast), allowing you to build custom interactive components using strictly DSL modifiers.
Fx.button("Black Text")
.none
.bg(Colors.slate50)
.textColor(Colors.black)
.roundedSizes
Control button density using preset size modifiers.
Fx.button("Tiny").sizeXs()
Fx.button("Small").sizeSm()
Fx.button("Default") // md
Fx.button("Large").sizeLg()
Fx.button("Extra Large").sizeXl()Modifiers
Fluxy buttons support fluent modifiers for rapid customization.
| Modifier | Description | Example |
|---|---|---|
.rounded | Makes the button pill-shaped | .rounded |
.square | Removes all border radius | .square |
.fullWidth() | Expands to fill available width | .fullWidth() |
.loading(bool) | Replaces content with a spinner | .loading(isLoading.value) |
.withIcon(Widget) | Adds a leading icon | .withIcon(Icon(Icons.add)) |
.withTrailing(Widget) | Adds a trailing icon | .withTrailing(Icon(Icons.arrow_right)) |
.shadowSm() | Adds subtle elevation | .shadowSm() |
API Reference
Properties
| Name | Type | Default | Description |
|---|---|---|---|
label | String | required | The text content of the button. |
onTap | VoidCallback? | null | Callback when button is pressed. |
variant | FxButtonVariant | primary | Visual style of the button. |
size | FxButtonSize | md | Size density token. |
isLoading | bool | false | Whether to show a spinner. |
style | FxStyle? | null | Custom atomic style override. |
Variants
- primary: Standard brand action (White text contrast).
- secondary: Muted action.
- danger: Destructive action (Red).
- success: Positive action (Green).
- warning: Cautionary action (Orange).
- outline: Transparent background with border.
- ghost: No background or border until hover.
- text: Pure text action with primary tint.
- none: Completely unstyled. Honors all manual
.bg()and.textColor()modifiers without theme overrides.
Buttonizers (Extension API)
Turn any String or Widget into a button instantly using the .btn() extension.
"Login".primaryBtn(onTap: () => login())
Icon(Icons.settings).btn(onTap: () => openSettings())The Master Button Implementation
In a professional application, you don't just use a single button. You create a cohesive set of actions with consistent spacing, variants, and responsive behavior. Here is a complex "Action Bar" example using Fluxy's atomic button system.
class ProfileActionView extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isLoading = flux(false);
return Fx.col(
gap: 20,
children: [
// 1. Primary Action Group (Responsive)
Fx.layout(
mobile: Fx.col(
gap: 12,
children: [
Fx.button("Complete Profile").primary.fullWidth(),
Fx.button("Skip for now").outline.fullWidth(),
],
),
tablet: Fx.row(
gap: 16,
children: [
Fx.button("Complete Profile").primary.sizeLg(),
Fx.button("Skip for now").outline.sizeLg(),
],
),
),
// 2. Interaction-Only Primitive (.none variant)
// Building a custom interactive card with no default button styles
Fx.button("")
.none
.bg(FxTokens.colors.card)
.rounded(16)
.border(1, FxTokens.colors.border)
.p(20)
.onTap(() => print("Custom Card Tapped"))
.child(
Fx.row(
children: [
Icon(Icons.verified_user).color(Fx.primary),
Fx.text("Upgrade to Pro").font.md().ml(4),
Spacer(),
Icon(Icons.chevron_right),
],
)
),
// 3. Destructive Guard (Dynamic State)
Fx(() => Fx.button("Delete Account")
.danger
.outline
.loading(isLoading.value) // Built-in reactive loading
.onTap(() async {
isLoading.value = true;
await Future.delayed(Duration(seconds: 2));
isLoading.value = false;
Fx.toast.info("Account Deleted (Demo)");
})
.fullWidth()
),
// 4. Iconic Actions
Fx.row(
gap: 12,
children: [
Fx.button("Share").withIcon(Icon(Icons.share)).secondary,
Fx.button("Export").withTrailing(Icon(Icons.download)).secondary,
],
),
],
);
}
}Fluxy's button system is built for composition. Use .none when you want only the tap-logic, and semantic variants like .primary or .danger when you want pre-styled, branded UI.