main
dart 384 lines 13.9 KB
Raw
1 import 'package:flutter/material.dart';
2 import 'package:shadcn_ui/shadcn_ui.dart';
3
4 import 'colors.dart';
5 import 'typography.dart';
6
7 enum AppThemeType { system, light, dark, claude }
8
9 class AppTheme {
10 static ShadThemeData get darkShadTheme => ShadThemeData(
11 brightness: Brightness.dark,
12 colorScheme: const ShadSlateColorScheme.dark(
13 background: AppColors.darkBackground,
14 primary: AppColors.darkAccent,
15 primaryForeground: AppColors.darkBackground,
16 secondary: AppColors.darkSurface,
17 secondaryForeground: AppColors.darkPrimaryText,
18 border: AppColors.darkBorder,
19 card: AppColors.darkSurface,
20 cardForeground: AppColors.darkPrimaryText,
21 muted: AppColors.darkSurface,
22 mutedForeground: AppColors.darkMutedText,
23 ),
24 );
25
26 static ShadThemeData get lightShadTheme => ShadThemeData(
27 brightness: Brightness.light,
28 colorScheme: const ShadSlateColorScheme.light(
29 background: AppColors.lightBackground,
30 primary: AppColors.lightAccent,
31 primaryForeground: Colors.white,
32 secondary: AppColors.lightSurface,
33 secondaryForeground: AppColors.lightPrimaryText,
34 border: AppColors.lightBorder,
35 card: AppColors.lightSurface,
36 cardForeground: AppColors.lightPrimaryText,
37 muted: AppColors.lightSurface,
38 mutedForeground: AppColors.lightMutedText,
39 ),
40 );
41
42 static ShadThemeData get claudeShadTheme => ShadThemeData(
43 brightness: Brightness.light,
44 colorScheme: const ShadSlateColorScheme.light(
45 background: AppColors.claudeBackground,
46 primary: AppColors.claudeAccent,
47 primaryForeground: Colors.white,
48 secondary: AppColors.claudeSurface,
49 secondaryForeground: AppColors.claudePrimaryText,
50 border: AppColors.claudeBorder,
51 card: AppColors.claudeSurface,
52 cardForeground: AppColors.claudePrimaryText,
53 muted: AppColors.claudeSurface,
54 mutedForeground: AppColors.claudeMutedText,
55 ),
56 radius: const BorderRadius.all(Radius.circular(12)),
57 );
58
59 static ThemeData get darkTheme => ThemeData(
60 useMaterial3: true,
61 brightness: Brightness.dark,
62 scaffoldBackgroundColor: AppColors.darkBackground,
63 colorScheme: const ColorScheme.dark(
64 surface: AppColors.darkSurface,
65 primary: AppColors.darkAccent,
66 onPrimary: AppColors.darkBackground, // White accent, black text
67 secondary: AppColors.darkAccent,
68 onSecondary: AppColors.darkBackground,
69 error: AppColors.error,
70 onSurface: AppColors.darkPrimaryText,
71 outline: AppColors.darkBorder,
72 ),
73 textTheme: AppTypography.darkTextTheme,
74 appBarTheme: const AppBarTheme(
75 backgroundColor: AppColors.darkBackground,
76 foregroundColor: AppColors.darkPrimaryText,
77 elevation: 0,
78 centerTitle: false,
79 ),
80 cardTheme: CardThemeData(
81 color: AppColors.darkSurface,
82 elevation: 0,
83 shape: RoundedRectangleBorder(
84 borderRadius: BorderRadius.circular(8),
85 side: const BorderSide(color: AppColors.darkBorder),
86 ),
87 ),
88 dividerTheme: const DividerThemeData(
89 color: AppColors.darkBorder,
90 thickness: 1,
91 ),
92 inputDecorationTheme: InputDecorationTheme(
93 filled: true,
94 fillColor: AppColors.darkSurfaceInput,
95 border: OutlineInputBorder(
96 borderRadius: BorderRadius.circular(12),
97 borderSide: const BorderSide(color: AppColors.darkBorder),
98 ),
99 enabledBorder: OutlineInputBorder(
100 borderRadius: BorderRadius.circular(12),
101 borderSide: const BorderSide(color: AppColors.darkBorder),
102 ),
103 focusedBorder: OutlineInputBorder(
104 borderRadius: BorderRadius.circular(12),
105 borderSide: const BorderSide(color: AppColors.darkAccent, width: 2),
106 ),
107 contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
108 ),
109 elevatedButtonTheme: ElevatedButtonThemeData(
110 style: ElevatedButton.styleFrom(
111 backgroundColor: AppColors.darkAccent,
112 foregroundColor: AppColors.darkBackground,
113 shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
114 padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
115 ),
116 ),
117 outlinedButtonTheme: OutlinedButtonThemeData(
118 style: OutlinedButton.styleFrom(
119 foregroundColor: AppColors.darkPrimaryText,
120 side: const BorderSide(color: AppColors.darkBorder),
121 shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
122 padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
123 ),
124 ),
125 textButtonTheme: TextButtonThemeData(
126 style: TextButton.styleFrom(
127 foregroundColor: AppColors.darkPrimaryText,
128 shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
129 padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
130 ),
131 ),
132 bottomSheetTheme: const BottomSheetThemeData(
133 backgroundColor: AppColors.darkSurface,
134 shape: RoundedRectangleBorder(
135 borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
136 ),
137 ),
138 dialogTheme: DialogThemeData(
139 backgroundColor: AppColors.darkSurface,
140 shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
141 ),
142 snackBarTheme: SnackBarThemeData(
143 backgroundColor: AppColors.darkSurface,
144 contentTextStyle: AppTypography.darkTextTheme.bodyMedium,
145 shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
146 behavior: SnackBarBehavior.floating,
147 ),
148 listTileTheme: const ListTileThemeData(
149 contentPadding: EdgeInsets.symmetric(horizontal: 16, vertical: 4),
150 ),
151 navigationBarTheme: NavigationBarThemeData(
152 backgroundColor: AppColors.darkBackground,
153 indicatorColor: AppColors.darkSurface,
154 labelTextStyle: WidgetStateProperty.resolveWith((states) {
155 if (states.contains(WidgetState.selected)) {
156 return const TextStyle(
157 color: AppColors.darkPrimaryText,
158 fontSize: 12,
159 fontWeight: FontWeight.w600,
160 );
161 }
162 return const TextStyle(
163 color: AppColors.darkMutedText,
164 fontSize: 12,
165 fontWeight: FontWeight.w500,
166 );
167 }),
168 iconTheme: WidgetStateProperty.resolveWith((states) {
169 if (states.contains(WidgetState.selected)) {
170 return const IconThemeData(color: AppColors.darkPrimaryText);
171 }
172 return const IconThemeData(color: AppColors.darkMutedText);
173 }),
174 ),
175 );
176
177 static ThemeData get lightTheme => ThemeData(
178 useMaterial3: true,
179 brightness: Brightness.light,
180 scaffoldBackgroundColor: AppColors.lightBackground,
181 colorScheme: const ColorScheme.light(
182 surface: AppColors.lightSurface,
183 primary: AppColors.lightAccent,
184 onPrimary: Colors.white,
185 secondary: AppColors.lightAccent,
186 onSecondary: Colors.white,
187 error: AppColors.error,
188 onSurface: AppColors.lightPrimaryText,
189 outline: AppColors.lightBorder,
190 ),
191 textTheme: AppTypography.lightTextTheme,
192 appBarTheme: const AppBarTheme(
193 backgroundColor: AppColors.lightBackground,
194 foregroundColor: AppColors.lightPrimaryText,
195 elevation: 0,
196 centerTitle: false,
197 ),
198 cardTheme: CardThemeData(
199 color: AppColors.lightSurface,
200 elevation: 0,
201 shape: RoundedRectangleBorder(
202 borderRadius: BorderRadius.circular(8),
203 side: const BorderSide(color: AppColors.lightBorder),
204 ),
205 ),
206 dividerTheme: const DividerThemeData(
207 color: AppColors.lightBorder,
208 thickness: 1,
209 ),
210 inputDecorationTheme: InputDecorationTheme(
211 filled: true,
212 fillColor: AppColors.lightSurface,
213 border: OutlineInputBorder(
214 borderRadius: BorderRadius.circular(12),
215 borderSide: const BorderSide(color: AppColors.lightBorder),
216 ),
217 enabledBorder: OutlineInputBorder(
218 borderRadius: BorderRadius.circular(12),
219 borderSide: const BorderSide(color: AppColors.lightBorder),
220 ),
221 focusedBorder: OutlineInputBorder(
222 borderRadius: BorderRadius.circular(12),
223 borderSide: const BorderSide(color: AppColors.lightAccent, width: 2),
224 ),
225 contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
226 ),
227 elevatedButtonTheme: ElevatedButtonThemeData(
228 style: ElevatedButton.styleFrom(
229 backgroundColor: AppColors.lightAccent,
230 foregroundColor: Colors.white,
231 shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
232 padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
233 ),
234 ),
235 outlinedButtonTheme: OutlinedButtonThemeData(
236 style: OutlinedButton.styleFrom(
237 foregroundColor: AppColors.lightPrimaryText,
238 side: const BorderSide(color: AppColors.lightBorder),
239 shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
240 padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
241 ),
242 ),
243 bottomSheetTheme: const BottomSheetThemeData(
244 backgroundColor: AppColors.lightSurface,
245 shape: RoundedRectangleBorder(
246 borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
247 ),
248 ),
249 dialogTheme: DialogThemeData(
250 backgroundColor: AppColors.lightSurface,
251 shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
252 ),
253 navigationBarTheme: NavigationBarThemeData(
254 backgroundColor: AppColors.lightBackground,
255 indicatorColor: AppColors.lightSurface,
256 labelTextStyle: WidgetStateProperty.resolveWith((states) {
257 if (states.contains(WidgetState.selected)) {
258 return const TextStyle(
259 color: AppColors.lightPrimaryText,
260 fontSize: 12,
261 fontWeight: FontWeight.w600,
262 );
263 }
264 return const TextStyle(
265 color: AppColors.lightMutedText,
266 fontSize: 12,
267 fontWeight: FontWeight.w500,
268 );
269 }),
270 iconTheme: WidgetStateProperty.resolveWith((states) {
271 if (states.contains(WidgetState.selected)) {
272 return const IconThemeData(color: AppColors.lightPrimaryText);
273 }
274 return const IconThemeData(color: AppColors.lightMutedText);
275 }),
276 ),
277 );
278
279 static ThemeData get claudeTheme => ThemeData(
280 useMaterial3: true,
281 brightness: Brightness.light,
282 scaffoldBackgroundColor: AppColors.claudeBackground,
283 colorScheme: const ColorScheme.light(
284 surface: AppColors.claudeSurface,
285 primary: AppColors.claudeAccent,
286 onPrimary: Colors.white,
287 secondary: AppColors.claudeAccent,
288 onSecondary: Colors.white,
289 error: AppColors.error,
290 onSurface: AppColors.claudePrimaryText,
291 outline: AppColors.claudeBorder,
292 ),
293 textTheme: AppTypography.claudeTextTheme,
294 appBarTheme: const AppBarTheme(
295 backgroundColor: AppColors.claudeBackground,
296 foregroundColor: AppColors.claudePrimaryText,
297 elevation: 0,
298 centerTitle: false,
299 ),
300 cardTheme: CardThemeData(
301 color: AppColors.claudeSurface,
302 elevation: 0,
303 shape: RoundedRectangleBorder(
304 borderRadius: BorderRadius.circular(8),
305 side: const BorderSide(color: AppColors.claudeBorder),
306 ),
307 ),
308 dividerTheme: const DividerThemeData(
309 color: AppColors.claudeBorder,
310 thickness: 1,
311 ),
312 inputDecorationTheme: InputDecorationTheme(
313 filled: true,
314 fillColor: AppColors.claudeSurface,
315 border: OutlineInputBorder(
316 borderRadius: BorderRadius.circular(12),
317 borderSide: const BorderSide(color: AppColors.claudeBorder),
318 ),
319 enabledBorder: OutlineInputBorder(
320 borderRadius: BorderRadius.circular(12),
321 borderSide: const BorderSide(color: AppColors.claudeBorder),
322 ),
323 focusedBorder: OutlineInputBorder(
324 borderRadius: BorderRadius.circular(12),
325 borderSide: const BorderSide(color: AppColors.claudeAccent, width: 2),
326 ),
327 contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
328 ),
329 elevatedButtonTheme: ElevatedButtonThemeData(
330 style: ElevatedButton.styleFrom(
331 backgroundColor: AppColors.claudeAccent,
332 foregroundColor: Colors.white,
333 shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
334 padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
335 ),
336 ),
337 outlinedButtonTheme: OutlinedButtonThemeData(
338 style: OutlinedButton.styleFrom(
339 foregroundColor: AppColors.claudePrimaryText,
340 side: const BorderSide(color: AppColors.claudeBorder),
341 shape: RoundedRectangleBorder(
342 borderRadius: BorderRadius.circular(
343 8,
344 ), // Secondary buttons are less rounded
345 ),
346 padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
347 ),
348 ),
349 bottomSheetTheme: const BottomSheetThemeData(
350 backgroundColor: AppColors.claudeSurface,
351 shape: RoundedRectangleBorder(
352 borderRadius: BorderRadius.vertical(top: Radius.circular(12)),
353 ),
354 ),
355 dialogTheme: DialogThemeData(
356 backgroundColor: AppColors.claudeSurface,
357 shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
358 ),
359 navigationBarTheme: NavigationBarThemeData(
360 backgroundColor: AppColors.claudeBackground,
361 indicatorColor: AppColors.claudeSurface,
362 labelTextStyle: WidgetStateProperty.resolveWith((states) {
363 if (states.contains(WidgetState.selected)) {
364 return const TextStyle(
365 color: AppColors.claudePrimaryText,
366 fontSize: 12,
367 fontWeight: FontWeight.w600,
368 );
369 }
370 return const TextStyle(
371 color: AppColors.claudeSecondaryText,
372 fontSize: 12,
373 fontWeight: FontWeight.w500,
374 );
375 }),
376 iconTheme: WidgetStateProperty.resolveWith((states) {
377 if (states.contains(WidgetState.selected)) {
378 return const IconThemeData(color: AppColors.claudePrimaryText);
379 }
380 return const IconThemeData(color: AppColors.claudeSecondaryText);
381 }),
382 ),
383 );
384 }