main
dart 222 lines 7.93 KB
Raw
1 import 'package:flutter/material.dart';
2 import 'package:flutter_riverpod/flutter_riverpod.dart';
3 import 'package:go_router/go_router.dart';
4 import 'package:hugeicons/hugeicons.dart';
5 import 'package:shadcn_ui/shadcn_ui.dart';
6 import 'package:cue/cue.dart';
7
8 import '../../../core/providers/app_providers.dart';
9 import '../../../core/routes/app_routes.dart';
10 import '../../../core/theme/app_theme.dart';
11 import '../../../l10n/app_localizations.dart';
12
13 class OnboardingThemeScreen extends ConsumerStatefulWidget {
14 const OnboardingThemeScreen({super.key});
15
16 @override
17 ConsumerState<OnboardingThemeScreen> createState() =>
18 _OnboardingThemeScreenState();
19 }
20
21 class _OnboardingThemeScreenState extends ConsumerState<OnboardingThemeScreen> {
22 @override
23 Widget build(BuildContext context) {
24 final l10n = AppLocalizations.of(context)!;
25 final theme = Theme.of(context);
26 final currentTheme = ref.watch(themeModeProvider);
27
28 return Scaffold(
29 appBar: AppBar(
30 title: Text(l10n.choose_theme),
31 leading: IconButton(
32 icon: const HugeIcon(icon: HugeIcons.strokeRoundedArrowLeft01),
33 onPressed: () {
34 context.pop();
35 },
36 ),
37 ),
38 body: SafeArea(
39 child: Cue.onMount(
40 motion: .smooth(),
41 child: Padding(
42 padding: const EdgeInsets.symmetric(
43 horizontal: 24.0,
44 vertical: 16.0,
45 ),
46 child: Column(
47 crossAxisAlignment: CrossAxisAlignment.stretch,
48 children: [
49 Actor(
50 acts: [.fadeIn(), .slideY(from: 0.08)],
51 child: Text(
52 l10n.choose_theme_desc,
53 style: theme.textTheme.titleMedium?.copyWith(
54 color: theme.colorScheme.onSurface.withValues(alpha: 0.7),
55 ),
56 ),
57 ),
58 const SizedBox(height: 32),
59
60 Expanded(
61 child: Actor(
62 delay: 60.ms,
63 acts: [.fadeIn(), .slideY(from: 0.08)],
64 child: ListView(
65 children: [
66 _buildThemeCard(
67 type: AppThemeType.system,
68 title: l10n.theme_card_system,
69 subtitle: l10n.theme_card_system_sub,
70 iconWidget: HugeIcon(
71 icon: HugeIcons.strokeRoundedSettings01,
72 color: currentTheme == AppThemeType.system
73 ? theme.colorScheme.primary
74 : theme.colorScheme.onSurface,
75 ),
76 currentTheme: currentTheme,
77 context: context,
78 ),
79 const SizedBox(height: 16),
80 _buildThemeCard(
81 type: AppThemeType.light,
82 title: l10n.theme_card_light,
83 subtitle: l10n.theme_card_light_sub,
84 iconWidget: HugeIcon(
85 icon: HugeIcons.strokeRoundedSun01,
86 color: currentTheme == AppThemeType.light
87 ? theme.colorScheme.primary
88 : theme.colorScheme.onSurface,
89 ),
90 currentTheme: currentTheme,
91 context: context,
92 ),
93 const SizedBox(height: 16),
94 _buildThemeCard(
95 type: AppThemeType.dark,
96 title: l10n.theme_card_dark,
97 subtitle: l10n.theme_card_dark_sub,
98 iconWidget: HugeIcon(
99 icon: HugeIcons.strokeRoundedMoon02,
100 color: currentTheme == AppThemeType.dark
101 ? theme.colorScheme.primary
102 : theme.colorScheme.onSurface,
103 ),
104 currentTheme: currentTheme,
105 context: context,
106 ),
107 const SizedBox(height: 16),
108 _buildThemeCard(
109 type: AppThemeType.claude,
110 title: l10n.theme_card_claude,
111 subtitle: l10n.theme_card_claude_sub,
112 iconWidget: HugeIcon(
113 icon: HugeIcons.strokeRoundedPaintBrush02,
114 color: currentTheme == AppThemeType.claude
115 ? theme.colorScheme.primary
116 : theme.colorScheme.onSurface,
117 ),
118 currentTheme: currentTheme,
119 context: context,
120 ),
121 ],
122 ),
123 ),
124 ),
125
126 const SizedBox(height: 24),
127 Actor(
128 delay: 120.ms,
129 acts: [.fadeIn(), .slideY(from: 0.08)],
130 child: ShadButton(
131 width: double.infinity,
132 onPressed: () async {
133 if (context.mounted) {
134 context.push(AppRoutes.onboardingNotifications);
135 }
136 },
137 child: Text(
138 l10n.finish_setup,
139 style: const TextStyle(
140 fontSize: 16,
141 fontWeight: FontWeight.w600,
142 ),
143 ),
144 ),
145 ),
146 ],
147 ),
148 ),
149 ),
150 ),
151 );
152 }
153
154 Widget _buildThemeCard({
155 required AppThemeType type,
156 required String title,
157 required String subtitle,
158 required Widget iconWidget,
159 required AppThemeType currentTheme,
160 required BuildContext context,
161 }) {
162 final theme = Theme.of(context);
163 final isSelected = currentTheme == type;
164
165 return GestureDetector(
166 onTap: () {
167 ref.read(themeModeProvider.notifier).setThemeMode(type);
168 },
169 child: Container(
170 padding: const EdgeInsets.all(20),
171 decoration: BoxDecoration(
172 color: theme.colorScheme.surface,
173 borderRadius: BorderRadius.circular(16),
174 border: Border.all(
175 color: isSelected
176 ? theme.colorScheme.primary
177 : theme.colorScheme.outline.withValues(alpha: 0.3),
178 width: isSelected ? 2 : 1,
179 ),
180 ),
181 child: Row(
182 children: [
183 Container(
184 padding: const EdgeInsets.all(12),
185 decoration: BoxDecoration(
186 color: theme.scaffoldBackgroundColor,
187 borderRadius: BorderRadius.circular(12),
188 ),
189 child: iconWidget,
190 ),
191 const SizedBox(width: 16),
192 Expanded(
193 child: Column(
194 crossAxisAlignment: CrossAxisAlignment.start,
195 children: [
196 Text(
197 title,
198 style: theme.textTheme.titleMedium?.copyWith(
199 fontWeight: FontWeight.bold,
200 ),
201 ),
202 const SizedBox(height: 4),
203 Text(
204 subtitle,
205 style: theme.textTheme.labelMedium?.copyWith(
206 color: theme.colorScheme.onSurface.withValues(alpha: 0.5),
207 ),
208 ),
209 ],
210 ),
211 ),
212 if (isSelected)
213 HugeIcon(
214 icon: HugeIcons.strokeRoundedCheckmarkCircle01,
215 color: theme.colorScheme.primary,
216 ),
217 ],
218 ),
219 ),
220 );
221 }
222 }