Files
chat/client/flutter/lib/theme/app_theme.dart
2026-04-25 16:36:34 +08:00

324 lines
11 KiB
Dart
Raw Permalink 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';
/// Twitter 风格现代主题 —— 简洁、专业、值得信赖
///
/// 设计原则:
/// - 主色Twitter Blue #1DA1F2
/// - 背景:纯净白 + 浅灰蓝
/// - 无阴影,细线边框分隔
/// - 文字:清晰的三级灰度
class AppTheme {
// 主色系 —— Twitter Blue
static const Color primaryColor = Color(0xFF1DA1F2);
static const Color primaryColorDark = Color(0xFF1A91DA);
static const Color primaryColorLight = Color(0xFFE8F5FE);
// 背景
static const Color scaffoldBackground = Color(0xFFF5F8FA);
static const Color scaffoldBackgroundDark = Color(0xFF000000);
// 语义色
static const Color accentColor = Color(0xFF1DA1F2);
static const Color errorColor = Color(0xFFE0245E);
static const Color successColor = Color(0xFF17BF63);
static const Color warningColor = Color(0xFFFFAD1F);
static const Color infoColor = Color(0xFF1DA1F2);
// 文字层级
static const Color textPrimary = Color(0xFF14171A);
static const Color textSecondary = Color(0xFF657786);
static const Color textHint = Color(0xFFAAB8C2);
static const Color textDisabled = Color(0xFFCCD6DD);
// 分隔线 / 边框
static const Color dividerColor = Color(0xFFE1E8ED);
// 卡片/面板
static const Color cardBackground = Colors.white;
static const Color cardBackgroundDark = Color(0xFF16202A);
// 聊天气泡
static const Color bubbleSelf = Color(0xFF1DA1F2); // 蓝色气泡(己方)
static const Color bubbleOther = Color(0xFFEFF3F6); // 浅灰气泡(对方)
static const Color chatInputBg = Color(0xFFF5F8FA);
// 辅助色
static const Color dragHandleColor = Color(0xFFCCD6DD);
static const Color timestampBg = Color(0xFFE8ECF0);
static const Color timestampText = Color(0xFF657786);
// ============================================================
// 浅色主题(默认)
// ============================================================
static ThemeData get lightTheme {
return ThemeData(
useMaterial3: true,
brightness: Brightness.light,
colorScheme: ColorScheme.light(
primary: primaryColor,
onPrimary: Colors.white,
secondary: primaryColor,
onSecondary: Colors.white,
surface: Colors.white,
onSurface: textPrimary,
error: errorColor,
onError: Colors.white,
outline: dividerColor,
outlineVariant: Color(0xFFE1E8ED),
),
scaffoldBackgroundColor: scaffoldBackground,
// AppBar —— 白底,底部分隔线
appBarTheme: const AppBarTheme(
backgroundColor: Colors.white,
foregroundColor: textPrimary,
elevation: 0,
centerTitle: false,
titleTextStyle: TextStyle(
color: textPrimary,
fontSize: 20,
fontWeight: FontWeight.w800,
),
surfaceTintColor: Colors.transparent,
shadowColor: Colors.transparent,
),
// 卡片 —— 白底、细边框、无阴影
cardTheme: CardThemeData(
color: cardBackground,
elevation: 0,
margin: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: const BorderSide(color: dividerColor, width: 0.5),
),
),
// 按钮
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: primaryColor,
foregroundColor: Colors.white,
disabledBackgroundColor: const Color(0xFFAADAF5),
disabledForegroundColor: Colors.white70,
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
textStyle: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
),
elevation: 0,
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: primaryColor,
side: const BorderSide(color: primaryColor, width: 1.5),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
),
),
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: primaryColor,
textStyle: const TextStyle(fontSize: 15, fontWeight: FontWeight.w600),
),
),
// 输入框
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: const Color(0xFFF5F8FA),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: dividerColor, width: 1),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: dividerColor, width: 1),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: primaryColor, width: 2),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: errorColor, width: 1),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
hintStyle: const TextStyle(color: textHint, fontSize: 15),
labelStyle: const TextStyle(color: textSecondary, fontSize: 14),
),
// 底部导航
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
backgroundColor: Colors.white,
selectedItemColor: primaryColor,
unselectedItemColor: textHint,
type: BottomNavigationBarType.fixed,
elevation: 0,
selectedLabelStyle: TextStyle(fontSize: 11, fontWeight: FontWeight.w600),
unselectedLabelStyle: TextStyle(fontSize: 11),
),
// 分隔线
dividerTheme: const DividerThemeData(
color: dividerColor,
thickness: 0.5,
space: 0.5,
),
// 对话框
dialogTheme: DialogThemeData(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
titleTextStyle: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w800,
color: textPrimary,
),
),
// SnackBar
snackBarTheme: SnackBarThemeData(
behavior: SnackBarBehavior.floating,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
// FAB
floatingActionButtonTheme: const FloatingActionButtonThemeData(
backgroundColor: primaryColor,
foregroundColor: Colors.white,
elevation: 2,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(16)),
),
),
// TabBar
tabBarTheme: TabBarThemeData(
labelColor: primaryColor,
unselectedLabelColor: textSecondary,
indicatorColor: primaryColor,
indicatorSize: TabBarIndicatorSize.label,
labelStyle: const TextStyle(fontSize: 15, fontWeight: FontWeight.w700),
unselectedLabelStyle: const TextStyle(fontSize: 15),
),
// Chip
chipTheme: ChipThemeData(
backgroundColor: const Color(0xFFF5F8FA),
selectedColor: primaryColorLight,
labelStyle: const TextStyle(fontSize: 13, color: textPrimary),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
side: BorderSide.none,
),
// List Tile
listTileTheme: const ListTileThemeData(
contentPadding: EdgeInsets.symmetric(horizontal: 16),
minVerticalPadding: 12,
),
// 页面过渡
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.android: CupertinoPageTransitionsBuilder(),
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
},
),
);
}
// ============================================================
// 深色主题
// ============================================================
static ThemeData get darkTheme {
const darkSurface = Color(0xFF192734);
const darkBg = Color(0xFF000000);
return ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
colorScheme: ColorScheme.dark(
primary: primaryColor,
onPrimary: Colors.white,
secondary: primaryColor,
surface: darkSurface,
onSurface: Colors.white,
error: errorColor,
),
scaffoldBackgroundColor: darkBg,
appBarTheme: const AppBarTheme(
backgroundColor: darkSurface,
foregroundColor: Colors.white,
elevation: 0,
centerTitle: false,
titleTextStyle: TextStyle(
color: Colors.white,
fontSize: 20,
fontWeight: FontWeight.w800,
),
surfaceTintColor: Colors.transparent,
),
cardTheme: CardThemeData(
color: darkSurface,
elevation: 0,
margin: EdgeInsets.zero,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: const BorderSide(color: Color(0xFF38444D), width: 0.5),
),
),
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: primaryColor,
foregroundColor: Colors.white,
padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 14),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
elevation: 0,
),
),
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: const Color(0xFF192734),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: Color(0xFF38444D), width: 1),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: Color(0xFF38444D), width: 1),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(color: primaryColor, width: 2),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
),
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
backgroundColor: darkSurface,
selectedItemColor: primaryColor,
unselectedItemColor: Color(0xFF657786),
type: BottomNavigationBarType.fixed,
elevation: 0,
),
dividerTheme: const DividerThemeData(
color: Color(0xFF38444D),
thickness: 0.5,
),
dialogTheme: DialogThemeData(
backgroundColor: darkSurface,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
),
tabBarTheme: const TabBarThemeData(
labelColor: primaryColor,
unselectedLabelColor: Color(0xFF657786),
indicatorColor: primaryColor,
),
);
}
}