kasy-cli 1.12.1 → 1.14.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 (249) hide show
  1. package/bin/kasy.js +143 -7
  2. package/lib/commands/add.js +2 -2
  3. package/lib/commands/codemagic.js +11 -4
  4. package/lib/commands/deploy.js +3 -3
  5. package/lib/commands/favicon.js +115 -0
  6. package/lib/commands/icon.js +143 -0
  7. package/lib/commands/ios.js +20 -5
  8. package/lib/commands/new.js +8 -20
  9. package/lib/commands/remove.js +1 -1
  10. package/lib/commands/reset.js +287 -0
  11. package/lib/commands/run.js +24 -17
  12. package/lib/commands/splash.js +219 -0
  13. package/lib/commands/update.js +1 -1
  14. package/lib/scaffold/CHANGELOG.json +9 -0
  15. package/lib/scaffold/backends/api/patch/README.md +1 -1
  16. package/lib/scaffold/backends/api/patch/android/app/src/main/AndroidManifest.xml +1 -1
  17. package/lib/scaffold/backends/api/patch/lib/features/notifications/api/device_api.dart +53 -0
  18. package/lib/scaffold/backends/api/patch/lib/main.dart +29 -10
  19. package/lib/scaffold/backends/api/pubspec.yaml.tpl +11 -1
  20. package/lib/scaffold/backends/firebase/tokens.js +2 -2
  21. package/lib/scaffold/backends/supabase/edge-functions/send-push-notification/index.ts +8 -2
  22. package/lib/scaffold/backends/supabase/migrations/20240101000011_dedupe_device_tokens.sql +34 -0
  23. package/lib/scaffold/backends/supabase/patch/README.md +1 -1
  24. package/lib/scaffold/backends/supabase/patch/android/app/src/main/AndroidManifest.xml +1 -1
  25. package/lib/scaffold/backends/supabase/patch/lib/features/notifications/api/device_api.dart +43 -0
  26. package/lib/scaffold/backends/supabase/patch/lib/main.dart +29 -10
  27. package/lib/scaffold/backends/supabase/pubspec.yaml.tpl +11 -1
  28. package/lib/scaffold/features/README.md +15 -139
  29. package/lib/scaffold/shared/generator-utils.js +16 -15
  30. package/lib/utils/apple-release.js +85 -16
  31. package/lib/utils/checks.js +4 -105
  32. package/lib/utils/flutter-run.js +173 -0
  33. package/lib/utils/i18n.js +413 -0
  34. package/lib/utils/mobile-identity.js +35 -0
  35. package/lib/utils/ui.js +114 -0
  36. package/package.json +2 -3
  37. package/templates/firebase/README.en.md +1 -1
  38. package/templates/firebase/README.es.md +1 -1
  39. package/templates/firebase/README.md +1 -1
  40. package/templates/firebase/android/app/build.gradle.kts +10 -1
  41. package/templates/firebase/android/app/src/main/AndroidManifest.xml +1 -1
  42. package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/MainActivity.kt +25 -1
  43. package/templates/firebase/android/app/src/main/kotlin/com/aicrus/firebase/kit/MyWidget.kt +160 -11
  44. package/templates/firebase/android/app/src/main/res/drawable/widget_add_button.xml +15 -0
  45. package/templates/firebase/android/app/src/main/res/drawable/widget_gradient_bg.xml +9 -0
  46. package/templates/firebase/android/app/src/main/res/drawable/widget_gradient_inner.xml +12 -0
  47. package/templates/firebase/android/app/src/main/res/drawable/widget_plan_pill_bg.xml +5 -0
  48. package/templates/firebase/android/app/src/main/res/drawable/widget_preview_image.xml +17 -0
  49. package/templates/firebase/android/app/src/main/res/drawable/widget_pro_pill_bg.xml +5 -0
  50. package/templates/firebase/android/app/src/main/res/drawable-hdpi/android12splash.png +0 -0
  51. package/templates/firebase/android/app/src/main/res/drawable-hdpi/splash.png +0 -0
  52. package/templates/firebase/android/app/src/main/res/drawable-mdpi/android12splash.png +0 -0
  53. package/templates/firebase/android/app/src/main/res/drawable-mdpi/splash.png +0 -0
  54. package/templates/firebase/android/app/src/main/res/drawable-night/background.png +0 -0
  55. package/templates/firebase/android/app/src/main/res/drawable-night/launch_background.xml +9 -0
  56. package/templates/firebase/android/app/src/main/res/drawable-night-hdpi/android12splash.png +0 -0
  57. package/templates/firebase/android/app/src/main/res/drawable-night-hdpi/splash.png +0 -0
  58. package/templates/firebase/android/app/src/main/res/drawable-night-mdpi/android12splash.png +0 -0
  59. package/templates/firebase/android/app/src/main/res/drawable-night-mdpi/splash.png +0 -0
  60. package/templates/firebase/android/app/src/main/res/drawable-night-v21/background.png +0 -0
  61. package/templates/firebase/android/app/src/main/res/drawable-night-v21/launch_background.xml +9 -0
  62. package/templates/firebase/android/app/src/main/res/drawable-night-xhdpi/android12splash.png +0 -0
  63. package/templates/firebase/android/app/src/main/res/drawable-night-xhdpi/splash.png +0 -0
  64. package/templates/firebase/android/app/src/main/res/drawable-night-xxhdpi/android12splash.png +0 -0
  65. package/templates/firebase/android/app/src/main/res/drawable-night-xxhdpi/splash.png +0 -0
  66. package/templates/firebase/android/app/src/main/res/drawable-night-xxxhdpi/android12splash.png +0 -0
  67. package/templates/firebase/android/app/src/main/res/drawable-night-xxxhdpi/splash.png +0 -0
  68. package/templates/firebase/android/app/src/main/res/drawable-xhdpi/android12splash.png +0 -0
  69. package/templates/firebase/android/app/src/main/res/drawable-xhdpi/splash.png +0 -0
  70. package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/android12splash.png +0 -0
  71. package/templates/firebase/android/app/src/main/res/drawable-xxhdpi/splash.png +0 -0
  72. package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/android12splash.png +0 -0
  73. package/templates/firebase/android/app/src/main/res/drawable-xxxhdpi/splash.png +0 -0
  74. package/templates/firebase/android/app/src/main/res/layout/widget_loading.xml +8 -0
  75. package/templates/firebase/android/app/src/main/res/layout/widget_preview.xml +46 -0
  76. package/templates/firebase/android/app/src/main/res/mipmap-hdpi/ic_launcher.png +0 -0
  77. package/templates/firebase/android/app/src/main/res/mipmap-mdpi/ic_launcher.png +0 -0
  78. package/templates/firebase/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png +0 -0
  79. package/templates/firebase/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png +0 -0
  80. package/templates/firebase/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png +0 -0
  81. package/templates/firebase/android/app/src/main/res/values-night-v31/styles.xml +2 -1
  82. package/templates/firebase/android/app/src/main/res/values-v31/styles.xml +1 -0
  83. package/templates/firebase/android/app/src/main/res/xml/mywidget_info.xml +9 -3
  84. package/templates/firebase/assets/images/favicon.png +0 -0
  85. package/templates/firebase/assets/images/icon.png +0 -0
  86. package/templates/firebase/assets/images/splash_logo_dark.png +0 -0
  87. package/templates/firebase/assets/images/splash_logo_light.png +0 -0
  88. package/templates/firebase/firestore.indexes.json +10 -0
  89. package/templates/firebase/functions/src/core/data/entities/user_device_entity.ts +3 -0
  90. package/templates/firebase/functions/src/core/data/repositories/user_device_repository.ts +17 -1
  91. package/templates/firebase/functions/src/index.ts +1 -0
  92. package/templates/firebase/functions/src/notifications/device_triggers.ts +58 -0
  93. package/templates/firebase/ios/HomeWidgetExtension/MyWidget.swift +116 -33
  94. package/templates/firebase/ios/Runner/AppDelegate.swift +17 -1
  95. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png +0 -0
  96. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png +0 -0
  97. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png +0 -0
  98. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png +0 -0
  99. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png +0 -0
  100. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png +0 -0
  101. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png +0 -0
  102. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png +0 -0
  103. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png +0 -0
  104. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png +0 -0
  105. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png +0 -0
  106. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png +0 -0
  107. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png +0 -0
  108. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png +0 -0
  109. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png +0 -0
  110. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png +0 -0
  111. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png +0 -0
  112. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png +0 -0
  113. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png +0 -0
  114. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png +0 -0
  115. package/templates/firebase/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png +0 -0
  116. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json +9 -8
  117. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchBackground.imageset/darkbackground.png +0 -0
  118. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json +33 -0
  119. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png +0 -0
  120. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png +0 -0
  121. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png +0 -0
  122. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png +0 -0
  123. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@2x.png +0 -0
  124. package/templates/firebase/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark@3x.png +0 -0
  125. package/templates/firebase/ios/Runner/Base.lproj/LaunchScreen.storyboard +1 -1
  126. package/templates/firebase/ios/Runner/Info.plist +2 -2
  127. package/templates/firebase/ios/Runner/es.lproj/InfoPlist.strings +1 -1
  128. package/templates/firebase/ios/Runner/pt-BR.lproj/InfoPlist.strings +1 -1
  129. package/templates/firebase/ios/Runner/pt.lproj/InfoPlist.strings +1 -1
  130. package/templates/firebase/lib/components/kasy_button.dart +8 -0
  131. package/templates/firebase/lib/core/bottom_menu/kasy_bottom_bar_factory.dart +18 -0
  132. package/templates/firebase/lib/core/home_widgets/home_widget_mywidget_service.dart +67 -19
  133. package/templates/firebase/lib/core/home_widgets/home_widget_service.dart +22 -8
  134. package/templates/firebase/lib/core/shared_preferences/shared_preferences.dart +16 -4
  135. package/templates/firebase/lib/core/states/components/maybeshow_component.dart +4 -8
  136. package/templates/firebase/lib/core/states/user_state_notifier.dart +13 -1
  137. package/templates/firebase/lib/core/theme/providers/theme_provider.dart +48 -24
  138. package/templates/firebase/lib/features/home/home_page.dart +0 -6
  139. package/templates/firebase/lib/features/notifications/api/device_api.dart +57 -0
  140. package/templates/firebase/lib/features/notifications/providers/models/notification.dart +11 -1
  141. package/templates/firebase/lib/features/notifications/repositories/device_repository.dart +9 -0
  142. package/templates/firebase/lib/features/notifications/repositories/notifications_repository.dart +1 -4
  143. package/templates/firebase/lib/features/notifications/shared/att_permission.dart +28 -8
  144. package/templates/firebase/lib/features/notifications/ui/notifications_page.dart +16 -1
  145. package/templates/firebase/lib/features/notifications/ui/widgets/empty_notifications.dart +44 -11
  146. package/templates/firebase/lib/features/onboarding/ui/components/onboarding_features.dart +4 -0
  147. package/templates/firebase/lib/features/onboarding/ui/onboarding_page.dart +1 -0
  148. package/templates/firebase/lib/features/onboarding/ui/widgets/onboarding_feature.dart +13 -0
  149. package/templates/firebase/lib/features/settings/settings_page.dart +158 -18
  150. package/templates/firebase/lib/features/settings/ui/components/admin/admin_bottom_sheet.dart +31 -29
  151. package/templates/firebase/lib/features/settings/ui/components/language_switcher.dart +21 -5
  152. package/templates/firebase/lib/i18n/en.i18n.json +10 -3
  153. package/templates/firebase/lib/i18n/es.i18n.json +10 -3
  154. package/templates/firebase/lib/i18n/pt.i18n.json +10 -3
  155. package/templates/firebase/lib/main.dart +29 -10
  156. package/templates/firebase/pubspec.yaml +10 -6
  157. package/templates/firebase/test/core/data/repositories/user_repository_test.dart +1 -1
  158. package/templates/firebase/test/features/notifications/data/device_api_fake.dart +9 -0
  159. package/templates/firebase/web/favicon.png +0 -0
  160. package/templates/firebase/web/icons/Icon-192.png +0 -0
  161. package/templates/firebase/web/icons/Icon-512.png +0 -0
  162. package/templates/firebase/web/icons/Icon-maskable-192.png +0 -0
  163. package/templates/firebase/web/icons/Icon-maskable-512.png +0 -0
  164. package/templates/firebase/web/index.html +50 -39
  165. package/templates/firebase/web/manifest.json +3 -3
  166. package/templates/firebase/web/splash/img/dark-1x.png +0 -0
  167. package/templates/firebase/web/splash/img/dark-2x.png +0 -0
  168. package/templates/firebase/web/splash/img/dark-3x.png +0 -0
  169. package/templates/firebase/web/splash/img/dark-4x.png +0 -0
  170. package/templates/firebase/web/splash/img/light-1x.png +0 -0
  171. package/templates/firebase/web/splash/img/light-2x.png +0 -0
  172. package/templates/firebase/web/splash/img/light-3x.png +0 -0
  173. package/templates/firebase/web/splash/img/light-4x.png +0 -0
  174. package/lib/scaffold/features/analytics/lib/core/data/api/analytics_api.dart +0 -124
  175. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/BUG.es.md +0 -35
  176. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/BUG.md +0 -35
  177. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/BUG.pt.md +0 -35
  178. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/feature_request.es.md +0 -12
  179. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/feature_request.md +0 -12
  180. package/lib/scaffold/features/ci/.github/ISSUE_TEMPLATE/feature_request.pt.md +0 -12
  181. package/lib/scaffold/features/ci/.github/PULL_REQUEST_TEMPLATE.es.md +0 -17
  182. package/lib/scaffold/features/ci/.github/PULL_REQUEST_TEMPLATE.md +0 -17
  183. package/lib/scaffold/features/ci/.github/PULL_REQUEST_TEMPLATE.pt.md +0 -17
  184. package/lib/scaffold/features/ci/.github/dependabot.yml +0 -16
  185. package/lib/scaffold/features/ci/.github/workflows/app.yml +0 -20
  186. package/lib/scaffold/features/ci/.gitlab/templates/deploy.yaml +0 -14
  187. package/lib/scaffold/features/ci/.gitlab/templates/dropbox.yaml +0 -19
  188. package/lib/scaffold/features/ci/.gitlab/templates/flutter.yaml +0 -163
  189. package/lib/scaffold/features/ci/.gitlab/templates/mailgun.yaml +0 -28
  190. package/lib/scaffold/features/ci/.gitlab-ci.yml +0 -37
  191. package/lib/scaffold/features/ci/codemagic.yaml +0 -157
  192. package/lib/scaffold/features/facebook/lib/core/data/api/tracking_api.dart +0 -111
  193. package/lib/scaffold/features/feedback/lib/features/feedbacks/api/entities/feature_request_entity.dart +0 -27
  194. package/lib/scaffold/features/feedback/lib/features/feedbacks/api/entities/feature_vote_entity.dart +0 -27
  195. package/lib/scaffold/features/feedback/lib/features/feedbacks/api/feature_request_api.dart +0 -50
  196. package/lib/scaffold/features/feedback/lib/features/feedbacks/api/feature_vote_api.dart +0 -79
  197. package/lib/scaffold/features/feedback/lib/features/feedbacks/models/feature_requests.dart +0 -48
  198. package/lib/scaffold/features/feedback/lib/features/feedbacks/models/feedback_state.dart +0 -42
  199. package/lib/scaffold/features/feedback/lib/features/feedbacks/providers/feedback_page_notifier.dart +0 -147
  200. package/lib/scaffold/features/feedback/lib/features/feedbacks/repositories/feature_request_repository.dart +0 -95
  201. package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/component/add_feature_form.dart +0 -199
  202. package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/feedback_page.dart +0 -175
  203. package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/widgets/add_feature_button.dart +0 -76
  204. package/lib/scaffold/features/feedback/lib/features/feedbacks/ui/widgets/feature_card.dart +0 -279
  205. package/lib/scaffold/features/ios-release/.kasy/apple.env.example +0 -8
  206. package/lib/scaffold/features/ios-release/.kasy/codemagic.env.example +0 -7
  207. package/lib/scaffold/features/ios-release/docs/codemagic-release.en.md +0 -50
  208. package/lib/scaffold/features/ios-release/docs/codemagic-release.es.md +0 -50
  209. package/lib/scaffold/features/ios-release/docs/codemagic-release.pt.md +0 -50
  210. package/lib/scaffold/features/ios-release/docs/ios-release.en.md +0 -41
  211. package/lib/scaffold/features/ios-release/docs/ios-release.es.md +0 -41
  212. package/lib/scaffold/features/ios-release/docs/ios-release.pt.md +0 -41
  213. package/lib/scaffold/features/ios-release/scripts/bump-ios-version.js +0 -38
  214. package/lib/scaffold/features/ios-release/scripts/release-ios.sh +0 -137
  215. package/lib/scaffold/features/llm_chat/lib/features/llm_chat/llm_chat_page.dart +0 -301
  216. package/lib/scaffold/features/local_notifications/lib/features/local_reminder/providers/reminder_notifier.dart +0 -81
  217. package/lib/scaffold/features/local_notifications/lib/features/local_reminder/repositories/reminder_preferences.dart +0 -76
  218. package/lib/scaffold/features/local_notifications/lib/features/local_reminder/ui/reminder_page.dart +0 -282
  219. package/lib/scaffold/features/onboarding/lib/features/onboarding/api/entities/user_info_entity.dart +0 -24
  220. package/lib/scaffold/features/onboarding/lib/features/onboarding/api/user_infos_api.dart +0 -71
  221. package/lib/scaffold/features/onboarding/lib/features/onboarding/models/user_info.dart +0 -92
  222. package/lib/scaffold/features/onboarding/lib/features/onboarding/providers/onboarding_model.dart +0 -15
  223. package/lib/scaffold/features/onboarding/lib/features/onboarding/providers/onboarding_provider.dart +0 -78
  224. package/lib/scaffold/features/onboarding/lib/features/onboarding/repositories/user_infos_repository.dart +0 -29
  225. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/animations/page_transitions.dart +0 -30
  226. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_att_setup.dart +0 -66
  227. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_features.dart +0 -72
  228. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_loader.dart +0 -92
  229. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_notifications_setup.dart +0 -73
  230. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/components/onboarding_questions.dart +0 -89
  231. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/onboarding_page.dart +0 -94
  232. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_background.dart +0 -80
  233. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_feature.dart +0 -139
  234. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_illustration_scaffold.dart +0 -110
  235. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_progress.dart +0 -84
  236. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_radio_question.dart +0 -173
  237. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_reassurance.dart +0 -45
  238. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/onboarding_sticky_footer.dart +0 -77
  239. package/lib/scaffold/features/onboarding/lib/features/onboarding/ui/widgets/selectable_row_tile.dart +0 -392
  240. package/lib/scaffold/features/revenuecat/lib/core/data/api/tracking_api.dart +0 -116
  241. package/lib/scaffold/features/revenuecat/lib/core/data/models/subscription.dart +0 -322
  242. package/lib/scaffold/features/revenuecat/lib/core/home_widgets/home_widget_background_task.dart +0 -41
  243. package/lib/scaffold/features/revenuecat/lib/core/states/user_state_notifier.dart +0 -305
  244. package/templates/firebase/assets/images/app_icon.png +0 -0
  245. package/templates/firebase/assets/images/onboarding/img1.jpg +0 -0
  246. package/templates/firebase/assets/images/onboarding/onboarding.png +0 -0
  247. package/templates/firebase/assets/images/splashscreen.png +0 -0
  248. package/templates/firebase/lib/core/states/components/maybe_ask_biometric_setup.dart +0 -88
  249. package/templates/firebase/lib/features/notifications/shared/notification_permission_bottom_sheet.dart +0 -144
@@ -1,76 +0,0 @@
1
- import 'package:shared_preferences/shared_preferences.dart';
2
-
3
- enum ReminderType { daily, weekly, specificDate }
4
-
5
- class ReminderState {
6
- final bool enabled;
7
- final ReminderType type;
8
- final int hour;
9
- final int minute;
10
- final int dayOfWeek;
11
- final DateTime? date;
12
-
13
- const ReminderState({
14
- this.enabled = false,
15
- this.type = ReminderType.daily,
16
- this.hour = 9,
17
- this.minute = 0,
18
- this.dayOfWeek = 1,
19
- this.date,
20
- });
21
-
22
- ReminderState copyWith({
23
- bool? enabled,
24
- ReminderType? type,
25
- int? hour,
26
- int? minute,
27
- int? dayOfWeek,
28
- DateTime? date,
29
- bool clearDate = false,
30
- }) =>
31
- ReminderState(
32
- enabled: enabled ?? this.enabled,
33
- type: type ?? this.type,
34
- hour: hour ?? this.hour,
35
- minute: minute ?? this.minute,
36
- dayOfWeek: dayOfWeek ?? this.dayOfWeek,
37
- date: clearDate ? null : (date ?? this.date),
38
- );
39
- }
40
-
41
- // ignore: avoid_classes_with_only_static_members
42
- class ReminderPreferences {
43
- static const _enabledKey = 'reminder_enabled';
44
- static const _typeKey = 'reminder_type';
45
- static const _hourKey = 'reminder_hour';
46
- static const _minuteKey = 'reminder_minute';
47
- static const _dayOfWeekKey = 'reminder_day_of_week';
48
- static const _dateKey = 'reminder_date_ms';
49
-
50
- static Future<ReminderState> load() async {
51
- final prefs = await SharedPreferences.getInstance();
52
- final dateMs = prefs.getInt(_dateKey);
53
- return ReminderState(
54
- enabled: prefs.getBool(_enabledKey) ?? false,
55
- type: ReminderType.values[prefs.getInt(_typeKey) ?? 0],
56
- hour: prefs.getInt(_hourKey) ?? 9,
57
- minute: prefs.getInt(_minuteKey) ?? 0,
58
- dayOfWeek: prefs.getInt(_dayOfWeekKey) ?? 1,
59
- date: dateMs != null ? DateTime.fromMillisecondsSinceEpoch(dateMs) : null,
60
- );
61
- }
62
-
63
- static Future<void> save(ReminderState state) async {
64
- final prefs = await SharedPreferences.getInstance();
65
- await prefs.setBool(_enabledKey, state.enabled);
66
- await prefs.setInt(_typeKey, state.type.index);
67
- await prefs.setInt(_hourKey, state.hour);
68
- await prefs.setInt(_minuteKey, state.minute);
69
- await prefs.setInt(_dayOfWeekKey, state.dayOfWeek);
70
- if (state.date != null) {
71
- await prefs.setInt(_dateKey, state.date!.millisecondsSinceEpoch);
72
- } else {
73
- await prefs.remove(_dateKey);
74
- }
75
- }
76
- }
@@ -1,282 +0,0 @@
1
- import 'package:flutter/material.dart';
2
- import 'package:flutter_riverpod/flutter_riverpod.dart';
3
- import 'package:kasy_kit/core/theme/theme.dart';
4
- import 'package:kasy_kit/i18n/translations.g.dart';
5
- import 'package:kasy_kit/features/local_reminder/providers/reminder_notifier.dart';
6
- import 'package:kasy_kit/features/local_reminder/repositories/reminder_preferences.dart';
7
-
8
- class ReminderPage extends ConsumerWidget {
9
- const ReminderPage({super.key});
10
-
11
- @override
12
- Widget build(BuildContext context, WidgetRef ref) {
13
- final asyncState = ref.watch(reminderProvider);
14
- final tr = Translations.of(context).reminderPage;
15
-
16
- return Scaffold(
17
- appBar: AppBar(
18
- title: Text(tr.title),
19
- centerTitle: true,
20
- ),
21
- body: asyncState.when(
22
- loading: () => const Center(child: CircularProgressIndicator()),
23
- error: (e, _) => Center(child: Text(e.toString())),
24
- data: (state) => _ReminderForm(state: state),
25
- ),
26
- );
27
- }
28
- }
29
-
30
- class _ReminderForm extends ConsumerWidget {
31
- final ReminderState state;
32
-
33
- const _ReminderForm({required this.state});
34
-
35
- @override
36
- Widget build(BuildContext context, WidgetRef ref) {
37
- final tr = Translations.of(context).reminderPage;
38
- final notifier = ref.read(reminderProvider.notifier);
39
-
40
- return ListView(
41
- padding: const EdgeInsets.all(KasySpacing.md),
42
- children: [
43
- // Enable toggle
44
- SwitchListTile(
45
- title: Text(
46
- tr.toggleLabel,
47
- style: context.textTheme.bodyLarge,
48
- ),
49
- value: state.enabled,
50
- onChanged: notifier.setEnabled,
51
- ),
52
- if (state.enabled) ...[
53
- const Divider(),
54
- const SizedBox(height: KasySpacing.sm),
55
-
56
- // Type selector
57
- Text(
58
- tr.typeLabel,
59
- style: context.textTheme.labelLarge?.copyWith(
60
- color: context.colors.muted,
61
- ),
62
- ),
63
- const SizedBox(height: KasySpacing.sm),
64
- _TypeSelector(current: state.type, onChanged: notifier.setType),
65
- const SizedBox(height: KasySpacing.lg),
66
-
67
- // Time picker
68
- Text(
69
- tr.timeLabel,
70
- style: context.textTheme.labelLarge?.copyWith(
71
- color: context.colors.muted,
72
- ),
73
- ),
74
- const SizedBox(height: KasySpacing.sm),
75
- _TimeTile(
76
- hour: state.hour,
77
- minute: state.minute,
78
- onChanged: (h, m) => notifier.setTime(h, m),
79
- ),
80
-
81
- // Day of week (only for weekly)
82
- if (state.type == ReminderType.weekly) ...[
83
- const SizedBox(height: KasySpacing.lg),
84
- Text(
85
- tr.dayLabel,
86
- style: context.textTheme.labelLarge?.copyWith(
87
- color: context.colors.muted,
88
- ),
89
- ),
90
- const SizedBox(height: KasySpacing.sm),
91
- _DaySelector(current: state.dayOfWeek, onChanged: notifier.setDayOfWeek),
92
- ],
93
-
94
- // Date picker (only for specific date)
95
- if (state.type == ReminderType.specificDate) ...[
96
- const SizedBox(height: KasySpacing.lg),
97
- Text(
98
- tr.dateLabel,
99
- style: context.textTheme.labelLarge?.copyWith(
100
- color: context.colors.muted,
101
- ),
102
- ),
103
- const SizedBox(height: KasySpacing.sm),
104
- _DateTile(
105
- date: state.date,
106
- onChanged: notifier.setDate,
107
- ),
108
- ],
109
- ],
110
- ],
111
- );
112
- }
113
- }
114
-
115
- class _TypeSelector extends StatelessWidget {
116
- final ReminderType current;
117
- final ValueChanged<ReminderType> onChanged;
118
-
119
- const _TypeSelector({required this.current, required this.onChanged});
120
-
121
- @override
122
- Widget build(BuildContext context) {
123
- final tr = Translations.of(context).reminderPage;
124
- return SegmentedButton<ReminderType>(
125
- segments: [
126
- ButtonSegment(value: ReminderType.daily, label: Text(tr.daily)),
127
- ButtonSegment(value: ReminderType.weekly, label: Text(tr.weekly)),
128
- ButtonSegment(value: ReminderType.specificDate, label: Text(tr.specificDate)),
129
- ],
130
- selected: {current},
131
- onSelectionChanged: (s) => onChanged(s.first),
132
- style: ButtonStyle(
133
- textStyle: WidgetStateProperty.all(context.textTheme.labelSmall),
134
- ),
135
- );
136
- }
137
- }
138
-
139
- class _TimeTile extends StatelessWidget {
140
- final int hour;
141
- final int minute;
142
- final void Function(int hour, int minute) onChanged;
143
-
144
- const _TimeTile({required this.hour, required this.minute, required this.onChanged});
145
-
146
- String _pad(int v) => v.toString().padLeft(2, '0');
147
-
148
- @override
149
- Widget build(BuildContext context) {
150
- return InkWell(
151
- onTap: () async {
152
- final picked = await showTimePicker(
153
- context: context,
154
- initialTime: TimeOfDay(hour: hour, minute: minute),
155
- );
156
- if (picked != null) {
157
- onChanged(picked.hour, picked.minute);
158
- }
159
- },
160
- borderRadius: BorderRadius.circular(KasySpacing.sm),
161
- child: Container(
162
- padding: const EdgeInsets.symmetric(
163
- horizontal: KasySpacing.md,
164
- vertical: KasySpacing.smd,
165
- ),
166
- decoration: BoxDecoration(
167
- color: context.colors.surface,
168
- borderRadius: BorderRadius.circular(KasySpacing.sm),
169
- ),
170
- child: Row(
171
- children: [
172
- Icon(Icons.access_time, color: context.colors.primary),
173
- const SizedBox(width: KasySpacing.sm),
174
- Text(
175
- '${_pad(hour)}:${_pad(minute)}',
176
- style: context.textTheme.headlineMedium?.copyWith(
177
- color: context.colors.onSurface,
178
- fontWeight: FontWeight.bold,
179
- ),
180
- ),
181
- const Spacer(),
182
- Icon(Icons.arrow_forward_ios, size: 14, color: context.colors.muted),
183
- ],
184
- ),
185
- ),
186
- );
187
- }
188
- }
189
-
190
- class _DaySelector extends StatelessWidget {
191
- final int current;
192
- final ValueChanged<int> onChanged;
193
-
194
- const _DaySelector({required this.current, required this.onChanged});
195
-
196
- @override
197
- Widget build(BuildContext context) {
198
- // 1=Monday ... 7=Sunday (matches DateTime.weekday)
199
- final days = [
200
- 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb', 'Dom',
201
- ];
202
- return Wrap(
203
- spacing: KasySpacing.xs,
204
- children: List.generate(7, (i) {
205
- final dayIndex = i + 1;
206
- final selected = current == dayIndex;
207
- return ChoiceChip(
208
- label: Text(days[i]),
209
- selected: selected,
210
- onSelected: (_) => onChanged(dayIndex),
211
- );
212
- }),
213
- );
214
- }
215
- }
216
-
217
- class _DateTile extends StatelessWidget {
218
- final DateTime? date;
219
- final ValueChanged<DateTime> onChanged;
220
-
221
- const _DateTile({required this.date, required this.onChanged});
222
-
223
- @override
224
- Widget build(BuildContext context) {
225
- final label = date != null
226
- ? '${date!.day.toString().padLeft(2, '0')}/${date!.month.toString().padLeft(2, '0')}/${date!.year} ${date!.hour.toString().padLeft(2, '0')}:${date!.minute.toString().padLeft(2, '0')}'
227
- : Translations.of(context).reminderPage.selectDate;
228
-
229
- return InkWell(
230
- onTap: () async {
231
- final now = DateTime.now();
232
- final pickedDate = await showDatePicker(
233
- context: context,
234
- initialDate: date ?? now,
235
- firstDate: now,
236
- lastDate: now.add(const Duration(days: 365)),
237
- );
238
- if (pickedDate == null || !context.mounted) return;
239
- final pickedTime = await showTimePicker(
240
- context: context,
241
- initialTime: TimeOfDay(
242
- hour: date?.hour ?? 9,
243
- minute: date?.minute ?? 0,
244
- ),
245
- );
246
- if (pickedTime == null) return;
247
- onChanged(DateTime(
248
- pickedDate.year,
249
- pickedDate.month,
250
- pickedDate.day,
251
- pickedTime.hour,
252
- pickedTime.minute,
253
- ));
254
- },
255
- borderRadius: BorderRadius.circular(KasySpacing.sm),
256
- child: Container(
257
- padding: const EdgeInsets.symmetric(
258
- horizontal: KasySpacing.md,
259
- vertical: KasySpacing.smd,
260
- ),
261
- decoration: BoxDecoration(
262
- color: context.colors.surface,
263
- borderRadius: BorderRadius.circular(KasySpacing.sm),
264
- ),
265
- child: Row(
266
- children: [
267
- Icon(Icons.calendar_today, color: context.colors.primary),
268
- const SizedBox(width: KasySpacing.sm),
269
- Text(
270
- label,
271
- style: context.textTheme.bodyLarge?.copyWith(
272
- color: context.colors.onSurface,
273
- ),
274
- ),
275
- const Spacer(),
276
- Icon(Icons.arrow_forward_ios, size: 14, color: context.colors.muted),
277
- ],
278
- ),
279
- ),
280
- );
281
- }
282
- }
@@ -1,24 +0,0 @@
1
- import 'package:freezed_annotation/freezed_annotation.dart';
2
- // ignore: depend_on_referenced_packages
3
- // ignore_for_file: invalid_annotation_target
4
-
5
- import 'package:kasy_kit/core/data/entities/json_converters.dart';
6
-
7
- part 'user_info_entity.freezed.dart';
8
- part 'user_info_entity.g.dart';
9
-
10
-
11
- @freezed
12
- sealed class UserInfoEntity with _$UserInfoEntity {
13
- const factory UserInfoEntity({
14
- @JsonKey(includeIfNull: false, toJson: Converters.id) String? id,
15
- required String key,
16
- required String value,
17
- }) = UserInfoEntityData;
18
-
19
- factory UserInfoEntity.fromJson(String id, Map<String, dynamic> json) =>
20
- _$UserInfoEntityFromJson(json..['id'] = id);
21
- }
22
-
23
-
24
-
@@ -1,71 +0,0 @@
1
- import 'package:cloud_firestore/cloud_firestore.dart';
2
- import 'package:flutter_riverpod/flutter_riverpod.dart';
3
- import 'package:logger/logger.dart';
4
- import 'package:kasy_kit/features/onboarding/api/entities/user_info_entity.dart';
5
-
6
- final userInfosApiProvider = Provider<UserInfosApi>(
7
- (ref) => UserInfosApi(
8
- client: FirebaseFirestore.instance,
9
- ),
10
- );
11
-
12
- class UserInfosApi {
13
- final FirebaseFirestore _client;
14
- // ignore: unused_field
15
- final Logger _logger;
16
-
17
- UserInfosApi({
18
- required FirebaseFirestore client,
19
- }) : _client = client,
20
- _logger = Logger();
21
-
22
- CollectionReference<UserInfoEntity?> _collection(String userId) =>
23
- _client.collection('users').doc(userId).collection("infos").withConverter(
24
- fromFirestore: (snapshot, _) {
25
- if (snapshot.exists) {
26
- return UserInfoEntity.fromJson(snapshot.id, snapshot.data()!);
27
- }
28
- return null;
29
- },
30
- toFirestore: (data, _) => data!.toJson(),
31
- );
32
-
33
- Future<List<UserInfoEntity>> getAll(String userId) {
34
- return _collection(userId).get().then((value) {
35
- if (value.docs.isEmpty) {
36
- return [];
37
- }
38
- return value.docs.map((el) => el.data()!).toList();
39
- });
40
- }
41
-
42
- Future<UserInfoEntity?> get(String userId, String id) {
43
- return _collection(userId).doc(id).get().then((value) => value.data());
44
- }
45
-
46
- Future<UserInfoEntity?> getByKey(String userId, String key) {
47
- return _collection(userId)
48
- .where("key", isEqualTo: key)
49
- .get()
50
- .then((value) => value.docs.firstOrNull?.data());
51
- }
52
-
53
- Future<void> update(String userId, UserInfoEntity info) async {
54
- await _collection(userId).doc(info.id).set(info);
55
- }
56
-
57
- Future<void> delete(String userId, String id) async {
58
- await _collection(userId).doc(id).delete();
59
- }
60
-
61
- Future<void> deleteAll(String userId) async {
62
- final infos = await getAll(userId);
63
- for (final info in infos) {
64
- await delete(userId, info.id!);
65
- }
66
- }
67
-
68
- Future<void> create(String userId, UserInfoEntity info) async {
69
- await _collection(userId).add(info);
70
- }
71
- }
@@ -1,92 +0,0 @@
1
- import 'package:kasy_kit/features/onboarding/api/entities/user_info_entity.dart';
2
-
3
- enum UserInfoKeys {
4
- genre,
5
- age,
6
- }
7
-
8
- enum Genre {
9
- male,
10
- female,
11
- none,
12
- }
13
-
14
- enum AgeRange {
15
- age18_30,
16
- age31_40,
17
- age41_50,
18
- age51_60,
19
- none,
20
- }
21
-
22
- abstract class UserInfoDetail<T> {
23
- final T value;
24
-
25
- UserInfoDetail(this.value);
26
-
27
- UserInfoEntity toEntity();
28
-
29
-
30
- }
31
-
32
- /// ======================================
33
- /// user age info
34
- /// ======================================
35
- class UserAgeInfo extends UserInfoDetail<AgeRange> {
36
- UserAgeInfo(super.value);
37
-
38
- factory UserAgeInfo.fromEntity(UserInfoEntity entity) {
39
- final value = AgeRange.values.firstWhere(
40
- (element) => element.name == entity.value,
41
- orElse: () => AgeRange.none,
42
- );
43
- return UserAgeInfo(value);
44
- }
45
-
46
- factory UserAgeInfo.fromString(String value) {
47
- final sexe = AgeRange.values.firstWhere(
48
- (element) => element.name == value,
49
- orElse: () => AgeRange.none,
50
- );
51
- return UserAgeInfo(sexe);
52
- }
53
-
54
- @override
55
- UserInfoEntity toEntity() => UserInfoEntity(
56
- key: UserInfoKeys.age.name,
57
- value: value.name,
58
- );
59
-
60
-
61
- }
62
-
63
- /// ======================================
64
- /// user genre info
65
- /// ======================================
66
- class UserSexeInfo extends UserInfoDetail<Genre> {
67
- UserSexeInfo(super.value);
68
-
69
- factory UserSexeInfo.fromEntity(UserInfoEntity entity) {
70
- final value = Genre.values.firstWhere(
71
- (element) => element.name == entity.value,
72
- orElse: () => Genre.none,
73
- );
74
- return UserSexeInfo(value);
75
- }
76
-
77
- factory UserSexeInfo.fromString(String value) {
78
- final genre = Genre.values.firstWhere(
79
- (element) => element.name == value,
80
- orElse: () => Genre.none,
81
- );
82
- return UserSexeInfo(genre);
83
- }
84
-
85
- @override
86
- UserInfoEntity toEntity() => UserInfoEntity(
87
- key: UserInfoKeys.genre.name,
88
- value: value.name,
89
- );
90
-
91
-
92
- }
@@ -1,15 +0,0 @@
1
- class OnboardingState {
2
- DateTime? reminder;
3
-
4
- OnboardingState({
5
- this.reminder,
6
- });
7
-
8
- OnboardingState copyWith({
9
- DateTime? reminder,
10
- }) {
11
- return OnboardingState(
12
- reminder: reminder ?? this.reminder,
13
- );
14
- }
15
- }
@@ -1,78 +0,0 @@
1
- import 'dart:async';
2
-
3
- import 'package:flutter_riverpod/flutter_riverpod.dart';
4
- import 'package:kasy_kit/core/data/api/analytics_api.dart';
5
- import 'package:kasy_kit/core/states/user_state_notifier.dart';
6
- import 'package:kasy_kit/features/notifications/providers/models/notification.dart';
7
- import 'package:kasy_kit/features/notifications/repositories/notifications_repository.dart';
8
- import 'package:kasy_kit/features/onboarding/models/user_info.dart';
9
- import 'package:kasy_kit/features/onboarding/providers/onboarding_model.dart';
10
- import 'package:kasy_kit/features/onboarding/repositories/user_infos_repository.dart';
11
- import 'package:riverpod_annotation/riverpod_annotation.dart';
12
-
13
- part 'onboarding_provider.g.dart';
14
-
15
- extension OnboardingNotifierExt on WidgetRef {
16
- OnboardingNotifier get onboardingNotifier =>
17
- read(onboardingProvider.notifier);
18
-
19
- OnboardingState? get onboardingState$ => watch(onboardingProvider);
20
-
21
- OnboardingNotifier get onboardingState =>
22
- read(onboardingProvider.notifier);
23
- }
24
-
25
- @riverpod
26
- class OnboardingNotifier extends _$OnboardingNotifier {
27
- @override
28
- OnboardingState build() {
29
- return OnboardingState();
30
- }
31
-
32
- Future<void> onAnsweredQuestion(UserInfoDetail value) async {
33
- final userId = ref.read(userStateNotifierProvider).user.idOrNull;
34
- if (userId == null) return; // Guest: no account yet, skip Firestore write
35
- await ref.read(userInfosRepositoryProvider).save(userId, value);
36
- }
37
-
38
- Future<void> setupNotifications() async {
39
- final userStateNotifier = ref.read(userStateNotifierProvider.notifier);
40
- final notificationsRepository = ref.read(notificationRepositoryProvider);
41
-
42
- var permission = await notificationsRepository.getPermissionStatus();
43
- await permission.maybeAsk();
44
-
45
- // The system permission dialog causes the app to go to background.
46
- // The provider may have been disposed while waiting — bail out if so.
47
- if (!ref.mounted) return;
48
-
49
- permission = await notificationsRepository.getPermissionStatus();
50
-
51
- // save analytics event
52
- if (permission is NotificationPermissionGranted) {
53
- unawaited(
54
- ref
55
- .read(analyticsApiProvider)
56
- .logEvent('setup_notifications_accepted', {}),
57
- );
58
- }
59
-
60
- // Uncomment and create translations to schedule daily notification after user accept permissions
61
- // --
62
- // final translations = ref.read(translationsProvider);
63
- // await ref.read(localNotifierProvider).scheduleDailyAt(
64
- // title: translations.dailyNotification.title,
65
- // body: translations.dailyNotification.body,
66
- // date: state.reminder!,
67
- // );
68
-
69
- await userStateNotifier.refresh();
70
- }
71
-
72
- Future<void> onOnboardingCompleted() async {
73
- final userStateNotifier = ref.read(userStateNotifierProvider.notifier);
74
-
75
- await userStateNotifier.onOnboarded();
76
- await userStateNotifier.refresh();
77
- }
78
- }
@@ -1,29 +0,0 @@
1
- import 'package:flutter_riverpod/flutter_riverpod.dart';
2
- import 'package:kasy_kit/features/onboarding/api/user_infos_api.dart';
3
- import 'package:kasy_kit/features/onboarding/models/user_info.dart';
4
-
5
- final userInfosRepositoryProvider = Provider<UserInfosRepository>(
6
- (ref) => UserInfosRepository(
7
- userInfosApi: ref.read(userInfosApiProvider),
8
- ),
9
- );
10
-
11
- class UserInfosRepository {
12
- final UserInfosApi _userInfosApi;
13
-
14
- UserInfosRepository({
15
- required UserInfosApi userInfosApi,
16
- }) : _userInfosApi = userInfosApi;
17
-
18
- Future<void> save(String userId, UserInfoDetail info) async {
19
- final entity = info.toEntity();
20
-
21
- final alreadyExistingInfo =
22
- await _userInfosApi.getByKey(userId, entity.key);
23
- if (alreadyExistingInfo != null) {
24
- return _userInfosApi.update(userId, entity);
25
- }
26
-
27
- return _userInfosApi.create(userId, entity);
28
- }
29
- }
@@ -1,30 +0,0 @@
1
- import 'package:animations/animations.dart';
2
- import 'package:flutter/cupertino.dart';
3
- import 'package:kasy_kit/core/theme/theme.dart';
4
-
5
- class OnboardingRouteTransition extends CupertinoPageRoute {
6
- OnboardingRouteTransition({
7
- required super.builder,
8
- super.settings,
9
- });
10
-
11
- // OPTIONAL IF YOU WISH TO HAVE SOME EXTRA ANIMATION WHILE ROUTING
12
- @override
13
- Widget buildPage(
14
- BuildContext context,
15
- Animation<double> animation,
16
- Animation<double> secondaryAnimation,
17
- ) {
18
- // return FadeTransition(
19
- // opacity: animation,
20
- // child: builder(context),
21
- // );
22
- return SharedAxisTransition(
23
- animation: animation,
24
- secondaryAnimation: secondaryAnimation,
25
- transitionType: SharedAxisTransitionType.horizontal,
26
- fillColor: context.colors.background,
27
- child: builder(context),
28
- );
29
- }
30
- }