231 lines
8.3 KiB
Dart
231 lines
8.3 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'package:flutter/services.dart';
|
||
import 'app_colors.dart';
|
||
import 'app_typography.dart';
|
||
import 'app_spacing.dart';
|
||
|
||
/// Genex Material 3 Theme Configuration
|
||
///
|
||
/// 干净清爽型紫色主题,零区块链感知的金融App体验
|
||
class AppTheme {
|
||
AppTheme._();
|
||
|
||
static ThemeData get light => ThemeData(
|
||
useMaterial3: true,
|
||
brightness: Brightness.light,
|
||
|
||
// Color Scheme
|
||
colorScheme: const ColorScheme.light(
|
||
primary: AppColors.primary,
|
||
primaryContainer: AppColors.primaryContainer,
|
||
secondary: AppColors.success,
|
||
secondaryContainer: AppColors.successLight,
|
||
tertiary: AppColors.info,
|
||
error: AppColors.error,
|
||
errorContainer: AppColors.errorLight,
|
||
surface: AppColors.surface,
|
||
surfaceContainerHighest: AppColors.surfaceVariant,
|
||
onPrimary: Colors.white,
|
||
onSecondary: Colors.white,
|
||
onSurface: AppColors.textPrimary,
|
||
onSurfaceVariant: AppColors.textSecondary,
|
||
outline: AppColors.border,
|
||
outlineVariant: AppColors.borderLight,
|
||
scrim: AppColors.scrim,
|
||
),
|
||
|
||
scaffoldBackgroundColor: AppColors.background,
|
||
|
||
// AppBar
|
||
appBarTheme: const AppBarTheme(
|
||
elevation: 0,
|
||
scrolledUnderElevation: 0.5,
|
||
centerTitle: true,
|
||
backgroundColor: AppColors.surface,
|
||
foregroundColor: AppColors.textPrimary,
|
||
surfaceTintColor: Colors.transparent,
|
||
systemOverlayStyle: SystemUiOverlayStyle(
|
||
statusBarColor: Colors.transparent,
|
||
statusBarIconBrightness: Brightness.dark,
|
||
statusBarBrightness: Brightness.light,
|
||
),
|
||
titleTextStyle: AppTypography.h3,
|
||
iconTheme: IconThemeData(color: AppColors.textPrimary, size: 24),
|
||
),
|
||
|
||
// Bottom Navigation
|
||
bottomNavigationBarTheme: const BottomNavigationBarThemeData(
|
||
type: BottomNavigationBarType.fixed,
|
||
backgroundColor: AppColors.surface,
|
||
selectedItemColor: AppColors.primary,
|
||
unselectedItemColor: AppColors.textTertiary,
|
||
elevation: 0,
|
||
selectedLabelStyle: TextStyle(fontSize: 11, fontWeight: FontWeight.w600),
|
||
unselectedLabelStyle: TextStyle(fontSize: 11, fontWeight: FontWeight.w400),
|
||
),
|
||
|
||
// Navigation Bar (M3)
|
||
navigationBarTheme: NavigationBarThemeData(
|
||
backgroundColor: AppColors.surface,
|
||
indicatorColor: AppColors.primaryContainer,
|
||
surfaceTintColor: Colors.transparent,
|
||
elevation: 0,
|
||
height: AppSpacing.bottomNavHeight,
|
||
labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
|
||
iconTheme: WidgetStateProperty.resolveWith((states) {
|
||
if (states.contains(WidgetState.selected)) {
|
||
return const IconThemeData(color: AppColors.primary, size: 24);
|
||
}
|
||
return const IconThemeData(color: AppColors.textTertiary, size: 24);
|
||
}),
|
||
labelTextStyle: WidgetStateProperty.resolveWith((states) {
|
||
if (states.contains(WidgetState.selected)) {
|
||
return AppTypography.caption.copyWith(
|
||
color: AppColors.primary,
|
||
fontWeight: FontWeight.w600,
|
||
);
|
||
}
|
||
return AppTypography.caption;
|
||
}),
|
||
),
|
||
|
||
// Card
|
||
cardTheme: CardThemeData(
|
||
elevation: 0,
|
||
color: AppColors.surface,
|
||
surfaceTintColor: Colors.transparent,
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: AppSpacing.borderRadiusMd,
|
||
side: const BorderSide(color: AppColors.borderLight, width: 1),
|
||
),
|
||
margin: EdgeInsets.zero,
|
||
),
|
||
|
||
// Elevated Button
|
||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||
style: ElevatedButton.styleFrom(
|
||
backgroundColor: AppColors.primary,
|
||
foregroundColor: Colors.white,
|
||
elevation: 0,
|
||
minimumSize: const Size(0, AppSpacing.buttonHeight),
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: AppSpacing.borderRadiusMd,
|
||
),
|
||
textStyle: AppTypography.labelLarge,
|
||
),
|
||
),
|
||
|
||
// Outlined Button
|
||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||
style: OutlinedButton.styleFrom(
|
||
foregroundColor: AppColors.primary,
|
||
side: const BorderSide(color: AppColors.primary, width: 1.5),
|
||
minimumSize: const Size(0, AppSpacing.buttonHeight),
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: AppSpacing.borderRadiusMd,
|
||
),
|
||
textStyle: AppTypography.labelLarge,
|
||
),
|
||
),
|
||
|
||
// Text Button
|
||
textButtonTheme: TextButtonThemeData(
|
||
style: TextButton.styleFrom(
|
||
foregroundColor: AppColors.primary,
|
||
textStyle: AppTypography.labelMedium,
|
||
),
|
||
),
|
||
|
||
// Input Decoration
|
||
inputDecorationTheme: InputDecorationTheme(
|
||
filled: true,
|
||
fillColor: AppColors.gray50,
|
||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
||
border: OutlineInputBorder(
|
||
borderRadius: AppSpacing.borderRadiusMd,
|
||
borderSide: BorderSide.none,
|
||
),
|
||
enabledBorder: OutlineInputBorder(
|
||
borderRadius: AppSpacing.borderRadiusMd,
|
||
borderSide: const BorderSide(color: AppColors.borderLight, width: 1),
|
||
),
|
||
focusedBorder: OutlineInputBorder(
|
||
borderRadius: AppSpacing.borderRadiusMd,
|
||
borderSide: const BorderSide(color: AppColors.primary, width: 1.5),
|
||
),
|
||
errorBorder: OutlineInputBorder(
|
||
borderRadius: AppSpacing.borderRadiusMd,
|
||
borderSide: const BorderSide(color: AppColors.error, width: 1),
|
||
),
|
||
hintStyle: AppTypography.bodyMedium.copyWith(color: AppColors.textTertiary),
|
||
labelStyle: AppTypography.bodyMedium,
|
||
errorStyle: AppTypography.caption.copyWith(color: AppColors.error),
|
||
),
|
||
|
||
// Chip
|
||
chipTheme: ChipThemeData(
|
||
backgroundColor: AppColors.gray50,
|
||
selectedColor: AppColors.primaryContainer,
|
||
labelStyle: AppTypography.labelSmall,
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: AppSpacing.borderRadiusFull,
|
||
),
|
||
side: const BorderSide(color: AppColors.borderLight),
|
||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
|
||
),
|
||
|
||
// TabBar
|
||
tabBarTheme: TabBarThemeData(
|
||
labelColor: AppColors.primary,
|
||
unselectedLabelColor: AppColors.textTertiary,
|
||
labelStyle: AppTypography.labelMedium,
|
||
unselectedLabelStyle: AppTypography.labelMedium,
|
||
indicatorSize: TabBarIndicatorSize.label,
|
||
indicator: const UnderlineTabIndicator(
|
||
borderSide: BorderSide(color: AppColors.primary, width: 2.5),
|
||
),
|
||
dividerColor: Colors.transparent,
|
||
),
|
||
|
||
// Dialog
|
||
dialogTheme: DialogThemeData(
|
||
backgroundColor: AppColors.surface,
|
||
surfaceTintColor: Colors.transparent,
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: AppSpacing.borderRadiusLg,
|
||
),
|
||
titleTextStyle: AppTypography.h2,
|
||
contentTextStyle: AppTypography.bodyMedium,
|
||
),
|
||
|
||
// BottomSheet
|
||
bottomSheetTheme: const BottomSheetThemeData(
|
||
backgroundColor: AppColors.surface,
|
||
surfaceTintColor: Colors.transparent,
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: BorderRadius.vertical(top: Radius.circular(20)),
|
||
),
|
||
showDragHandle: true,
|
||
dragHandleColor: AppColors.gray300,
|
||
dragHandleSize: Size(36, 4),
|
||
),
|
||
|
||
// Divider
|
||
dividerTheme: const DividerThemeData(
|
||
color: AppColors.borderLight,
|
||
thickness: 1,
|
||
space: 0,
|
||
),
|
||
|
||
// Snackbar
|
||
snackBarTheme: SnackBarThemeData(
|
||
backgroundColor: AppColors.gray800,
|
||
contentTextStyle: AppTypography.bodyMedium.copyWith(color: Colors.white),
|
||
behavior: SnackBarBehavior.floating,
|
||
shape: RoundedRectangleBorder(
|
||
borderRadius: AppSpacing.borderRadiusMd,
|
||
),
|
||
),
|
||
);
|
||
}
|