319 lines
10 KiB
Dart
319 lines
10 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
/// 应用级语义色扩展(浅色/深色各一套,随主题自动切换)
|
|
///
|
|
/// 用法:`context.appColors.cardBackground`
|
|
/// 原理:颜色挂载在 ThemeData.extensions 上,主题切换时组件树自动重建,
|
|
/// 页面无需写 `isDark ? A : B` 判断
|
|
@immutable
|
|
class AppColors extends ThemeExtension<AppColors> {
|
|
const AppColors({
|
|
required this.pageBackground,
|
|
required this.cardBackground,
|
|
required this.fillBackground,
|
|
required this.textPrimary,
|
|
required this.textSecondary,
|
|
required this.textTertiary,
|
|
required this.divider,
|
|
required this.cardShadow,
|
|
required this.primary,
|
|
required this.danger,
|
|
required this.warning,
|
|
required this.success,
|
|
});
|
|
|
|
/// 页面背景色
|
|
final Color pageBackground;
|
|
|
|
/// 卡片背景色
|
|
final Color cardBackground;
|
|
|
|
/// 卡片内嵌块填充色(输入框、上传按钮等)
|
|
final Color fillBackground;
|
|
|
|
/// 主要文字色
|
|
final Color textPrimary;
|
|
|
|
/// 次要文字色
|
|
final Color textSecondary;
|
|
|
|
/// 辅助文字色(占位、说明)
|
|
final Color textTertiary;
|
|
|
|
/// 分割线/浅色边框
|
|
final Color divider;
|
|
|
|
/// 卡片阴影色
|
|
final Color cardShadow;
|
|
|
|
/// 品牌主色(两种模式下保持一致)
|
|
final Color primary;
|
|
|
|
/// 危险/错误色(两种模式下保持一致)
|
|
final Color danger;
|
|
|
|
/// 警告色(两种模式下保持一致)
|
|
final Color warning;
|
|
|
|
/// 成功/完成色(两种模式下保持一致)
|
|
final Color success;
|
|
|
|
/// 浅色语义色(取自设计稿)
|
|
static const AppColors light = AppColors(
|
|
pageBackground: Color(0xFFF5F7FA),
|
|
cardBackground: Color(0xFFFFFFFF),
|
|
fillBackground: Color(0xFFF8F9FA),
|
|
textPrimary: Color(0xFF1D2129),
|
|
textSecondary: Color(0xFF4E5969),
|
|
textTertiary: Color(0xFF86909C),
|
|
divider: Color(0xFFF0F0F0),
|
|
cardShadow: Color(0x0D000000),
|
|
primary: Color(0xFF165DFF),
|
|
danger: Color(0xFFF53F3F),
|
|
warning: Color(0xFFFF7D00),
|
|
success: Color(0xFF00B42A),
|
|
);
|
|
|
|
/// 深色语义色(与 AppTheme.darkTheme 的 surface 色保持一致)
|
|
static const AppColors dark = AppColors(
|
|
pageBackground: Color(0xFF1C1C1E),
|
|
cardBackground: Color(0xFF2C2C2E),
|
|
fillBackground: Color(0xFF3A3A3C),
|
|
textPrimary: Color(0xFFF2F3F5),
|
|
textSecondary: Color(0xFFC9CDD4),
|
|
textTertiary: Color(0xFF86909C),
|
|
divider: Color(0xFF3A3A3C),
|
|
cardShadow: Color(0x00000000), // 深色下无阴影,靠色差分层级
|
|
primary: Color(0xFF165DFF),
|
|
danger: Color(0xFFF53F3F),
|
|
warning: Color(0xFFFF7D00),
|
|
success: Color(0xFF00B42A),
|
|
);
|
|
|
|
@override
|
|
AppColors copyWith({
|
|
Color? pageBackground,
|
|
Color? cardBackground,
|
|
Color? fillBackground,
|
|
Color? textPrimary,
|
|
Color? textSecondary,
|
|
Color? textTertiary,
|
|
Color? divider,
|
|
Color? cardShadow,
|
|
Color? primary,
|
|
Color? danger,
|
|
Color? warning,
|
|
Color? success,
|
|
}) {
|
|
return AppColors(
|
|
pageBackground: pageBackground ?? this.pageBackground,
|
|
cardBackground: cardBackground ?? this.cardBackground,
|
|
fillBackground: fillBackground ?? this.fillBackground,
|
|
textPrimary: textPrimary ?? this.textPrimary,
|
|
textSecondary: textSecondary ?? this.textSecondary,
|
|
textTertiary: textTertiary ?? this.textTertiary,
|
|
divider: divider ?? this.divider,
|
|
cardShadow: cardShadow ?? this.cardShadow,
|
|
primary: primary ?? this.primary,
|
|
danger: danger ?? this.danger,
|
|
warning: warning ?? this.warning,
|
|
success: success ?? this.success,
|
|
);
|
|
}
|
|
|
|
@override
|
|
AppColors lerp(AppColors? other, double t) {
|
|
if (other == null) return this;
|
|
return AppColors(
|
|
pageBackground: Color.lerp(pageBackground, other.pageBackground, t)!,
|
|
cardBackground: Color.lerp(cardBackground, other.cardBackground, t)!,
|
|
fillBackground: Color.lerp(fillBackground, other.fillBackground, t)!,
|
|
textPrimary: Color.lerp(textPrimary, other.textPrimary, t)!,
|
|
textSecondary: Color.lerp(textSecondary, other.textSecondary, t)!,
|
|
textTertiary: Color.lerp(textTertiary, other.textTertiary, t)!,
|
|
divider: Color.lerp(divider, other.divider, t)!,
|
|
cardShadow: Color.lerp(cardShadow, other.cardShadow, t)!,
|
|
primary: Color.lerp(primary, other.primary, t)!,
|
|
danger: Color.lerp(danger, other.danger, t)!,
|
|
warning: Color.lerp(warning, other.warning, t)!,
|
|
success: Color.lerp(success, other.success, t)!,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// 便捷访问扩展:`context.appColors`
|
|
extension AppColorsX on BuildContext {
|
|
AppColors get appColors => Theme.of(this).extension<AppColors>()!;
|
|
}
|
|
|
|
class AppTheme {
|
|
// 基础色值定义
|
|
static const Color _pureWhite = Color(0xFFFFFFFF);
|
|
static const Color _offWhite = Color(0xFFfafafc); // 稍微带点灰的白,用于背景增加层次感
|
|
static const Color _pureBlack = Color(0xFF000000);
|
|
static const Color _darkGrey = Color(0xFF1C1C1E); // iOS 风格的深灰黑色
|
|
static const Color _surfaceDark = Color(0xFF2C2C2E); // 深色模式下卡片、弹窗表面色
|
|
|
|
static ThemeData get lightTheme {
|
|
return ThemeData(
|
|
useMaterial3: true,
|
|
brightness: Brightness.light,
|
|
fontFamily: (Platform.isWindows) ? 'Microsoft YaHei' : null,
|
|
|
|
// 语义色扩展
|
|
extensions: const [AppColors.light],
|
|
|
|
// 1. 整体背景色
|
|
scaffoldBackgroundColor: _offWhite,
|
|
|
|
// 2. 核心颜色方案
|
|
colorScheme: const ColorScheme.light(
|
|
primary: _pureBlack, // 主色设为黑色
|
|
onPrimary: _pureWhite, // 黑色背景上的文字设为白色
|
|
surface: _pureWhite, // 卡片、弹窗等表面颜色
|
|
onSurface: _pureBlack, // 表面上的文字色
|
|
background: _offWhite,
|
|
),
|
|
|
|
// 3. 全局按钮主题
|
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: _pureBlack,
|
|
foregroundColor: _pureWhite,
|
|
elevation: 0,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24),
|
|
),
|
|
),
|
|
|
|
// 4. 填充按钮主题 (常用语 Material 3)
|
|
filledButtonTheme: FilledButtonThemeData(
|
|
style: FilledButton.styleFrom(
|
|
backgroundColor: _pureBlack,
|
|
foregroundColor: _pureWhite,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
),
|
|
|
|
// 5. AppBar 主题 (纯白背景,黑色文字)
|
|
appBarTheme: const AppBarTheme(
|
|
backgroundColor: _pureWhite,
|
|
surfaceTintColor: Colors.transparent,
|
|
elevation: 0,
|
|
centerTitle: true,
|
|
iconTheme: IconThemeData(color: _pureBlack),
|
|
titleTextStyle: TextStyle(
|
|
color: _pureBlack,
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: -0.5,
|
|
),
|
|
),
|
|
|
|
// 6. 输入框主题 (黑白简约)
|
|
inputDecorationTheme: InputDecorationTheme(
|
|
filled: true,
|
|
fillColor: _pureWhite,
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: const BorderSide(color: Colors.black12),
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: const BorderSide(color: Colors.black12),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: const BorderSide(color: _pureBlack, width: 1.5),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// 深色主题 (与浅色主题对称的黑白简约风格)
|
|
static ThemeData get darkTheme {
|
|
return ThemeData(
|
|
useMaterial3: true,
|
|
brightness: Brightness.dark,
|
|
fontFamily: (Platform.isWindows) ? 'Microsoft YaHei' : null,
|
|
|
|
// 语义色扩展
|
|
extensions: const [AppColors.dark],
|
|
|
|
// 1. 整体背景色 (iOS 风格深灰黑)
|
|
scaffoldBackgroundColor: _darkGrey,
|
|
|
|
// 2. 核心颜色方案 (与浅色主题反色:主色白色)
|
|
colorScheme: const ColorScheme.dark(
|
|
primary: _pureWhite, // 主色设为白色
|
|
onPrimary: _pureBlack, // 白色背景上的文字设为黑色
|
|
surface: _surfaceDark, // 卡片、弹窗等表面颜色
|
|
onSurface: _pureWhite, // 表面上的文字色
|
|
),
|
|
|
|
// 3. 全局按钮主题 (白底黑字)
|
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: _pureWhite,
|
|
foregroundColor: _pureBlack,
|
|
elevation: 0,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
padding: const EdgeInsets.symmetric(vertical: 16, horizontal: 24),
|
|
),
|
|
),
|
|
|
|
// 4. 填充按钮主题 (常用语 Material 3)
|
|
filledButtonTheme: FilledButtonThemeData(
|
|
style: FilledButton.styleFrom(
|
|
backgroundColor: _pureWhite,
|
|
foregroundColor: _pureBlack,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
),
|
|
),
|
|
|
|
// 5. AppBar 主题 (深灰黑背景,白色文字)
|
|
appBarTheme: const AppBarTheme(
|
|
backgroundColor: _darkGrey,
|
|
surfaceTintColor: Colors.transparent,
|
|
elevation: 0,
|
|
centerTitle: true,
|
|
iconTheme: IconThemeData(color: _pureWhite),
|
|
titleTextStyle: TextStyle(
|
|
color: _pureWhite,
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: -0.5,
|
|
),
|
|
),
|
|
|
|
// 6. 输入框主题 (深色填充,白色边框)
|
|
inputDecorationTheme: InputDecorationTheme(
|
|
filled: true,
|
|
fillColor: _surfaceDark,
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: const BorderSide(color: Colors.white12),
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: const BorderSide(color: Colors.white12),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: const BorderSide(color: _pureWhite, width: 1.5),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|