Irbisa · Cheatsheet
Theming & Design Systems
ThemeData, ColorScheme.fromSeed, Material 3, ThemeExtension, custom fonts, dark mode
How do you theme a Flutter app with
ThemeData, and how does a widget read the theme?class App extends StatelessWidget { const App({super.key}); @override Widget build(BuildContext context) { return MaterialApp(…What does
ColorScheme.fromSeeddo, and why is the resultingprimarynot your exact brand colour?const brand = Color(0xFF00695C); final scheme = ColorScheme.fromSeed(seedColor: brand);// scheme.primary != brand: it is the palette tone that reads well on surface // When the literal brand colour is required…What is
TextTheme, and why use its roles instead of writing aTextStyleon every screen?// before: one decision copied into every screenText('Monthly total', style: const TextStyle(fontSize: 22, fontWeight: FontWeight.w600)); // after: a role the theme ownsText('Monthly total', style: Theme.of(context).textTheme.titleLarge);…How do you add custom fonts and custom icons to a Flutter app?
# pubspec.yamlflutter: uses-material-design: true # the font behind Icons.* fonts: - family: Inter…What is the difference between Material and Cupertino widgets, and can you use Cupertino widgets inside a
MaterialApp?How do you support light and dark mode with
theme,darkThemeandthemeMode?const _brand = Color(0xFF6750A4); ThemeData buildTheme(Brightness brightness) { final cs = ColorScheme.fromSeed(seedColor: _brand, brightness: brightness); return ThemeData( colorScheme: cs, // brightness comes from the scheme…Find the bug: on some phones the white logo is invisible on the app bar even though the app is in its light theme.
class BrandLogo extends StatelessWidget { const BrandLogo({super.key}); @override Widget build(BuildContext context) { final isDark = MediaQuery.platformBrightnessOf(context) == Brightness.dark; return Image.asset( isDark ? 'assets/logo_white.png' : 'assets/logo_black.png', height: 24, ); }}This widget builds
MaterialAppwith a brand theme, yet the box is painted in the default purple. Why?class App extends StatelessWidget { const App({super.key}); @override Widget build(BuildContext context) { final primary = Theme.of(context).colorScheme.primary; return MaterialApp( theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF00695C)), ), home: ColoredBox(color: primary, child: const HomePage()), ); }}Find the bug: after switching to dark mode the card turns dark, but the price on it keeps the light theme's colour.
class _PriceTagState extends State<PriceTag> { late final TextStyle _priceStyle = Theme.of(context) .textTheme .headlineSmall! .copyWith(color: Theme.of(context).colorScheme.primary); @override Widget build(BuildContext context) { return Card( child: Padding( padding: const EdgeInsets.all(16), child: Text(widget.price, style: _priceStyle), ), ); }}This section was meant to get red buttons only, but its text lost the app font and a
ThemeExtensionlookup now returns null. What went wrong?@overrideWidget build(BuildContext context) { return Theme( data: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.red), ), child: const DangerZone(), // buttons, text, BrandTokens.of(context) );}_canPayis false, yet this button still paints in full brand colour. Why, and how does a Material button resolve its style?const brand = Color(0xFF00695C); FilledButton( onPressed: _canPay ? _pay : null, style: const ButtonStyle( backgroundColor: WidgetStatePropertyAll(brand), ), child: const Text('Pay'),);The design uses Inter at 400, 500, 600 and 700, but on the device several weights look identical. What is wrong with the font setup?
# pubspec.yaml: every weight the design uses is a declared fileflutter: fonts: - family: Inter fonts: - asset: assets/fonts/Inter-Regular.ttf # weight 400 by default…What are design tokens, and how do primitive and semantic tokens map onto a Flutter theme?
What is
ThemeExtension, and how do you add custom tokens like a success colour or a spacing value toThemeData?@immutableclass BrandTokens extends ThemeExtension<BrandTokens> { const BrandTokens({required this.success, required this.gutter}); final Color success; final double gutter;…What changes when an app moves from Material 2 to Material 3, and what breaks without anyone noticing?
How does a
MaterialApptheme reach Cupertino widgets, and when do you needcupertinoOverrideTheme?final theme = ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF0B6E4F)), fontFamily: 'Inter', // Mainly brightness, primaryColor, primaryContrastingColor and // scaffoldBackgroundColor are derived from the Material theme above. cupertinoOverrideTheme: const CupertinoThemeData(…How do you support Material You dynamic colour on Android while keeping a brand fallback, and when should you not use it?
import 'package:dynamic_color/dynamic_color.dart'; const _brandSeed = Color(0xFF6750A4); class App extends StatelessWidget { const App({super.key});…When should a design system ship its own
InheritedWidgetinstead of putting values in aThemeExtension, and what does each choice cost?How does
MaterialAppanimate a theme change, and why might switching to dark mode flash white or make a gradient jump?MaterialApp( theme: AcmeTheme.light(), darkTheme: AcmeTheme.dark(), themeMode: mode, // MaterialApp inserts AnimatedTheme; these are its knobs themeAnimationDuration: const Duration(seconds: 1), // slow it down to debug…How would you build one design-system package shared by several apps, and change a token without breaking them?
// packages/acme_design/lib/acme_design.dart: the whole public surfaceexport 'src/theme/acme_theme.dart' show AcmeTheme;export 'src/theme/acme_tokens.dart' show AcmeTokens, AcmeSpacing, AcmeContextX;export 'src/components/acme_empty_state.dart' show AcmeEmptyState; // lib/src/theme/acme_theme.dart…How do you stop raw colours and off-scale spacing from creeping back into a codebase that already has a design system?
double contrast(Color a, Color b) { final la = a.computeLuminance(); final lb = b.computeLuminance(); final (hi, lo) = la > lb ? (la, lb) : (lb, la); return (hi + 0.05) / (lo + 0.05);}…You inherit an app with 400 hard-coded colours and text styles and cannot freeze releases. How do you move it onto a design system?
// 1. inventory: grep -rEoh "Color\(0x[0-9A-Fa-f]{8}\)" lib | sort | uniq -c// 412 literals, 31 distinct values; 0xFFF5F5F5 and 0xFFF6F6F6 are one grey // 4. compat layer: nothing renders differently the day it lands,// and the analyzer now lists every remaining call site@Deprecated('Use context.acme.surfaceMuted; removed in app 4.0')…