Theming
Themes set the default look of a Fleury app. Define shared colors, text styles,
borders, and interaction feedback in ThemeData, then pass it to FleuryApp.
Use a widget’s style property when that widget needs a local override.
| You need to… | Use |
|---|---|
| Theme the whole app | FleuryApp(theme: …) |
| Style widgets | style: CellStyle(…) |
| Style widget interactions such as focus, hover, and selection | CellStyle.interactive(…) |
Create a theme
Section titled “Create a theme”ThemeData.dark() and ThemeData.light() are Fleury’s built-in starting
themes. Use copyWith(...) to change the shared colors, styles, and borders
your app needs:
Pass the result to FleuryApp. If no theme is supplied, Fleury uses its built-in
defaults, so Theme.of(context) always returns a value. Inside a widget,
context.theme is shorthand for Theme.of(context), and context.colors for
its colorScheme.
What a theme contains
Section titled “What a theme contains”Every ThemeData field has a default, so a theme changes only the fields you
set:
| Field | Default | Used for |
|---|---|---|
brightness | Brightness.dark | Whether the theme targets a dark or a light terminal |
textStyle | No style | The base style of every Text below the theme |
mutedStyle | Dim | De-emphasized text such as hints, separators, and disabled rows |
selectionStyle | Inverse | The selected or current item, such as a list’s highlighted row |
focusedStyle | Bold | Focused controls |
errorStyle | Red and underlined | Invalid controls and validation messages |
interactiveStyle | None | App-wide control state styles; see Interactive styles |
borderStyle | BorderStyle.rounded | Framed surfaces such as panels and Container.framed |
colorScheme | ColorScheme() | The color roles below |
extensions | Empty | Your own theme objects |
ColorScheme holds the colors an app draws from, by role:
| Role | Default in ColorScheme() | Used for |
|---|---|---|
foreground, background | null: the terminal’s own colors | Default text and background |
surface | null: near-black on a dark theme, near-white on a light one | Opaque fills such as Container.filled and presented dialogs |
primary | Colors.mint | Interactive and active accents |
focus | Colors.azure | Focused regions and text entry |
success, warning, error, info | ANSI green, yellow, red, and cyan | Status |
ThemeData.dark() and ThemeData.light() each set their own primary.
extensions can hold objects of any class you define; read one by type with
context.theme.extension<MyColors>(), which returns null when the theme has
none.
To change the theme for part of the app, wrap that subtree in a Theme and
build its data from the ambient theme:
Theme( data: context.theme.copyWith(borderStyle: BorderStyle.double), child: const SettingsPanel(),)Styling widgets
Section titled “Styling widgets”Pass a CellStyle to a control’s style to change its base appearance. This
input draws its text in cyan:
That style becomes the control’s base appearance. It does not erase the
framework’s focus, disabled, or validation cues; those still layer on when the
state changes. The same CellStyle works on Text, Button, Checkbox,
Select, and the rest of the control set.
CellStyle
Section titled “CellStyle”A CellStyle describes terminal paint: foreground and background colors plus
attributes such as bold, italic, underline, and strikethrough.
Styles merge field by field. An unset value inherits; explicitly setting a
boolean attribute to false turns that inherited attribute off.
Interactive styles
Section titled “Interactive styles”Use CellStyle.interactive when a widget should change as users interact with
it. Assign it to ThemeData.interactiveStyle for the whole app, or pass it to
one widget’s ordinary style property for a local exception. Native hover
feedback also needs TerminalMode(mouseMotion: true) in runApp;
mouse: true alone doesn’t report plain motion, so hovered changes only when
a press, drag, or wheel event arrives. An app created by fleury create starts
with mouse: true;
Enable mouse input in a terminal
shows the change. Browser hosts already provide pointer motion. This reference
renders each entry directly so the differences are easy to compare:
When assigned to ThemeData.interactiveStyle, controls apply the relevant
entry automatically. If states overlap, Fleury layers selected, hovered,
focused, pressed, and invalid paint in that order; disabled paint stands alone.
pressed: CellStyle(…) styles a held primary pointer press on buttons and other
activatable controls; Input & gestures
explains when it applies. Pair color with an attribute such as bold, underline,
inverse, or dim so feedback does not depend on hue alone.
Local interactive styles
Section titled “Local interactive styles”The same style property accepts either kind of value. Here a Theme stands
in for the app theme and gives every control an inverse, bold focus cue;
Theme focus inherits it, and Local focus overrides it:
A plain CellStyle changes the base paint while inherited interaction cues
remain. CellStyle.interactive is the local escape hatch when one control’s
state should differ. A local entry replaces the theme’s entry for that state,
then combines with any other active states.
Light and dark themes
Section titled “Light and dark themes”Choose the mode when you construct the app theme. ThemeData.dark() and
ThemeData.light() set brightness for you, or pass it explicitly to
ThemeData(...):
FleuryApp(title: 'Dashboard', theme: ThemeData.dark(), home: const Dashboard());Fleury does not guess the terminal’s background. When another value needs a
light and dark variant, pick it with the ambient theme’s adaptive:
final shadow = context.theme.adaptive( light: Colors.gray, dark: Colors.black,);For code outside a widget build method, use
theme.brightness.pick(light: ..., dark: ...).
Advanced overrides
Section titled “Advanced overrides”Use CellStyle.none for a state when one widget should deliberately suppress
an inherited visual cue. The validation message and semantics remain intact;
only the input’s invalid paint is neutral:
Authors of reusable interactive widgets can call CellStyle.resolve(...) to
apply the same theme, local-style, and state cascade as Fleury’s controls.
Ordinary application code should not need to call it.
Community themes
Section titled “Community themes”package:fleury/themes.dart
is Fleury’s opt-in community theme collection, included in the core package. It currently includes Nord, Dracula,
Gruvbox Dark, Tokyo Night, Catppuccin Mocha, One Dark, and Solarized in dark
and light variants, and contributions can add more:
import 'package:fleury/fleury.dart';import 'package:fleury/themes.dart';
FleuryApp(title: 'Dashboard', theme: tokyoNight, home: const Dashboard());Arrow through the live picker to compare the same UI across every palette:
Every preset is a plain const ThemeData. The package also exports
fleuryThemes, a named list ready for a theme picker.
For a broader comparison—and a custom editor for primary, focus, and status colors, brightness, and borders—open the Theme studio showcase.
Next steps
Section titled “Next steps”- Forms & validation shows how invalid control state is produced automatically.
- Focus management explains how controls gain and move focus.
- Terminal capabilities covers color fidelity and runtime fallback behavior.