Files
monisuo/flutter_monisuo/specs/modernization-v2.md
sion c4cf23a4a1 feat(ui): 添加明暗主题切换支持
- 创建 ThemeProvider 管理主题状态
- 配置浅色和深色主题(Vercel/Linear 风格)
- 集成 Google Fonts(Inter + JetBrains Mono)
- 在我的页面添加主题切换开关
- 更新颜色系统符合 modernization-v2.md 规范
- 优化间距和圆角系统

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 14:12:00 +08:00

462 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Flutter Monisuo 现代化改造规范 v2.0
## 目标
将 Flutter Monisuo 应用打造为现代化、简约、专业的虚拟货币交易平台,参考 SuperDesign 设计原则。
## 设计原则
### 1. 现代化简约风格
- **Vercel/Linear 风格**:干净的深色主题,微妙的阴影,大量留白
- **避免过时设计**:不使用 Bootstrap 蓝、沉重的阴影、复杂的渐变
- **微交互**细腻的动画反馈150-400ms
### 2. 明暗主题支持
- 完整的 Light/Dark 主题切换
- 使用 ColorScheme 管理主题
- 主题切换时平滑过渡
### 3. 颜色系统(基于 OKLCH 转换)
#### 现代深色主题
```dart
// Vercel/Linear 风格
background: Color(0xFF0A0A0B) // oklch(0.098 0.005 270)
cardBackground: Color(0xFF111113) // oklch(0.148 0.004 270)
primary: Color(0xFF00D4AA) // 品牌青绿色
primaryForeground: Color(0xFFFFFFFF)
secondary: Color(0xFF1C1C1F)
muted: Color(0xFF27272A)
border: Color(0xFF27272A)
```
#### 现代浅色主题
```dart
background: Color(0xFFFFFFFF) // oklch(1 0 0)
cardBackground: Color(0xFFFAFAFA) // oklch(0.98 0 0)
primary: Color(0xFF00B894) // 品牌青绿色(深色版)
primaryForeground: Color(0xFFFFFFFF)
secondary: Color(0xFFF4F4F5)
muted: Color(0xFFE4E4E7)
border: Color(0xFFE4E4E7)
```
#### 涨跌色(明暗通用)
```dart
up: Color(0xFF00C853) // 涨/买入(绿色)
down: Color(0xFFFF5252) // 跌/卖出(红色)
```
### 4. 字体系统
#### 使用 Google Fonts
```yaml
dependencies:
google_fonts: ^6.1.0
```
#### 字体选择
- **主字体**Inter现代、清晰
- **数字字体**JetBrains Mono等宽用于价格/数量)
- **回退字体**system-ui
#### 字号系统
```dart
// 标题
displayLarge: 32sp, weight: 700
displayMedium: 24sp, weight: 600
displaySmall: 20sp, weight: 600
// 正文
bodyLarge: 16sp, weight: 400
bodyMedium: 14sp, weight: 400
bodySmall: 12sp, weight: 400
// 数字(等宽)
numberLarge: 20sp, JetBrains Mono
numberMedium: 16sp, JetBrains Mono
numberSmall: 14sp, JetBrains Mono
```
### 5. 间距系统
```dart
class Spacing {
static const double xs = 4.0; // 0.25rem
static const double sm = 8.0; // 0.5rem
static const double md = 16.0; // 1rem
static const double lg = 24.0; // 1.5rem
static const double xl = 32.0; // 2rem
static const double xxl = 48.0; // 3rem
}
```
### 6. 圆角系统
```dart
class BorderRadius {
static const double sm = 4.0;
static const double md = 8.0;
static const double lg = 12.0;
static const double xl = 16.0;
static const double xxl = 24.0;
static const double full = 9999.0;
}
```
### 7. 阴影系统
```dart
// 微妙的阴影Vercel 风格)
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 4,
offset: Offset(0, 2),
)
// 悬停阴影
BoxShadow(
color: Colors.black.withOpacity(0.1),
blurRadius: 8,
offset: Offset(0, 4),
)
```
### 8. 动画系统
```dart
class AnimationDurations {
static const Duration fast = Duration(milliseconds: 150);
static const Duration normal = Duration(milliseconds: 250);
static const Duration slow = Duration(milliseconds: 400);
static const Duration verySlow = Duration(milliseconds: 600);
}
// Curves
Curves.easeOutCubic // 入场动画
Curves.easeInOutCubic // 过渡动画
Curves.elasticOut // 弹性反馈
```
## 组件设计规范
### 1. 按钮
#### 主要按钮Primary
```dart
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).colorScheme.primary,
foregroundColor: Theme.of(context).colorScheme.onPrimary,
minimumSize: Size(44, 44), // 触摸目标
padding: EdgeInsets.symmetric(horizontal: Spacing.lg, vertical: Spacing.md),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(BorderRadius.md),
),
elevation: 0, // 现代风格:无阴影或微阴影
),
)
```
#### 次要按钮Secondary
```dart
OutlinedButton(
style: OutlinedButton.styleFrom(
minimumSize: Size(44, 44),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(BorderRadius.md),
),
side: BorderSide(color: Theme.of(context).colorScheme.border),
),
)
```
#### 幽灵按钮Ghost
```dart
TextButton(
style: TextButton.styleFrom(
minimumSize: Size(44, 44),
),
)
```
### 2. 卡片
```dart
Card(
elevation: 0, // 使用边框代替阴影
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(BorderRadius.lg),
side: BorderSide(
color: Theme.of(context).colorScheme.border,
width: 1,
),
),
color: Theme.of(context).colorScheme.cardBackground,
child: Padding(
padding: EdgeInsets.all(Spacing.md),
child: Column(...),
),
)
```
### 3. 输入框
```dart
TextFormField(
decoration: InputDecoration(
filled: true,
fillColor: Theme.of(context).colorScheme.cardBackground,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(BorderRadius.md),
borderSide: BorderSide(color: Theme.of(context).colorScheme.border),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(BorderRadius.md),
borderSide: BorderSide(color: Theme.of(context).colorScheme.border),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(BorderRadius.md),
borderSide: BorderSide(
color: Theme.of(context).colorScheme.primary,
width: 2,
),
),
contentPadding: EdgeInsets.symmetric(
horizontal: Spacing.md,
vertical: Spacing.sm,
),
),
)
```
### 4. 现代弹窗
#### 标准弹窗
```dart
showDialog(
context: context,
builder: (context) => Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(BorderRadius.xl),
),
backgroundColor: Theme.of(context).colorScheme.cardBackground,
child: Container(
padding: EdgeInsets.all(Spacing.lg),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 标题
Text(
'Dialog Title',
style: Theme.of(context).textTheme.displaySmall,
),
SizedBox(height: Spacing.md),
// 内容
Text('Dialog content here...'),
SizedBox(height: Spacing.lg),
// 按钮
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(child: Text('Cancel')),
SizedBox(width: Spacing.sm),
ElevatedButton(child: Text('Confirm')),
],
),
],
),
),
),
);
```
#### 底部抽屉Bottom Sheet
```dart
showModalBottomSheet(
context: context,
isScrollControlled: true,
backgroundColor: Colors.transparent,
builder: (context) => Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.cardBackground,
borderRadius: BorderRadius.vertical(
top: Radius.circular(BorderRadius.xl),
),
),
padding: EdgeInsets.all(Spacing.lg),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// 拖动指示器
Container(
width: 40,
height: 4,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.muted,
borderRadius: BorderRadius.circular(2),
),
),
SizedBox(height: Spacing.md),
// 内容
...
],
),
),
);
```
### 5. 列表项
```dart
ListTile(
contentPadding: EdgeInsets.symmetric(
horizontal: Spacing.md,
vertical: Spacing.sm,
),
leading: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.secondary,
borderRadius: BorderRadius.circular(BorderRadius.md),
),
child: Icon(icon, size: 20),
),
title: Text(
title,
style: Theme.of(context).textTheme.bodyLarge,
),
subtitle: Text(
subtitle,
style: Theme.of(context).textTheme.bodySmall,
),
trailing: Icon(Icons.chevron_right, size: 20),
)
```
## 页面布局规范
### 1. 通用布局
```dart
Scaffold(
backgroundColor: Theme.of(context).colorScheme.background,
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.background,
elevation: 0,
title: Text('Page Title'),
),
body: SafeArea(
child: SingleChildScrollView(
padding: EdgeInsets.all(Spacing.md),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 内容
],
),
),
),
)
```
### 2. 响应式布局
```dart
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth >= 1024) {
// 桌面布局
return _DesktopLayout();
} else if (constraints.maxWidth >= 768) {
// 平板布局
return _TabletLayout();
} else {
// 移动布局
return _MobileLayout();
}
},
)
```
## 主题切换实现
### 1. 主题 Provider
```dart
class ThemeProvider extends ChangeNotifier {
ThemeMode _themeMode = ThemeMode.system;
ThemeMode get themeMode => _themeMode;
void toggleTheme() {
_themeMode = _themeMode == ThemeMode.light
? ThemeMode.dark
: ThemeMode.light;
notifyListeners();
}
void setTheme(ThemeMode mode) {
_themeMode = mode;
notifyListeners();
}
}
```
### 2. 主题切换按钮
```dart
IconButton(
icon: Icon(
Provider.of<ThemeProvider>(context).themeMode == ThemeMode.light
? Icons.dark_mode
: Icons.light_mode,
),
onPressed: () {
Provider.of<ThemeProvider>(context, listen: false).toggleTheme();
},
)
```
## 无障碍设计
### 1. 对比度
- 所有文字/背景组合 >= 4.5:1WCAG AA
- 大文字18sp+>= 3:1
### 2. 触摸目标
- 最小触摸目标 44x44
### 3. 语义化
```dart
Semantics(
label: 'Submit button',
button: true,
child: ElevatedButton(...),
)
```
## 禁止事项
- ❌ 使用过时的 Bootstrap 蓝 (#007bff)
- ❌ 沉重的阴影
- ❌ 复杂的渐变
- ❌ 文字与背景颜色相同
- ❌ 对比度 < 4.5:1
- ❌ 触摸目标 < 44x44
- ❌ 硬编码颜色值
- ❌ 不一致的间距/圆角
## 验证清单
- [ ] 明暗主题切换正常
- [ ] 所有页面风格一致
- [ ] 对比度 >= 4.5:1
- [ ] 触摸目标 >= 44x44
- [ ] flutter analyze 无错误
- [ ] 响应式布局正常
- [ ] 动画流畅60fps
- [ ] 无硬编码颜色
- [ ] 间距/圆角一致