flutter-design

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Flutter 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, reference
mobile-app-design-mastery
skill.

用于 mobile-app-design-mastery Skill 的 Flutter/Dart 实现配套指南。将 Refactoring UI 原则转化为 Flutter 代码。
前置条件: 此 Skill 提供 Flutter 专用语法。有关移动端设计理论和决策,请参阅
mobile-app-design-mastery
Skill。

⚠️ CRITICAL: Project Theme First

⚠️ 关键:优先使用项目主题

ALWAYS check existing theme configuration before creating new styles.
If the project has
ThemeData
,
AppColors
,
AppTextStyles
, or similar—USE THEM:
dart
// Check lib/core/theme/ or lib/config/
// Common patterns:
AppColors.primary
AppTextStyles.headline
AppSpacing.md
context.theme.colorScheme.primary
Priority order:
  1. Project-defined theme (AppColors, AppTextStyles, custom ThemeExtension)
  2. Theme.of(context) access
  3. Hardcoded values as last resort only

在创建新样式前,始终检查现有主题配置。
如果项目已有
ThemeData
、
AppColors
、
AppTextStyles
或类似定义——请直接使用:
dart
// Check lib/core/theme/ or lib/config/
// Common patterns:
AppColors.primary
AppTextStyles.headline
AppSpacing.md
context.theme.colorScheme.primary
优先级顺序:
  1. 项目定义的主题(AppColors、AppTextStyles、自定义 ThemeExtension)
  2. Theme.of(context) 访问方式
  3. 仅将硬编码值作为最后手段

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 theme
ColorScheme roles:
RoleLight ModeDark ModeUse Case
primary
Brand colorLighter brandCTAs, active states
onPrimary
WhiteDarkText on primary
surface
WhiteGray-900Cards, sheets
onSurface
Gray-900WhiteBody text
surfaceContainerHighest
Gray-100Gray-800Elevated surfaces
outline
Gray-400Gray-600Borders
error
RedLight redError 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 theme
ColorScheme 角色:
角色浅色模式深色模式使用场景
primary
品牌色较浅的品牌色CTA、激活状态
onPrimary
白色深色primary 上的文字
surface
白色Gray-900卡片、面板
onSurface
Gray-900白色正文文本
surfaceContainerHighest
Gray-100Gray-800悬浮表面
outline
Gray-400Gray-600边框
error
红色浅红色错误状态

Typography (TextTheme)

排版(TextTheme)

Material 3 Type Scale:
StyleSize (sp)Use Case
displayLarge
57Hero text
displayMedium
45Large display
displaySmall
36Display
headlineLarge
32Large headings
headlineMedium
28Page titles
headlineSmall
24Section titles
titleLarge
22Card titles
titleMedium
16Subtitles
titleSmall
14Small titles
bodyLarge
16Emphasis body
bodyMedium
14Default body
bodySmall
12Secondary text
labelLarge
14Buttons
labelMedium
12Labels
labelSmall
11Captions
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.bodyMedium

Material 3 字号层级:
样式大小(sp)使用场景
displayLarge
57Hero 文本
displayMedium
45大型展示文本
displaySmall
36展示文本
headlineLarge
32大标题
headlineMedium
28页面标题
headlineSmall
24区块标题
titleLarge
22卡片标题
titleMedium
16副标题
titleSmall
14小标题
bodyLarge
16强调正文
bodyMedium
14默认正文
bodySmall
12次要文本
labelLarge
14按钮
labelMedium
12标签
labelSmall
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.bodyMedium

Spacing 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)             // Symmetric

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)             // Symmetric

Reference Files

参考文件

TopicFile
ThemeData & ColorSchemetheming.md
Widget recipeswidgets.md
ThemeExtension patternsextensions.md
主题文件
ThemeData 与 ColorSchemetheming.md
Widget 配方widgets.md
ThemeExtension 模式extensions.md