Example: Counter with Dark Mode¶
A classic counter that demonstrates reactive state, button variants, and runtime theme switching — all in one self-contained page.
counter.py
import fluxui as ui
app = ui.App("Counter")
@app.page("/")
def home(session):
count = ui.state(0) # (1) per-session reactive integer
dark_mode = ui.state(False)
async def toggle_theme(v: bool) -> None: # (2) async on_change handler
dark_mode.set(v)
await session.set_theme("dark" if v else "light")
# ── Top bar ──────────────────────────────────────────────────────────
with ui.Row(justify="space-between", align="center"):
ui.Heading("Counter Demo", level=1)
ui.Switch("Dark mode", checked=dark_mode, on_change=toggle_theme)
# ── Counter card ─────────────────────────────────────────────────────
with ui.Card(title="Click the buttons", description="Each click updates only the metric"):
with ui.Row(gap="var(--s-3)", align="center", justify="center"):
ui.Button(
"−",
variant="secondary",
on_click=lambda: count.update(lambda v: v - 1),
)
ui.Metric( # (3) only this component re-renders on count change
label="Count",
value=lambda: str(count.value),
delta=lambda: f"+{count.value}" if count.value > 0
else (str(count.value) if count.value < 0 else ""),
)
ui.Button("+", on_click=lambda: count.update(lambda v: v + 1))
ui.Button("Reset", variant="ghost", on_click=lambda: count.set(0))
# ── Progress bar ─────────────────────────────────────────────────────
with ui.Card(title="Progress"):
ui.Progress(
lambda: max(0.0, min(100.0, count.value * 10.0)), # (4) clamp 0-100
label="10 × count",
)
# ── Status badge ─────────────────────────────────────────────────────
def badge_variant(): # (5) computed variant
if count.value > 0: return "success"
if count.value < 0: return "danger"
return "neutral"
with ui.Row(gap="var(--s-2)"):
ui.Text("Status:", variant="label")
ui.Badge(
lambda: "Positive" if count.value > 0
else ("Negative" if count.value < 0 else "Zero"),
variant=badge_variant,
)
ui.Alert(
"Try opening this page in two browser tabs — each tab gets its own counter.",
variant="info",
)
if __name__ == "__main__":
app.run(port=8000)
ui.state(0)creates aReactiveVar[int]scoped to this session.async def toggle_theme— FluxUI calls async handlers natively.session.set_theme()is a coroutine that sends a theme-switch message over the WebSocket.ui.Metric(value=lambda: ...)— the lambda is called inside the metric's render context, so only the metric subscribes tocount. Clicking a button does not re-render the buttons themselves.max(0.0, min(100.0, ...))clamps the progress value to the 0–100 range expected byui.Progress.- Reactive badge variant — the lambda is evaluated during render, so it subscribes to
countand re-renders when the value crosses zero.
Run it¶
Open http://127.0.0.1:8000.