import 'package:flutter/material.dart'; /// 内部使用的调色板定义(顶层私有类,以满足 Dart 语法) class _Palette { final Color background; final Color surface; final Color card; final Color primary; final Color secondary; final Color textPrimary; final Color textSecondary; final Color border; final Color borderSecondary; final Color emptyState; const _Palette({ required this.background, required this.surface, required this.card, required this.primary, required this.secondary, required this.textPrimary, required this.textSecondary, required this.border, required this.borderSecondary, required this.emptyState, }); } /// Web应用的统一主题配置 /// 采用现代简洁的黑白配色方案,不使用蓝色等鲜艳颜色 class WebTheme { /// 私有构造函数,防止实例化 WebTheme._(); // ============= 主题变体支持 ============= /// 可选的主题变体 static const String variantMonochrome = 'monochrome'; static const String variantBlueWhite = 'blueWhite'; static const String variantPinkWhite = 'pinkWhite'; static const String variantPaper = 'paperWhite'; /// 当前主题变体(全局,简单实现) static String _currentVariant = variantMonochrome; /// 设置当前主题变体 static void applyVariant(String variant) { if (variant == variantBlueWhite || variant == variantPinkWhite || variant == variantPaper || variant == variantMonochrome) { _currentVariant = variant; try { variantNotifier.value = variant; } catch (_) {} } else { _currentVariant = variantMonochrome; try { variantNotifier.value = variantMonochrome; } catch (_) {} } } /// 获取当前主题变体 static String get currentVariant => _currentVariant; /// 颜色调色板(见顶层类 _Palette) /// 根据主题变体与明暗模式获取调色板 static _Palette _getPalette(BuildContext context) { final bool isDark = isDarkMode(context); // 纸张风格(偏米色) if (_currentVariant == variantPaper && !isDark) { final background = const Color(0xFFF6F1E7); // 纸张背景 final surface = const Color(0xFFFAF6EE); final border = const Color(0xFFE7DECC); return _Palette( background: background, surface: surface, card: surface, primary: const Color(0xFF3E3A2F), secondary: const Color(0xFF6B675D), textPrimary: const Color(0xFF2F2B21), textSecondary: const Color(0xFF6E6856), border: border, borderSecondary: const Color(0xFFD7CCB6), emptyState: const Color(0xFFF3ECE0), ); } // 蓝白风格(亮色) if (_currentVariant == variantBlueWhite && !isDark) { return const _Palette( background: white, surface: grey50, card: white, primary: Color(0xFF1E88E5), secondary: Color(0xFF1565C0), textPrimary: grey900, textSecondary: grey700, border: grey300, borderSecondary: grey200, emptyState: grey50, ); } // 粉白风格(亮色) if (_currentVariant == variantPinkWhite && !isDark) { return const _Palette( background: white, surface: grey50, card: white, primary: Color(0xFFD81B60), secondary: Color(0xFFAD1457), textPrimary: grey900, textSecondary: grey700, border: grey300, borderSecondary: grey200, emptyState: grey50, ); } // 暗色模式:保持原来的暗色基调 if (isDark) { return const _Palette( background: darkBackground, surface: darkGrey100, card: darkGrey100, primary: darkPrimary, secondary: darkSecondary, textPrimary: darkGrey900, textSecondary: darkGrey700, border: darkGrey200, borderSecondary: darkGrey300, emptyState: darkGrey200, ); } // 默认:黑白单色 return const _Palette( background: lightBackground, surface: white, card: white, primary: lightPrimary, secondary: lightSecondary, textPrimary: grey900, textSecondary: grey700, border: grey200, borderSecondary: grey300, emptyState: grey50, ); } /// 无需上下文的调色板获取(用于构建全局 ThemeData) static _Palette _getPaletteForBrightness(Brightness brightness) { final bool isDark = brightness == Brightness.dark; if (_currentVariant == variantPaper && !isDark) { final background = const Color(0xFFF6F1E7); final surface = const Color(0xFFFAF6EE); final border = const Color(0xFFE7DECC); return _Palette( background: background, surface: surface, card: surface, primary: const Color(0xFF3E3A2F), secondary: const Color(0xFF6B675D), textPrimary: const Color(0xFF2F2B21), textSecondary: const Color(0xFF6E6856), border: border, borderSecondary: const Color(0xFFD7CCB6), emptyState: const Color(0xFFF3ECE0), ); } if (_currentVariant == variantBlueWhite && !isDark) { return const _Palette( background: white, surface: grey50, card: white, primary: Color(0xFF1E88E5), secondary: Color(0xFF1565C0), textPrimary: grey900, textSecondary: grey700, border: grey300, borderSecondary: grey200, emptyState: grey50, ); } if (_currentVariant == variantPinkWhite && !isDark) { return const _Palette( background: white, surface: grey50, card: white, primary: Color(0xFFD81B60), secondary: Color(0xFFAD1457), textPrimary: grey900, textSecondary: grey700, border: grey300, borderSecondary: grey200, emptyState: grey50, ); } if (isDark) { return const _Palette( background: darkBackground, surface: darkGrey100, card: darkGrey100, primary: darkPrimary, secondary: darkSecondary, textPrimary: darkGrey900, textSecondary: darkGrey700, border: darkGrey200, borderSecondary: darkGrey300, emptyState: darkGrey200, ); } return const _Palette( background: lightBackground, surface: white, card: white, primary: lightPrimary, secondary: lightSecondary, textPrimary: grey900, textSecondary: grey700, border: grey200, borderSecondary: grey300, emptyState: grey50, ); } /// 主题变体的变更通知器 static final ValueNotifier variantNotifier = ValueNotifier(_currentVariant); /// 提供给外部监听 static ValueNotifier get variantListenable => variantNotifier; // 基础颜色 - 黑白配色 static const Color white = Color(0xFFFFFFFF); static const Color black = Color(0xFF000000); // 灰色系列 - 用于不同层次的视觉分层 static const Color grey50 = Color(0xFFFAFAFA); // 最浅的背景 static const Color grey100 = Color(0xFFF5F5F5); // 卡片背景 static const Color grey200 = Color(0xFFEEEEEE); // 分割线 static const Color grey300 = Color(0xFFE0E0E0); // 边框 static const Color grey400 = Color(0xFFBDBDBD); // 禁用文字 static const Color grey500 = Color(0xFF757575); // 次要文字 - 加深一些 static const Color grey600 = Color(0xFF616161); // 图标 - 加深一些 static const Color grey700 = Color(0xFF424242); // 主要文字 static const Color grey800 = Color(0xFF212121); // 标题文字 static const Color grey900 = Color(0xFF000000); // 最深的文字 // 暗色主题的灰色系列 static const Color darkGrey50 = Color(0xFF1A1A1A); // 最深的背景 static const Color darkGrey100 = Color(0xFF2D2D2D); // 卡片背景 static const Color darkGrey200 = Color(0xFF404040); // 分割线 static const Color darkGrey300 = Color(0xFF525252); // 边框 static const Color darkGrey400 = Color(0xFF737373); // 禁用文字 static const Color darkGrey500 = Color(0xFF9E9E9E); // 次要文字 static const Color darkGrey600 = Color(0xFFBDBDBD); // 图标 static const Color darkGrey700 = Color(0xFFE0E0E0); // 主要文字 static const Color darkGrey800 = Color(0xFFF5F5F5); // 标题文字 static const Color darkGrey900 = Color(0xFFFFFFFF); // 最亮的文字 // 功能性颜色 - 使用灰色调,保持一致性 static const Color success = Color(0xFF2E7D32); // 成功 - 深绿 static const Color warning = Color(0xFFE65100); // 警告 - 深橙 static const Color error = Color(0xFFD32F2F); // 错误 - 深红 static const Color info = Color(0xFF424242); // 信息 - 深灰 // 亮色主题配色 static const Color lightPrimary = grey900; // 主色调 static const Color lightSecondary = grey700; // 次要色调 static const Color lightBackground = white; // 主背景 static const Color lightSurface = grey50; // 表面背景 static const Color lightCard = white; // 卡片背景 static const Color lightOnPrimary = white; // 主色调上的文字 static const Color lightOnSecondary = white; // 次要色调上的文字 static const Color lightOnBackground = grey900; // 背景上的文字 static const Color lightOnSurface = grey900; // 表面上的文字 // 暗色主题配色 static const Color darkPrimary = darkGrey900; // 主色调 static const Color darkSecondary = darkGrey700; // 次要色调 static const Color darkBackground = darkGrey50; // 主背景 static const Color darkSurface = darkGrey100; // 表面背景 static const Color darkCard = darkGrey100; // 卡片背景 static const Color darkOnPrimary = darkGrey50; // 主色调上的文字 static const Color darkOnSecondary = darkGrey50; // 次要色调上的文字 static const Color darkOnBackground = darkGrey900; // 背景上的文字 static const Color darkOnSurface = darkGrey900; // 表面上的文字 /// 文字样式定义 static const TextStyle headlineLarge = TextStyle( fontSize: 32, fontWeight: FontWeight.w300, letterSpacing: -0.25, ); static const TextStyle headlineMedium = TextStyle( fontSize: 28, fontWeight: FontWeight.w400, letterSpacing: 0, ); static const TextStyle headlineSmall = TextStyle( fontSize: 24, fontWeight: FontWeight.w400, letterSpacing: 0, ); static const TextStyle titleLarge = TextStyle( fontSize: 22, fontWeight: FontWeight.w400, letterSpacing: 0, ); static const TextStyle titleMedium = TextStyle( fontSize: 16, fontWeight: FontWeight.w500, letterSpacing: 0.15, ); static const TextStyle titleSmall = TextStyle( fontSize: 14, fontWeight: FontWeight.w500, letterSpacing: 0.1, ); static const TextStyle bodyLarge = TextStyle( fontSize: 16, fontWeight: FontWeight.w400, letterSpacing: 0.5, ); static const TextStyle bodyMedium = TextStyle( fontSize: 14, fontWeight: FontWeight.w400, letterSpacing: 0.25, ); static const TextStyle bodySmall = TextStyle( fontSize: 12, fontWeight: FontWeight.w400, letterSpacing: 0.4, ); static const TextStyle labelLarge = TextStyle( fontSize: 14, fontWeight: FontWeight.w500, letterSpacing: 0.1, ); static const TextStyle labelMedium = TextStyle( fontSize: 12, fontWeight: FontWeight.w500, letterSpacing: 0.5, ); static const TextStyle labelSmall = TextStyle( fontSize: 11, fontWeight: FontWeight.w500, letterSpacing: 0.5, ); /// 亮色主题 static ThemeData buildLightTheme() { final p = _getPaletteForBrightness(Brightness.light); return ThemeData( useMaterial3: true, brightness: Brightness.light, colorScheme: ColorScheme.light( brightness: Brightness.light, primary: p.primary, onPrimary: lightOnPrimary, secondary: p.secondary, onSecondary: lightOnSecondary, error: error, onError: white, surface: p.background, onSurface: p.textPrimary, outline: p.borderSecondary, // 统一扩展:补齐常用的容器/变体色,避免默认Material色系导致风格不一致 outlineVariant: p.border, ).copyWith( // 容器色使用主/次色,具体透明度由调用处控制 primaryContainer: p.primary, onPrimaryContainer: lightOnPrimary, secondaryContainer: p.secondary, onSurfaceVariant: p.textSecondary, surfaceContainerHighest: p.surface, ), scaffoldBackgroundColor: p.background, appBarTheme: AppBarTheme( backgroundColor: p.card, foregroundColor: p.textPrimary, elevation: 0, surfaceTintColor: Colors.transparent, titleTextStyle: const TextStyle( fontSize: 20, fontWeight: FontWeight.w500, ).copyWith(color: p.textPrimary), iconTheme: IconThemeData( color: p.textSecondary, size: 24, ), ), cardTheme: CardThemeData( color: p.card, elevation: 0, shadowColor: Colors.transparent, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0), ), clipBehavior: Clip.antiAlias, ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( backgroundColor: p.primary, foregroundColor: white, elevation: 0, shadowColor: Colors.transparent, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), textStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( foregroundColor: p.textPrimary, side: BorderSide.none, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), textStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), ), textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( foregroundColor: p.textSecondary, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), textStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), ), filledButtonTheme: FilledButtonThemeData( style: FilledButton.styleFrom( backgroundColor: p.primary, foregroundColor: white, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), textStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), ), inputDecorationTheme: InputDecorationTheme( filled: false, fillColor: Colors.transparent, border: InputBorder.none, enabledBorder: InputBorder.none, focusedBorder: InputBorder.none, errorBorder: InputBorder.none, disabledBorder: InputBorder.none, focusedErrorBorder: InputBorder.none, contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), labelStyle: TextStyle( color: p.textSecondary, decoration: TextDecoration.none, ), hintStyle: TextStyle( color: p.textSecondary, decoration: TextDecoration.none, ), ), iconTheme: IconThemeData( color: p.textSecondary, size: 24, ), dividerTheme: DividerThemeData( color: Colors.transparent, thickness: 0, ), textTheme: const TextTheme( headlineLarge: headlineLarge, headlineMedium: headlineMedium, headlineSmall: headlineSmall, titleLarge: titleLarge, titleMedium: titleMedium, titleSmall: titleSmall, bodyLarge: bodyLarge, bodyMedium: bodyMedium, bodySmall: bodySmall, labelLarge: labelLarge, labelMedium: labelMedium, labelSmall: labelSmall, ).apply( bodyColor: p.textPrimary, displayColor: p.textPrimary, ), ); } /// 暗色主题 static ThemeData buildDarkTheme() { final p = _getPaletteForBrightness(Brightness.dark); return ThemeData( useMaterial3: true, brightness: Brightness.dark, colorScheme: ColorScheme.dark( brightness: Brightness.dark, primary: p.primary, onPrimary: darkOnPrimary, secondary: p.secondary, onSecondary: darkOnSecondary, error: error, onError: white, surface: p.background, onSurface: p.textPrimary, outline: p.borderSecondary, outlineVariant: p.border, ).copyWith( primaryContainer: p.primary, onPrimaryContainer: darkOnPrimary, secondaryContainer: p.secondary, onSurfaceVariant: p.textSecondary, surfaceContainerHighest: p.surface, ), scaffoldBackgroundColor: p.background, appBarTheme: AppBarTheme( backgroundColor: p.card, foregroundColor: p.textPrimary, elevation: 0, surfaceTintColor: Colors.transparent, titleTextStyle: const TextStyle( fontSize: 20, fontWeight: FontWeight.w500, ).copyWith(color: p.textPrimary), iconTheme: IconThemeData( color: p.textSecondary, size: 24, ), ), cardTheme: CardThemeData( color: p.card, elevation: 0, shadowColor: Colors.transparent, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(0), ), clipBehavior: Clip.antiAlias, ), elevatedButtonTheme: ElevatedButtonThemeData( style: ElevatedButton.styleFrom( backgroundColor: p.primary, foregroundColor: darkGrey50, elevation: 0, shadowColor: Colors.transparent, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), textStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), ), outlinedButtonTheme: OutlinedButtonThemeData( style: OutlinedButton.styleFrom( foregroundColor: p.textPrimary, side: BorderSide.none, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), textStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), ), textButtonTheme: TextButtonThemeData( style: TextButton.styleFrom( foregroundColor: p.textSecondary, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), textStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), ), filledButtonTheme: FilledButtonThemeData( style: FilledButton.styleFrom( backgroundColor: p.primary, foregroundColor: darkGrey50, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), textStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ), ), inputDecorationTheme: InputDecorationTheme( filled: false, fillColor: Colors.transparent, border: InputBorder.none, enabledBorder: InputBorder.none, focusedBorder: InputBorder.none, errorBorder: InputBorder.none, disabledBorder: InputBorder.none, focusedErrorBorder: InputBorder.none, contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), labelStyle: TextStyle( color: p.textSecondary, decoration: TextDecoration.none, ), hintStyle: TextStyle( color: p.textSecondary, decoration: TextDecoration.none, ), ), iconTheme: IconThemeData( color: p.textSecondary, size: 24, ), dividerTheme: DividerThemeData( color: Colors.transparent, thickness: 0, ), textTheme: const TextTheme( headlineLarge: headlineLarge, headlineMedium: headlineMedium, headlineSmall: headlineSmall, titleLarge: titleLarge, titleMedium: titleMedium, titleSmall: titleSmall, bodyLarge: bodyLarge, bodyMedium: bodyMedium, bodySmall: bodySmall, labelLarge: labelLarge, labelMedium: labelMedium, labelSmall: labelSmall, ).apply( bodyColor: p.textPrimary, displayColor: p.textPrimary, ), ); } /// 便捷方法:获取当前主题的颜色 static Color getPrimaryColor(BuildContext context) { final p = _getPalette(context); return p.primary; } static Color getSecondaryColor(BuildContext context) { final p = _getPalette(context); return p.secondary; } static Color getBackgroundColor(BuildContext context) { final p = _getPalette(context); return p.background; } static Color getSurfaceColor(BuildContext context) { final p = _getPalette(context); return p.surface; } static Color getOnSurfaceColor(BuildContext context) { final p = _getPalette(context); return p.textPrimary; } static bool isDarkMode(BuildContext context) { return Theme.of(context).brightness == Brightness.dark; } /// 统一的按钮样式 static ButtonStyle primaryButtonStyle = ElevatedButton.styleFrom( backgroundColor: grey900, foregroundColor: white, elevation: 2, shadowColor: grey300.withValues(alpha: 0.3), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), textStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ); static ButtonStyle secondaryButtonStyle = OutlinedButton.styleFrom( foregroundColor: grey900, side: const BorderSide(color: grey300, width: 1.5), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), textStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ); static ButtonStyle iconButtonStyle = ElevatedButton.styleFrom( backgroundColor: grey900, foregroundColor: white, elevation: 3, shadowColor: grey300.withValues(alpha: 0.4), padding: const EdgeInsets.all(16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), minimumSize: const Size(120, 50), ); /// 获取主要按钮样式 static ButtonStyle getPrimaryButtonStyle(BuildContext context) { final isDark = WebTheme.isDarkMode(context); final p = _getPalette(context); return ElevatedButton.styleFrom( backgroundColor: p.primary, foregroundColor: white, elevation: 2, shadowColor: isDark ? black.withValues(alpha: 0.4) : grey300.withValues(alpha: 0.3), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), textStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ); } /// 获取图标按钮样式 static ButtonStyle getIconButtonStyle(BuildContext context) { final isDark = WebTheme.isDarkMode(context); final p = _getPalette(context); return ElevatedButton.styleFrom( backgroundColor: p.primary, foregroundColor: white, elevation: 3, shadowColor: isDark ? black.withValues(alpha: 0.4) : grey300.withValues(alpha: 0.4), padding: const EdgeInsets.all(16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), minimumSize: const Size(120, 50), ); } /// 获取次要按钮样式(outline样式) static ButtonStyle getSecondaryButtonStyle(BuildContext context) { final isDark = WebTheme.isDarkMode(context); final p = _getPalette(context); return OutlinedButton.styleFrom( foregroundColor: isDark ? darkGrey800 : p.textPrimary, side: BorderSide( color: isDark ? darkGrey400 : p.border, width: 1.5, ), padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(8), ), elevation: 0, textStyle: const TextStyle( fontSize: 14, fontWeight: FontWeight.w500, ), ); } /// 获取无边框的输入框装饰样式(用于编辑器标题等) static InputDecoration getBorderlessInputDecoration({ String? hintText, String? labelText, bool isDense = true, EdgeInsetsGeometry? contentPadding, BuildContext? context, }) { return InputDecoration( border: InputBorder.none, enabledBorder: InputBorder.none, focusedBorder: InputBorder.none, errorBorder: InputBorder.none, disabledBorder: InputBorder.none, focusedErrorBorder: InputBorder.none, filled: false, hintText: hintText, labelText: labelText, isDense: isDense, contentPadding: contentPadding ?? EdgeInsets.zero, hintStyle: context != null ? TextStyle( color: getSecondaryTextColor(context), decoration: TextDecoration.none, // 明确去掉下划线 ) : const TextStyle( color: grey500, decoration: TextDecoration.none, // 明确去掉下划线 ), labelStyle: context != null ? TextStyle( color: getSecondaryTextColor(context), decoration: TextDecoration.none, // 明确去掉下划线 ) : const TextStyle( color: grey600, decoration: TextDecoration.none, // 明确去掉下划线 ), ); } /// 获取有边框的输入框装饰样式(用于表单) static InputDecoration getBorderedInputDecoration({ String? hintText, String? labelText, bool isDense = true, EdgeInsetsGeometry? contentPadding, BuildContext? context, }) { final isDark = context != null ? isDarkMode(context) : false; return InputDecoration( border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: isDark ? darkGrey300 : grey300, width: 1, ), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: isDark ? darkGrey300 : grey300, width: 1, ), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: isDark ? darkGrey600 : grey600, width: 2, ), ), errorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: const BorderSide( color: error, width: 1, ), ), disabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide( color: isDark ? darkGrey200 : grey200, width: 1, ), ), focusedErrorBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: const BorderSide( color: error, width: 2, ), ), filled: true, fillColor: context != null ? (isDark ? darkGrey50 : white) : white, hintText: hintText, labelText: labelText, isDense: isDense, contentPadding: contentPadding ?? const EdgeInsets.symmetric(horizontal: 12, vertical: 16), hintStyle: context != null ? TextStyle( color: getSecondaryTextColor(context), decoration: TextDecoration.none, ) : const TextStyle( color: grey500, decoration: TextDecoration.none, ), labelStyle: context != null ? TextStyle( color: getSecondaryTextColor(context), decoration: TextDecoration.none, ) : const TextStyle( color: grey600, decoration: TextDecoration.none, ), ); } /// 获取Material组件的透明样式(去掉黄色下划线) static Widget getMaterialWrapper({ required Widget child, Color? color, }) { return Material( type: MaterialType.transparency, // 使用透明类型避免黄色下划线 color: color ?? Colors.transparent, child: child, ); } /// 获取纯净卡片样式(去掉elevation和边框) static BoxDecoration getCleanCardDecoration({ Color? backgroundColor, BorderRadius? borderRadius, BuildContext? context, }) { Color defaultColor = white; if (context != null) { defaultColor = getSurfaceColor(context); } return BoxDecoration( color: backgroundColor ?? defaultColor, borderRadius: borderRadius ?? BorderRadius.circular(0), ); } /// 获取文字样式确保垂直对齐 static TextStyle getAlignedTextStyle({ required TextStyle baseStyle, double? height, }) { return baseStyle.copyWith( height: height ?? 1.0, textBaseline: TextBaseline.alphabetic, ); } /// 获取一致的文字颜色 static Color getTextColor(BuildContext context, {bool isPrimary = true}) { final p = _getPalette(context); return isPrimary ? p.textPrimary : p.textSecondary; } /// 获取次要文字颜色 static Color getSecondaryTextColor(BuildContext context) { final p = _getPalette(context); return p.textSecondary; } /// 获取卡片背景颜色 static Color getCardColor(BuildContext context) { final p = _getPalette(context); return p.card; } /// 获取边框颜色 static Color getBorderColor(BuildContext context) { final p = _getPalette(context); return p.border; } /// 获取次要边框颜色 static Color getSecondaryBorderColor(BuildContext context) { final p = _getPalette(context); return p.borderSecondary; } /// 获取阴影颜色 static Color getShadowColor(BuildContext context, {double opacity = 0.1}) { final isDark = WebTheme.isDarkMode(context); return isDark ? black.withOpacity(opacity * 2) : grey200.withOpacity(opacity); } /// 获取空状态背景颜色 static Color getEmptyStateColor(BuildContext context) { final p = _getPalette(context); return p.emptyState; } }