import 'package:flutter/material.dart'; /// 语义色 token(ADR-005:以 AI宠物_iOS_UI设计稿.html 珊瑚橙暖色系为品牌正典, /// 详见 iteration-1-reports/04-ui-login-design-spec.md §1.1)。 abstract final class AppColors { /// 品牌珊瑚橙:图标、装饰、渐变起点;不用于承载文字的实心填充(对比度不足)。 static const primary = Color(0xFFFF6F4C); /// 加深珊瑚:实心按钮填充、可点击文字链接(白字约 4.5:1,WCAG AA)。 static const primaryStrong = Color(0xFFD6431A); /// 浅色底上的强调文字、按钮反白替代。 static const primaryDark = Color(0xFF7A2E12); /// amber:渐变终点、徽章、会员/促销 CTA。 static const accent = Color(0xFFFFB648); /// amber 底上的文字。 static const accentDark = Color(0xFF7A4B0A); /// 页面背景(奶油底)。 static const canvas = Color(0xFFFFF7ED); /// 卡片、输入框背景。 static const surface = Color(0xFFFFFFFF); /// peach:图标底、占位块、选中指示。 static const surfaceTint = Color(0xFFFFE8D6); /// 主文字。 static const ink = Color(0xFF3E2A1F); /// 次级文字、占位符。 static const muted = Color(0xFF9C8977); /// 描边、分隔线。 static const border = Color(0xFFF0DCC8); /// sage:成功提示的图标/描边。 static const success = Color(0xFF7FA88A); /// 成功提示底上的文字。 static const successInk = Color(0xFF3F5744); /// 成功提示底色。 static const successSurface = Color(0xFFE8F0E8); static const warning = Color(0xFFF59E0B); /// 校验错误文字、错误描边、失败提示(白底上约 5.4:1,AA 达标)。 static const error = Color(0xFFD0342C); /// 品牌渐变:hero 区、头像环、装饰性大面积(不承载正文文字)。 static const brandGradient = LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [primary, accent], ); } /// 圆角 token(设计规范 §1.3)。 abstract final class AppRadius { /// 小按钮、内嵌 CTA、错误横幅。 static const sm = 12.0; /// 主按钮、SnackBar、菜单组。 static const md = 16.0; /// 输入框、feed 卡。 static const lg = 18.0; /// 大卡片。 static const xl = 24.0; /// 芯片、徽章。 static const pill = 999.0; } ThemeData buildAppTheme() { final scheme = ColorScheme.fromSeed( seedColor: AppColors.primary, brightness: Brightness.light, surface: AppColors.surface, ).copyWith(error: AppColors.error, onError: Colors.white); return ThemeData( useMaterial3: true, colorScheme: scheme, scaffoldBackgroundColor: AppColors.canvas, fontFamilyFallback: const [ 'Noto Sans CJK SC', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', 'sans-serif', ], textTheme: const TextTheme( headlineSmall: TextStyle( color: AppColors.ink, fontSize: 22, fontWeight: FontWeight.w800, ), titleLarge: TextStyle( color: AppColors.ink, fontSize: 18, fontWeight: FontWeight.w800, ), titleMedium: TextStyle( color: AppColors.ink, fontSize: 15, fontWeight: FontWeight.w700, ), bodyMedium: TextStyle(color: AppColors.ink, fontSize: 14, height: 1.5), bodySmall: TextStyle(color: AppColors.muted, fontSize: 12, height: 1.4), ), cardTheme: const CardThemeData( color: AppColors.surface, elevation: 0, margin: EdgeInsets.zero, shape: RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(AppRadius.xl)), side: BorderSide(color: AppColors.border), ), ), filledButtonTheme: FilledButtonThemeData( style: FilledButton.styleFrom( backgroundColor: AppColors.primaryStrong, foregroundColor: Colors.white, disabledBackgroundColor: AppColors.ink.withAlpha(31), disabledForegroundColor: AppColors.ink.withAlpha(97), minimumSize: const Size(64, 52), shape: const RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(AppRadius.md)), ), textStyle: const TextStyle(fontSize: 15, fontWeight: FontWeight.w700), ), ), inputDecorationTheme: const InputDecorationTheme( filled: true, fillColor: AppColors.surface, contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 14), border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(AppRadius.lg)), borderSide: BorderSide(color: AppColors.border), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(AppRadius.lg)), borderSide: BorderSide(color: AppColors.border), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(AppRadius.lg)), borderSide: BorderSide(color: AppColors.primary, width: 1.5), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(AppRadius.lg)), borderSide: BorderSide(color: AppColors.error, width: 1.5), ), focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(AppRadius.lg)), borderSide: BorderSide(color: AppColors.error, width: 1.5), ), errorStyle: TextStyle(color: AppColors.error, fontSize: 12), helperStyle: TextStyle(color: AppColors.muted, fontSize: 12), ), snackBarTheme: const SnackBarThemeData( behavior: SnackBarBehavior.floating, shape: RoundedRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(AppRadius.md)), ), ), navigationBarTheme: NavigationBarThemeData( backgroundColor: AppColors.surface, indicatorColor: AppColors.surfaceTint, height: 72, labelTextStyle: WidgetStateProperty.resolveWith((states) { return TextStyle( fontSize: 11, fontWeight: states.contains(WidgetState.selected) ? FontWeight.w700 : FontWeight.w500, color: states.contains(WidgetState.selected) ? AppColors.primaryStrong : AppColors.muted, ); }), ), ); }