Skip to content
CrossFly is 100% open sourceFollow us on GitHub
CrossFlyCrossFly
Get started

This page was machine-translated from Portuguese and may contain errors. The Portuguese version is canonical.

Styles and themes

Appearance in CrossFly boils down to a mnemonic from the framework itself:

Skin = structure / renderer · Theme = semantic roles per mode.

Three distinct responsibilities collaborate.

1. Style per state (default / focused / pressed)

Each Component can have up to three styles and the framework picks the right one per frame according to the interaction. This is why a button “sinks” when pressed without you animating anything.

comp.style(normal).focusedStyle(focado).pressedStyle(apertado);

2. Skin — the shape factory

The Skin defines the structure: corner radii, border thicknesses, spacings, default icons. There are MaterialSkin, IOSSkin, FlutterSkin. A custom Skin overrides shape/spacing — never color. Swapping the global Skin re-clothes all components.

3. Theme + ColorScheme — the semantic roles

The colors come from roles (PRIMARY, SURFACE, ON_SURFACE, ERROR, TITLE, HINT…) resolved to the current light/dark mode, from a ColorScheme generated from a seed color (Material You).

Styles.theme(0xFF6750A4, ThemeMode.DARK);   // one call themes everything, in dark mode

The most powerful point: switching theme at runtime without recreating the UI

Here is the subtlety that makes light/dark mode trivial:

  • The colors of the styles are not “frozen” values — they are PalColor, live references to the palette.
  • There is a global style version counter (styleVersion). When applying a new scheme / toggling the mode, the framework increments the version and reapplies the theme across all open windows — each style based on PalColor re-resolves its colors on the next frame. No component is destroyed and recreated.

Complementing this, components whose style changes in shape by mode register how to restyle themselves (onRestyle/styled) and restyle() reapplies the subtree in place:

Label titulo = new Label("Bem-vindo").styled(() -> Styles.theme().label(LabelRole.TITLE));
// ...when the mode is toggled, the framework re-evaluates this provider and re-skins without recreating the Label.
Styles.toggleDarkMode();

Why this architecture

  • Separating shape (Skin) from color (Theme) lets you swap visual identity and palette independently — and adapt to light/dark without touching the Skin.
  • PalColor + version turns “switching theme” from “rebuild the tree” into “re-resolve colors on the next frame” — cheap and without flicker.

See also

Was this page helpful?