accessibility
Audits or remediates Flutter widgets against WCAG 2.2 conformance levels A, AA, or AAA across iOS, Android, Web, macOS, Windows,…
Best practices for Flutter theming with Material 3, treating ThemeData as the single source of truth for colors, typography, component styles, and spacing. Use when creating, modifying, or reviewing ThemeData, ColorScheme, TextTheme, component themes, spacing systems, or
$ npx -y skills add VeryGoodOpenSource/vgv-ai-flutter-plugin --skill material-theming --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/material-themingContext preview
The summary Claude sees to decide when to auto-load this skill.
Best practices for Flutter theming with Material 3, treating ThemeData as the single source of truth for colors, typography, component styles, and spacing. Use when creating, modifying, or reviewing ThemeData, ColorScheme, TextTheme, component themes, spacing systems, or
name: material-theming description: > Best practices for Flutter theming with Material 3, treating ThemeData as the single source of truth for colors, typography, component styles, and spacing. Use when creating, modifying, or reviewing ThemeData, ColorScheme, TextTheme, component themes, spacing systems, or light/dark mode support, and whenever widget code carries its own styling: a hardcoded Color, an inline TextStyle, raw padding or gap numbers, the same decoration repeated across widget instances, or a brightness/dark-mode conditional inside build, even when the request only says "review this widget", "cut the duplication", "stop repeating this", or "tidy this up". allowed-tools: Read Glob Grep
Material 3 theming best practices for Flutter applications using `ThemeData` as the single source of truth for colors, typography, component styles, and spacing.
Apply these standards to all theming work:
Centralize all color definitions in a dedicated class:
abstract class AppColors {
static const primaryColor = Color(0xFF4F46E5);
static const secondaryColor = Color(0xFF9C27B0);
static const errorColor = Color(0xFFDC2626);
static const surfaceColor = Color(0xFFFAFAFA);
}Every color reaches widgets through a `ColorScheme` role. Define two schemes — one light, one dark — inside a single `AppTheme` class, and pass both to `MaterialApp` as `theme` and `darkTheme`. That pair is what makes brightness branching in widget code unnecessary:
class AppTheme {
static ThemeData get light => ThemeData(
colorScheme: const ColorScheme.light(
brightness: Brightness.light,
surface: AppColors.surfaceColor,
// ... remaining color roles
),
);
static ThemeData get dark => ThemeData(
colorScheme: const ColorScheme.dark(
brightness: Brightness.dark,
surface: AppColors.surfaceColorDark,
// ... remaining color roles
),
);
}See [references/color-scheme.md](references/color-scheme.md) for the full 45-role `ColorScheme` configuration, `ColorScheme.fromSeed`, and how widgets read colors.
Define an `AppTextStyle` class with a base style and named variants (displayLarge, headlineMedium, bodyLarge, etc.), then integrate them into `ThemeData.textTheme`. Access styles via `Theme.of(context).textTheme`.
See [references/typography.md](references/typography.md) for font asset setup, the full `AppTextStyle` class, `TextTheme` integration, and widget access patterns.
Map the literal to the nearest slot `AppTextStyle` already defines, by size and weight — 18px/w500 lands on `titleLarge` (20/w500), 16px/w400 on `bodyLarge`, 14px/w500 on `labelLarge`. Adjust that slot's size in `AppTextStyle` if the app needs a different one; do not keep the number at the call site. Do not land on a slot `AppTextStyle` does not define and `TextTheme` does not register: the read still returns a style, but it comes from Material's default typography, so the app's font silently reverts. `copyWith` at the call site sets a color role and nothing else:
final theme = Theme.of(context);
Text(
label,
style: theme.textTheme.titleLarge?.copyWith(
color: theme.colorScheme.onPrimary,
),
)Swapping only the color and leaving `TextStyle(fontSize: 18, fontWeight: FontWeight.w500)` in the widget is not a fix — the typography still lives outside the theme.
Define component themes centrally in `ThemeData` (e.g., `filledButtonTheme`, `inputDecorationTheme`, `appBarTheme`) instead of styling individual widget instances. A complete `AppTheme` class assembles `ColorScheme`, `TextTheme`, and all component themes into a single `ThemeData`.
See [references/components.md](references/components.md) for FilledButton, InputDecoration, and AppBar theme examples, the complete theme assembly, and widget access patterns.
When the same decoration or style appears on many widget instances, move it into the matching component theme and delete it from every call site. Do not extract it into a wrapper widget, a shared `InputDecoration` constant, or a `buildDecoration()` helper: those still require each call site to opt in, still leave the values outside `ThemeData`, and are bypassed the
A Claude Code plugin that accelerates Flutter & Dart development with best-practices skills from Very Good Ventures. Developed with 💙 by Very Good Ventures 🦄
Audits or remediates Flutter widgets against WCAG 2.2 conformance levels A, AA, or AAA across iOS, Android, Web, macOS, Windows,…
Best practices for Flutter animations using the built-in animation framework, covering implicit animations, explicit…
Best practices for Bloc state management in Flutter/Dart, covering Cubit versus Bloc, event and state naming, sealed classes with…
Scaffold a new Dart or Flutter project from a Very Good CLI template, covering the flutter_app, dart_package, flutter_package,…
VGV-specific reference for bumping Dart and Flutter SDK constraints across packages, covering pubspec.yaml environment…
Drives a Dart or Flutter package fully green through a verify-fix-rerun loop across four gates: analyze, format, test, coverage.…