flutter-design
1
总安装量
1
周安装量
#53516
全站排名
安装命令
npx skills add https://github.com/anilcancakir/my-claude-code --skill flutter-design
Agent 安装分布
junie
1
opencode
1
cursor
1
claude-code
1
antigravity
1
gemini-cli
1
Skill 文档
Flutter Design Patterns
Flutter/Dart implementation companion for mobile-app-design-mastery skill. Translates Refactoring UI principles into Flutter code.
Prerequisite: This skill provides Flutter-specific syntax. For mobile design theory and decision-making, reference
mobile-app-design-masteryskill.
â ï¸ CRITICAL: Project Theme First
ALWAYS check existing theme configuration before creating new styles.
If the project has ThemeData, AppColors, AppTextStyles, or similarâUSE THEM:
// Check lib/core/theme/ or lib/config/
// Common patterns:
AppColors.primary
AppTextStyles.headline
AppSpacing.md
context.theme.colorScheme.primary
Priority order:
- Project-defined theme (AppColors, AppTextStyles, custom ThemeExtension)
- Theme.of(context) access
- Hardcoded values as last resort only
ThemeData Setup (Material 3)
MaterialApp(
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue, // Brand color
brightness: Brightness.light,
),
textTheme: _textTheme,
),
darkTheme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
brightness: Brightness.dark,
),
textTheme: _textTheme,
),
);
Color Access Patterns
// â
CORRECT: Use ColorScheme
final colors = Theme.of(context).colorScheme;
Container(color: colors.primary)
Text('Hello', style: TextStyle(color: colors.onSurface))
// â
With extension
extension BuildContextX on BuildContext {
ColorScheme get colors => Theme.of(this).colorScheme;
}
// Usage: context.colors.primary
// â AVOID: Hardcoded colors
Container(color: Colors.blue) // Ignores theme
ColorScheme roles:
| Role | Light Mode | Dark Mode | Use Case |
|---|---|---|---|
primary |
Brand color | Lighter brand | CTAs, active states |
onPrimary |
White | Dark | Text on primary |
surface |
White | Gray-900 | Cards, sheets |
onSurface |
Gray-900 | White | Body text |
surfaceContainerHighest |
Gray-100 | Gray-800 | Elevated surfaces |
outline |
Gray-400 | Gray-600 | Borders |
error |
Red | Light red | Error states |
Typography (TextTheme)
Material 3 Type Scale:
| Style | Size (sp) | Use Case |
|---|---|---|
displayLarge |
57 | Hero text |
displayMedium |
45 | Large display |
displaySmall |
36 | Display |
headlineLarge |
32 | Large headings |
headlineMedium |
28 | Page titles |
headlineSmall |
24 | Section titles |
titleLarge |
22 | Card titles |
titleMedium |
16 | Subtitles |
titleSmall |
14 | Small titles |
bodyLarge |
16 | Emphasis body |
bodyMedium |
14 | Default body |
bodySmall |
12 | Secondary text |
labelLarge |
14 | Buttons |
labelMedium |
12 | Labels |
labelSmall |
11 | Captions |
// â
CORRECT: Use TextTheme
Text('Title', style: Theme.of(context).textTheme.headlineMedium)
// With extension
extension BuildContextX on BuildContext {
TextTheme get textTheme => Theme.of(this).textTheme;
}
// Usage: context.textTheme.bodyMedium
Spacing System (4dp Grid)
Create reusable spacing constants:
abstract class AppSpacing {
static const double xs = 4;
static const double sm = 8;
static const double md = 12;
static const double base = 16;
static const double lg = 24;
static const double xl = 32;
static const double xxl = 48;
// SizedBox shortcuts
static const SizedBox gapXs = SizedBox(height: xs, width: xs);
static const SizedBox gapSm = SizedBox(height: sm, width: sm);
static const SizedBox gapMd = SizedBox(height: md, width: md);
static const SizedBox gapBase = SizedBox(height: base, width: base);
static const SizedBox gapLg = SizedBox(height: lg, width: lg);
}
// Usage
Padding(padding: EdgeInsets.all(AppSpacing.base))
Column(children: [widget1, AppSpacing.gapMd, widget2])
BoxDecoration Patterns
Card (Flat)
Container(
decoration: BoxDecoration(
color: colors.surface,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: colors.outline.withOpacity(0.2)),
),
)
Card (Elevated)
Container(
decoration: BoxDecoration(
color: colors.surface,
borderRadius: BorderRadius.circular(12),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.08),
blurRadius: 10,
offset: const Offset(0, 2),
),
],
),
)
Input Field
Container(
decoration: BoxDecoration(
color: colors.surface,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: colors.outline),
),
)
Shadow Scale
abstract class AppShadows {
static List<BoxShadow> sm = [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 4,
offset: const Offset(0, 1),
),
];
static List<BoxShadow> md = [
BoxShadow(
color: Colors.black.withOpacity(0.08),
blurRadius: 8,
offset: const Offset(0, 2),
),
];
static List<BoxShadow> lg = [
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 16,
offset: const Offset(0, 4),
),
];
static List<BoxShadow> xl = [
BoxShadow(
color: Colors.black.withOpacity(0.15),
blurRadius: 24,
offset: const Offset(0, 8),
),
];
}
// Usage
Container(decoration: BoxDecoration(boxShadow: AppShadows.md))
Anti-Patterns
// â NEVER
Colors.blue // Hardcoded, ignores theme
TextStyle(fontSize: 16) // Not from TextTheme
SizedBox(height: 17) // Off 4dp grid
EdgeInsets.only(top: 20, left: 8) // Asymmetric without reason
// â
INSTEAD
context.colors.primary
context.textTheme.bodyLarge
SizedBox(height: 16) // On grid
EdgeInsets.all(16) // Symmetric
Reference Files
| Topic | File |
|---|---|
| ThemeData & ColorScheme | theming.md |
| Widget recipes | widgets.md |
| ThemeExtension patterns | extensions.md |