kasy-cli 1.13.0 → 1.15.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.
- package/bin/kasy.js +140 -12
- package/lib/commands/add.js +2 -2
- package/lib/commands/codemagic.js +11 -4
- package/lib/commands/deploy.js +3 -3
- package/lib/commands/favicon.js +115 -0
- package/lib/commands/icon.js +143 -0
- package/lib/commands/ios.js +28 -7
- package/lib/commands/new.js +8 -20
- package/lib/commands/remove.js +1 -1
- package/lib/commands/reset.js +385 -0
- package/lib/commands/run.js +24 -17
- package/lib/commands/splash.js +14 -4
- package/lib/commands/update.js +1 -1
- package/lib/scaffold/backends/api/patch/README.md +1 -1
- package/lib/scaffold/backends/api/patch/android/app/src/main/AndroidManifest.xml +1 -1
- package/lib/scaffold/backends/api/patch/lib/features/notifications/api/device_api.dart +53 -0
- package/lib/scaffold/backends/api/pubspec.yaml.tpl +11 -1
- package/lib/scaffold/backends/firebase/tokens.js +2 -2
- package/lib/scaffold/backends/supabase/edge-functions/send-push-notification/index.ts +8 -2
- package/lib/scaffold/backends/supabase/migrations/20240101000011_dedupe_device_tokens.sql +34 -0
- package/lib/scaffold/backends/supabase/patch/README.md +1 -1
- package/lib/scaffold/backends/supabase/patch/android/app/src/main/AndroidManifest.xml +1 -1
- package/lib/scaffold/backends/supabase/patch/lib/features/notifications/api/device_api.dart +43 -0
- package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +11 -1
- package/lib/utils/apple-release.js +115 -16
- package/lib/utils/checks.js +45 -107
- package/lib/utils/debug.js +75 -0
- package/lib/utils/flutter-run.js +173 -0
- package/lib/utils/friendly-error.js +91 -0
- package/lib/utils/i18n/messages-en.js +970 -0
- package/lib/utils/i18n/messages-es.js +968 -0
- package/lib/utils/i18n/messages-pt.js +968 -0
- package/lib/utils/i18n.js +21 -2483
- package/lib/utils/mobile-identity.js +35 -0
- package/lib/utils/png-padding.js +120 -0
- package/lib/utils/ui.js +114 -0
- package/package.json +8 -4
- package/templates/firebase/README.en.md +1 -1
- package/templates/firebase/README.es.md +1 -1
- package/templates/firebase/README.md +1 -1
- package/templates/firebase/android/app/build.gradle.kts +10 -1
- package/templates/firebase/android/app/src/main/AndroidManifest.xml +1 -1
- package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/MainActivity.kt +25 -1
- package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/MyWidget.kt +161 -11
- package/templates/firebase/android/app/src/main/res/drawable/widget_add_button.xml +15 -0
- package/templates/firebase/android/app/src/main/res/drawable/widget_gradient_bg.xml +9 -0
- package/templates/firebase/android/app/src/main/res/drawable/widget_gradient_inner.xml +12 -0
- package/templates/firebase/android/app/src/main/res/drawable/widget_plan_pill_bg.xml +5 -0
- package/templates/firebase/android/app/src/main/res/drawable/widget_preview_image.xml +17 -0
- package/templates/firebase/android/app/src/main/res/drawable/widget_pro_pill_bg.xml +5 -0
- package/templates/firebase/android/app/src/main/res/drawable-hdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-hdpi/ic_launcher_background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-mdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-mdpi/ic_launcher_background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-hdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-mdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xhdpi/ic_launcher_background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/ic_launcher_background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/android12splash.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_background.png +0 -0
- package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png +0 -0
- package/templates/firebase/android/app/src/main/res/layout/widget_loading.xml +8 -0
- package/templates/firebase/android/app/src/main/res/layout/widget_preview.xml +53 -0
- package/templates/firebase/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +9 -0
- package/templates/firebase/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
- package/templates/firebase/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
- package/templates/firebase/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
- package/templates/firebase/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
- package/templates/firebase/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
- package/templates/firebase/android/app/src/main/res/xml/mywidget_info.xml +9 -3
- package/templates/firebase/assets/images/favicon.png +0 -0
- package/templates/firebase/assets/images/icon.png +0 -0
- package/templates/firebase/assets/images/icon_android.png +0 -0
- package/templates/firebase/assets/images/icon_foreground_empty.png +0 -0
- package/templates/firebase/assets/images/splash_logo_dark_android12.png +0 -0
- package/templates/firebase/assets/images/splash_logo_light_android12.png +0 -0
- package/templates/firebase/firestore.indexes.json +10 -0
- package/templates/firebase/functions/src/core/data/entities/user_device_entity.ts +3 -0
- package/templates/firebase/functions/src/core/data/repositories/user_device_repository.ts +17 -1
- package/templates/firebase/functions/src/index.ts +1 -0
- package/templates/firebase/functions/src/notifications/device_triggers.ts +58 -0
- package/templates/firebase/ios/HomeWidgetExtension/MyWidget.swift +116 -33
- package/templates/firebase/ios/Runner/AppDelegate.swift +17 -1
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png +0 -0
- package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png +0 -0
- package/templates/firebase/ios/Runner/Info.plist +2 -2
- package/templates/firebase/ios/Runner/es.lproj/InfoPlist.strings +1 -1
- package/templates/firebase/ios/Runner/pt-BR.lproj/InfoPlist.strings +1 -1
- package/templates/firebase/ios/Runner/pt.lproj/InfoPlist.strings +1 -1
- package/templates/firebase/lib/components/components.dart +1 -0
- package/templates/firebase/lib/components/kasy_avatar.dart +88 -57
- package/templates/firebase/lib/components/kasy_avatar_presets.dart +116 -74
- package/templates/firebase/lib/components/kasy_button.dart +8 -0
- package/templates/firebase/lib/components/kasy_tabs.dart +431 -0
- package/templates/firebase/lib/core/bottom_menu/kasy_bottom_bar_factory.dart +18 -0
- package/templates/firebase/lib/core/home_widgets/home_widget_mywidget_service.dart +73 -19
- package/templates/firebase/lib/core/home_widgets/home_widget_service.dart +22 -8
- package/templates/firebase/lib/core/shared_preferences/shared_preferences.dart +16 -4
- package/templates/firebase/lib/core/states/components/maybeshow_component.dart +4 -8
- package/templates/firebase/lib/core/states/user_state_notifier.dart +13 -1
- package/templates/firebase/lib/features/home/home_components_page.dart +1 -1
- package/templates/firebase/lib/features/home/home_components_preview_registry.dart +316 -93
- package/templates/firebase/lib/features/home/home_page.dart +0 -6
- package/templates/firebase/lib/features/notifications/api/device_api.dart +57 -0
- package/templates/firebase/lib/features/notifications/providers/models/notification.dart +11 -1
- package/templates/firebase/lib/features/notifications/repositories/device_repository.dart +9 -0
- package/templates/firebase/lib/features/notifications/repositories/notifications_repository.dart +1 -4
- package/templates/firebase/lib/features/notifications/shared/att_permission.dart +28 -8
- package/templates/firebase/lib/features/notifications/ui/notifications_page.dart +16 -1
- package/templates/firebase/lib/features/notifications/ui/widgets/empty_notifications.dart +44 -11
- package/templates/firebase/lib/features/settings/ui/components/admin/admin_bottom_sheet.dart +31 -29
- package/templates/firebase/lib/features/settings/ui/components/language_switcher.dart +21 -5
- package/templates/firebase/lib/i18n/en.i18n.json +4 -1
- package/templates/firebase/lib/i18n/es.i18n.json +4 -1
- package/templates/firebase/lib/i18n/pt.i18n.json +4 -1
- package/templates/firebase/pubspec.yaml +10 -3
- package/templates/firebase/test/features/notifications/data/device_api_fake.dart +9 -0
- package/templates/firebase/web/favicon.png +0 -0
- package/templates/firebase/web/icons/Icon-192.png +0 -0
- package/templates/firebase/web/icons/Icon-512.png +0 -0
- package/templates/firebase/web/icons/Icon-maskable-192.png +0 -0
- package/templates/firebase/web/icons/Icon-maskable-512.png +0 -0
- package/templates/firebase/web/index.html +9 -0
- package/templates/firebase/web/manifest.json +3 -3
- package/templates/firebase/assets/images/app_icon.png +0 -0
- package/templates/firebase/assets/images/onboarding/img1.jpg +0 -0
- package/templates/firebase/assets/images/onboarding/onboarding.png +0 -0
- package/templates/firebase/lib/core/states/components/maybe_ask_biometric_setup.dart +0 -88
- package/templates/firebase/lib/features/notifications/shared/notification_permission_bottom_sheet.dart +0 -144
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
<string>pt-BR</string>
|
|
19
19
|
</array>
|
|
20
20
|
<key>CFBundleDisplayName</key>
|
|
21
|
-
<string>
|
|
21
|
+
<string>Kasy App</string>
|
|
22
22
|
<key>CFBundleExecutable</key>
|
|
23
23
|
<string>$(EXECUTABLE_NAME)</string>
|
|
24
24
|
<key>CFBundleIdentifier</key>
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
<key>FacebookClientToken</key>
|
|
58
58
|
<string>00000000000000000000000000000000</string>
|
|
59
59
|
<key>FacebookDisplayName</key>
|
|
60
|
-
<string>
|
|
60
|
+
<string>Kasy App</string>
|
|
61
61
|
<key>LSRequiresIPhoneOS</key>
|
|
62
62
|
<true/>
|
|
63
63
|
<key>NSPhotoLibraryAddUsageDescription</key>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* Localized Info.plist strings — es resources for system picker/camera UI (iOS). */
|
|
2
|
-
"CFBundleDisplayName" = "
|
|
2
|
+
"CFBundleDisplayName" = "Kasy App";
|
|
3
3
|
"NSCameraUsageDescription" = "Necesitamos la cámara para tomar fotos y vídeos.";
|
|
4
4
|
"NSPhotoLibraryAddUsageDescription" = "Necesitamos acceso para guardar fotos y vídeos en la galería.";
|
|
5
5
|
"NSPhotoLibraryUsageDescription" = "Necesitamos acceso para mostrar tus fotos recientes y abrir la galería desde la cámara.";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* Localized Info.plist strings — presence of pt-BR resources lets iOS use Portuguese for system UI (e.g. image picker). */
|
|
2
|
-
"CFBundleDisplayName" = "
|
|
2
|
+
"CFBundleDisplayName" = "Kasy App";
|
|
3
3
|
"NSCameraUsageDescription" = "Precisamos da câmera para tirar fotos e vídeos.";
|
|
4
4
|
"NSPhotoLibraryAddUsageDescription" = "Precisamos de acesso para salvar fotos e vídeos na galeria.";
|
|
5
5
|
"NSPhotoLibraryUsageDescription" = "Precisamos de acesso para mostrar suas fotos recentes e abrir a galeria a partir da câmera.";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* Localized Info.plist strings — presence of pt-BR resources lets iOS use Portuguese for system UI (e.g. image picker). */
|
|
2
|
-
"CFBundleDisplayName" = "
|
|
2
|
+
"CFBundleDisplayName" = "Kasy App";
|
|
3
3
|
"NSCameraUsageDescription" = "Precisamos da câmera para tirar fotos e vídeos.";
|
|
4
4
|
"NSPhotoLibraryAddUsageDescription" = "Precisamos de acesso para salvar fotos e vídeos na galeria.";
|
|
5
5
|
"NSPhotoLibraryUsageDescription" = "Precisamos de acesso para mostrar suas fotos recentes e abrir a galeria a partir da câmera.";
|
|
@@ -22,6 +22,7 @@ export 'kasy_dialog.dart';
|
|
|
22
22
|
export 'kasy_otp_verification_bottom_sheet.dart';
|
|
23
23
|
export 'kasy_skeleton.dart';
|
|
24
24
|
export 'kasy_swipe_action.dart';
|
|
25
|
+
export 'kasy_tabs.dart';
|
|
25
26
|
export 'kasy_text_area.dart';
|
|
26
27
|
export 'kasy_text_field.dart';
|
|
27
28
|
export 'kasy_text_field_otp.dart';
|
|
@@ -28,7 +28,7 @@ enum KasyAvatarStatus { online }
|
|
|
28
28
|
|
|
29
29
|
/// Design-system avatar: image, initials, icon, gradient, or custom child.
|
|
30
30
|
///
|
|
31
|
-
/// Decorative
|
|
31
|
+
/// Decorative linear fills: optional [KasyAvatarGradients] (barrel presets).
|
|
32
32
|
class KasyAvatar extends StatelessWidget {
|
|
33
33
|
final KasyAvatarSize size;
|
|
34
34
|
|
|
@@ -60,7 +60,7 @@ class KasyAvatar extends StatelessWidget {
|
|
|
60
60
|
this.tone = KasyAvatarTone.blue,
|
|
61
61
|
this.fallbackSurface = KasyAvatarFallbackSurface.default_,
|
|
62
62
|
this.shape = KasyAvatarShape.circle,
|
|
63
|
-
this.showShadow =
|
|
63
|
+
this.showShadow = false,
|
|
64
64
|
this.showStoryRing = false,
|
|
65
65
|
this.storyRingGradient,
|
|
66
66
|
this.status,
|
|
@@ -244,7 +244,7 @@ class KasyAvatar extends StatelessWidget {
|
|
|
244
244
|
return ColoredBox(
|
|
245
245
|
color: bg,
|
|
246
246
|
child: Center(
|
|
247
|
-
child: Icon(glyph, size: _d * 0.
|
|
247
|
+
child: Icon(glyph, size: _d * 0.444, color: fg),
|
|
248
248
|
),
|
|
249
249
|
);
|
|
250
250
|
}
|
|
@@ -253,11 +253,12 @@ class KasyAvatar extends StatelessWidget {
|
|
|
253
253
|
child: Center(
|
|
254
254
|
child: Text(
|
|
255
255
|
label ?? '',
|
|
256
|
-
style:
|
|
256
|
+
style: TextStyle(
|
|
257
257
|
color: fg,
|
|
258
|
-
fontWeight: FontWeight.
|
|
259
|
-
fontSize: _d * 0.
|
|
258
|
+
fontWeight: FontWeight.w500,
|
|
259
|
+
fontSize: _d * 0.333,
|
|
260
260
|
height: 1,
|
|
261
|
+
leadingDistribution: TextLeadingDistribution.even,
|
|
261
262
|
),
|
|
262
263
|
),
|
|
263
264
|
),
|
|
@@ -320,7 +321,10 @@ class KasyAvatar extends StatelessWidget {
|
|
|
320
321
|
}
|
|
321
322
|
}
|
|
322
323
|
|
|
323
|
-
/// Horizontal stacked avatars with [KasyColors.surface] separation ring
|
|
324
|
+
/// Horizontal stacked avatars with [KasyColors.surface] separation ring,
|
|
325
|
+
/// optional +N counter, and optional add-action button.
|
|
326
|
+
///
|
|
327
|
+
/// Set [onAdd] to show the "+" button at the end (12 px gap, per Figma spec).
|
|
324
328
|
class KasyAvatarGroup extends StatelessWidget {
|
|
325
329
|
final List<Widget> avatars;
|
|
326
330
|
final int maxVisible;
|
|
@@ -329,14 +333,18 @@ class KasyAvatarGroup extends StatelessWidget {
|
|
|
329
333
|
final bool showShadow;
|
|
330
334
|
final double itemDiameter;
|
|
331
335
|
|
|
336
|
+
/// When non-null a circular "+" button is shown after the group.
|
|
337
|
+
final VoidCallback? onAdd;
|
|
338
|
+
|
|
332
339
|
const KasyAvatarGroup({
|
|
333
340
|
super.key,
|
|
334
341
|
required this.avatars,
|
|
335
342
|
this.maxVisible = 4,
|
|
336
343
|
this.extraCount = 0,
|
|
337
|
-
this.overlapFactor = 0.
|
|
338
|
-
this.showShadow =
|
|
344
|
+
this.overlapFactor = 0.28,
|
|
345
|
+
this.showShadow = false,
|
|
339
346
|
this.itemDiameter = 48,
|
|
347
|
+
this.onAdd,
|
|
340
348
|
});
|
|
341
349
|
|
|
342
350
|
@override
|
|
@@ -346,9 +354,10 @@ class KasyAvatarGroup extends StatelessWidget {
|
|
|
346
354
|
final double step = d * (1 - overlapFactor);
|
|
347
355
|
final int showCounter = extraCount > 0 ? 1 : 0;
|
|
348
356
|
final int totalSlots = n + showCounter;
|
|
349
|
-
final double
|
|
350
|
-
|
|
351
|
-
|
|
357
|
+
final double stackW = totalSlots <= 0 ? 0 : step * (totalSlots - 1) + d + 4;
|
|
358
|
+
|
|
359
|
+
final Widget stack = SizedBox(
|
|
360
|
+
width: stackW,
|
|
352
361
|
height: d + 4,
|
|
353
362
|
child: Stack(
|
|
354
363
|
clipBehavior: Clip.none,
|
|
@@ -371,9 +380,14 @@ class KasyAvatarGroup extends StatelessWidget {
|
|
|
371
380
|
child: Center(
|
|
372
381
|
child: Text(
|
|
373
382
|
'+$extraCount',
|
|
374
|
-
style:
|
|
375
|
-
color: context.
|
|
376
|
-
|
|
383
|
+
style: TextStyle(
|
|
384
|
+
color: context.isDark
|
|
385
|
+
? const Color(0xFFFCFCFC)
|
|
386
|
+
: const Color(0xFF18181B),
|
|
387
|
+
fontWeight: FontWeight.w500,
|
|
388
|
+
fontSize: 12,
|
|
389
|
+
height: 16 / 12,
|
|
390
|
+
leadingDistribution: TextLeadingDistribution.even,
|
|
377
391
|
),
|
|
378
392
|
),
|
|
379
393
|
),
|
|
@@ -384,22 +398,50 @@ class KasyAvatarGroup extends StatelessWidget {
|
|
|
384
398
|
],
|
|
385
399
|
),
|
|
386
400
|
);
|
|
401
|
+
|
|
402
|
+
if (onAdd == null) return stack;
|
|
403
|
+
|
|
404
|
+
// Add-action button: circle with "+" icon, 12 px gap (Figma layout_EJ08D4).
|
|
405
|
+
final Widget addBtn = _KasyAvatarPressable(
|
|
406
|
+
onPressed: onAdd!,
|
|
407
|
+
semanticLabel: 'Add member',
|
|
408
|
+
minSide: d < 44 ? 44.0 : d,
|
|
409
|
+
child: Container(
|
|
410
|
+
width: d,
|
|
411
|
+
height: d,
|
|
412
|
+
decoration: BoxDecoration(
|
|
413
|
+
color: context.colors.avatarFallbackFill,
|
|
414
|
+
shape: BoxShape.circle,
|
|
415
|
+
),
|
|
416
|
+
child: Center(
|
|
417
|
+
child: Icon(
|
|
418
|
+
KasyIcons.add,
|
|
419
|
+
size: d * 0.444,
|
|
420
|
+
color: context.colors.primary,
|
|
421
|
+
),
|
|
422
|
+
),
|
|
423
|
+
),
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
return Row(
|
|
427
|
+
mainAxisSize: MainAxisSize.min,
|
|
428
|
+
children: [stack, const SizedBox(width: 12), addBtn],
|
|
429
|
+
);
|
|
387
430
|
}
|
|
388
431
|
|
|
389
432
|
Widget _ringWrap(BuildContext context, double d, Widget child, bool shadow) {
|
|
390
|
-
|
|
433
|
+
return Container(
|
|
391
434
|
width: d,
|
|
392
435
|
height: d,
|
|
393
436
|
decoration: BoxDecoration(
|
|
394
437
|
shape: BoxShape.circle,
|
|
395
|
-
border: Border.all(color: context.colors.surface, width: 2.
|
|
438
|
+
border: Border.all(color: context.colors.surface, width: 2.0),
|
|
396
439
|
boxShadow: shadow ? [KasyShadows.component(context)] : null,
|
|
397
440
|
),
|
|
398
441
|
child: ClipOval(
|
|
399
442
|
child: SizedBox(width: d, height: d, child: child),
|
|
400
443
|
),
|
|
401
444
|
);
|
|
402
|
-
return bordered;
|
|
403
445
|
}
|
|
404
446
|
}
|
|
405
447
|
|
|
@@ -418,52 +460,41 @@ _KasyAvatarColors _colorsForTone(
|
|
|
418
460
|
final KasyColors k = context.colors;
|
|
419
461
|
final bool dark = context.isDark;
|
|
420
462
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
}
|
|
463
|
+
// Accent color per tone (Figma: accent=#0485F7, success=#17C964,
|
|
464
|
+
// warning=#F5A524, danger=#FF383C, neutral uses neutral fg).
|
|
465
|
+
Color accentColor() => switch (tone) {
|
|
466
|
+
KasyAvatarTone.blue => k.primary,
|
|
467
|
+
KasyAvatarTone.neutral => k.grey3,
|
|
468
|
+
KasyAvatarTone.green => k.success,
|
|
469
|
+
KasyAvatarTone.orange => k.warning,
|
|
470
|
+
KasyAvatarTone.red => k.error,
|
|
471
|
+
};
|
|
430
472
|
|
|
473
|
+
// Solid surface: all types share the same #EBEBEC bg (Figma fill_J18KAR).
|
|
474
|
+
// Foreground: neutral → onSurface (#18181B light), others → accent color.
|
|
431
475
|
if (fallbackSurface == KasyAvatarFallbackSurface.default_) {
|
|
476
|
+
final Color fg = tone == KasyAvatarTone.neutral
|
|
477
|
+
? k.onSurface
|
|
478
|
+
: accentColor();
|
|
479
|
+
return _KasyAvatarColors(background: k.avatarFallbackFill, foreground: fg);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Neutral soft = same as neutral solid (Figma: no tint for default type).
|
|
483
|
+
if (tone == KasyAvatarTone.neutral) {
|
|
432
484
|
return _KasyAvatarColors(
|
|
433
485
|
background: k.avatarFallbackFill,
|
|
434
|
-
foreground:
|
|
486
|
+
foreground: k.onSurface,
|
|
435
487
|
);
|
|
436
488
|
}
|
|
437
489
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
return
|
|
446
|
-
KasyAvatarTone.blue => const _KasyAvatarColors(
|
|
447
|
-
background: Color(0xFFE3F2FD),
|
|
448
|
-
foreground: Color(0xFF1565C0),
|
|
449
|
-
),
|
|
450
|
-
KasyAvatarTone.neutral => const _KasyAvatarColors(
|
|
451
|
-
background: Color(0xFFF0F0F0),
|
|
452
|
-
foreground: Color(0xFF212121),
|
|
453
|
-
),
|
|
454
|
-
KasyAvatarTone.green => const _KasyAvatarColors(
|
|
455
|
-
background: Color(0xFFE8F5E9),
|
|
456
|
-
foreground: Color(0xFF1B5E20),
|
|
457
|
-
),
|
|
458
|
-
KasyAvatarTone.orange => const _KasyAvatarColors(
|
|
459
|
-
background: Color(0xFFFFF3E0),
|
|
460
|
-
foreground: Color(0xFFE65100),
|
|
461
|
-
),
|
|
462
|
-
KasyAvatarTone.red => const _KasyAvatarColors(
|
|
463
|
-
background: Color(0xFFFFEBEE),
|
|
464
|
-
foreground: Color(0xFFB71C1C),
|
|
465
|
-
),
|
|
466
|
-
};
|
|
490
|
+
// Colored soft: rgba(accent, 0.15) blended over surface (white light / dark).
|
|
491
|
+
// Figma fill_MVOTCC: rgba(4,133,247,0.15) + #FFFFFF light
|
|
492
|
+
// rgba(4,133,247,0.15) + #18181B dark
|
|
493
|
+
// Foreground: pure accent color (Figma uses raw accent even in soft).
|
|
494
|
+
final Color a = accentColor();
|
|
495
|
+
final Color base = dark ? k.surface : const Color(0xFFFFFFFF);
|
|
496
|
+
final Color bg = Color.alphaBlend(a.withValues(alpha: 0.15), base);
|
|
497
|
+
return _KasyAvatarColors(background: bg, foreground: a);
|
|
467
498
|
}
|
|
468
499
|
|
|
469
500
|
class _KasyAvatarPressable extends StatefulWidget {
|
|
@@ -1,94 +1,136 @@
|
|
|
1
1
|
import 'package:flutter/material.dart';
|
|
2
2
|
|
|
3
|
-
///
|
|
3
|
+
/// Linear gradient presets for [KasyAvatar] via `backgroundGradient` or
|
|
4
4
|
/// [KasyAvatar.gradientFill].
|
|
5
5
|
///
|
|
6
|
-
///
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
6
|
+
/// Matches the HeroUI Figma Kit V3 avatar palette exactly.
|
|
7
|
+
/// All gradients use 141° (upper-left → lower-right), flat from 0%→27%,
|
|
8
|
+
/// then transition to the second color at 100% — same as Figma specification.
|
|
9
|
+
abstract final class KasyAvatarGradients {
|
|
10
|
+
// 141° CSS angle → Flutter Alignment
|
|
11
|
+
// begin = upper-left region (-sin141°, cos141°) ≈ (-0.63, -0.78)
|
|
12
|
+
// end = lower-right region (+sin141°, -cos141°) ≈ (+0.63, +0.78)
|
|
13
|
+
static const Alignment _b = Alignment(-0.63, -0.78);
|
|
14
|
+
static const Alignment _e = Alignment(0.63, 0.78);
|
|
15
|
+
|
|
16
|
+
/// Cornflower blue → deep blue
|
|
17
|
+
static const LinearGradient blue = LinearGradient(
|
|
18
|
+
begin: _b,
|
|
19
|
+
end: _e,
|
|
20
|
+
colors: [Color(0xFF5D9BE7), Color(0xFF5D9BE7), Color(0xFF0026FF)],
|
|
21
|
+
stops: [0.0, 0.27, 1.0],
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
/// Cyan-blue → violet
|
|
25
|
+
static const LinearGradient sky = LinearGradient(
|
|
26
|
+
begin: _b,
|
|
27
|
+
end: _e,
|
|
28
|
+
colors: [Color(0xFF5DD0E7), Color(0xFF5DD0E7), Color(0xFF7300FF)],
|
|
29
|
+
stops: [0.0, 0.27, 1.0],
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
/// Aqua → deep blue
|
|
33
|
+
static const LinearGradient teal = LinearGradient(
|
|
34
|
+
begin: _b,
|
|
35
|
+
end: _e,
|
|
36
|
+
colors: [Color(0xFF5DE7E7), Color(0xFF5DE7E7), Color(0xFF001AFF)],
|
|
37
|
+
stops: [0.0, 0.27, 1.0],
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
/// Mint green → royal blue
|
|
41
|
+
static const LinearGradient emerald = LinearGradient(
|
|
42
|
+
begin: _b,
|
|
43
|
+
end: _e,
|
|
44
|
+
colors: [Color(0xFF5DE79D), Color(0xFF5DE79D), Color(0xFF0033FF)],
|
|
45
|
+
stops: [0.0, 0.27, 1.0],
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
/// Seafoam → dark forest green
|
|
49
|
+
static const LinearGradient green = LinearGradient(
|
|
50
|
+
begin: _b,
|
|
51
|
+
end: _e,
|
|
52
|
+
colors: [Color(0xFF52DEB0), Color(0xFF52DEB0), Color(0xFF0D5C45)],
|
|
53
|
+
stops: [0.0, 0.27, 1.0],
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
/// Turquoise → deep forest green
|
|
57
|
+
static const LinearGradient forest = LinearGradient(
|
|
58
|
+
begin: _b,
|
|
59
|
+
end: _e,
|
|
60
|
+
colors: [Color(0xFF5DD9AF), Color(0xFF5DD9AF), Color(0xFF094E39)],
|
|
61
|
+
stops: [0.0, 0.27, 1.0],
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
/// Golden yellow → deep red-orange
|
|
65
|
+
static const LinearGradient orange = LinearGradient(
|
|
66
|
+
begin: _b,
|
|
67
|
+
end: _e,
|
|
68
|
+
colors: [Color(0xFFE7BD5D), Color(0xFFE7BD5D), Color(0xFFFF1E00)],
|
|
69
|
+
stops: [0.0, 0.27, 1.0],
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
/// Salmon → scarlet
|
|
73
|
+
static const LinearGradient red = LinearGradient(
|
|
74
|
+
begin: _b,
|
|
75
|
+
end: _e,
|
|
76
|
+
colors: [Color(0xFFE7885D), Color(0xFFE7885D), Color(0xFFFF0004)],
|
|
77
|
+
stops: [0.0, 0.27, 1.0],
|
|
18
78
|
);
|
|
19
79
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
Color(0xFFE91E63),
|
|
27
|
-
Color(0xFF880E4F),
|
|
28
|
-
],
|
|
29
|
-
stops: [0.0, 0.35, 0.71, 1.0],
|
|
80
|
+
/// Soft purple → cobalt blue
|
|
81
|
+
static const LinearGradient indigo = LinearGradient(
|
|
82
|
+
begin: _b,
|
|
83
|
+
end: _e,
|
|
84
|
+
colors: [Color(0xFFB45DE7), Color(0xFFB45DE7), Color(0xFF0055FF)],
|
|
85
|
+
stops: [0.0, 0.27, 1.0],
|
|
30
86
|
);
|
|
31
87
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
Color(0xFFFF6E40),
|
|
39
|
-
Color(0xFFB71C1C),
|
|
40
|
-
],
|
|
41
|
-
stops: [0.0, 0.33, 0.69, 1.0],
|
|
88
|
+
/// Medium purple → crimson
|
|
89
|
+
static const LinearGradient purple = LinearGradient(
|
|
90
|
+
begin: _b,
|
|
91
|
+
end: _e,
|
|
92
|
+
colors: [Color(0xFF8D5DE7), Color(0xFF8D5DE7), Color(0xFFFF0009)],
|
|
93
|
+
stops: [0.0, 0.27, 1.0],
|
|
42
94
|
);
|
|
43
95
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
Color(0xFF00838F),
|
|
51
|
-
Color(0xFF004D40),
|
|
52
|
-
],
|
|
53
|
-
stops: [0.0, 0.34, 0.7, 1.0],
|
|
96
|
+
/// Orchid pink → deep magenta-red
|
|
97
|
+
static const LinearGradient rose = LinearGradient(
|
|
98
|
+
begin: _b,
|
|
99
|
+
end: _e,
|
|
100
|
+
colors: [Color(0xFFE75DCB), Color(0xFFE75DCB), Color(0xFFFF000D)],
|
|
101
|
+
stops: [0.0, 0.27, 1.0],
|
|
54
102
|
);
|
|
55
103
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
Color(0xFF8E24AA),
|
|
63
|
-
Color(0xFF311B92),
|
|
64
|
-
],
|
|
65
|
-
stops: [0.0, 0.36, 0.72, 1.0],
|
|
104
|
+
/// Hot pink → coral red
|
|
105
|
+
static const LinearGradient hotPink = LinearGradient(
|
|
106
|
+
begin: _b,
|
|
107
|
+
end: _e,
|
|
108
|
+
colors: [Color(0xFFE43673), Color(0xFFE43673), Color(0xFFFB5059)],
|
|
109
|
+
stops: [0.0, 0.27, 1.0],
|
|
66
110
|
);
|
|
67
111
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
Color(0xFFFF8F00),
|
|
75
|
-
Color(0xFFE65100),
|
|
76
|
-
],
|
|
77
|
-
stops: [0.0, 0.33, 0.69, 1.0],
|
|
112
|
+
/// Silver → near-black
|
|
113
|
+
static const LinearGradient silver = LinearGradient(
|
|
114
|
+
begin: _b,
|
|
115
|
+
end: _e,
|
|
116
|
+
colors: [Color(0xFF949494), Color(0xFF949494), Color(0xFF080808)],
|
|
117
|
+
stops: [0.0, 0.27, 1.0],
|
|
78
118
|
);
|
|
79
119
|
|
|
80
|
-
///
|
|
81
|
-
static const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
colors: [
|
|
85
|
-
|
|
86
|
-
Color(0xFFFF4081),
|
|
87
|
-
Color(0xFFB388FF),
|
|
88
|
-
Color(0xFF4527A0),
|
|
89
|
-
],
|
|
90
|
-
stops: [0.0, 0.3, 0.62, 1.0],
|
|
120
|
+
/// Mid-grey → very dark grey
|
|
121
|
+
static const LinearGradient black = LinearGradient(
|
|
122
|
+
begin: _b,
|
|
123
|
+
end: _e,
|
|
124
|
+
colors: [Color(0xFF7F7F7F), Color(0xFF7F7F7F), Color(0xFF1F1F1F)],
|
|
125
|
+
stops: [0.0, 0.27, 1.0],
|
|
91
126
|
);
|
|
127
|
+
|
|
128
|
+
/// All 14 presets in display order (matches Figma "Image Use" section).
|
|
129
|
+
static const List<LinearGradient> all = [
|
|
130
|
+
blue, sky, teal, emerald, green, forest,
|
|
131
|
+
orange, red, indigo, purple, rose, hotPink,
|
|
132
|
+
silver, black,
|
|
133
|
+
];
|
|
92
134
|
}
|
|
93
135
|
|
|
94
136
|
/// Fixed Unsplash URLs for demos/catalog (network + permissions in prod).
|
|
@@ -377,6 +377,9 @@ class KasyButton extends StatelessWidget {
|
|
|
377
377
|
}
|
|
378
378
|
final KasyColors c = context.colors;
|
|
379
379
|
final Color soft = c.surfaceNeutralSoft;
|
|
380
|
+
// Variants used on non-theme-matched backgrounds (e.g. inverse on the paywall gradient)
|
|
381
|
+
// need an explicit case — the generic disabled fallback blends with surfaceNeutralSoft,
|
|
382
|
+
// which goes near-black in dark mode and kills contrast on colored surfaces.
|
|
380
383
|
return switch (variant) {
|
|
381
384
|
KasyButtonVariant.primary => _KasyButtonPalette(
|
|
382
385
|
background: Color.alphaBlend(c.primary.withValues(alpha: 0.62), soft),
|
|
@@ -388,6 +391,11 @@ class KasyButton extends StatelessWidget {
|
|
|
388
391
|
foreground: c.primary.withValues(alpha: 0.90),
|
|
389
392
|
border: Colors.transparent,
|
|
390
393
|
),
|
|
394
|
+
KasyButtonVariant.inverse => _KasyButtonPalette(
|
|
395
|
+
background: c.onPrimary,
|
|
396
|
+
foreground: c.primary.withValues(alpha: 0.62),
|
|
397
|
+
border: Colors.transparent,
|
|
398
|
+
),
|
|
391
399
|
_ => null,
|
|
392
400
|
};
|
|
393
401
|
}
|