322 lines
10 KiB
Dart
322 lines
10 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'app_colors.dart';
|
|
|
|
class AppTheme {
|
|
static ThemeData lightTheme = (() {
|
|
// Color scheme for light theme
|
|
const scheme = ColorScheme.light(
|
|
primary: AppColors.primaryColor,
|
|
onPrimary: Colors.white,
|
|
secondary: AppColors.secondaryColor,
|
|
tertiary: AppColors.infoColor,
|
|
error: AppColors.errorColor,
|
|
surface: AppColors.surfaceColor,
|
|
);
|
|
|
|
return ThemeData(
|
|
useMaterial3: true,
|
|
colorScheme: scheme,
|
|
|
|
// 기본 배경색
|
|
scaffoldBackgroundColor: AppColors.backgroundColor,
|
|
|
|
// 카드 스타일 - Material 3 표면 중심
|
|
cardTheme: CardThemeData(
|
|
elevation: 1,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
|
|
),
|
|
|
|
// 앱바 스타일 - 기본 M3 사용(투명 배경 유지)
|
|
appBarTheme: const AppBarTheme(
|
|
backgroundColor: Colors.transparent,
|
|
elevation: 0,
|
|
centerTitle: false,
|
|
),
|
|
|
|
// 타이포그래피 - Material 3 + onSurface 정렬
|
|
textTheme: ThemeData.light(useMaterial3: true)
|
|
.textTheme
|
|
.copyWith(
|
|
headlineLarge: const TextStyle(
|
|
fontSize: 32,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: -0.5,
|
|
height: 1.2,
|
|
),
|
|
headlineMedium: const TextStyle(
|
|
fontSize: 28,
|
|
fontWeight: FontWeight.w700,
|
|
letterSpacing: -0.5,
|
|
height: 1.2,
|
|
),
|
|
headlineSmall: const TextStyle(
|
|
fontSize: 24,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: -0.25,
|
|
height: 1.3,
|
|
),
|
|
titleLarge: const TextStyle(
|
|
fontSize: 20,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: -0.2,
|
|
height: 1.4,
|
|
),
|
|
titleMedium: const TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: -0.1,
|
|
height: 1.4,
|
|
),
|
|
titleSmall: const TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
height: 1.4,
|
|
),
|
|
bodyLarge: const TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w400,
|
|
letterSpacing: 0.1,
|
|
height: 1.5,
|
|
),
|
|
bodyMedium: const TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w400,
|
|
letterSpacing: 0.1,
|
|
height: 1.5,
|
|
),
|
|
bodySmall: const TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w400,
|
|
letterSpacing: 0.2,
|
|
height: 1.5,
|
|
),
|
|
labelLarge: const TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: 0.1,
|
|
height: 1.4,
|
|
),
|
|
labelMedium: const TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: 0.2,
|
|
height: 1.4,
|
|
),
|
|
labelSmall: const TextStyle(
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w500,
|
|
letterSpacing: 0.2,
|
|
height: 1.4,
|
|
),
|
|
)
|
|
.apply(
|
|
// 본문/헤드라인 공통 색상은 onSurface로 적용
|
|
bodyColor: scheme.onSurface,
|
|
displayColor: scheme.onSurface,
|
|
),
|
|
|
|
// 입력 필드 스타일 - M3 surface/outline 기반
|
|
inputDecorationTheme: InputDecorationTheme(
|
|
filled: true,
|
|
fillColor: scheme.surface,
|
|
contentPadding:
|
|
const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
|
|
border: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: BorderSide.none,
|
|
),
|
|
enabledBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: BorderSide(color: scheme.outline, width: 1),
|
|
),
|
|
focusedBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: BorderSide(color: scheme.primary, width: 1.5),
|
|
),
|
|
errorBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: BorderSide(color: scheme.error, width: 1),
|
|
),
|
|
focusedErrorBorder: OutlineInputBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
borderSide: BorderSide(color: scheme.error, width: 1.5),
|
|
),
|
|
labelStyle: TextStyle(
|
|
color: scheme.onSurfaceVariant,
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
hintStyle: TextStyle(
|
|
color: scheme.onSurfaceVariant,
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w400,
|
|
),
|
|
errorStyle: TextStyle(
|
|
color: scheme.error,
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w400,
|
|
),
|
|
),
|
|
|
|
// 버튼 스타일 - 프라이머리 버튼
|
|
elevatedButtonTheme: ElevatedButtonThemeData(
|
|
style: ElevatedButton.styleFrom(
|
|
backgroundColor: scheme.primary,
|
|
foregroundColor: scheme.onPrimary,
|
|
minimumSize: const Size(0, 48),
|
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
elevation: 0,
|
|
),
|
|
),
|
|
|
|
// 텍스트 버튼 스타일
|
|
textButtonTheme: TextButtonThemeData(
|
|
style: TextButton.styleFrom(
|
|
foregroundColor: scheme.primary,
|
|
minimumSize: const Size(0, 40),
|
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
// Text style inherits from theme.labelLarge
|
|
),
|
|
),
|
|
|
|
// 아웃라인 버튼 스타일
|
|
outlinedButtonTheme: OutlinedButtonThemeData(
|
|
style: OutlinedButton.styleFrom(
|
|
foregroundColor: scheme.primary,
|
|
minimumSize: const Size(0, 48),
|
|
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(12),
|
|
),
|
|
side: BorderSide(color: scheme.outline, width: 1),
|
|
),
|
|
),
|
|
|
|
// FAB 스타일
|
|
floatingActionButtonTheme: FloatingActionButtonThemeData(
|
|
backgroundColor: scheme.primary,
|
|
foregroundColor: scheme.onPrimary,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
elevation: 2,
|
|
extendedPadding:
|
|
const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
|
|
extendedTextStyle: const TextStyle(
|
|
fontSize: 15,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: 0.1,
|
|
),
|
|
),
|
|
|
|
// 스위치 스타일 (공통 테마)
|
|
switchTheme: SwitchThemeData(
|
|
thumbColor: WidgetStateProperty.resolveWith<Color>((states) {
|
|
if (states.contains(WidgetState.selected)) {
|
|
return scheme.primary;
|
|
}
|
|
return scheme.onSurfaceVariant; // OFF 썸을 명확하게
|
|
}),
|
|
trackColor: WidgetStateProperty.resolveWith<Color>((states) {
|
|
if (states.contains(WidgetState.selected)) {
|
|
return scheme.primary.withValues(alpha: 0.5);
|
|
}
|
|
// OFF 트랙 대비 강화
|
|
return scheme.surfaceContainerHighest.withValues(alpha: 0.5);
|
|
}),
|
|
),
|
|
|
|
// 체크박스 스타일
|
|
checkboxTheme: CheckboxThemeData(
|
|
fillColor: WidgetStateProperty.resolveWith<Color>((states) {
|
|
if (states.contains(WidgetState.selected)) {
|
|
return scheme.primary;
|
|
}
|
|
return Colors.transparent;
|
|
}),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(4),
|
|
),
|
|
side: BorderSide(color: scheme.outline, width: 1.5),
|
|
),
|
|
|
|
// 라디오 버튼 스타일
|
|
radioTheme: RadioThemeData(
|
|
fillColor: WidgetStateProperty.resolveWith<Color>((states) {
|
|
if (states.contains(WidgetState.selected)) {
|
|
return scheme.primary;
|
|
}
|
|
return scheme.onSurfaceVariant;
|
|
}),
|
|
),
|
|
|
|
// 슬라이더 스타일
|
|
sliderTheme: SliderThemeData(
|
|
activeTrackColor: scheme.primary,
|
|
inactiveTrackColor: scheme.onSurfaceVariant,
|
|
thumbColor: scheme.primary,
|
|
overlayColor: scheme.primary.withValues(alpha: 0.3),
|
|
trackHeight: 4,
|
|
thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 10),
|
|
overlayShape: const RoundSliderOverlayShape(overlayRadius: 20),
|
|
),
|
|
|
|
// 탭바 스타일
|
|
tabBarTheme: TabBarThemeData(
|
|
labelColor: scheme.primary,
|
|
unselectedLabelColor: scheme.onSurfaceVariant,
|
|
indicatorColor: scheme.primary,
|
|
labelStyle: const TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
letterSpacing: 0.1,
|
|
),
|
|
unselectedLabelStyle: const TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
letterSpacing: 0.1,
|
|
),
|
|
),
|
|
|
|
// 디바이더 스타일
|
|
dividerTheme: DividerThemeData(
|
|
color: scheme.outline,
|
|
thickness: 1,
|
|
space: 16,
|
|
),
|
|
|
|
// 페이지 트랜지션
|
|
pageTransitionsTheme: const PageTransitionsTheme(
|
|
builders: {
|
|
TargetPlatform.android: ZoomPageTransitionsBuilder(),
|
|
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
|
|
TargetPlatform.macOS: CupertinoPageTransitionsBuilder(),
|
|
},
|
|
),
|
|
|
|
// 스낵바 스타일 (기본 유지)
|
|
snackBarTheme: SnackBarThemeData(
|
|
backgroundColor: scheme.primary,
|
|
contentTextStyle: TextStyle(
|
|
color: scheme.onPrimary,
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
behavior: SnackBarBehavior.floating,
|
|
),
|
|
);
|
|
})();
|
|
}
|