Files
submanager/lib/theme/app_theme.dart
JiWoong Sul 83c5e3d64e Refactor screens to MVC architecture with modular widgets
- Extract business logic from screens into dedicated controllers
- Split large screen files into smaller, reusable widget components
- Add controllers for AddSubscriptionScreen and DetailScreen
- Create modular widgets for subscription and detail features
- Improve code organization and maintainability
- Remove duplicated code and improve reusability

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-11 00:21:18 +09:00

357 lines
11 KiB
Dart

import 'package:flutter/material.dart';
import 'app_colors.dart';
class AppTheme {
static ThemeData lightTheme = ThemeData(
useMaterial3: true,
colorScheme: const ColorScheme.light(
primary: AppColors.primaryColor,
onPrimary: Colors.white,
secondary: AppColors.secondaryColor,
tertiary: AppColors.infoColor,
error: AppColors.dangerColor,
background: AppColors.backgroundColor,
surface: AppColors.surfaceColor,
),
// 기본 배경색
scaffoldBackgroundColor: AppColors.backgroundColor,
// 카드 스타일 - 부드러운 그림자, 둥근 모서리
cardTheme: CardTheme(
color: AppColors.cardColor,
elevation: 1,
shadowColor: Colors.black.withValues(alpha: 0.04),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
side: const BorderSide(color: AppColors.borderColor, width: 0.5),
),
clipBehavior: Clip.antiAlias,
margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
),
// 앱바 스타일 - 깔끔하고 투명한 디자인
appBarTheme: const AppBarTheme(
backgroundColor: AppColors.surfaceColor,
foregroundColor: AppColors.textPrimary,
elevation: 0,
centerTitle: false,
titleTextStyle: const TextStyle(
color: AppColors.textPrimary,
fontSize: 22,
fontWeight: FontWeight.w600,
letterSpacing: -0.2,
),
iconTheme: const IconThemeData(
color: AppColors.secondaryColor,
size: 24,
),
),
// 타이포그래피 - Metronic Tailwind 스타일
textTheme: const TextTheme(
// 헤드라인 - 페이지 제목
headlineLarge: const TextStyle(
color: AppColors.textPrimary,
fontSize: 32,
fontWeight: FontWeight.w700,
letterSpacing: -0.5,
height: 1.2,
),
headlineMedium: const TextStyle(
color: AppColors.textPrimary,
fontSize: 28,
fontWeight: FontWeight.w700,
letterSpacing: -0.5,
height: 1.2,
),
headlineSmall: const TextStyle(
color: AppColors.textPrimary,
fontSize: 24,
fontWeight: FontWeight.w600,
letterSpacing: -0.25,
height: 1.3,
),
// 타이틀 - 카드, 섹션 제목
titleLarge: const TextStyle(
color: AppColors.textPrimary,
fontSize: 20,
fontWeight: FontWeight.w600,
letterSpacing: -0.2,
height: 1.4,
),
titleMedium: TextStyle(
color: AppColors.textPrimary,
fontSize: 18,
fontWeight: FontWeight.w600,
letterSpacing: -0.1,
height: 1.4,
),
titleSmall: TextStyle(
color: AppColors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.w600,
letterSpacing: 0,
height: 1.4,
),
// 본문 텍스트
bodyLarge: TextStyle(
color: AppColors.textPrimary,
fontSize: 16,
fontWeight: FontWeight.w400,
letterSpacing: 0.1,
height: 1.5,
),
bodyMedium: TextStyle(
color: AppColors.textSecondary,
fontSize: 14,
fontWeight: FontWeight.w400,
letterSpacing: 0.1,
height: 1.5,
),
bodySmall: TextStyle(
color: AppColors.textMuted,
fontSize: 12,
fontWeight: FontWeight.w400,
letterSpacing: 0.2,
height: 1.5,
),
// 라벨 텍스트
labelLarge: TextStyle(
color: AppColors.textPrimary,
fontSize: 14,
fontWeight: FontWeight.w600,
letterSpacing: 0.1,
height: 1.4,
),
labelMedium: TextStyle(
color: AppColors.textSecondary,
fontSize: 12,
fontWeight: FontWeight.w600,
letterSpacing: 0.2,
height: 1.4,
),
labelSmall: TextStyle(
color: AppColors.textMuted,
fontSize: 11,
fontWeight: FontWeight.w500,
letterSpacing: 0.2,
height: 1.4,
),
),
// 입력 필드 스타일 - 깔끔하고 현대적인 디자인
inputDecorationTheme: InputDecorationTheme(
filled: true,
fillColor: AppColors.surfaceColorAlt,
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: BorderSide.none,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: AppColors.borderColor, width: 1),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: AppColors.primaryColor, width: 1.5),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: AppColors.dangerColor, width: 1),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12),
borderSide: const BorderSide(color: AppColors.dangerColor, width: 1.5),
),
labelStyle: const TextStyle(
color: AppColors.textSecondary,
fontSize: 14,
fontWeight: FontWeight.w500,
),
hintStyle: const TextStyle(
color: AppColors.textMuted,
fontSize: 14,
fontWeight: FontWeight.w400,
),
errorStyle: const TextStyle(
color: AppColors.dangerColor,
fontSize: 12,
fontWeight: FontWeight.w400,
),
),
// 버튼 스타일 - 프라이머리 버튼
elevatedButtonTheme: ElevatedButtonThemeData(
style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryColor,
foregroundColor: Colors.white,
minimumSize: const Size(0, 48),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
elevation: 0,
textStyle: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
letterSpacing: 0.1,
),
),
),
// 텍스트 버튼 스타일
textButtonTheme: TextButtonThemeData(
style: TextButton.styleFrom(
foregroundColor: AppColors.primaryColor,
minimumSize: const Size(0, 40),
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
textStyle: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
letterSpacing: 0.1,
),
),
),
// 아웃라인 버튼 스타일
outlinedButtonTheme: OutlinedButtonThemeData(
style: OutlinedButton.styleFrom(
foregroundColor: AppColors.textPrimary,
minimumSize: const Size(0, 48),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
side: const BorderSide(color: AppColors.borderColor, width: 1),
textStyle: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w600,
letterSpacing: 0.1,
),
),
),
// FAB 스타일
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: AppColors.primaryColor,
foregroundColor: Colors.white,
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: MaterialStateProperty.resolveWith<Color>((states) {
if (states.contains(MaterialState.selected)) {
return AppColors.primaryColor;
}
return Colors.white;
}),
trackColor: MaterialStateProperty.resolveWith<Color>((states) {
if (states.contains(MaterialState.selected)) {
return AppColors.primaryColor.withValues(alpha: 0.5);
}
return AppColors.borderColor;
}),
),
// 체크박스 스타일
checkboxTheme: CheckboxThemeData(
fillColor: MaterialStateProperty.resolveWith<Color>((states) {
if (states.contains(MaterialState.selected)) {
return AppColors.primaryColor;
}
return Colors.transparent;
}),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
side: const BorderSide(color: AppColors.borderColor, width: 1.5),
),
// 라디오 버튼 스타일
radioTheme: RadioThemeData(
fillColor: MaterialStateProperty.resolveWith<Color>((states) {
if (states.contains(MaterialState.selected)) {
return AppColors.primaryColor;
}
return AppColors.borderColor;
}),
),
// 슬라이더 스타일
sliderTheme: SliderThemeData(
activeTrackColor: AppColors.primaryColor,
inactiveTrackColor: AppColors.borderColor,
thumbColor: AppColors.primaryColor,
overlayColor: AppColors.primaryColor.withValues(alpha: 0.2),
trackHeight: 4,
thumbShape: const RoundSliderThumbShape(enabledThumbRadius: 10),
overlayShape: const RoundSliderOverlayShape(overlayRadius: 20),
),
// 탭바 스타일
tabBarTheme: const TabBarTheme(
labelColor: AppColors.primaryColor,
unselectedLabelColor: AppColors.textSecondary,
indicatorColor: AppColors.primaryColor,
labelStyle: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
letterSpacing: 0.1,
),
unselectedLabelStyle: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
letterSpacing: 0.1,
),
),
// 디바이더 스타일
dividerTheme: const DividerThemeData(
color: AppColors.dividerColor,
thickness: 1,
space: 16,
),
// 페이지 트랜지션
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.android: ZoomPageTransitionsBuilder(),
TargetPlatform.iOS: CupertinoPageTransitionsBuilder(),
TargetPlatform.macOS: CupertinoPageTransitionsBuilder(),
},
),
// 스낵바 스타일
snackBarTheme: SnackBarThemeData(
backgroundColor: AppColors.textPrimary,
contentTextStyle: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w500,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
behavior: SnackBarBehavior.floating,
),
);
}