462 lines
10 KiB
Markdown
462 lines
10 KiB
Markdown
|
|
# 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:1(WCAG 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)
|
|||
|
|
- [ ] 无硬编码颜色
|
|||
|
|
- [ ] 间距/圆角一致
|