kasy-cli 1.19.3 → 1.20.1
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.
- package/README.md +11 -3
- package/bin/kasy.js +1 -0
- package/lib/commands/new.js +87 -37
- package/lib/commands/run.js +14 -0
- package/lib/scaffold/backends/api/patch/lib/core/data/api/meta_ads_api.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/core/data/api/storage_api.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/core/data/entities/user_entity.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/features/authentication/api/authentication_api.dart +4 -5
- package/lib/scaffold/backends/api/patch/lib/features/feedbacks/api/feature_request_api.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/features/feedbacks/api/feature_vote_api.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/features/llm_chat/api/llm_chat_api.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/features/llm_chat/providers/llm_chat_notifier.dart +317 -0
- package/lib/scaffold/backends/api/patch/lib/features/notifications/api/device_api.dart +40 -1
- package/lib/scaffold/backends/api/patch/lib/features/notifications/api/entities/notifications_entity.dart +2 -0
- package/lib/scaffold/backends/api/patch/lib/features/onboarding/api/entities/user_info_entity.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/features/subscription/api/entities/subscription_entity.dart +1 -1
- package/lib/scaffold/backends/api/patch/lib/features/subscription/shared/maybeshow_premium.dart +0 -2
- package/lib/scaffold/backends/api/pubspec.yaml.tpl +2 -0
- package/lib/scaffold/backends/firebase/enable-auth-via-cli.js +11 -8
- package/lib/scaffold/backends/firebase/setup-from-scratch.js +91 -2
- package/lib/scaffold/backends/supabase/deploy.js +56 -3
- package/lib/scaffold/backends/supabase/patch/lib/core/data/api/storage_api.dart +5 -11
- package/lib/scaffold/backends/supabase/patch/lib/core/data/entities/user_entity.dart +2 -2
- package/lib/scaffold/backends/supabase/patch/lib/features/notifications/api/device_api.dart +31 -1
- package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/api/entities/user_info_entity.dart +1 -1
- package/lib/scaffold/backends/supabase/patch/lib/features/subscription/api/entities/subscription_entity.dart +1 -1
- package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +2 -0
- package/lib/scaffold/catalog.js +2 -2
- package/lib/scaffold/engine.js +5 -0
- package/lib/scaffold/generate.js +23 -3
- package/lib/scaffold/shared/generator-utils.js +303 -56
- package/lib/scaffold/shared/post-build.js +11 -0
- package/lib/utils/i18n/messages-en.js +6 -1
- package/lib/utils/i18n/messages-es.js +6 -1
- package/lib/utils/i18n/messages-pt.js +6 -1
- package/package.json +1 -1
- package/templates/firebase/android/app/src/main/res/drawable/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-v21/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-v21/background.png +0 -0
- package/templates/firebase/android/app/src/main/res/values-night-v31/styles.xml +1 -1
- package/templates/firebase/android/app/src/main/res/values-v31/styles.xml +1 -1
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png +0 -0
- package/templates/firebase/lib/components/kasy_date_picker.dart +14 -8
- package/templates/firebase/lib/components/kasy_sidebar_pro.dart +1150 -0
- package/templates/firebase/lib/components/kasy_tabs.dart +156 -43
- package/templates/firebase/lib/components/kasy_text_field.dart +37 -34
- package/templates/firebase/lib/core/bottom_menu/bottom_menu.dart +13 -82
- package/templates/firebase/lib/core/bottom_menu/bottom_router.dart +6 -102
- package/templates/firebase/lib/core/bottom_menu/kasy_bottom_bar_factory.dart +8 -1
- package/templates/firebase/lib/core/dev_inspector/dev_inspector.dart +433 -243
- package/templates/firebase/lib/core/dev_inspector/dev_inspector_service.dart +198 -83
- package/templates/firebase/lib/core/icons/kasy_icons.dart +1 -0
- package/templates/firebase/lib/core/states/user_state_notifier.dart +8 -10
- package/templates/firebase/lib/core/theme/colors.dart +6 -2
- package/templates/firebase/lib/core/web_device_preview/web_device_preview.dart +119 -19
- package/templates/firebase/lib/core/widgets/kasy_hover.dart +68 -27
- package/templates/firebase/lib/features/home/home_components_page.dart +11 -14
- package/templates/firebase/lib/features/home/home_components_preview_registry.dart +121 -66
- package/templates/firebase/lib/features/home/home_page.dart +7 -8
- package/templates/firebase/lib/features/settings/settings_page.dart +27 -146
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_bottom_sheet.dart +16 -3
- package/templates/firebase/lib/features/settings/ui/widgets/settings_tile.dart +22 -5
- package/templates/firebase/lib/i18n/en.i18n.json +3 -1
- package/templates/firebase/lib/i18n/es.i18n.json +3 -1
- package/templates/firebase/lib/i18n/pt.i18n.json +3 -1
- package/templates/firebase/lib/router.dart +60 -0
- package/templates/firebase/pubspec.yaml +6 -4
- package/templates/firebase/test/core/bottom_menu/detail_route_menu_test.dart +57 -0
- package/templates/firebase/web/index.html +7 -17
- package/lib/scaffold/backends/api/patch/lib/core/rating/widgets/review_popup.dart +0 -211
- package/lib/scaffold/backends/api/patch/lib/features/notifications/providers/models/notification.dart +0 -185
- package/lib/scaffold/backends/api/patch/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +0 -73
- package/lib/scaffold/backends/api/patch/lib/main.dart +0 -275
- package/lib/scaffold/backends/api/patch/lib/router.dart +0 -133
- package/lib/scaffold/backends/supabase/patch/lib/core/rating/widgets/review_popup.dart +0 -211
- package/lib/scaffold/backends/supabase/patch/lib/features/feedbacks/ui/component/add_feature_form.dart +0 -199
- package/lib/scaffold/backends/supabase/patch/lib/features/notifications/providers/models/notification.dart +0 -174
- package/lib/scaffold/backends/supabase/patch/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +0 -73
- package/lib/scaffold/backends/supabase/patch/lib/main.dart +0 -307
- package/lib/scaffold/backends/supabase/patch/lib/router.dart +0 -133
- package/templates/firebase/lib/firebase_options.dart +0 -75
|
@@ -7,18 +7,33 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
7
7
|
import 'package:kasy_kit/core/haptics/haptic_feedback_notifier.dart';
|
|
8
8
|
import 'package:kasy_kit/core/theme/theme.dart';
|
|
9
9
|
|
|
10
|
-
/// Universal hover/press wrapper — works on any widget shape.
|
|
10
|
+
/// Universal hover/press wrapper — works on any widget shape, any platform.
|
|
11
11
|
///
|
|
12
|
-
///
|
|
13
|
-
/// The pressed state
|
|
14
|
-
/// even on quick taps.
|
|
12
|
+
/// **Press feedback:** animated background fill + optional haptic. No Material
|
|
13
|
+
/// ripple. The pressed state persists for at least [_kMinPressDuration] so
|
|
14
|
+
/// it is always visible, even on quick taps.
|
|
15
15
|
///
|
|
16
|
-
///
|
|
16
|
+
/// **Hover feedback:** subtle overlay on pointer-capable devices (web,
|
|
17
|
+
/// desktop, trackpad). Has no visible effect on touch-only devices.
|
|
18
|
+
///
|
|
19
|
+
/// **Cursor:** [SystemMouseCursors.click] is set automatically on all
|
|
20
|
+
/// platforms that support pointer input.
|
|
21
|
+
///
|
|
22
|
+
/// ### Customising colours
|
|
23
|
+
///
|
|
24
|
+
/// - [pressColor] — tint base for the press overlay (defaults to
|
|
25
|
+
/// `onSurface`). Pass the surface accent colour (e.g. `primary`) to avoid
|
|
26
|
+
/// a mismatched gray cast on tinted backgrounds.
|
|
27
|
+
///
|
|
28
|
+
/// - [hoverColor] — exact solid background colour shown while the pointer is
|
|
29
|
+
/// over the widget. When set, **replaces** the default semi-transparent
|
|
30
|
+
/// overlay with a solid fill. Use this on navigation items where hover and
|
|
31
|
+
/// selected states must look identical (same bg, bolder text/icon in the
|
|
32
|
+
/// child widget signals selection). When null, falls back to a subtle
|
|
33
|
+
/// [pressColor]-tinted overlay.
|
|
17
34
|
///
|
|
18
35
|
/// Contrast with [KasyPressableDepth]: this component uses a background fill
|
|
19
|
-
/// while [KasyPressableDepth] uses a scale animation.
|
|
20
|
-
/// surfaces that already have their own shape (list rows, cards, menu items,
|
|
21
|
-
/// custom buttons); use [KasyPressableDepth] when you want a "push-in" feel.
|
|
36
|
+
/// while [KasyPressableDepth] uses a scale/depth animation.
|
|
22
37
|
class KasyHover extends ConsumerStatefulWidget {
|
|
23
38
|
const KasyHover({
|
|
24
39
|
super.key,
|
|
@@ -29,6 +44,7 @@ class KasyHover extends ConsumerStatefulWidget {
|
|
|
29
44
|
this.margin,
|
|
30
45
|
this.semanticLabel,
|
|
31
46
|
this.hapticEnabled = true,
|
|
47
|
+
this.hoverColor,
|
|
32
48
|
this.pressColor,
|
|
33
49
|
});
|
|
34
50
|
|
|
@@ -51,15 +67,24 @@ class KasyHover extends ConsumerStatefulWidget {
|
|
|
51
67
|
/// Haptic on tap (automatically ignored on web).
|
|
52
68
|
final bool hapticEnabled;
|
|
53
69
|
|
|
54
|
-
///
|
|
70
|
+
/// Exact background colour used while the pointer hovers over the widget.
|
|
71
|
+
///
|
|
72
|
+
/// When non-null, this solid colour replaces the default semi-transparent
|
|
73
|
+
/// overlay during hover. Ideal for navigation items: pass the same colour
|
|
74
|
+
/// used for the selected state so hover and active look identical — the
|
|
75
|
+
/// child widget is responsible for rendering the selected indicator (e.g.
|
|
76
|
+
/// bolder text, accent icon colour).
|
|
77
|
+
///
|
|
78
|
+
/// When null, a subtle [pressColor]-tinted overlay is used instead.
|
|
79
|
+
final Color? hoverColor;
|
|
80
|
+
|
|
81
|
+
/// Tint base for the press overlay.
|
|
55
82
|
///
|
|
56
83
|
/// When null, falls back to [ColorScheme.onSurface] — a neutral dark/light
|
|
57
84
|
/// overlay suited for plain surfaces.
|
|
58
85
|
///
|
|
59
|
-
/// Pass the surface's accent
|
|
60
|
-
/// background is already tinted so the feedback stays on-palette
|
|
61
|
-
/// a mismatched gray cast (e.g. a primary-tinted button should deepen with
|
|
62
|
-
/// primary, not darken with neutral gray).
|
|
86
|
+
/// Pass the surface's accent colour (e.g. [KasyColors.primary]) when the
|
|
87
|
+
/// background is already tinted so the feedback stays on-palette.
|
|
63
88
|
final Color? pressColor;
|
|
64
89
|
|
|
65
90
|
@override
|
|
@@ -85,7 +110,21 @@ class _KasyHoverState extends ConsumerState<KasyHover> {
|
|
|
85
110
|
final bool isDark = Theme.of(context).brightness == Brightness.dark;
|
|
86
111
|
final Color base = widget.pressColor ?? context.colors.onSurface;
|
|
87
112
|
if (_pressed) return base.withValues(alpha: isDark ? 0.04 : 0.10);
|
|
88
|
-
if (_hovered)
|
|
113
|
+
if (_hovered) {
|
|
114
|
+
// Solid hover colour — used for nav items where hover must exactly
|
|
115
|
+
// match the selected background.
|
|
116
|
+
if (widget.hoverColor != null) return widget.hoverColor!;
|
|
117
|
+
return base.withValues(alpha: isDark ? 0.02 : 0.06);
|
|
118
|
+
}
|
|
119
|
+
// IMPORTANT: when hoverColor is set, the resting state must be the same
|
|
120
|
+
// colour at alpha=0 (not Colors.transparent = 0x00000000 black).
|
|
121
|
+
// AnimatedContainer lerps between the two values — lerping from black-
|
|
122
|
+
// transparent to a light solid colour passes through dark intermediates,
|
|
123
|
+
// causing a visible flash. Lerping from the same hue at alpha=0 to
|
|
124
|
+
// alpha=1 only changes opacity → no dark artefact.
|
|
125
|
+
if (widget.hoverColor != null) {
|
|
126
|
+
return widget.hoverColor!.withValues(alpha: 0);
|
|
127
|
+
}
|
|
89
128
|
return Colors.transparent;
|
|
90
129
|
}
|
|
91
130
|
|
|
@@ -136,19 +175,21 @@ class _KasyHoverState extends ConsumerState<KasyHover> {
|
|
|
136
175
|
child: content,
|
|
137
176
|
);
|
|
138
177
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
178
|
+
// MouseRegion is active on all platforms:
|
|
179
|
+
// - Web / desktop / trackpad: fires onEnter/onExit for real hover effects.
|
|
180
|
+
// - Touch-only (iOS/Android): pointer events never fire, zero overhead.
|
|
181
|
+
// The click cursor is also set universally so desktop feels native.
|
|
182
|
+
interactive = MouseRegion(
|
|
183
|
+
cursor: SystemMouseCursors.click,
|
|
184
|
+
onEnter: (_) => setState(() => _hovered = true),
|
|
185
|
+
onExit: (_) {
|
|
186
|
+
setState(() {
|
|
187
|
+
_hovered = false;
|
|
188
|
+
_pressed = false;
|
|
189
|
+
});
|
|
190
|
+
},
|
|
191
|
+
child: interactive,
|
|
192
|
+
);
|
|
152
193
|
|
|
153
194
|
if (widget.semanticLabel != null) {
|
|
154
195
|
interactive = Semantics(
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import 'package:flutter/material.dart';
|
|
2
|
+
import 'package:go_router/go_router.dart';
|
|
2
3
|
import 'package:kasy_kit/components/kasy_app_bar.dart';
|
|
3
|
-
import 'package:kasy_kit/core/bottom_menu/bart_inner_paths.dart';
|
|
4
4
|
import 'package:kasy_kit/core/haptics/kasy_haptics.dart';
|
|
5
5
|
import 'package:kasy_kit/core/theme/theme.dart';
|
|
6
6
|
import 'package:kasy_kit/core/widgets/kasy_hover.dart';
|
|
7
7
|
import 'package:kasy_kit/core/widgets/kasy_scroll_behavior.dart';
|
|
8
|
-
import 'package:kasy_kit/features/home/home_components_preview_page.dart';
|
|
9
8
|
import 'package:kasy_kit/features/home/home_components_preview_registry.dart';
|
|
10
9
|
|
|
11
10
|
/// UI-kit catalog, reachable from the home dashboard.
|
|
@@ -66,22 +65,17 @@ class HomeComponentsPage extends StatelessWidget {
|
|
|
66
65
|
onTap: () {
|
|
67
66
|
KasyHaptics.selection(context);
|
|
68
67
|
if (row.name == 'Design System') {
|
|
69
|
-
|
|
70
|
-
bartInnerPath('home', 'design-system'),
|
|
71
|
-
);
|
|
68
|
+
context.push('/design-system');
|
|
72
69
|
return;
|
|
73
70
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
71
|
+
// Skip rows without a preview yet (registry returns
|
|
72
|
+
// null); otherwise open /components/:name, which the
|
|
73
|
+
// router resolves back to this component's preview.
|
|
74
|
+
if (getComponentPreviewDefinition(row.name) == null) {
|
|
77
75
|
return;
|
|
78
76
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
arguments: HomeComponentPreviewRouteExtra(
|
|
82
|
-
title: definition.title,
|
|
83
|
-
variants: definition.variants,
|
|
84
|
-
),
|
|
77
|
+
context.push(
|
|
78
|
+
'/components/${Uri.encodeComponent(row.name)}',
|
|
85
79
|
);
|
|
86
80
|
},
|
|
87
81
|
child: Padding(
|
|
@@ -189,6 +183,7 @@ const Set<String> _kReadyComponents = <String>{
|
|
|
189
183
|
'TextArea',
|
|
190
184
|
'TextField',
|
|
191
185
|
'Sidebar',
|
|
186
|
+
'Sidebar Pro',
|
|
192
187
|
'Skeleton',
|
|
193
188
|
'SwipeAction',
|
|
194
189
|
'TextFieldOTP',
|
|
@@ -211,6 +206,7 @@ const Set<String> _kWebReadyComponents = <String>{
|
|
|
211
206
|
'Dialog',
|
|
212
207
|
'Hover',
|
|
213
208
|
'Sidebar',
|
|
209
|
+
'Sidebar Pro',
|
|
214
210
|
'Skeleton',
|
|
215
211
|
'SwipeAction',
|
|
216
212
|
'TextArea',
|
|
@@ -241,6 +237,7 @@ const List<_CatalogRow> _kCatalog = [
|
|
|
241
237
|
_CatalogRow('Dialog'),
|
|
242
238
|
_CatalogRow('Hover'),
|
|
243
239
|
_CatalogRow('Sidebar'),
|
|
240
|
+
_CatalogRow('Sidebar Pro'),
|
|
244
241
|
_CatalogRow('Skeleton'),
|
|
245
242
|
_CatalogRow('SwipeAction'),
|
|
246
243
|
_CatalogRow('TextArea'),
|
|
@@ -3,6 +3,7 @@ import 'package:flutter/foundation.dart' show kIsWeb;
|
|
|
3
3
|
import 'package:flutter/material.dart';
|
|
4
4
|
import 'package:flutter/services.dart';
|
|
5
5
|
import 'package:kasy_kit/components/components.dart';
|
|
6
|
+
import 'package:kasy_kit/components/kasy_sidebar_pro.dart';
|
|
6
7
|
import 'package:kasy_kit/core/haptics/kasy_haptics.dart';
|
|
7
8
|
import 'package:kasy_kit/core/theme/theme.dart';
|
|
8
9
|
import 'package:kasy_kit/core/widgets/kasy_hover.dart';
|
|
@@ -244,10 +245,6 @@ ComponentPreviewDefinition? getComponentPreviewDefinition(
|
|
|
244
245
|
label: 'Range selection',
|
|
245
246
|
builder: _buildDatePickerRange,
|
|
246
247
|
),
|
|
247
|
-
ComponentPreviewVariant(
|
|
248
|
-
label: 'Multi-month',
|
|
249
|
-
builder: _buildDatePickerMultiMonth,
|
|
250
|
-
),
|
|
251
248
|
ComponentPreviewVariant(
|
|
252
249
|
label: 'Date formats',
|
|
253
250
|
builder: _buildDatePickerFormats,
|
|
@@ -260,6 +257,13 @@ ComponentPreviewDefinition? getComponentPreviewDefinition(
|
|
|
260
257
|
label: 'Field states',
|
|
261
258
|
builder: _buildDatePickerFieldStates,
|
|
262
259
|
),
|
|
260
|
+
// Multi-month last — it's a desktop-only opt-in layout and the
|
|
261
|
+
// preview itself falls back to an explainer card on mobile, so
|
|
262
|
+
// keeping it at the end avoids burying the main use cases.
|
|
263
|
+
ComponentPreviewVariant(
|
|
264
|
+
label: 'Multi-month',
|
|
265
|
+
builder: _buildDatePickerMultiMonth,
|
|
266
|
+
),
|
|
263
267
|
],
|
|
264
268
|
);
|
|
265
269
|
case 'Dialog':
|
|
@@ -513,6 +517,20 @@ ComponentPreviewDefinition? getComponentPreviewDefinition(
|
|
|
513
517
|
),
|
|
514
518
|
],
|
|
515
519
|
);
|
|
520
|
+
case 'Sidebar Pro':
|
|
521
|
+
return const ComponentPreviewDefinition(
|
|
522
|
+
title: 'Sidebar Pro',
|
|
523
|
+
variants: [
|
|
524
|
+
ComponentPreviewVariant(
|
|
525
|
+
label: 'Expanded',
|
|
526
|
+
builder: _buildSidebarProExpanded,
|
|
527
|
+
),
|
|
528
|
+
ComponentPreviewVariant(
|
|
529
|
+
label: 'Collapsed',
|
|
530
|
+
builder: _buildSidebarProCollapsed,
|
|
531
|
+
),
|
|
532
|
+
],
|
|
533
|
+
);
|
|
516
534
|
case 'Skeleton':
|
|
517
535
|
return const ComponentPreviewDefinition(
|
|
518
536
|
title: 'Skeleton',
|
|
@@ -685,6 +703,91 @@ class _SidebarPreviewState extends State<_SidebarPreview> {
|
|
|
685
703
|
}
|
|
686
704
|
|
|
687
705
|
|
|
706
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
707
|
+
// Sidebar Pro — interactive preview
|
|
708
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
709
|
+
|
|
710
|
+
Widget _buildSidebarProExpanded(BuildContext context) =>
|
|
711
|
+
const _SidebarProPreview(initiallyCollapsed: false);
|
|
712
|
+
|
|
713
|
+
Widget _buildSidebarProCollapsed(BuildContext context) =>
|
|
714
|
+
const _SidebarProPreview(initiallyCollapsed: true);
|
|
715
|
+
|
|
716
|
+
class _SidebarProPreview extends StatelessWidget {
|
|
717
|
+
const _SidebarProPreview({required this.initiallyCollapsed});
|
|
718
|
+
|
|
719
|
+
final bool initiallyCollapsed;
|
|
720
|
+
|
|
721
|
+
void _open(BuildContext context, {required bool fromEnd}) {
|
|
722
|
+
showGeneralDialog<void>(
|
|
723
|
+
context: context,
|
|
724
|
+
barrierDismissible: true,
|
|
725
|
+
barrierLabel: 'Dismiss',
|
|
726
|
+
barrierColor: Colors.black.withValues(alpha: 0.42),
|
|
727
|
+
transitionDuration: const Duration(milliseconds: 290),
|
|
728
|
+
pageBuilder: (ctx, anim, secAnim) => Align(
|
|
729
|
+
alignment: fromEnd ? Alignment.centerRight : Alignment.centerLeft,
|
|
730
|
+
child: SizedBox(
|
|
731
|
+
height: double.infinity,
|
|
732
|
+
child: KasySidebarPro(
|
|
733
|
+
initiallyCollapsed: initiallyCollapsed,
|
|
734
|
+
side: fromEnd
|
|
735
|
+
? KasySidebarProSide.right
|
|
736
|
+
: KasySidebarProSide.left,
|
|
737
|
+
),
|
|
738
|
+
),
|
|
739
|
+
),
|
|
740
|
+
transitionBuilder: (ctx, anim, secAnim, child) {
|
|
741
|
+
final curved = CurvedAnimation(
|
|
742
|
+
parent: anim,
|
|
743
|
+
curve: Curves.easeOutCubic,
|
|
744
|
+
);
|
|
745
|
+
return SlideTransition(
|
|
746
|
+
position: Tween<Offset>(
|
|
747
|
+
begin: Offset(fromEnd ? 1.0 : -1.0, 0),
|
|
748
|
+
end: Offset.zero,
|
|
749
|
+
).animate(curved),
|
|
750
|
+
child: FadeTransition(
|
|
751
|
+
opacity: Tween<double>(begin: 0.85, end: 1.0).animate(curved),
|
|
752
|
+
child: child,
|
|
753
|
+
),
|
|
754
|
+
);
|
|
755
|
+
},
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
@override
|
|
760
|
+
Widget build(BuildContext context) {
|
|
761
|
+
return Column(
|
|
762
|
+
mainAxisSize: MainAxisSize.min,
|
|
763
|
+
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
764
|
+
children: [
|
|
765
|
+
Padding(
|
|
766
|
+
padding: const EdgeInsets.symmetric(horizontal: KasySpacing.md),
|
|
767
|
+
child: _previewIntrinsicLaunch(
|
|
768
|
+
KasyButton(
|
|
769
|
+
label: 'Open Left',
|
|
770
|
+
variant: KasyButtonVariant.soft,
|
|
771
|
+
onPressed: () => _open(context, fromEnd: false),
|
|
772
|
+
),
|
|
773
|
+
),
|
|
774
|
+
),
|
|
775
|
+
const SizedBox(height: KasySpacing.sm),
|
|
776
|
+
Padding(
|
|
777
|
+
padding: const EdgeInsets.symmetric(horizontal: KasySpacing.md),
|
|
778
|
+
child: _previewIntrinsicLaunch(
|
|
779
|
+
KasyButton(
|
|
780
|
+
label: 'Open Right',
|
|
781
|
+
variant: KasyButtonVariant.soft,
|
|
782
|
+
onPressed: () => _open(context, fromEnd: true),
|
|
783
|
+
),
|
|
784
|
+
),
|
|
785
|
+
),
|
|
786
|
+
],
|
|
787
|
+
);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
688
791
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
689
792
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
690
793
|
// Tabs — interactive demos
|
|
@@ -2627,29 +2730,18 @@ Widget _buildTextFieldCharacterLimit(BuildContext context) {
|
|
|
2627
2730
|
}
|
|
2628
2731
|
|
|
2629
2732
|
Widget _buildTextFieldVariantsVariant(BuildContext context) {
|
|
2630
|
-
|
|
2631
|
-
context.textTheme.labelSmall?.copyWith(
|
|
2632
|
-
color: context.colors.muted,
|
|
2633
|
-
letterSpacing: 1.2,
|
|
2634
|
-
fontWeight: FontWeight.w700,
|
|
2635
|
-
) ??
|
|
2636
|
-
const TextStyle();
|
|
2637
|
-
return Column(
|
|
2733
|
+
return const Column(
|
|
2638
2734
|
mainAxisSize: MainAxisSize.min,
|
|
2639
2735
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
2640
2736
|
children: [
|
|
2641
|
-
|
|
2642
|
-
const SizedBox(height: KasySpacing.sm),
|
|
2643
|
-
const KasyTextField(
|
|
2737
|
+
KasyTextField(
|
|
2644
2738
|
label: 'Primary Variant',
|
|
2645
2739
|
hint: 'Primary style input',
|
|
2646
2740
|
description: 'Default variant with primary styling',
|
|
2647
2741
|
contentType: KasyTextFieldContentType.email,
|
|
2648
2742
|
),
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
const SizedBox(height: KasySpacing.sm),
|
|
2652
|
-
const KasyTextField(
|
|
2743
|
+
SizedBox(height: KasySpacing.lg),
|
|
2744
|
+
KasyTextField(
|
|
2653
2745
|
label: 'Secondary Variant',
|
|
2654
2746
|
hint: 'Secondary style input',
|
|
2655
2747
|
description: 'Secondary variant for surfaces',
|
|
@@ -2661,39 +2753,26 @@ Widget _buildTextFieldVariantsVariant(BuildContext context) {
|
|
|
2661
2753
|
}
|
|
2662
2754
|
|
|
2663
2755
|
Widget _buildTextFieldStates(BuildContext context) {
|
|
2664
|
-
|
|
2665
|
-
context.textTheme.labelSmall?.copyWith(
|
|
2666
|
-
color: context.colors.muted,
|
|
2667
|
-
letterSpacing: 1.2,
|
|
2668
|
-
fontWeight: FontWeight.w700,
|
|
2669
|
-
) ??
|
|
2670
|
-
const TextStyle();
|
|
2671
|
-
return Column(
|
|
2756
|
+
return const Column(
|
|
2672
2757
|
mainAxisSize: MainAxisSize.min,
|
|
2673
2758
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
2674
2759
|
children: [
|
|
2675
|
-
|
|
2676
|
-
const SizedBox(height: KasySpacing.sm),
|
|
2677
|
-
const KasyTextField(
|
|
2760
|
+
KasyTextField(
|
|
2678
2761
|
label: 'Default State',
|
|
2679
2762
|
hint: 'Enter your email',
|
|
2680
2763
|
description: 'Normal input state',
|
|
2681
2764
|
contentType: KasyTextFieldContentType.email,
|
|
2682
2765
|
),
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
const SizedBox(height: KasySpacing.sm),
|
|
2686
|
-
const KasyTextField(
|
|
2766
|
+
SizedBox(height: KasySpacing.lg),
|
|
2767
|
+
KasyTextField(
|
|
2687
2768
|
label: 'Disabled State',
|
|
2688
2769
|
hint: 'Read only value',
|
|
2689
2770
|
description: 'TextField is disabled and cannot be edited',
|
|
2690
2771
|
enabled: false,
|
|
2691
2772
|
contentType: KasyTextFieldContentType.email,
|
|
2692
2773
|
),
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
const SizedBox(height: KasySpacing.sm),
|
|
2696
|
-
const KasyTextField(
|
|
2774
|
+
SizedBox(height: KasySpacing.lg),
|
|
2775
|
+
KasyTextField(
|
|
2697
2776
|
label: 'Invalid State',
|
|
2698
2777
|
hint: 'Enter your email',
|
|
2699
2778
|
errorText: 'Please enter a valid email address',
|
|
@@ -2721,29 +2800,18 @@ Widget _buildTextAreaWithLabelDescription(BuildContext context) {
|
|
|
2721
2800
|
}
|
|
2722
2801
|
|
|
2723
2802
|
Widget _buildTextAreaStates(BuildContext context) {
|
|
2724
|
-
|
|
2725
|
-
context.textTheme.labelSmall?.copyWith(
|
|
2726
|
-
color: context.colors.muted,
|
|
2727
|
-
letterSpacing: 1.2,
|
|
2728
|
-
fontWeight: FontWeight.w700,
|
|
2729
|
-
) ??
|
|
2730
|
-
const TextStyle();
|
|
2731
|
-
return Column(
|
|
2803
|
+
return const Column(
|
|
2732
2804
|
mainAxisSize: MainAxisSize.min,
|
|
2733
2805
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
2734
2806
|
children: [
|
|
2735
|
-
|
|
2736
|
-
const SizedBox(height: KasySpacing.sm),
|
|
2737
|
-
const KasyTextArea(
|
|
2807
|
+
KasyTextArea(
|
|
2738
2808
|
label: 'Disabled State',
|
|
2739
2809
|
hint: 'Read only value',
|
|
2740
2810
|
description: 'Text area is disabled and cannot be edited',
|
|
2741
2811
|
enabled: false,
|
|
2742
2812
|
),
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
const SizedBox(height: KasySpacing.sm),
|
|
2746
|
-
const KasyTextArea(
|
|
2813
|
+
SizedBox(height: KasySpacing.lg),
|
|
2814
|
+
KasyTextArea(
|
|
2747
2815
|
label: 'Invalid State',
|
|
2748
2816
|
hint: 'Enter your message',
|
|
2749
2817
|
isInvalid: true,
|
|
@@ -4860,19 +4928,10 @@ class _TextFieldWithIconContentState extends State<_TextFieldWithIconContent> {
|
|
|
4860
4928
|
|
|
4861
4929
|
@override
|
|
4862
4930
|
Widget build(BuildContext context) {
|
|
4863
|
-
final TextStyle labelStyle =
|
|
4864
|
-
context.textTheme.labelSmall?.copyWith(
|
|
4865
|
-
color: context.colors.muted,
|
|
4866
|
-
letterSpacing: 1.2,
|
|
4867
|
-
fontWeight: FontWeight.w700,
|
|
4868
|
-
) ??
|
|
4869
|
-
const TextStyle();
|
|
4870
4931
|
return Column(
|
|
4871
4932
|
mainAxisSize: MainAxisSize.min,
|
|
4872
4933
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
4873
4934
|
children: [
|
|
4874
|
-
Text('PHONE', style: labelStyle),
|
|
4875
|
-
const SizedBox(height: KasySpacing.sm),
|
|
4876
4935
|
const KasyTextField(
|
|
4877
4936
|
label: 'Phone',
|
|
4878
4937
|
hint: '5550000000',
|
|
@@ -4880,16 +4939,12 @@ class _TextFieldWithIconContentState extends State<_TextFieldWithIconContent> {
|
|
|
4880
4939
|
contentType: KasyTextFieldContentType.phone,
|
|
4881
4940
|
),
|
|
4882
4941
|
const SizedBox(height: KasySpacing.lg),
|
|
4883
|
-
Text('PASSWORD', style: labelStyle),
|
|
4884
|
-
const SizedBox(height: KasySpacing.sm),
|
|
4885
4942
|
const KasyTextField(
|
|
4886
4943
|
label: 'Password',
|
|
4887
4944
|
hint: 'Enter your password',
|
|
4888
4945
|
contentType: KasyTextFieldContentType.password,
|
|
4889
4946
|
),
|
|
4890
4947
|
const SizedBox(height: KasySpacing.lg),
|
|
4891
|
-
Text('MESSAGE', style: labelStyle),
|
|
4892
|
-
const SizedBox(height: KasySpacing.sm),
|
|
4893
4948
|
KasyTextField(
|
|
4894
4949
|
controller: _messageController,
|
|
4895
4950
|
label: 'Message',
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import 'package:flutter/material.dart';
|
|
2
2
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
3
|
+
import 'package:go_router/go_router.dart';
|
|
3
4
|
import 'package:kasy_kit/components/kasy_app_bar.dart';
|
|
4
|
-
import 'package:kasy_kit/core/bottom_menu/bart_inner_paths.dart';
|
|
5
5
|
import 'package:kasy_kit/core/haptics/kasy_haptics.dart';
|
|
6
6
|
import 'package:kasy_kit/core/states/components/maybe_ask_rating.dart';
|
|
7
7
|
import 'package:kasy_kit/core/states/components/maybe_show_update_bottom_sheet.dart';
|
|
@@ -13,7 +13,7 @@ import 'package:kasy_kit/features/notifications/shared/att_permission.dart';
|
|
|
13
13
|
import 'package:kasy_kit/features/subscription/shared/maybeshow_premium.dart';
|
|
14
14
|
import 'package:kasy_kit/i18n/translations.g.dart';
|
|
15
15
|
|
|
16
|
-
/// Home hub with dashboard cards
|
|
16
|
+
/// Home hub with dashboard cards linking to the Features and Components screens.
|
|
17
17
|
class HomePage extends ConsumerWidget {
|
|
18
18
|
const HomePage({super.key});
|
|
19
19
|
|
|
@@ -59,9 +59,10 @@ class HomePage extends ConsumerWidget {
|
|
|
59
59
|
gradient: _featuresGradient(context, isDark),
|
|
60
60
|
onOpen: () {
|
|
61
61
|
KasyHaptics.medium(context);
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
// Top-level go_router route: opens full-screen on the
|
|
63
|
+
// root navigator (above the BottomMenu, no bottom bar)
|
|
64
|
+
// and pops back to home with the menu intact.
|
|
65
|
+
context.push('/features');
|
|
65
66
|
},
|
|
66
67
|
);
|
|
67
68
|
final componentsCard = _DashboardGradientCard(
|
|
@@ -74,9 +75,7 @@ class HomePage extends ConsumerWidget {
|
|
|
74
75
|
gradient: _componentsGradient(context, isDark),
|
|
75
76
|
onOpen: () {
|
|
76
77
|
KasyHaptics.medium(context);
|
|
77
|
-
|
|
78
|
-
bartInnerPath('home', 'components'),
|
|
79
|
-
);
|
|
78
|
+
context.push('/components');
|
|
80
79
|
},
|
|
81
80
|
);
|
|
82
81
|
if (isWide) {
|