kasy-cli 1.24.0 → 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/lib/commands/new.js +53 -3
  2. package/lib/utils/flutter-install.js +26 -1
  3. package/lib/utils/i18n/messages-en.js +3 -0
  4. package/lib/utils/i18n/messages-es.js +3 -0
  5. package/lib/utils/i18n/messages-pt.js +3 -0
  6. package/package.json +1 -1
  7. package/templates/firebase/lib/components/kasy_app_bar.dart +12 -6
  8. package/templates/firebase/lib/components/kasy_avatar.dart +17 -10
  9. package/templates/firebase/lib/components/kasy_card.dart +4 -0
  10. package/templates/firebase/lib/components/kasy_checkbox.dart +24 -15
  11. package/templates/firebase/lib/components/kasy_date_picker.dart +27 -20
  12. package/templates/firebase/lib/components/kasy_otp_verification_bottom_sheet.dart +19 -14
  13. package/templates/firebase/lib/components/kasy_sidebar.dart +6 -0
  14. package/templates/firebase/lib/components/kasy_tabs.dart +76 -65
  15. package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +103 -3
  16. package/templates/firebase/lib/core/bottom_menu/web_content_wrapper.dart +45 -2
  17. package/templates/firebase/lib/core/widgets/kasy_pressable_depth.dart +37 -1
  18. package/templates/firebase/lib/features/ai_chat/ui/widgets/ai_conversation_tile.dart +9 -4
  19. package/templates/firebase/lib/features/authentication/ui/recover_password_page.dart +1 -0
  20. package/templates/firebase/lib/features/authentication/ui/signin_page.dart +10 -5
  21. package/templates/firebase/lib/features/authentication/ui/signup_page.dart +1 -0
  22. package/templates/firebase/lib/features/feedbacks/ui/widgets/add_feature_button.dart +1 -0
  23. package/templates/firebase/lib/features/feedbacks/ui/widgets/feature_card.dart +2 -0
  24. package/templates/firebase/lib/features/home/home_feed.dart +21 -5
  25. package/templates/firebase/lib/features/home/home_image_grid.dart +83 -58
  26. package/templates/firebase/lib/features/local_reminders/ui/reminder_page.dart +124 -101
  27. package/templates/firebase/lib/features/notifications/ui/components/notification_tile.dart +1 -0
  28. package/templates/firebase/lib/features/notifications/ui/widgets/notification_tile.dart +85 -85
  29. package/templates/firebase/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +67 -62
  30. package/templates/firebase/lib/features/settings/settings_page.dart +17 -1
  31. package/templates/firebase/lib/features/settings/ui/components/avatar_component.dart +37 -23
  32. package/templates/firebase/lib/features/settings/ui/components/language_switcher.dart +63 -48
  33. package/templates/firebase/lib/features/settings/ui/widgets/admin_card.dart +14 -6
  34. package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +2 -0
  35. package/templates/firebase/lib/features/subscriptions/ui/widgets/premium_banner.dart +9 -3
  36. package/templates/firebase/lib/features/subscriptions/ui/widgets/premium_close_button.dart +24 -16
  37. package/templates/firebase/lib/features/subscriptions/ui/widgets/selectable_col.dart +29 -22
  38. package/templates/firebase/lib/features/subscriptions/ui/widgets/selectable_row.dart +19 -12
  39. package/templates/firebase/lib/i18n/en.i18n.json +2 -1
  40. package/templates/firebase/lib/i18n/es.i18n.json +2 -1
  41. package/templates/firebase/lib/i18n/pt.i18n.json +2 -1
@@ -1,5 +1,6 @@
1
1
  import 'package:flutter/material.dart';
2
2
  import 'package:kasy_kit/core/theme/theme.dart';
3
+ import 'package:kasy_kit/core/widgets/kasy_focus_ring.dart';
3
4
 
4
5
  typedef OnSelectRow = void Function(int index, bool selected);
5
6
 
@@ -51,38 +52,50 @@ class _OnboardingSelectableRowGroupState
51
52
  return ListView.separated(
52
53
  physics: widget.physics,
53
54
  shrinkWrap: true,
54
- separatorBuilder: (context, index) => const SizedBox(height: KasySpacing.sm),
55
+ separatorBuilder: (context, index) =>
56
+ const SizedBox(height: KasySpacing.sm),
55
57
  itemCount: widget.options.length,
56
58
  itemBuilder: (context, index) {
57
- final hasOnSelectInfo = widget.onSelectInfoWidget != null &&
59
+ final hasOnSelectInfo =
60
+ widget.onSelectInfoWidget != null &&
58
61
  widget.onSelectInfoWidget!.length > index &&
59
62
  widget.onSelectInfoWidget![index] != null;
63
+ void selectRow() {
64
+ if (_selectedIndex == index) {
65
+ return;
66
+ }
67
+ setState(() {
68
+ _selectedIndex = index;
69
+ });
70
+ widget.onSelect?.call(index, true);
71
+ }
72
+
60
73
  return Column(
61
74
  crossAxisAlignment: CrossAxisAlignment.stretch,
62
75
  children: [
63
- Material(
64
- color: Colors.transparent,
76
+ KasyFocusRing(
77
+ onActivate: selectRow,
65
78
  borderRadius: KasyRadius.lgBorderRadius,
66
- child: InkWell(
79
+ child: Material(
80
+ color: Colors.transparent,
67
81
  borderRadius: KasyRadius.lgBorderRadius,
68
- onTap: () {
69
- if (_selectedIndex == index) {
70
- return;
71
- }
72
- setState(() {
73
- _selectedIndex = index;
74
- });
75
- widget.onSelect?.call(index, true);
76
- },
77
- child: switch (hasOnSelectInfo && _selectedIndex == index) {
78
- false => widget.options[index],
79
- true => widget.options[index],
80
- },
82
+ child: InkWell(
83
+ canRequestFocus: false,
84
+ borderRadius: KasyRadius.lgBorderRadius,
85
+ onTap: selectRow,
86
+ child: switch (hasOnSelectInfo && _selectedIndex == index) {
87
+ false => widget.options[index],
88
+ true => widget.options[index],
89
+ },
90
+ ),
81
91
  ),
82
92
  ),
83
93
  if (hasOnSelectInfo && _selectedIndex == index)
84
94
  Padding(
85
- padding: const EdgeInsets.only(top: KasySpacing.sm, bottom: KasySpacing.md),
95
+ padding: const EdgeInsets.only(
96
+ top: KasySpacing.sm,
97
+ bottom: KasySpacing.md,
98
+ ),
86
99
  child: widget.onSelectInfoWidget![index],
87
100
  ),
88
101
  ],
@@ -126,26 +139,33 @@ class _OnboardingMultiSelectableRowGroupState
126
139
  runSpacing: KasySpacing.sm,
127
140
  children: widget.options.map((option) {
128
141
  final index = widget.options.indexOf(option);
129
- return Material(
130
- color: Colors.transparent,
142
+ void toggleSelection() {
143
+ if (_selectedIndex.contains(index)) {
144
+ _selectedIndex.remove(index);
145
+ widget.onSelect?.call(index, false);
146
+ } else {
147
+ _selectedIndex.add(index);
148
+ widget.onSelect?.call(index, true);
149
+ }
150
+ setState(() {});
151
+ }
152
+
153
+ return KasyFocusRing(
154
+ onActivate: toggleSelection,
131
155
  borderRadius: KasyRadius.mdBorderRadius,
132
- child: InkWell(
133
- splashColor: context.colors.primary.withValues(alpha:.1),
134
- onTap: () {
135
- if (_selectedIndex.contains(index)) {
136
- _selectedIndex.remove(index);
137
- widget.onSelect?.call(index, false);
138
- } else {
139
- _selectedIndex.add(index);
140
- widget.onSelect?.call(index, true);
141
- }
142
- setState(() {});
143
- },
144
- child: SelectableRowTile(
145
- title: option.title,
146
- subtitle: option.subtitle,
147
- selected: _selectedIndex.contains(index),
148
- emoj: option.emoj,
156
+ child: Material(
157
+ color: Colors.transparent,
158
+ borderRadius: KasyRadius.mdBorderRadius,
159
+ child: InkWell(
160
+ canRequestFocus: false,
161
+ splashColor: context.colors.primary.withValues(alpha: .1),
162
+ onTap: toggleSelection,
163
+ child: SelectableRowTile(
164
+ title: option.title,
165
+ subtitle: option.subtitle,
166
+ selected: _selectedIndex.contains(index),
167
+ emoj: option.emoj,
168
+ ),
149
169
  ),
150
170
  ),
151
171
  );
@@ -205,15 +225,11 @@ class _SelectableRowTileState extends State<SelectableRowTile>
205
225
  _opacityAnimation = Tween(
206
226
  begin: 0.0,
207
227
  end: 1.0,
208
- ).animate(
209
- CurvedAnimation(parent: _controller, curve: Curves.ease),
210
- );
228
+ ).animate(CurvedAnimation(parent: _controller, curve: Curves.ease));
211
229
  _iconSizeAnimation = Tween(
212
230
  begin: 0.0,
213
231
  end: 1.0,
214
- ).animate(
215
- CurvedAnimation(parent: _controller, curve: Curves.elasticOut),
216
- );
232
+ ).animate(CurvedAnimation(parent: _controller, curve: Curves.elasticOut));
217
233
  if (widget.selected) {
218
234
  _controller.forward();
219
235
  }
@@ -267,9 +283,7 @@ class _SelectableRowTileState extends State<SelectableRowTile>
267
283
  padding: const EdgeInsets.only(right: KasySpacing.md),
268
284
  child: Text(
269
285
  widget.emoj!,
270
- style: context.textTheme.titleMedium?.copyWith(
271
- fontSize: 28,
272
- ),
286
+ style: context.textTheme.titleMedium?.copyWith(fontSize: 28),
273
287
  ),
274
288
  ),
275
289
  ),
@@ -282,8 +296,9 @@ class _SelectableRowTileState extends State<SelectableRowTile>
282
296
  widget.title!,
283
297
  style: context.textTheme.bodyLarge?.copyWith(
284
298
  color: colors.onSurface,
285
- fontWeight:
286
- widget.selected ? FontWeight.w700 : FontWeight.w600,
299
+ fontWeight: widget.selected
300
+ ? FontWeight.w700
301
+ : FontWeight.w600,
287
302
  ),
288
303
  ),
289
304
  if (widget.subtitle != null)
@@ -301,10 +316,7 @@ class _SelectableRowTileState extends State<SelectableRowTile>
301
316
  ),
302
317
  const SizedBox(width: KasySpacing.sm),
303
318
  if (!widget.selected)
304
- Flexible(
305
- flex: 0,
306
- child: RoundRadioBox.unselected(context),
307
- ),
319
+ Flexible(flex: 0, child: RoundRadioBox.unselected(context)),
308
320
  if (widget.selected)
309
321
  Flexible(
310
322
  flex: 0,
@@ -371,10 +383,7 @@ class RoundRadioBox extends StatelessWidget {
371
383
  decoration: BoxDecoration(
372
384
  color: bgColor,
373
385
  shape: BoxShape.circle,
374
- border: Border.all(
375
- color: borderColor,
376
- width: 2,
377
- ),
386
+ border: Border.all(color: borderColor, width: 2),
378
387
  ),
379
388
  child: Center(
380
389
  child: icon != null
@@ -382,11 +391,7 @@ class RoundRadioBox extends StatelessWidget {
382
391
  opacity: iconOpacity,
383
392
  child: Transform.scale(
384
393
  scale: iconSize,
385
- child: Icon(
386
- icon,
387
- color: context.colors.onPrimary,
388
- size: 15,
389
- ),
394
+ child: Icon(icon, color: context.colors.onPrimary, size: 15),
390
395
  ),
391
396
  )
392
397
  : const SizedBox.shrink(),
@@ -12,6 +12,7 @@ import 'package:kasy_kit/core/security/biometric_ui_bundle.dart';
12
12
  import 'package:kasy_kit/core/states/logout_action.dart';
13
13
  import 'package:kasy_kit/core/states/user_state_notifier.dart';
14
14
  import 'package:kasy_kit/core/theme/theme.dart';
15
+ import 'package:kasy_kit/core/widgets/kasy_focus_ring.dart';
15
16
  import 'package:kasy_kit/core/widgets/kasy_hover.dart';
16
17
  import 'package:kasy_kit/features/settings/ui/components/avatar_component.dart';
17
18
  import 'package:kasy_kit/features/settings/ui/components/delete_user_component.dart';
@@ -329,7 +330,18 @@ class ProfileTile extends StatelessWidget {
329
330
  child: ClipRRect(
330
331
  borderRadius: KasyRadius.smBorderRadius,
331
332
  child: onTap != null
332
- ? InkWell(onTap: onTap, child: content)
333
+ ? KasyFocusRing(
334
+ onActivate: onTap,
335
+ borderRadius: KasyRadius.smBorderRadius,
336
+ gapColor: context.colors.surface,
337
+ // The InkWell keeps its tap ripple but yields focus to the ring,
338
+ // so the keyboard outline matches every other Kasy control.
339
+ child: InkWell(
340
+ canRequestFocus: false,
341
+ onTap: onTap,
342
+ child: content,
343
+ ),
344
+ )
333
345
  : content,
334
346
  ),
335
347
  );
@@ -547,6 +559,8 @@ class _NavTile extends StatelessWidget {
547
559
  borderRadius: KasyRadius.smBorderRadius,
548
560
  hoverColor: c.surfaceNeutralSoft,
549
561
  pressColor: c.onSurface,
562
+ focusable: true,
563
+ focusGapColor: c.surface,
550
564
  onTap: onTap,
551
565
  child: Container(
552
566
  padding: const EdgeInsets.symmetric(
@@ -770,6 +784,8 @@ class _LogoutRow extends StatelessWidget {
770
784
  return KasyHover(
771
785
  borderRadius: KasyRadius.smBorderRadius,
772
786
  pressColor: context.colors.error,
787
+ focusable: true,
788
+ focusGapColor: context.colors.surface,
773
789
  onTap: onTap,
774
790
  child: Padding(
775
791
  padding: const EdgeInsets.symmetric(vertical: KasySpacing.sm),
@@ -11,6 +11,7 @@ import 'package:kasy_kit/core/data/repositories/user_repository.dart';
11
11
  import 'package:kasy_kit/core/states/models/user_state.dart';
12
12
  import 'package:kasy_kit/core/states/user_state_notifier.dart';
13
13
  import 'package:kasy_kit/core/theme/theme.dart';
14
+ import 'package:kasy_kit/core/widgets/kasy_focus_ring.dart';
14
15
  import 'package:kasy_kit/features/settings/ui/widgets/avatar_utils.dart';
15
16
  import 'package:kasy_kit/features/settings/ui/widgets/round_progress.dart';
16
17
  import 'package:kasy_kit/i18n/translations.g.dart';
@@ -57,12 +58,18 @@ class _EditableUserAvatarState extends ConsumerState<EditableUserAvatar> {
57
58
  final displayAvatarPath = _optimisticAvatarUrl ?? avatarPath;
58
59
  final double d = widget.diameter ?? KasyAvatarSize.medium.diameter;
59
60
 
60
- return GestureDetector(
61
- behavior: HitTestBehavior.opaque,
62
- onTap: userId != null && !_isUploading
63
- ? () => _onTapAvatar(context, userState, avatarPath)
64
- : null,
65
- child: Padding(
61
+ final VoidCallback? onTapAvatar = userId != null && !_isUploading
62
+ ? () => _onTapAvatar(context, userState, avatarPath)
63
+ : null;
64
+
65
+ return KasyFocusRing(
66
+ enabled: onTapAvatar != null,
67
+ onActivate: onTapAvatar,
68
+ borderRadius: BorderRadius.circular(999),
69
+ child: GestureDetector(
70
+ behavior: HitTestBehavior.opaque,
71
+ onTap: onTapAvatar,
72
+ child: Padding(
66
73
  padding: const EdgeInsets.all(6),
67
74
  child: Stack(
68
75
  clipBehavior: Clip.none,
@@ -103,6 +110,7 @@ class _EditableUserAvatarState extends ConsumerState<EditableUserAvatar> {
103
110
  ),
104
111
  ],
105
112
  ),
113
+ ),
106
114
  ),
107
115
  );
108
116
  }
@@ -313,24 +321,30 @@ class _BottomSheetTile extends StatelessWidget {
313
321
  @override
314
322
  Widget build(BuildContext context) {
315
323
  final Color fg = color ?? context.colors.onSurface;
316
- return InkWell(
317
- onTap: onTap,
318
- child: Padding(
319
- padding: const EdgeInsets.symmetric(
320
- horizontal: KasySpacing.md,
321
- vertical: KasySpacing.smd,
322
- ),
323
- child: Row(
324
- children: [
325
- if (icon != null) ...[
326
- Icon(icon, size: 22, color: fg),
327
- const SizedBox(width: KasySpacing.sm),
324
+ return KasyFocusRing(
325
+ onActivate: onTap,
326
+ borderRadius: BorderRadius.circular(KasyRadius.sm),
327
+ gapColor: context.colors.surface,
328
+ child: InkWell(
329
+ canRequestFocus: false,
330
+ onTap: onTap,
331
+ child: Padding(
332
+ padding: const EdgeInsets.symmetric(
333
+ horizontal: KasySpacing.md,
334
+ vertical: KasySpacing.smd,
335
+ ),
336
+ child: Row(
337
+ children: [
338
+ if (icon != null) ...[
339
+ Icon(icon, size: 22, color: fg),
340
+ const SizedBox(width: KasySpacing.sm),
341
+ ],
342
+ Text(
343
+ label,
344
+ style: context.textTheme.bodyLarge?.copyWith(color: fg),
345
+ ),
328
346
  ],
329
- Text(
330
- label,
331
- style: context.textTheme.bodyLarge?.copyWith(color: fg),
332
- ),
333
- ],
347
+ ),
334
348
  ),
335
349
  ),
336
350
  );
@@ -5,6 +5,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
5
5
  import 'package:kasy_kit/core/home_widgets/home_widget_mywidget_service.dart';
6
6
  import 'package:kasy_kit/core/shared_preferences/shared_preferences.dart';
7
7
  import 'package:kasy_kit/core/theme/theme.dart';
8
+ import 'package:kasy_kit/core/widgets/kasy_focus_ring.dart';
8
9
  import 'package:kasy_kit/features/settings/ui/widgets/settings_tile.dart';
9
10
  import 'package:kasy_kit/i18n/app_locale_display.dart';
10
11
  import 'package:kasy_kit/i18n/translations.g.dart';
@@ -17,31 +18,40 @@ class LanguageSwitcher extends ConsumerWidget {
17
18
  @override
18
19
  Widget build(BuildContext context, WidgetRef ref) {
19
20
  final AppLocale current = TranslationProvider.of(context).locale;
20
- return InkWell(
21
- onTap: () => _showLanguagePicker(context, ref, current),
22
- child: Padding(
23
- padding: const EdgeInsets.symmetric(vertical: KasySpacing.sm),
24
- child: Row(
25
- children: <Widget>[
26
- Icon(KasyIcons.language, size: 21, color: context.colors.onSurface),
27
- const SizedBox(width: KasySpacing.sm),
28
- Expanded(
29
- child: Text(
30
- context.t.settings.language_title,
31
- style: context.textTheme.titleMedium?.copyWith(
32
- color: context.colors.onSurface,
21
+ return KasyFocusRing(
22
+ onActivate: () => _showLanguagePicker(context, ref, current),
23
+ borderRadius: KasyRadius.smBorderRadius,
24
+ child: InkWell(
25
+ canRequestFocus: false,
26
+ onTap: () => _showLanguagePicker(context, ref, current),
27
+ child: Padding(
28
+ padding: const EdgeInsets.symmetric(vertical: KasySpacing.sm),
29
+ child: Row(
30
+ children: <Widget>[
31
+ Icon(
32
+ KasyIcons.language,
33
+ size: 21,
34
+ color: context.colors.onSurface,
35
+ ),
36
+ const SizedBox(width: KasySpacing.sm),
37
+ Expanded(
38
+ child: Text(
39
+ context.t.settings.language_title,
40
+ style: context.textTheme.titleMedium?.copyWith(
41
+ color: context.colors.onSurface,
42
+ ),
33
43
  ),
34
44
  ),
35
- ),
36
- Text(
37
- current.nativeName,
38
- style: context.textTheme.bodyMedium?.copyWith(
39
- color: context.colors.muted,
45
+ Text(
46
+ current.nativeName,
47
+ style: context.textTheme.bodyMedium?.copyWith(
48
+ color: context.colors.muted,
49
+ ),
40
50
  ),
41
- ),
42
- const SizedBox(width: KasySpacing.xs),
43
- const SettingsListChevron(),
44
- ],
51
+ const SizedBox(width: KasySpacing.xs),
52
+ const SettingsListChevron(),
53
+ ],
54
+ ),
45
55
  ),
46
56
  ),
47
57
  );
@@ -147,36 +157,41 @@ class _LocaleOptionTile extends StatelessWidget {
147
157
  final Color primary = context.colors.primary;
148
158
  final Color fg = isSelected ? primary : context.colors.onSurface;
149
159
 
150
- return InkWell(
151
- onTap: onTap,
160
+ return KasyFocusRing(
161
+ onActivate: onTap,
152
162
  borderRadius: BorderRadius.circular(KasyRadius.sm),
153
- child: Padding(
154
- padding: const EdgeInsets.symmetric(
155
- horizontal: KasySpacing.xs,
156
- vertical: KasySpacing.smd,
157
- ),
158
- child: Row(
159
- children: <Widget>[
160
- Expanded(
161
- child: Text(
162
- locale.nativeName,
163
- style: context.textTheme.bodyLarge?.copyWith(
164
- color: fg,
165
- fontWeight:
166
- isSelected ? FontWeight.w600 : FontWeight.w400,
163
+ child: InkWell(
164
+ canRequestFocus: false,
165
+ onTap: onTap,
166
+ borderRadius: BorderRadius.circular(KasyRadius.sm),
167
+ child: Padding(
168
+ padding: const EdgeInsets.symmetric(
169
+ horizontal: KasySpacing.xs,
170
+ vertical: KasySpacing.smd,
171
+ ),
172
+ child: Row(
173
+ children: <Widget>[
174
+ Expanded(
175
+ child: Text(
176
+ locale.nativeName,
177
+ style: context.textTheme.bodyLarge?.copyWith(
178
+ color: fg,
179
+ fontWeight:
180
+ isSelected ? FontWeight.w600 : FontWeight.w400,
181
+ ),
167
182
  ),
168
183
  ),
169
- ),
170
- if (isSelected)
171
- Container(
172
- width: 10,
173
- height: 10,
174
- decoration: BoxDecoration(
175
- color: primary,
176
- shape: BoxShape.circle,
184
+ if (isSelected)
185
+ Container(
186
+ width: 10,
187
+ height: 10,
188
+ decoration: BoxDecoration(
189
+ color: primary,
190
+ shape: BoxShape.circle,
191
+ ),
177
192
  ),
178
- ),
179
- ],
193
+ ],
194
+ ),
180
195
  ),
181
196
  ),
182
197
  );
@@ -1,6 +1,7 @@
1
1
  import 'package:flutter/material.dart';
2
2
  import 'package:kasy_kit/core/haptics/kasy_haptics.dart';
3
3
  import 'package:kasy_kit/core/theme/theme.dart';
4
+ import 'package:kasy_kit/core/widgets/kasy_focus_ring.dart';
4
5
 
5
6
  /// Here is just a simple content card
6
7
  class AdminPanelCard extends StatelessWidget {
@@ -21,12 +22,18 @@ class AdminPanelCard extends StatelessWidget {
21
22
 
22
23
  @override
23
24
  Widget build(BuildContext context) {
24
- return InkWell(
25
- onTap: () {
26
- KasyHaptics.medium(context);
27
- onTap.call();
28
- },
29
- child: Card(
25
+ void handleActivate() {
26
+ KasyHaptics.medium(context);
27
+ onTap.call();
28
+ }
29
+
30
+ return KasyFocusRing(
31
+ onActivate: handleActivate,
32
+ borderRadius: BorderRadius.circular(KasyRadius.sm),
33
+ child: InkWell(
34
+ canRequestFocus: false,
35
+ onTap: handleActivate,
36
+ child: Card(
30
37
  color: backgroundColor ?? context.colors.primary.withValues(alpha: .15),
31
38
  margin: EdgeInsets.zero,
32
39
  elevation: 0,
@@ -53,6 +60,7 @@ class AdminPanelCard extends StatelessWidget {
53
60
  ],
54
61
  ),
55
62
  ),
63
+ ),
56
64
  ),
57
65
  );
58
66
  }
@@ -157,6 +157,8 @@ class SettingsTile extends StatelessWidget {
157
157
  onTap: onTap,
158
158
  hoverEnabled: false,
159
159
  pressEnabled: false,
160
+ focusable: true,
161
+ borderRadius: KasyRadius.smBorderRadius,
160
162
  semanticLabel: title,
161
163
  padding: const EdgeInsets.symmetric(vertical: KasySpacing.sm),
162
164
  child: Row(
@@ -1,6 +1,7 @@
1
1
  import 'package:flutter/material.dart';
2
2
  import 'package:google_fonts/google_fonts.dart';
3
3
  import 'package:kasy_kit/core/theme/theme.dart';
4
+ import 'package:kasy_kit/core/widgets/kasy_focus_ring.dart';
4
5
 
5
6
  typedef PremiumWideBannerOnTap = void Function();
6
7
 
@@ -32,9 +33,13 @@ class PremiumWideBanner extends StatelessWidget {
32
33
  color: bgColor,
33
34
  // border: Border.all(color: borderColor),
34
35
  ),
35
- child: InkWell(
36
- onTap: () => onTap(),
37
- child: Row(
36
+ child: KasyFocusRing(
37
+ onActivate: () => onTap(),
38
+ borderRadius: BorderRadius.zero,
39
+ child: InkWell(
40
+ canRequestFocus: false,
41
+ onTap: () => onTap(),
42
+ child: Row(
38
43
  children: [
39
44
  Image.asset(
40
45
  imagePath,
@@ -72,6 +77,7 @@ class PremiumWideBanner extends StatelessWidget {
72
77
  const SizedBox(width: KasySpacing.lg),
73
78
  ],
74
79
  ),
80
+ ),
75
81
  ),
76
82
  ),
77
83
  );
@@ -1,6 +1,7 @@
1
1
  import 'package:flutter/material.dart';
2
2
  import 'package:flutter_riverpod/flutter_riverpod.dart';
3
3
  import 'package:kasy_kit/core/theme/theme.dart';
4
+ import 'package:kasy_kit/core/widgets/kasy_focus_ring.dart';
4
5
 
5
6
  class AppCloseButtonComponent extends ConsumerWidget {
6
7
  final ValueNotifier<bool> showCloseBtn;
@@ -34,24 +35,31 @@ class AppCloseButton extends StatelessWidget {
34
35
 
35
36
  @override
36
37
  Widget build(BuildContext context) {
38
+ final VoidCallback? onClose = onTap;
37
39
  return Material(
38
40
  color: Colors.transparent,
39
- child: InkWell(
40
- onTapUp: (_) {
41
- onTap?.call();
42
- },
43
- child: Ink(
44
- width: 32,
45
- height: 32,
46
- decoration: BoxDecoration(
47
- shape: BoxShape.circle,
48
- color: context.colors.onBackground.withValues(alpha: 0.6),
49
- ),
50
- child: Center(
51
- child: Icon(
52
- KasyIcons.close,
53
- color: context.colors.background,
54
- size: 21,
41
+ child: KasyFocusRing(
42
+ enabled: onClose != null,
43
+ onActivate: onClose,
44
+ borderRadius: BorderRadius.circular(999),
45
+ child: InkWell(
46
+ canRequestFocus: false,
47
+ onTapUp: (_) {
48
+ onClose?.call();
49
+ },
50
+ child: Ink(
51
+ width: 32,
52
+ height: 32,
53
+ decoration: BoxDecoration(
54
+ shape: BoxShape.circle,
55
+ color: context.colors.onBackground.withValues(alpha: 0.6),
56
+ ),
57
+ child: Center(
58
+ child: Icon(
59
+ KasyIcons.close,
60
+ color: context.colors.background,
61
+ size: 21,
62
+ ),
55
63
  ),
56
64
  ),
57
65
  ),