flutter-theming
Skill12 starsChanged 55 days agoMITView on GitHub
Use when creating, migrating or auditing a Material 3 theme in a Flutter app - assembling ColorScheme and ThemeData, theming a specific component, replacing a hardcoded colour with a theme token, or reviewing visual consistency. Triggers on any mention of colour, theme, dark mode, or "this looks off-brand" on a Flutter screen.
1 other project uses a version of this.
Adopted by
Most recently taken up by
- lubshad/skillstheir own version, first seen Nov 1, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- lubshad/skills★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository15 other files it gives its agents.
- flutter-accessibilitySkill
- flutter-cachingSkill
- flutter-clean-archSkill
- flutter-concurrencySkill
- flutter-dart-styleSkill
- flutter-design-systemSkill
- flutter-diSkill
- flutter-formsSkill
- flutter-monorepoSkill
- flutter-new-moduleSkill
- flutter-runSkill
- flutter-stateSkill
- flutter-store-cdSkill
- flutter-testingSkill
- flutter-webSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
18 rules. About 0 are common, 1 is distinctive, and 17 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
17
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 8
Always prefer importing a token from the design system over duplicating the value in an app.
- Very specific: line 217
Enable Material 3: Add useMaterial3: true to ThemeData
- Very specific: line 333
Read every app's root widget (app_widget.dart / main.dart)
- Very specific: line 334
Read all files under */lib/theme/, */lib/shared/theme/
- Very specific: line 335
Search for hardcoded Color(0x...) values in widget files (outside theme files)
- Very specific: line 336
Search for deprecated APIs: accentColor, primarySwatch, FlatButton, RaisedButton, MaterialState
- Very specific: line 337
Check that all apps import from design_system instead of duplicating token values
- Very specific: line 338
Verify useMaterial3: true is set in all apps
- Very specific: line 367
Always read the app's existing app_widget.dart and theme files before making changes
- Very specific: line 368
Import shared tokens from design_system — never duplicate DSColors, DSSpacing, DSRadius, or DSTypography values
- Very specific: line 369
Use abstract final class for all token/color/style classes (cannot be instantiated)
- Very specific: line 370
All token values must be static const
- Very specific: line 371
Use ColorScheme.fromSeed() — never construct ColorScheme() manually unless overriding specific slots
- Very specific: line 372
Use *ThemeData suffix for component themes (CardThemeData, AppBarThemeData, etc.)
- Very specific: line 373
Use WidgetStateProperty and WidgetState — never MaterialStateProperty or MaterialState
- Very specific: line 374
Use .withValues(alpha: 0.5) — never .withOpacity(0.5) (deprecated)
- Very specific: line 375
Prefer FilledButton for primary actions over ElevatedButton (Material 3 convention)
- Very specific: line 377
When the app already has an app-local <App>DesignTokens-style class, migrate its values into the design_system package or into …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
You are working on theming for a Flutter app. The patterns below assume a shared design_system/ package holding cross-app tokens (DSColors, DSSpacing, DSRadius, DSTypography), with each app free to keep its own lib/theme/ for app-specific overrides.
Always prefer importing a token from the design system over duplicating the value in an app.
Workflow: create
Scaffold a complete theme for the target app, taken from the request or the skill argument. If it is not clear which app, ask before writing anything: each one has its own theme directory and overwriting the wrong app is silent.
Theme Directory Structure
<app>/lib/shared/theme/
├── app_theme.dart ← ThemeData factory (light/dark)
├── app_colors.dart ← App-specific color overrides (imports DSColors)
└── app_text_styles.dart ← App-specific text style overrides (imports DSTypography)app_colors.dart
import 'package:flutter/material.dart';
import 'package:design_system/design_system.dart';
/// App-specific color aliases. Delegates to DSColors for shared tokens
/// and defines overrides only when this app diverges from the design system.
abstract final class AppColors {
// Re-export shared tokens for convenience
static const Color primary = DSColors.brandPrimary;
static const Color secondary = DSColors.brandSecondary;
static const Color error = DSColors.error;
static const Color success = DSColors.success;
static const Color warning = DSColors.warning;
// App-specific overrides
static const Color scaffoldBackground = Color(0xFFF8F9FA);
static const Color cardBackground = DSColors.neutral0;
static const Color divider = DSColors.neutral200;
}app_text_styles.dart
import 'package:flutter/material.dart';
import 'package:design_system/design_system.dart';
/// App-specific text style overrides. Use DSTypography directly when possible.
abstract final class AppTextStyles {
static TextStyle get pageTitle => DSTypography.headingH4;
static TextStyle get sectionTitle => DSTypography.headingH5;
static TextStyle get body => DSTypography.body14Regular;
static TextStyle get caption => DSTypography.caption;
// App-specific additions only
static TextStyle get price => DSTypography.price;
}app_theme.dart (Material 3)
import 'package:flutter/material.dart';
import 'package:design_system/design_system.dart';
import 'app_colors.dart';
class AppTheme {
static ThemeData light() {
final colorScheme = ColorScheme.fromSeed(
seedColor: AppColors.primary,
brightness: Brightness.light,
);
return ThemeData(
useMaterial3: true,
colorScheme: colorScheme,
fontFamily: 'Roboto',
scaffoldBackgroundColor: AppColors.scaffoldBackground,
// --- Component Themes (use *ThemeData suffix) ---
appBarTheme: AppBarThemeData(
backgroundColor: colorScheme.surface,
foregroundColor: colorScheme.onSurface,
elevation: 0,
centerTitle: false,
),
cardTheme: CardThemeData(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: DSRadius.lgBorderRadius,
side: BorderSide(color: AppColors.divider),
),
color: AppColors.cardBackground,
),
filledButtonTheme: FilledButtonThemeData(
style: FilledButton.styleFrom(
backgroundColor: AppColors.primary,
foregroundColor: DSColors.neutral0,
shape: RoundedRectangleBorder(
borderRadius: DSRadius.mdBorderRadius,
),
minimumSize: const Size(double.infinity, 48),
textStyle: const TextStyle(
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primary,
foregroundColor: DSColors.neutral0,
shape: RoundedRectangleBorder(
borderRadius: DSRadius.mdBorderRadius,
),
minimumSize: const Size(double.infinity, 48),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: AppColors.primary,
side: BorderSide(color: AppColors.primary),
shape: RoundedRectangleBorder(
borderRadius: DSRadius.mdBorderRadius,
),
minimumSize: const Size(double.infinity, 48),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: AppColors.primary,
),
),
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: DSRadius.mdBorderRadius,
),
contentPadding: const EdgeInsets.symmetric(
horizontal: DSSpacing.lg,
vertical: DSSpacing.md,
),
),
navigationBarTheme: NavigationBarThemeData(
indicatorColor: colorScheme.secondaryContainer,
labelTextStyle: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return DSTypography.label.copyWith(color: AppColors.primary);
}
return DSTypography.label;
}),
),
dividerTheme: DividerThemeData(
color: AppColors.divider,
thickness: 1,
space: 0,
),
// --- Typography ---
textTheme: const TextTheme(
displayLarge: DSTypography.headingH4,
headlineMedium: DSTypography.headingH5,
titleLarge: DSTypography.headingH6,
bodyLarge: DSTypography.body16Regular,
bodyMedium: DSTypography.body14Regular,
bodySmall: DSTypography.body12Regular,
labelSmall: DSTypography.label,
),
);
}
static ThemeData dark() {
final colorScheme = ColorScheme.fromSeed(
seedColor: AppColors.primary,
brightness: Brightness.dark,
);
return ThemeData(
useMaterial3: true,
colorScheme: colorScheme,
fontFamily: 'Roboto',
// Mirror the light theme structure but with dark-appropriate values.
// Override specific component themes as needed.
);
}
}Applying the Theme
In app_widget.dart:
MaterialApp(
theme: AppTheme.light(),
darkTheme: AppTheme.dark(),
themeMode: ThemeMode.system,
// ...
)Workflow: migrate
Migrate an existing app to Material 3 theming. Read the app's current app_widget.dart and any existing theme files first.
Migration Checklist
- Enable Material 3: Add
useMaterial3: truetoThemeData - Replace
primarySwatchwithcolorScheme: ColorScheme.fromSeed(seedColor: ...) - Apply deprecated replacements:
| Deprecated | Replacement |
accentColor | colorScheme.secondary |
primaryColor (in ThemeData) | colorScheme.primary |
AppBarTheme.color | AppBarThemeData.backgroundColor |
AppBarTheme.brightness | AppBarThemeData.systemOverlayStyle |
BottomNavigationBar | NavigationBar |
FlatButton | TextButton |
RaisedButton | ElevatedButton |
ButtonTheme | TextButtonThemeData / ElevatedButtonThemeData / OutlinedButtonThemeData |
MaterialStateProperty | WidgetStateProperty |
MaterialState | WidgetState |
- Normalize component theme class names (use
*ThemeDatasuffix):
| Old Name | Correct Name |
CardTheme(...) constructor in ThemeData | CardThemeData(...) |
AppBarTheme(...) | AppBarThemeData(...) |
IconTheme(...) in ThemeData | IconThemeData(...) |
TabBarTheme(...) | TabBarThemeData(...) |
DialogTheme(...) | DialogThemeData(...) |
BottomSheetTheme(...) | BottomSheetThemeData(...) |
TooltipTheme(...) | TooltipThemeData(...) |
- Replace hardcoded colors with
DSColorsorTheme.of(context).colorSchemereferences - Update typography to Material 3 scale:
| Old (Material 2) | New (Material 3) |
headline1 | displayLarge |
headline2 | displayMedium |
headline3 | displaySmall |
headline4 | headlineMedium |
headline5 | headlineSmall |
headline6 | titleLarge |
subtitle1 | titleMedium |
subtitle2 | titleSmall |
bodyText1 | bodyLarge |
bodyText2 | bodyMedium |
caption | bodySmall |
button | labelLarge |
overline | labelSmall |
Button Styling After Migration
// ElevatedButton with full styling
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primary,
foregroundColor: Colors.white,
disabledBackgroundColor: DSColors.neutral200,
shape: RoundedRectangleBorder(
borderRadius: DSRadius.mdBorderRadius,
),
padding: const EdgeInsets.symmetric(
horizontal: DSSpacing.xl,
vertical: DSSpacing.md,
),
),
onPressed: () {},
child: const Text('Action'),
)
// FilledButton (Material 3 preferred primary action)
FilledButton(
onPressed: () {},
child: const Text('Primary Action'),
)
// FilledButton.tonal (secondary emphasis)
FilledButton.tonal(
onPressed: () {},
child: const Text('Secondary Action'),
)State-Dependent Styling
// Use WidgetStateProperty (NOT MaterialStateProperty) for state-dependent styles
ButtonStyle(
backgroundColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.disabled)) return DSColors.neutral200;
if (states.contains(WidgetState.pressed)) return AppColors.primary.withValues(alpha: 0.8);
if (states.contains(WidgetState.hovered)) return AppColors.primary.withValues(alpha: 0.9);
return AppColors.primary;
}),
foregroundColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.disabled)) return DSColors.neutral500;
return DSColors.neutral0;
}),
overlayColor: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.pressed)) return Colors.white.withValues(alpha: 0.12);
if (states.contains(WidgetState.hovered)) return Colors.white.withValues(alpha: 0.08);
return null;
}),
)
// Shorthand for simple cases
WidgetStateProperty.all(DSColors.neutral0)Workflow: audit
Audit theme consistency across apps and report issues.
Audit Steps
- Read every app's root widget (
app_widget.dart/main.dart) - Read all files under
*/lib/theme/,*/lib/shared/theme/ - Search for hardcoded
Color(0x...)values in widget files (outside theme files) - Search for deprecated APIs:
accentColor,primarySwatch,FlatButton,RaisedButton,MaterialState - Check that all apps import from
design_systeminstead of duplicating token values - Verify
useMaterial3: trueis set in all apps
Report Format
Produce a markdown report with:
## Theme Audit
### Material 3 Status
- <app>: [enabled/disabled] (one line per app)
### Deprecated API Usage
- [file:line] — description of deprecated usage and fix
### Hardcoded Colors (should use DSColors/AppColors)
- [file:line] — Color(0xFF...) → suggested token
### Token Duplication
- [file] duplicates [token] already in design_system
### Recommendations
1. ...Instructions
- Always read the app's existing
app_widget.dartand theme files before making changes - Import shared tokens from
design_system— never duplicateDSColors,DSSpacing,DSRadius, orDSTypographyvalues - Use
abstract final classfor all token/color/style classes (cannot be instantiated) - All token values must be
static const - Use
ColorScheme.fromSeed()— never constructColorScheme()manually unless overriding specific slots - Use
*ThemeDatasuffix for component themes (CardThemeData,AppBarThemeData, etc.) - Use
WidgetStatePropertyandWidgetState— neverMaterialStatePropertyorMaterialState - Use
.withValues(alpha: 0.5)— never.withOpacity(0.5)(deprecated) - Prefer
FilledButtonfor primary actions overElevatedButton(Material 3 convention) - Always set
useMaterial3: true - When the app already has an app-local
<App>DesignTokens-style class, migrate its values into thedesign_systempackage or intoapp_colors.dart/app_text_styles.dart— do not leave duplicate token files
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

