Files
MaliangAINovalWriter/AINoval/lib/utils/web_theme.dart
2025-09-10 00:07:52 +08:00

1008 lines
31 KiB
Dart
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.

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<String> variantNotifier =
ValueNotifier<String>(_currentVariant);
/// 提供给外部监听
static ValueNotifier<String> 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;
}
}