Theming
How colors, fonts and component themes are wired, and the shortest path to making the app look like yours.
The class you cannot autocomplete
Every color in the app is declared in
lib/core/presentation/utils/constants/colors.dart, which holds two classes:
FlootColors, around 127 public semantic aliases such as appBarBgLight,
cardSurfaceDark and badgeError; and _ColorTokens, roughly 73 private
palette tokens such as primary500, neutral950 and error600.
_ColorTokens is private. It never appears in autocomplete, and nothing
outside that one file can reference it. A buyer who opens the app, types
FlootColors. and edits from there will end up repointing aliases one at a
time and miss the small change that would have recolored everything at once.
That indirection is the whole design. _ColorTokens defines seven families,
primary, secondary, neutral, success, error, warning, info, at
ten shades each (neutral gets eleven, plus black and white), and FlootColors
maps roles onto them. It does not use a ColorScheme pair: it declares
explicit *Light / *Dark siblings, most aliases coming in pairs.
static final Color primary500 = Colors.deepPurple.shade500; // token
static const Color neutral950 = Color.fromRGBO(17, 17, 17, 1); // token
static const Color cardSurfaceLight = _ColorTokens.neutralWhite; // alias
static const Color cardSurfaceDark = _ColorTokens.neutral950; // aliasEdit tokens to rebrand. Edit aliases only to change what a specific role means.
There is a second color system running alongside
lib/core/presentation/utils/theme/components/color_scheme.dart builds a real
Material ColorScheme by seeding off two aliases:
static final Color _seedColor = FlootColors.primary; // primary500
static final Color _secondaryColor = FlootColors.secondary; // secondary500
static ColorScheme lightTheme = ColorScheme.fromSeed(seedColor: _seedColor, secondary: _secondaryColor);theme.dart hands that scheme to ThemeData, and also to seventeen
hand-written component themes that set their colors from FlootColors
directly. Knowing which one you are editing matters: the explicit aliases win
anywhere a component theme covers the widget, app bars, cards, dialogs,
buttons, inputs, checkboxes, list tiles, the navigation bar; the seeded
ColorScheme wins everywhere else, meaning Material widgets with no component
theme, and anything that reads Theme.of(context).colorScheme, which a fresh
project never does.
So the scheme is a safety net for widgets you add later, not the source of
truth for what ships. Change primary500 and both systems move together;
change an alias and only the first one does.
Rebranding, in six edits
Repoint the brand families. In _ColorTokens, swap primary50 through
secondary900 (colors.dart): replace Colors.deepPurple and Colors.purple
with your own swatch, or drop the Material dependency and write hex values.
This is the edit that also moves the seeded ColorScheme.
Catch the two escapees. Two aliases bypass the token layer entirely and reach for a Material swatch directly:
static final Color tableHeaderSurfaceLight = Colors.blueGrey.shade50;
static final Color tableHeaderSurfaceDark = Colors.blueGrey.shade900;They are the only two, and no token change reaches them.
Fonts, in lib/core/presentation/utils/theme/theme.dart:
static const String _fontFamily = 'Poppins';
static final TextTheme _lightTextTheme = GoogleFonts.poppinsTextTheme();
static final TextTheme _darkTextTheme = GoogleFonts.poppinsTextTheme(
ThemeData.dark().textTheme,
);The string and the google_fonts calls are independent, so changing one alone
gives you a half-applied font. Change all three.
Splash colors, in the flutter_native_splash block of pubspec.yaml. The
dark value #111111 is neutral950 written out by hand, so if you retint
your neutrals, edit it here too.
dart run flutter_native_splash:createApp icons and the logo. Replace the sources in assets/icons/ and
assets/app/logo.svg, then regenerate:
dart run flutter_launcher_iconsCustomization covers which file feeds which platform, and the second place the logo lives (the transactional email templates).
The web manifest, web/manifest.json, still carrying Flutter's stock
blue, which no generator overwrites:
"background_color": "#0175C2",
"theme_color": "#0175C2",You are re-running the generators, not running them
floot create already ran flutter_launcher_icons and
flutter_native_splash:create once, against the placeholder assets. That is
why the native icon and splash already look like something. Replacing the
source images does nothing on its own, the generated platform files are what
the build reads.
Sizes and text styles
sizes.dart (spacing, radii, font sizes, borrowed from Tailwind's scale) and
styles.dart (composed TextStyles and shapes) sit beside colors.dart in
lib/core/presentation/utils/constants/, doc-commented per constant.
Two things that will bite you
Retokenising is safe; collapsing roles is not
No test in the project hardcodes a color; assertions reference FlootColors.*
by name, so you can repoint any token without touching a test.
What does break is making two roles equal. Several tests assert that colors
differ, not just that they match, expecting primary, then warning, then
destructive as, for example, a usage bar fills. Point two of those at one
value and the test suite tells you, which is the good outcome. Point them at
one value and adjust the test, and you have silently removed a signal from the
UI.
Theme regressions are largely untested
The shared widget-test harness does not use your theme:
theme: ThemeData(platform: platform),
darkTheme: ThemeData.dark(),Widget tests run against stock Material, not FlootAppTheme. A component
theme you break, a contrast failure, a color wired to the wrong alias, will
not turn a test red. Check theme work by running the app in both modes.