design-system
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDesign 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.textPrimaryAppColors.textSecondary - ❌ NEVER use ,
Color(0xFF...), or inline hex valuesColors.blue - Map semantic tokens to Material 3 for platform consistency
ColorScheme - Each color token MUST document its use case and ensure WCAG AA contrast ratio
- Support light and dark mode via — never branch on
ThemeDatamanually in widgetsBrightness
- ✅ 使用 、
AppColors.primary、AppColors.secondary、AppColors.error、AppColors.success、AppColors.warning、AppColors.surface、AppColors.textPrimaryAppColors.textSecondary - ❌ 绝对不要使用 、
Color(0xFF...)或者内联十六进制颜色值Colors.blue - 将语义 token 映射到 Material 3 以保证平台一致性
ColorScheme - 每个颜色 token 必须说明其使用场景,并确保符合 WCAG AA 对比度标准
- 通过 支持明暗模式,绝对不要在 widget 中手动根据
ThemeData分支判断Brightness
Spacing
间距
- ✅ Use (2),
AppSpacing.xxs(4),AppSpacing.xs(8),AppSpacing.sm(16),AppSpacing.md(24),AppSpacing.lg(32),AppSpacing.xl(48)AppSpacing.xxl - ✅ Use (24),
AppSpacing.screenHorizontal(16) for consistent screen paddingAppSpacing.screenVertical - ❌ NEVER use ,
EdgeInsets.all(16.0), or hardcoded padding valuesSizedBox(height: 8) - Use for gaps — not
SizedBox(height: AppSpacing.sm)orContainerwith empty childPadding
- ✅ 使用 (2)、
AppSpacing.xxs(4)、AppSpacing.xs(8)、AppSpacing.sm(16)、AppSpacing.md(24)、AppSpacing.lg(32)、AppSpacing.xl(48)AppSpacing.xxl - ✅ 使用 (24)、
AppSpacing.screenHorizontal(16) 来实现统一的页面内边距AppSpacing.screenVertical - ❌ 绝对不要使用 、
EdgeInsets.all(16.0)或者硬编码的内边距值SizedBox(height: 8) - 元素间距使用 实现,不要使用子元素为空的
SizedBox(height: AppSpacing.sm)或ContainerPadding
Border Radius
圆角
- ✅ Use (4),
AppRadius.xs(8),AppRadius.sm(12),AppRadius.md(16),AppRadius.lg(24),AppRadius.xl(999)AppRadius.full - ❌ NEVER use or inline radius values
BorderRadius.circular(12)
- ✅ 使用 (4)、
AppRadius.xs(8)、AppRadius.sm(12)、AppRadius.md(16)、AppRadius.lg(24)、AppRadius.xl(999)AppRadius.full - ❌ 绝对不要使用 或者内联圆角值
BorderRadius.circular(12)
Typography
排版
- ✅ Use ,
context.textTheme.headlineLarge, etc. via theme extensionscontext.textTheme.bodyMedium - ✅ Optionally use wrapper for custom token names mapping to
AppTypographyTextTheme - ❌ NEVER use or inline text styles
TextStyle(fontSize: 16) - Define font family, size, weight, and letter spacing as tokens
- ✅ 通过主题扩展使用 、
context.textTheme.headlineLarge等样式context.textTheme.bodyMedium - ✅ 可选择使用 封装类实现自定义 token 名称到
AppTypography的映射TextTheme - ❌ 绝对不要使用 或者内联文本样式
TextStyle(fontSize: 16) - 将字体、字号、字重和字间距定义为 token
Iconography
图标
- Use a consistent icon set (Material Icons, Cupertino Icons, or custom icon font)
- Wrap icon usage in constants for consistency
AppIcons - Use — not raw
Icon(AppIcons.settings, size: AppSpacing.lg)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.dartcore/
├── 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.dartComponent Best Practices
组件最佳实践
- All reusable widgets MUST accept parameter via
Keysuper.key - Use constructors in all reusable components
const - 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 in a single
ThemeDatafile using token classesapp_theme.dart - Use or map semantic
ColorScheme.fromSeed()toAppColorsslotsColorScheme - Define both and
lightThemein the same filedarkTheme - Use for custom tokens beyond Material's defaults (e.g.,
ThemeExtension<T>,AppSpacing)AppRadius
- 在单独的 文件中使用 token 类组装
app_theme.dartThemeData - 使用 或将语义化
ColorScheme.fromSeed()映射到AppColors插槽ColorScheme - 在同一个文件中定义 和
lightThemedarkTheme - 对 Material 默认不支持的自定义 token(例如 、
AppSpacing)使用AppRadius实现ThemeExtension<T>
Theme Access
主题访问
- ALWAYS access via extensions:
BuildContext,context.colorScheme,context.textThemecontext.theme - NEVER pass theme values as constructor parameters when is available
context - NEVER use directly — use extensions for readability
Theme.of(context)
- 始终通过 扩展访问主题:
BuildContext、context.colorScheme、context.textThemecontext.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 theme override
MediaQuery - Use semantic color tokens (,
surface,onSurface,primary) — not absolute colorsonPrimary
- 每个页面和组件都必须在明暗模式下都能正常渲染
- 在 widget 测试中通过 主题覆盖来测试深色模式
MediaQuery - 使用语义化颜色 token(、
surface、onSurface、primary),不要使用绝对颜色onPrimary
Accessibility
无障碍
- All interactive components MUST include labels
Semantics - 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)