rtc_prd/airhub_app/lib/theme/design_tokens.dart
seaislee1209 f9666d4aa3 feat: UI规范化 + 故事吸入动画 + 音乐页面优化
- 全局字体统一(Outfit/DM Sans), 头部/按钮/Toast规范化
- 故事详情页: Genie Suck吸入动画(标题+卡片一起缩小模糊消失)
- 书架页: bookPop弹出+粒子效果(三段式动画完整链路)
- 音乐页面: 心情卡片emoji换Material图标+彩色圆块横排布局
- 音乐页面: 进度条胶囊宽度对齐, 播放按钮位置修复, 间距均匀化
- 音乐播放: 接入just_audio, 支持播放暂停进度拖拽自动切歌
- 新增: iOS风格毛玻璃Toast, 渐变背景组件, 通知页面
- 阶段总结文档更新

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 19:34:53 +08:00

272 lines
7.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
/// 颜色定义 - 精确还原 Profile PRD
class AppColors {
// 文字颜色
static const Color textPrimary = Color(0xFF374151);
static const Color textSecondary = Color(0xFF9CA3AF);
static const Color textHint = Color(0xFFD1D5DB); // Arrow icon color
// 背景颜色
static const Color background = Color(0xFFFAFBFC);
static const Color cardSurface = Color(
0xCCFFFFFF,
); // rgba(255, 255, 255, 0.8)
// Story Page
static const Color storyBackground = Color(0xFFFDF9F3);
static const Color storyTitle = Color(0xFF4B2404);
static const Color storyText = Color(0xFF374151);
// Bookshelf (Story Book) - CSS .story-book, .story-slot
static const Color bookshelfBg = Color(
0x8CFFFFFF,
); // rgba(255, 255, 255, 0.55)
static const Color bookshelfBorder = Color(
0x99FFFFFF,
); // rgba(255, 255, 255, 0.6)
static const Color bookCountBg = Color(
0x80FFFFFF,
); // rgba(255, 255, 255, 0.5)
static const Color slotBg = Color(0x99FFFFFF); // rgba(255, 255, 255, 0.6)
static const Color slotClickableBg = Color(
0x66FFFFFF,
); // rgba(255, 255, 255, 0.4)
static const Color slotBorder = Color(0x0D000000); // rgba(0, 0, 0, 0.05)
static const Color slotTitleBarBg = Color(0x99000000); // rgba(0, 0, 0, 0.6)
static const Color slotFilledShadow = Color(0x1A000000); // rgba(0, 0, 0, 0.1)
static const Color emptyPlusColor = Color(0xFF9CA3AF);
static const Color bookTitleColor = Color(0xFF374151);
static const Color bookCountColor = Color(0xFF6B7280);
// 状态颜色
static const Color notificationDot = Color(0xFFEF4444);
static const Color badgeNew = Color(0xFFEF4444);
// 按钮/交互 — 统一规范: 白底 0.6 透明度, 无边框
static const Color iconBtnBg = Color(0x99FFFFFF); // rgba(255, 255, 255, 0.6)
static const Color iconBtnBorder = Color(
0x66FFFFFF,
); // rgba(255, 255, 255, 0.4) — 保留定义但不再使用
// 危险操作
static const Color danger = Color(0xFFEF4444);
// 表单 & 列表
static const Color formLabel = Color(0xFF6B7280);
static const Color sectionTitle = Color(0xFF9CA3AF);
static const Color divider = Color(0x0D000000); // rgba(0, 0, 0, 0.05)
// 渐变色 (Avatar & Buttons)
static const List<Color> avatarGradient = [
Color(0xFFECCFA8),
Color(0xFFC99672),
];
static const List<Color> saveBtnGradient = [
Color(0xFFECCFA8),
Color(0xFFC99672),
];
static const List<Color> btnCapybaraGradient = [
Color(0xFFECCFA8),
Color(0xFFC99672),
];
// 产品卡片渐变
static const List<Color> gradientCapybara = [
Color(0xFFE6B98D),
Color(0xFFE8C9A8),
Color(0xFFD4A373),
Color(0xFFB07D5A),
];
static const List<Color> gradientBadgeAI = [
Color(0xFF22D3EE),
Color(0xFF60A5FA),
Color(0xFF818CF8),
Color(0xFFA78BFA),
];
static const List<Color> gradientBadgeBasic = [
Color(0xFFC084FC),
Color(0xFFD8B4FE),
Color(0xFFC4B5FD),
Color(0xFFA78BFA),
];
static const List<Color> gradientBracelet = [
Color(0xFFFDBA74),
Color(0xFFFB923C),
Color(0xFFFBAF85),
Color(0xFFE07B54),
];
static const List<Color> gradientVSinger = [
Color(0xFF34D399),
Color(0xFF5EEAD4),
Color(0xFF22D3EE),
Color(0xFF2DD4BF),
];
// 遮罩
static const Color overlay = Color(0x80000000); // implied for modal overlay
}
/// 字体样式 - PRD规范: Outfit(标题) + DM Sans(正文) + Press Start 2P(Logo)
class AppTextStyles {
// 页面标题 — 统一规范: 17px w600 #1F2937
static final TextStyle title = GoogleFonts.outfit(
fontSize: 17,
fontWeight: FontWeight.w600,
color: const Color(0xFF1F2937),
);
// User Name → Outfit (heading/display)
static final TextStyle userName = GoogleFonts.outfit(
fontSize: 20,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
);
// User ID → DM Sans (body)
static final TextStyle userId = GoogleFonts.dmSans(
fontSize: 13,
fontWeight: FontWeight.w400,
color: AppColors.textSecondary,
);
// Menu Text → DM Sans (body/UI)
static final TextStyle menuText = GoogleFonts.dmSans(
fontSize: 16,
fontWeight: FontWeight.w400,
color: AppColors.textPrimary,
);
// Badge Text → DM Sans (small UI)
static final TextStyle badge = GoogleFonts.dmSans(
fontSize: 10,
fontWeight: FontWeight.w400,
color: Colors.white,
);
// Modal Title → Outfit (heading)
static final TextStyle modalTitle = GoogleFonts.outfit(
fontSize: 18,
fontWeight: FontWeight.w600,
color: AppColors.textPrimary,
);
// Book specific styles → Outfit (heading)
static final TextStyle bookTitle = GoogleFonts.outfit(
fontSize: 18,
fontWeight: FontWeight.w700,
color: AppColors.textPrimary,
);
// Book count → DM Sans (body)
static final TextStyle bookCount = GoogleFonts.dmSans(
fontSize: 13,
fontWeight: FontWeight.w600,
color: AppColors.textSecondary,
);
// Slot title → DM Sans (small UI)
static final TextStyle slotTitle = GoogleFonts.dmSans(
fontSize: 10,
color: Colors.white,
fontWeight: FontWeight.w400,
);
// PRD: font-size: 24px, color: #9CA3AF, font-weight: 300, opacity: 0.7
static final TextStyle emptyPlus = GoogleFonts.dmSans(
fontSize: 24,
fontWeight: FontWeight.w300,
color: const Color(0xB39CA3AF), // #9CA3AF with 0.7 opacity
);
// Button text → DM Sans (UI)
static final TextStyle createStoryBtn = GoogleFonts.dmSans(
fontSize: 17,
fontWeight: FontWeight.w600,
color: Colors.white,
);
}
/// 间距定义
class AppSpacing {
static const double xs = 4.0;
static const double sm = 8.0;
static const double md = 16.0;
static const double lg = 20.0;
static const double xl = 24.0;
}
/// 圆角定义
class AppRadius {
static const double card = 20.0;
static const double button = 12.0; // 统一规范: 圆角 12
static const double avatar = 32.0; // 64px size / 2
static const double badge = 10.0;
}
/// 阴影定义
class AppShadows {
static const BoxShadow card = BoxShadow(
color: Color(0x148B5E3C), // rgba(139, 94, 60, 0.08)
blurRadius: 20,
offset: Offset(0, 4),
);
static const BoxShadow btnCapybara = BoxShadow(
color: Color(0x59C99672), // rgba(201, 150, 114, 0.35)
blurRadius: 15,
offset: Offset(0, 4),
);
static const BoxShadow storyBook = BoxShadow(
color: Color(0x08000000), // rgba(0,0,0,0.03)
blurRadius: 40,
offset: Offset(0, 10),
);
static const BoxShadow storySlotFilled = BoxShadow(
color: Color(0x1A000000), // rgba(0,0,0,0.1)
blurRadius: 12,
offset: Offset(0, 4),
);
static const List<BoxShadow> createBtn = [
BoxShadow(color: Color(0x59C99672), blurRadius: 15), // glow
BoxShadow(color: Color(0x40C99672), blurRadius: 30), // outer glow
BoxShadow(
color: Color(0x66C99672),
blurRadius: 20,
offset: Offset(0, 6),
), // depth
];
}
/// Story Book Spacing
class StoryBookSpacing {
static const double bookPadding = 24.0;
static const double gridGap = 12.0;
static const double bookCoverMarginBottom = 20.0;
static const EdgeInsets bookCountPadding = EdgeInsets.symmetric(
horizontal: 10,
vertical: 4,
);
static const EdgeInsets titleBarPadding = EdgeInsets.symmetric(
horizontal: 6,
vertical: 4,
);
// PRD: padding: 16px 48px
static const EdgeInsets createBtnPadding = EdgeInsets.symmetric(
horizontal: 48, // PRD: 48px
vertical: 16, // PRD: 16px
);
}
/// Story Book Radius
class StoryBookRadius {
static const double book = 24.0;
static const double slot = 12.0;
static const double bookCount = 12.0;
static const double createBtn = 29.0;
}