flutter-design
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFlutter Design Patterns
Flutter 设计模式
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, referenceskill.mobile-app-design-mastery
用于 mobile-app-design-mastery Skill 的 Flutter/Dart 实现配套指南。将 Refactoring UI 原则转化为 Flutter 代码。
前置条件: 此 Skill 提供 Flutter 专用语法。有关移动端设计理论和决策,请参阅Skill。mobile-app-design-mastery
⚠️ CRITICAL: Project Theme First
⚠️ 关键:优先使用项目主题
ALWAYS check existing theme configuration before creating new styles.
If the project has , , , or similar—USE THEM:
ThemeDataAppColorsAppTextStylesdart
// Check lib/core/theme/ or lib/config/
// Common patterns:
AppColors.primary
AppTextStyles.headline
AppSpacing.md
context.theme.colorScheme.primaryPriority order:
- Project-defined theme (AppColors, AppTextStyles, custom ThemeExtension)
- Theme.of(context) access
- Hardcoded values as last resort only
在创建新样式前,始终检查现有主题配置。
如果项目已有 、、 或类似定义——请直接使用:
ThemeDataAppColorsAppTextStylesdart
// Check lib/core/theme/ or lib/config/
// Common patterns:
AppColors.primary
AppTextStyles.headline
AppSpacing.md
context.theme.colorScheme.primary优先级顺序:
- 项目定义的主题(AppColors、AppTextStyles、自定义 ThemeExtension)
- Theme.of(context) 访问方式
- 仅将硬编码值作为最后手段
ThemeData Setup (Material 3)
ThemeData 设置(Material 3)
dart
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,
),
);dart
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
颜色访问模式
dart
// ✅ 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 themeColorScheme roles:
| Role | Light Mode | Dark Mode | Use Case |
|---|---|---|---|
| Brand color | Lighter brand | CTAs, active states |
| White | Dark | Text on primary |
| White | Gray-900 | Cards, sheets |
| Gray-900 | White | Body text |
| Gray-100 | Gray-800 | Elevated surfaces |
| Gray-400 | Gray-600 | Borders |
| Red | Light red | Error states |
dart
// ✅ 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 themeColorScheme 角色:
| 角色 | 浅色模式 | 深色模式 | 使用场景 |
|---|---|---|---|
| 品牌色 | 较浅的品牌色 | CTA、激活状态 |
| 白色 | 深色 | primary 上的文字 |
| 白色 | Gray-900 | 卡片、面板 |
| Gray-900 | 白色 | 正文文本 |
| Gray-100 | Gray-800 | 悬浮表面 |
| Gray-400 | Gray-600 | 边框 |
| 红色 | 浅红色 | 错误状态 |
Typography (TextTheme)
排版(TextTheme)
Material 3 Type Scale:
| Style | Size (sp) | Use Case |
|---|---|---|
| 57 | Hero text |
| 45 | Large display |
| 36 | Display |
| 32 | Large headings |
| 28 | Page titles |
| 24 | Section titles |
| 22 | Card titles |
| 16 | Subtitles |
| 14 | Small titles |
| 16 | Emphasis body |
| 14 | Default body |
| 12 | Secondary text |
| 14 | Buttons |
| 12 | Labels |
| 11 | Captions |
dart
// ✅ 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.bodyMediumMaterial 3 字号层级:
| 样式 | 大小(sp) | 使用场景 |
|---|---|---|
| 57 | Hero 文本 |
| 45 | 大型展示文本 |
| 36 | 展示文本 |
| 32 | 大标题 |
| 28 | 页面标题 |
| 24 | 区块标题 |
| 22 | 卡片标题 |
| 16 | 副标题 |
| 14 | 小标题 |
| 16 | 强调正文 |
| 14 | 默认正文 |
| 12 | 次要文本 |
| 14 | 按钮 |
| 12 | 标签 |
| 11 | 说明文字 |
dart
// ✅ 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.bodyMediumSpacing System (4dp Grid)
间距系统(4dp 网格)
Create reusable spacing constants:
dart
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])创建可复用的间距常量:
dart
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
BoxDecoration 模式
Card (Flat)
卡片(扁平)
dart
Container(
decoration: BoxDecoration(
color: colors.surface,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: colors.outline.withOpacity(0.2)),
),
)dart
Container(
decoration: BoxDecoration(
color: colors.surface,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: colors.outline.withOpacity(0.2)),
),
)Card (Elevated)
卡片(悬浮)
dart
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),
),
],
),
)dart
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
输入框
dart
Container(
decoration: BoxDecoration(
color: colors.surface,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: colors.outline),
),
)dart
Container(
decoration: BoxDecoration(
color: colors.surface,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: colors.outline),
),
)Shadow Scale
阴影层级
dart
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))dart
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
反模式
dart
// ❌ 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) // Symmetricdart
// ❌ 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) // SymmetricReference Files
参考文件
| Topic | File |
|---|---|
| ThemeData & ColorScheme | theming.md |
| Widget recipes | widgets.md |
| ThemeExtension patterns | extensions.md |
| 主题 | 文件 |
|---|---|
| ThemeData 与 ColorScheme | theming.md |
| Widget 配方 | widgets.md |
| ThemeExtension 模式 | extensions.md |