design-system

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Design Tokens

Design Tokens

Design tokens are the atomic values of the design system. UI elements MUST use tokens — never raw values.
Design tokens 是设计系统的原子级值。UI 元素必须使用 tokens,绝对不能使用原始值。

Colors

颜色

  • ✅ Use
    AppColors.primary
    ,
    AppColors.secondary
    ,
    AppColors.error
    ,
    AppColors.success
    ,
    AppColors.warning
    ,
    AppColors.surface
    ,
    AppColors.textPrimary
    ,
    AppColors.textSecondary
  • ❌ NEVER use
    Color(0xFF...)
    ,
    Colors.blue
    , or inline hex values
  • Map semantic tokens to Material 3
    ColorScheme
    for platform consistency
  • Each color token MUST document its use case and ensure WCAG AA contrast ratio
  • Support light and dark mode via
    ThemeData
    — never branch on
    Brightness
    manually in widgets
  • ✅ 使用
    AppColors.primary
    AppColors.secondary
    AppColors.error
    AppColors.success
    AppColors.warning
    AppColors.surface
    AppColors.textPrimary
    AppColors.textSecondary
  • ❌ 绝对不要使用
    Color(0xFF...)
    Colors.blue
    或者内联十六进制颜色值
  • 将语义 token 映射到 Material 3
    ColorScheme
    以保证平台一致性
  • 每个颜色 token 必须说明其使用场景,并确保符合 WCAG AA 对比度标准
  • 通过
    ThemeData
    支持明暗模式,绝对不要在 widget 中手动根据
    Brightness
    分支判断

Spacing

间距

  • ✅ Use
    AppSpacing.xxs
    (2),
    AppSpacing.xs
    (4),
    AppSpacing.sm
    (8),
    AppSpacing.md
    (16),
    AppSpacing.lg
    (24),
    AppSpacing.xl
    (32),
    AppSpacing.xxl
    (48)
  • ✅ Use
    AppSpacing.screenHorizontal
    (24),
    AppSpacing.screenVertical
    (16) for consistent screen padding
  • ❌ NEVER use
    EdgeInsets.all(16.0)
    ,
    SizedBox(height: 8)
    , or hardcoded padding values
  • Use
    SizedBox(height: AppSpacing.sm)
    for gaps — not
    Container
    or
    Padding
    with empty child
  • ✅ 使用
    AppSpacing.xxs
    (2)、
    AppSpacing.xs
    (4)、
    AppSpacing.sm
    (8)、
    AppSpacing.md
    (16)、
    AppSpacing.lg
    (24)、
    AppSpacing.xl
    (32)、
    AppSpacing.xxl
    (48)
  • ✅ 使用
    AppSpacing.screenHorizontal
    (24)、
    AppSpacing.screenVertical
    (16) 来实现统一的页面内边距
  • ❌ 绝对不要使用
    EdgeInsets.all(16.0)
    SizedBox(height: 8)
    或者硬编码的内边距值
  • 元素间距使用
    SizedBox(height: AppSpacing.sm)
    实现,不要使用子元素为空的
    Container
    Padding

Border Radius

圆角

  • ✅ Use
    AppRadius.xs
    (4),
    AppRadius.sm
    (8),
    AppRadius.md
    (12),
    AppRadius.lg
    (16),
    AppRadius.xl
    (24),
    AppRadius.full
    (999)
  • ❌ NEVER use
    BorderRadius.circular(12)
    or inline radius values
  • ✅ 使用
    AppRadius.xs
    (4)、
    AppRadius.sm
    (8)、
    AppRadius.md
    (12)、
    AppRadius.lg
    (16)、
    AppRadius.xl
    (24)、
    AppRadius.full
    (999)
  • ❌ 绝对不要使用
    BorderRadius.circular(12)
    或者内联圆角值

Typography

排版

  • ✅ Use
    context.textTheme.headlineLarge
    ,
    context.textTheme.bodyMedium
    , etc. via theme extensions
  • ✅ Optionally use
    AppTypography
    wrapper for custom token names mapping to
    TextTheme
  • ❌ NEVER use
    TextStyle(fontSize: 16)
    or inline text styles
  • Define font family, size, weight, and letter spacing as tokens
  • ✅ 通过主题扩展使用
    context.textTheme.headlineLarge
    context.textTheme.bodyMedium
    等样式
  • ✅ 可选择使用
    AppTypography
    封装类实现自定义 token 名称到
    TextTheme
    的映射
  • ❌ 绝对不要使用
    TextStyle(fontSize: 16)
    或者内联文本样式
  • 将字体、字号、字重和字间距定义为 token

Iconography

图标

  • Use a consistent icon set (Material Icons, Cupertino Icons, or custom icon font)
  • Wrap icon usage in
    AppIcons
    constants for consistency
  • Use
    Icon(AppIcons.settings, size: AppSpacing.lg)
    — not raw
    Icons.settings
  • 使用统一的图标集(Material Icons、Cupertino Icons 或者自定义图标字体)
  • 将使用的图标封装到
    AppIcons
    常量中以保证一致性
  • 使用
    Icon(AppIcons.settings, size: AppSpacing.lg)
    ,不要直接使用原始的
    Icons.settings

Reusable Components

可复用组件

Component Rules

组件规则

  • Single Responsibility: Each component has one clear purpose. Complex UIs MUST be composed of smaller, focused components.
  • Parameterization: Components expose parameters for customization (text, callbacks, styles). Avoid creating near-duplicate widgets.
  • Composition Over Inheritance: Build widgets by composing smaller widgets. Do NOT extend framework widgets unless strictly necessary.
  • Immutable Widgets: Design widgets as stateless wherever possible. Manage state externally via BLoC.
  • Reuse Threshold: If a widget or pattern appears 2+ times, extract it into
    core/views/widgets/
    .
  • Testability: Every reusable component MUST have a corresponding widget test in the mirror structure.
  • 单一职责:每个组件只有一个明确的用途。复杂 UI 必须由更小的、职责明确的组件组合而成。
  • 参数化:组件暴露参数支持自定义(文本、回调、样式)。避免创建几乎重复的 widget。
  • 组合优于继承:通过组合更小的 widget 来构建新组件,除非绝对必要,否则不要继承框架 widget。
  • 不可变 Widget:尽可能将 widget 设计为无状态的。通过 BLoC 在外层管理状态。
  • 复用阈值:如果某一个 widget 或模式出现 2 次及以上,将其抽离到
    core/views/widgets/
    目录下。
  • 可测试性:每个可复用组件必须在对应镜像目录下有对应的 widget 测试用例。

Component Organization

组件组织结构

core/
├── views/
│   └── widgets/
│       ├── buttons/          (AppPrimaryButton, AppOutlinedButton, AppIconButton)
│       ├── cards/            (AppCard, AppInfoCard, AppActionCard)
│       ├── dialogs/          (AppAlertDialog, AppConfirmDialog)
│       ├── inputs/           (AppTextField, AppSearchField, AppDropdown)
│       ├── feedback/         (AppSnackbar, AppToast, AppEmptyState, AppErrorState)
│       ├── loading/          (AppLoadingIndicator, AppShimmer, AppSkeleton)
│       └── layout/           (AppScaffold, AppSectionHeader, AppDivider)
├── theme/
│   ├── app_colors.dart
│   ├── app_spacing.dart
│   ├── app_radius.dart
│   ├── app_typography.dart
│   ├── app_theme.dart        (ThemeData assembly)
│   └── app_theme_extensions.dart (BuildContext extensions)
└── constants/
    └── app_icons.dart
core/
├── views/
│   └── widgets/
│       ├── buttons/          (AppPrimaryButton, AppOutlinedButton, AppIconButton)
│       ├── cards/            (AppCard, AppInfoCard, AppActionCard)
│       ├── dialogs/          (AppAlertDialog, AppConfirmDialog)
│       ├── inputs/           (AppTextField, AppSearchField, AppDropdown)
│       ├── feedback/         (AppSnackbar, AppToast, AppEmptyState, AppErrorState)
│       ├── loading/          (AppLoadingIndicator, AppShimmer, AppSkeleton)
│       └── layout/           (AppScaffold, AppSectionHeader, AppDivider)
├── theme/
│   ├── app_colors.dart
│   ├── app_spacing.dart
│   ├── app_radius.dart
│   ├── app_typography.dart
│   ├── app_theme.dart        (ThemeData assembly)
│   └── app_theme_extensions.dart (BuildContext extensions)
└── constants/
    └── app_icons.dart

Component Best Practices

组件最佳实践

  • All reusable widgets MUST accept
    Key
    parameter via
    super.key
  • Use
    const
    constructors in all reusable components
  • Provide sensible defaults — components should work with minimal configuration
  • Document each public component with
    ///
    comment explaining purpose, required params, and an inline usage example
  • 所有可复用 widget 必须通过
    super.key
    接收
    Key
    参数
  • 所有可复用组件都要使用
    const
    构造函数
  • 提供合理的默认值——组件应当在最少配置下即可正常运行
  • 每个公开组件都要通过
    ///
    注释说明用途、必填参数,以及内联使用示例

Theming

主题配置

ThemeData Assembly

ThemeData 组装

  • Assemble
    ThemeData
    in a single
    app_theme.dart
    file using token classes
  • Use
    ColorScheme.fromSeed()
    or map semantic
    AppColors
    to
    ColorScheme
    slots
  • Define both
    lightTheme
    and
    darkTheme
    in the same file
  • Use
    ThemeExtension<T>
    for custom tokens beyond Material's defaults (e.g.,
    AppSpacing
    ,
    AppRadius
    )
  • 在单独的
    app_theme.dart
    文件中使用 token 类组装
    ThemeData
  • 使用
    ColorScheme.fromSeed()
    或将语义化
    AppColors
    映射到
    ColorScheme
    插槽
  • 在同一个文件中定义
    lightTheme
    darkTheme
  • 对 Material 默认不支持的自定义 token(例如
    AppSpacing
    AppRadius
    )使用
    ThemeExtension<T>
    实现

Theme Access

主题访问

  • ALWAYS access via
    BuildContext
    extensions:
    context.colorScheme
    ,
    context.textTheme
    ,
    context.theme
  • NEVER pass theme values as constructor parameters when
    context
    is available
  • NEVER use
    Theme.of(context)
    directly — use extensions for readability
  • 始终通过
    BuildContext
    扩展访问主题:
    context.colorScheme
    context.textTheme
    context.theme
  • 当可获取
    context
    时,绝对不要将主题值作为构造函数参数传递
  • 绝对不要直接使用
    Theme.of(context)
    ——使用扩展方法提升可读性

Dark Mode

深色模式

  • Every screen and component MUST render correctly in both light and dark mode
  • Test dark mode as part of widget tests using
    MediaQuery
    theme override
  • Use semantic color tokens (
    surface
    ,
    onSurface
    ,
    primary
    ,
    onPrimary
    ) — not absolute colors
  • 每个页面和组件都必须在明暗模式下都能正常渲染
  • 在 widget 测试中通过
    MediaQuery
    主题覆盖来测试深色模式
  • 使用语义化颜色 token(
    surface
    onSurface
    primary
    onPrimary
    ),不要使用绝对颜色

Accessibility

无障碍

  • All interactive components MUST include
    Semantics
    labels
  • Support minimum touch target size (48x48 dp) per Material guidelines
  • Ensure text scales correctly via
    MediaQuery.textScaleFactor
  • Use sufficient color contrast (WCAG AA: 4.5:1 for normal text, 3:1 for large text)
  • 所有交互组件必须包含
    Semantics
    标签
  • 按照 Material 规范支持最小点击区域尺寸(48x48 dp)
  • 确保文本可以随
    MediaQuery.textScaleFactor
    正确缩放
  • 使用足够的颜色对比度(WCAG AA 标准:普通文本对比度 4.5:1,大文本对比度 3:1)