Add lib/theme/README.md (UI-73): what Mornye is, which files form the theme layer, how DynamicColorWrapper builds each style, how Mornye maps onto Material 3 ColorScheme roles and AppTokens, and how widgets branch with context.isMornye and context.adaptiveIcon. Derived from every file in lib/theme/, lib/models/theme_settings.dart, lib/providers/theme_provider.dart, lib/providers/runtime_profile_provider.dart, lib/widgets/mornye_chrome.dart and test/design_system_test.dart.
5.0 KiB
Theme Layer
The app has two visual styles, chosen in Appearance settings and stored as
AppThemeStyle { material, mornye } (lib/models/theme_settings.dart,
preference key theme_style, loaded by lib/providers/theme_provider.dart):
- Material: Material 3 with dynamic color or a seed color, optional AMOLED surfaces, and Material 3 Expressive motion and components.
- Mornye: the Flutter counterpart of Mornye's iPhone visual system: iOS system colors, Cupertino typography and icons, and glass surfaces. Its palette is kept independent of wallpaper colors.
Both styles still produce a Flutter ThemeData with useMaterial3: true, so
widgets read Theme.of(context).colorScheme and textTheme the same way.
Mornye maps its colors onto Material 3 ColorScheme roles instead of
replacing them.
Files
| File | Role |
|---|---|
lib/theme/dynamic_color_wrapper.dart |
DynamicColorWrapper, used by lib/app.dart. Builds the light and dark ThemeData for the selected style |
lib/theme/app_theme.dart |
AppTheme.light and AppTheme.dark: Material 3 component themes over a ColorScheme |
lib/theme/app_tokens.dart |
AppTokens, a ThemeExtension with the shared radii, spacing, cover sizes and header metrics |
lib/theme/mornye_theme.dart |
MornyeTheme: the Mornye ThemeExtension, its ThemeData builder, context.isMornye, MornyeSettingsTheme |
lib/theme/mornye_icons.dart |
mornyeIconFor (Material icon to Cupertino icon) and context.adaptiveIcon |
lib/theme/material_symbols.dart |
Material Symbols Rounded counterparts of Icons.* for the Material style |
lib/theme/material_expressive.dart |
materialExpressiveEnabled and MaterialExpressiveScope, the bridge to the Material UI and Material 3 Expressive packages |
lib/theme/app_motion.dart |
AppMotionSpec, SpringCurve and AppMotion spring presets |
lib/theme/cover_palette.dart |
CoverPalette, HeaderPalette and CoverPaletteBuilder: colors extracted from artwork |
How a Theme Is Built
DynamicColorWrapper watches themeProvider and the custom font provider.
- Material:
DynamicColorBuildersupplies the wallpaper scheme when dynamic color is on; otherwiseColorScheme.fromSeeduses the seed color. AMOLED replaces the dark surfaces with black and near-black.AppThemethen adds component themes and registersAppTokens.standard. - Mornye:
MornyeTheme.build(brightness, ...)takes the Mornye accent (MornyeAccent, red by default), the system-font and custom-font choices, glass clarity (default 0.75) and glass outlines. It seeds aColorSchemefrom the accent and overrides the roles with iOS grouped colors (for example#F2F2F7and#1C1C1E). Text uses Cupertino styles. The font is the custom font when one is set; otherwise the Cupertino system font (CupertinoSystemText, SF on Apple) on iOS and macOS or when the system font is chosen, and Inter elsewhere. It registers its ownMornyeTheme.tokens, a copy ofAppTokens.standardwith different radii (smaller for most shapes, larger for sheets) and iOS title sizes, and removes ink splashes.
MornyeTheme.build caches up to 16 base themes; changing only glass clarity
or outlines reuses the cached theme. Use MornyeTheme.fromContext to rebuild
a local surface (another brightness or chrome color) without losing the
user's Mornye preferences.
Using the Layer in Widgets
- Read tokens with
context.tokens, notTheme.of(context).extension. It falls back toAppTokens.standardoutside a themed subtree. - Branch on
context.isMornye, which is true when the nearestThemecarries aMornyeThemeextension. Because it follows the nearestTheme, a subtree can be switched by wrapping it in another theme. - Keep
Icons.*at call sites and pass them throughcontext.adaptiveIcon; Mornye then shows the Cupertino glyph frommornyeIconFor. - Gate Material 3 Expressive motion with
materialExpressiveEnabled(false for Mornye and when the OS asks for reduced motion).AppMotionpresets return the call site's fallback spec in those cases. - Wrap settings pages in
MornyeSettingsThemefor iOS inset grouped surfaces; it does nothing in the Material style.
Mornye Outside This Folder
- Glass rendering level:
MornyeGlassLevel(flat,frosted,liquid) andmornyeGlassLevelProviderinlib/providers/runtime_profile_provider.dartchoose how much glass a device renders. - Mornye widgets live in
lib/widgets/mornye_*.dart, for exampleMornyeGlassinlib/widgets/mornye_chrome.dart. Mornye-only screens arelib/screens/mornye_library_screen.dartandlib/screens/track_metadata_mornye.dart; most other screens branch oncontext.isMornye.
Tests
test/design_system_test.dart checks that AppTokens is registered on the
Material themes (AppTokens.standard) and on Mornye (MornyeTheme.tokens),
the Mornye dark accent, and Mornye's font families and letter spacing. Mornye
widgets have their own test/mornye_*_test.dart suites.