create-flutterinit 0.1.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 (237) hide show
  1. package/bin/index.ts +3 -0
  2. package/package.json +46 -0
  3. package/templates/base/.cursor/rules/flutter-project.mdc.hbs +132 -0
  4. package/templates/base/AGENTS.md.hbs +137 -0
  5. package/templates/base/DESIGN.md.hbs +149 -0
  6. package/templates/base/README.md.hbs +19 -0
  7. package/templates/base/SETUP.md.hbs +253 -0
  8. package/templates/base/analysis_options.yaml +88 -0
  9. package/templates/base/assets/.keep +1 -0
  10. package/templates/base/assets/icons/apple.svg +1 -0
  11. package/templates/base/assets/icons/facebook.svg +1 -0
  12. package/templates/base/assets/icons/google.svg +1 -0
  13. package/templates/base/assets/images/.gitkeep +0 -0
  14. package/templates/base/flutter_native_splash.yaml.hbs +8 -0
  15. package/templates/base/lib/main.dart.hbs +43 -0
  16. package/templates/base/lib/src/app.dart.hbs +105 -0
  17. package/templates/base/lib/src/config/app_config.dart.hbs +112 -0
  18. package/templates/base/lib/src/extensions/collection_extension.dart.hbs +55 -0
  19. package/templates/base/lib/src/extensions/context_extension.dart.hbs +167 -0
  20. package/templates/base/lib/src/extensions/date_time_extension.dart.hbs +40 -0
  21. package/templates/base/lib/src/extensions/extensions.dart.hbs +6 -0
  22. package/templates/base/lib/src/extensions/num_extension.dart.hbs +14 -0
  23. package/templates/base/lib/src/extensions/string_extension.dart.hbs +91 -0
  24. package/templates/base/lib/src/extensions/widget_extension.dart.hbs +47 -0
  25. package/templates/base/lib/src/imports/core_imports.dart.hbs +50 -0
  26. package/templates/base/lib/src/imports/imports.dart.hbs +2 -0
  27. package/templates/base/lib/src/imports/packages_imports.dart.hbs +110 -0
  28. package/templates/base/lib/src/routing/(isGetX)@app_bindings.dart.hbs +23 -0
  29. package/templates/base/lib/src/routing/app_router.dart.hbs +252 -0
  30. package/templates/base/lib/src/routing/app_routes.dart.hbs +46 -0
  31. package/templates/base/lib/src/routing/global_navigator.dart.hbs +15 -0
  32. package/templates/base/lib/src/services/auth_service.dart.hbs +78 -0
  33. package/templates/base/lib/src/services/copy_service.dart.hbs +16 -0
  34. package/templates/base/lib/src/services/internet_connection_service.dart.hbs +10 -0
  35. package/templates/base/lib/src/services/services.dart.hbs +45 -0
  36. package/templates/base/lib/src/shared/app_assets.dart.hbs +15 -0
  37. package/templates/base/lib/src/shared/enums/app_status.dart.hbs +40 -0
  38. package/templates/base/lib/src/shared/enums/button_enums.dart.hbs +22 -0
  39. package/templates/base/lib/src/shared/enums/enums.dart.hbs +3 -0
  40. package/templates/base/lib/src/shared/enums/snack_bar_type.dart.hbs +22 -0
  41. package/templates/base/lib/src/shared/helpers/format_number.dart.hbs +18 -0
  42. package/templates/base/lib/src/shared/helpers/imports.dart.hbs +3 -0
  43. package/templates/base/lib/src/shared/helpers/show_app_sheet.dart.hbs +42 -0
  44. package/templates/base/lib/src/shared/helpers/show_dialog.dart.hbs +49 -0
  45. package/templates/base/lib/src/shared/helpers/show_toast.dart.hbs +92 -0
  46. package/templates/base/lib/src/shared/hooks/hooks.dart.hbs +13 -0
  47. package/templates/base/lib/src/shared/hooks/use_copy.dart.hbs +41 -0
  48. package/templates/base/lib/src/shared/hooks/use_timer.dart.hbs +158 -0
  49. package/templates/base/lib/src/shared/shared.dart.hbs +12 -0
  50. package/templates/base/lib/src/shared/widgets/app_button.dart.hbs +144 -0
  51. package/templates/base/lib/src/shared/widgets/app_card.dart.hbs +126 -0
  52. package/templates/base/lib/src/shared/widgets/app_divider.dart.hbs +88 -0
  53. package/templates/base/lib/src/shared/widgets/app_empty_state.dart.hbs +73 -0
  54. package/templates/base/lib/src/shared/widgets/app_error_widget.dart.hbs +69 -0
  55. package/templates/base/lib/src/shared/widgets/app_icon.dart.hbs +25 -0
  56. package/templates/base/lib/src/shared/widgets/app_loading.dart.hbs +54 -0
  57. package/templates/base/lib/src/shared/widgets/app_text_field.dart.hbs +89 -0
  58. package/templates/base/lib/src/shared/widgets/app_top_bar.dart.hbs +105 -0
  59. package/templates/base/lib/src/shared/widgets/common_image.dart.hbs +120 -0
  60. package/templates/base/lib/src/shared/widgets/toast/imports.dart.hbs +4 -0
  61. package/templates/base/lib/src/shared/widgets/toast/raw_toast.dart.hbs +109 -0
  62. package/templates/base/lib/src/shared/widgets/toast/toast.dart.hbs +142 -0
  63. package/templates/base/lib/src/shared/widgets/toast/toast_card.dart.hbs +57 -0
  64. package/templates/base/lib/src/shared/widgets/widgets.dart.hbs +14 -0
  65. package/templates/base/lib/src/shared/wrappers/(isRiverpod,isProvider,isBloc,isGetX,isMobX)@state_wrapper.dart.hbs +51 -0
  66. package/templates/base/lib/src/shared/wrappers/(supportsLocalization)@localization_wrapper.dart.hbs +25 -0
  67. package/templates/base/lib/src/shared/wrappers/(usesScreenutil)@screen_util_wrapper.dart.hbs +27 -0
  68. package/templates/base/lib/src/shared/wrappers/(usesSkeletonizer)@skeleton_wrapper.dart.hbs +81 -0
  69. package/templates/base/lib/src/shared/wrappers/session_listener_wrapper.dart.hbs +258 -0
  70. package/templates/base/lib/src/shared/wrappers/wrappers.dart.hbs +15 -0
  71. package/templates/base/lib/src/theme/app_borders.dart.hbs +70 -0
  72. package/templates/base/lib/src/theme/app_curves.dart.hbs +69 -0
  73. package/templates/base/lib/src/theme/app_durations.dart.hbs +48 -0
  74. package/templates/base/lib/src/theme/app_shadows.dart.hbs +73 -0
  75. package/templates/base/lib/src/theme/app_spacing.dart.hbs +80 -0
  76. package/templates/base/lib/src/theme/color_schemes.dart.hbs +126 -0
  77. package/templates/base/lib/src/theme/text_theme.dart.hbs +167 -0
  78. package/templates/base/lib/src/theme/theme.dart.hbs +431 -0
  79. package/templates/base/lib/src/theme/theme_constants.dart.hbs +20 -0
  80. package/templates/base/lib/src/utils/app_utils.dart.hbs +46 -0
  81. package/templates/base/lib/src/utils/debouncer.dart.hbs +31 -0
  82. package/templates/base/lib/src/utils/error_handler.dart.hbs +14 -0
  83. package/templates/base/lib/src/utils/failure.dart.hbs +30 -0
  84. package/templates/base/lib/src/utils/input_formatters.dart.hbs +27 -0
  85. package/templates/base/lib/src/utils/logger.dart.hbs +37 -0
  86. package/templates/base/lib/src/utils/platform_info.dart.hbs +13 -0
  87. package/templates/base/lib/src/utils/task_runner.dart.hbs +42 -0
  88. package/templates/base/lib/src/utils/typedefs.dart.hbs +6 -0
  89. package/templates/base/lib/src/utils/utils.dart.hbs +9 -0
  90. package/templates/base/pubspec.yaml.hbs +256 -0
  91. package/templates/base/test/widget_test.dart.hbs +56 -0
  92. package/templates/overlays/architecture/clean/architecture.md +6 -0
  93. package/templates/overlays/architecture/clean/lib/src/features/auth/data/models/user_model.dart.hbs +1 -0
  94. package/templates/overlays/architecture/clean/lib/src/features/auth/data/repositories/auth_repository_impl.dart.hbs +1 -0
  95. package/templates/overlays/architecture/clean/lib/src/features/auth/domain/entities/user.dart.hbs +1 -0
  96. package/templates/overlays/architecture/clean/lib/src/features/auth/domain/repositories/auth_repository.dart.hbs +1 -0
  97. package/templates/overlays/architecture/clean/lib/src/features/auth/presentation/providers/(isBloc)@auth_bloc.dart.hbs +1 -0
  98. package/templates/overlays/architecture/clean/lib/src/features/auth/presentation/providers/(isBloc)@session_bloc.dart.hbs +1 -0
  99. package/templates/overlays/architecture/clean/lib/src/features/auth/presentation/providers/(isGetX)@auth_controller.dart.hbs +1 -0
  100. package/templates/overlays/architecture/clean/lib/src/features/auth/presentation/providers/(isGetX)@session_controller.dart.hbs +1 -0
  101. package/templates/overlays/architecture/clean/lib/src/features/auth/presentation/providers/(isMobX)@auth_store.dart.hbs +1 -0
  102. package/templates/overlays/architecture/clean/lib/src/features/auth/presentation/providers/(isMobX)@session_store.dart.hbs +1 -0
  103. package/templates/overlays/architecture/clean/lib/src/features/auth/presentation/providers/(isNoneState)@session_manager.dart.hbs +1 -0
  104. package/templates/overlays/architecture/clean/lib/src/features/auth/presentation/providers/(isProvider,isRiverpod)@auth_provider.dart.hbs +1 -0
  105. package/templates/overlays/architecture/clean/lib/src/features/auth/presentation/providers/(isProvider,isRiverpod)@session_provider.dart.hbs +1 -0
  106. package/templates/overlays/architecture/clean/lib/src/features/auth/presentation/screens/forgot_password_screen.dart.hbs +1 -0
  107. package/templates/overlays/architecture/clean/lib/src/features/auth/presentation/screens/login_screen.dart.hbs +1 -0
  108. package/templates/overlays/architecture/clean/lib/src/features/auth/presentation/screens/signup_screen.dart.hbs +1 -0
  109. package/templates/overlays/architecture/clean/lib/src/features/home/presentation/screens/home_page.dart.hbs +1 -0
  110. package/templates/overlays/architecture/clean/lib/src/features/onboarding/presentation/screens/onboarding_page.dart.hbs +1 -0
  111. package/templates/overlays/architecture/feature-first/architecture.md +5 -0
  112. package/templates/overlays/architecture/feature-first/lib/src/features/auth/data/models/user_model.dart.hbs +1 -0
  113. package/templates/overlays/architecture/feature-first/lib/src/features/auth/data/repositories/auth_repository_impl.dart.hbs +1 -0
  114. package/templates/overlays/architecture/feature-first/lib/src/features/auth/domain/entities/user.dart.hbs +1 -0
  115. package/templates/overlays/architecture/feature-first/lib/src/features/auth/domain/repositories/auth_repository.dart.hbs +1 -0
  116. package/templates/overlays/architecture/feature-first/lib/src/features/auth/presentation/providers/(isBloc)@auth_bloc.dart.hbs +1 -0
  117. package/templates/overlays/architecture/feature-first/lib/src/features/auth/presentation/providers/(isBloc)@session_bloc.dart.hbs +1 -0
  118. package/templates/overlays/architecture/feature-first/lib/src/features/auth/presentation/providers/(isGetX)@auth_controller.dart.hbs +1 -0
  119. package/templates/overlays/architecture/feature-first/lib/src/features/auth/presentation/providers/(isGetX)@session_controller.dart.hbs +1 -0
  120. package/templates/overlays/architecture/feature-first/lib/src/features/auth/presentation/providers/(isMobX)@auth_store.dart.hbs +1 -0
  121. package/templates/overlays/architecture/feature-first/lib/src/features/auth/presentation/providers/(isMobX)@session_store.dart.hbs +1 -0
  122. package/templates/overlays/architecture/feature-first/lib/src/features/auth/presentation/providers/(isNoneState)@session_manager.dart.hbs +1 -0
  123. package/templates/overlays/architecture/feature-first/lib/src/features/auth/presentation/providers/(isProvider,isRiverpod)@auth_provider.dart.hbs +1 -0
  124. package/templates/overlays/architecture/feature-first/lib/src/features/auth/presentation/providers/(isProvider,isRiverpod)@session_provider.dart.hbs +1 -0
  125. package/templates/overlays/architecture/feature-first/lib/src/features/auth/presentation/screens/forgot_password_screen.dart.hbs +1 -0
  126. package/templates/overlays/architecture/feature-first/lib/src/features/auth/presentation/screens/login_screen.dart.hbs +1 -0
  127. package/templates/overlays/architecture/feature-first/lib/src/features/auth/presentation/screens/signup_screen.dart.hbs +1 -0
  128. package/templates/overlays/architecture/feature-first/lib/src/features/home/presentation/screens/home_page.dart.hbs +1 -0
  129. package/templates/overlays/architecture/feature-first/lib/src/features/onboarding/presentation/screens/onboarding_page.dart.hbs +1 -0
  130. package/templates/overlays/architecture/layer-first/architecture.md +6 -0
  131. package/templates/overlays/architecture/layer-first/lib/src/data/datasources/local/.gitkeep +0 -0
  132. package/templates/overlays/architecture/layer-first/lib/src/data/datasources/remote/.gitkeep +0 -0
  133. package/templates/overlays/architecture/layer-first/lib/src/data/models/user_model.dart.hbs +1 -0
  134. package/templates/overlays/architecture/layer-first/lib/src/data/repositories/auth_repository_impl.dart.hbs +1 -0
  135. package/templates/overlays/architecture/layer-first/lib/src/domain/entities/user.dart.hbs +1 -0
  136. package/templates/overlays/architecture/layer-first/lib/src/domain/repositories/auth_repository.dart.hbs +1 -0
  137. package/templates/overlays/architecture/layer-first/lib/src/domain/usecases/auth/.gitkeep +0 -0
  138. package/templates/overlays/architecture/layer-first/lib/src/presentation/providers/(isBloc)@auth_bloc.dart.hbs +1 -0
  139. package/templates/overlays/architecture/layer-first/lib/src/presentation/providers/(isBloc)@session_bloc.dart.hbs +1 -0
  140. package/templates/overlays/architecture/layer-first/lib/src/presentation/providers/(isGetX)@auth_controller.dart.hbs +1 -0
  141. package/templates/overlays/architecture/layer-first/lib/src/presentation/providers/(isGetX)@session_controller.dart.hbs +1 -0
  142. package/templates/overlays/architecture/layer-first/lib/src/presentation/providers/(isMobX)@auth_store.dart.hbs +1 -0
  143. package/templates/overlays/architecture/layer-first/lib/src/presentation/providers/(isMobX)@session_store.dart.hbs +1 -0
  144. package/templates/overlays/architecture/layer-first/lib/src/presentation/providers/(isNoneState)@auth_view_model.dart.hbs +1 -0
  145. package/templates/overlays/architecture/layer-first/lib/src/presentation/providers/(isNoneState)@session_manager.dart.hbs +1 -0
  146. package/templates/overlays/architecture/layer-first/lib/src/presentation/providers/(isProvider,isRiverpod)@auth_provider.dart.hbs +1 -0
  147. package/templates/overlays/architecture/layer-first/lib/src/presentation/providers/(isProvider,isRiverpod)@session_provider.dart.hbs +1 -0
  148. package/templates/overlays/architecture/layer-first/lib/src/presentation/screens/auth/forgot_password_screen.dart.hbs +1 -0
  149. package/templates/overlays/architecture/layer-first/lib/src/presentation/screens/auth/login_screen.dart.hbs +1 -0
  150. package/templates/overlays/architecture/layer-first/lib/src/presentation/screens/auth/signup_screen.dart.hbs +1 -0
  151. package/templates/overlays/architecture/layer-first/lib/src/presentation/screens/home/home_page.dart.hbs +1 -0
  152. package/templates/overlays/architecture/layer-first/lib/src/presentation/screens/onboarding/onboarding_page.dart.hbs +1 -0
  153. package/templates/overlays/architecture/layer-first/lib/src/presentation/widgets/.gitkeep +0 -0
  154. package/templates/overlays/architecture/mvc/architecture.md +5 -0
  155. package/templates/overlays/architecture/mvc/lib/src/controllers/auth/(isBloc)@auth_bloc.dart.hbs +1 -0
  156. package/templates/overlays/architecture/mvc/lib/src/controllers/auth/(isBloc)@session_bloc.dart.hbs +1 -0
  157. package/templates/overlays/architecture/mvc/lib/src/controllers/auth/(isGetX)@auth_controller.dart.hbs +1 -0
  158. package/templates/overlays/architecture/mvc/lib/src/controllers/auth/(isGetX)@session_controller.dart.hbs +1 -0
  159. package/templates/overlays/architecture/mvc/lib/src/controllers/auth/(isMobX)@auth_store.dart.hbs +1 -0
  160. package/templates/overlays/architecture/mvc/lib/src/controllers/auth/(isMobX)@session_store.dart.hbs +1 -0
  161. package/templates/overlays/architecture/mvc/lib/src/controllers/auth/(isNoneState)@session_manager.dart.hbs +1 -0
  162. package/templates/overlays/architecture/mvc/lib/src/controllers/auth/(isProvider,isRiverpod)@auth_provider.dart.hbs +1 -0
  163. package/templates/overlays/architecture/mvc/lib/src/controllers/auth/(isProvider,isRiverpod)@session_provider.dart.hbs +1 -0
  164. package/templates/overlays/architecture/mvc/lib/src/models/user_model.dart.hbs +1 -0
  165. package/templates/overlays/architecture/mvc/lib/src/services/auth_repository.dart.hbs +1 -0
  166. package/templates/overlays/architecture/mvc/lib/src/services/auth_repository_impl.dart.hbs +1 -0
  167. package/templates/overlays/architecture/mvc/lib/src/views/auth/forgot_password_screen.dart.hbs +1 -0
  168. package/templates/overlays/architecture/mvc/lib/src/views/auth/login_screen.dart.hbs +1 -0
  169. package/templates/overlays/architecture/mvc/lib/src/views/auth/signup_screen.dart.hbs +1 -0
  170. package/templates/overlays/architecture/mvc/lib/src/views/home/home_page.dart.hbs +1 -0
  171. package/templates/overlays/architecture/mvc/lib/src/views/onboarding/onboarding_page.dart.hbs +1 -0
  172. package/templates/overlays/architecture/mvvm/architecture.md +6 -0
  173. package/templates/overlays/architecture/mvvm/lib/src/data/models/user_model.dart.hbs +1 -0
  174. package/templates/overlays/architecture/mvvm/lib/src/data/repositories/auth_repository.dart.hbs +1 -0
  175. package/templates/overlays/architecture/mvvm/lib/src/data/repositories/auth_repository_impl.dart.hbs +1 -0
  176. package/templates/overlays/architecture/mvvm/lib/src/ui/auth/(isBloc)@bloc/auth_bloc.dart.hbs +1 -0
  177. package/templates/overlays/architecture/mvvm/lib/src/ui/auth/(isBloc)@bloc/session_bloc.dart.hbs +1 -0
  178. package/templates/overlays/architecture/mvvm/lib/src/ui/auth/(isGetX)@controllers/auth_controller.dart.hbs +1 -0
  179. package/templates/overlays/architecture/mvvm/lib/src/ui/auth/(isGetX)@controllers/session_controller.dart.hbs +1 -0
  180. package/templates/overlays/architecture/mvvm/lib/src/ui/auth/(isMobX)@stores/auth_store.dart.hbs +1 -0
  181. package/templates/overlays/architecture/mvvm/lib/src/ui/auth/(isMobX)@stores/session_store.dart.hbs +1 -0
  182. package/templates/overlays/architecture/mvvm/lib/src/ui/auth/(isNoneState)@view_models/auth_view_model.dart.hbs +1 -0
  183. package/templates/overlays/architecture/mvvm/lib/src/ui/auth/(isNoneState)@view_models/session_manager.dart.hbs +1 -0
  184. package/templates/overlays/architecture/mvvm/lib/src/ui/auth/(isProvider,isRiverpod)@providers/auth_provider.dart.hbs +1 -0
  185. package/templates/overlays/architecture/mvvm/lib/src/ui/auth/(isProvider,isRiverpod)@providers/session_provider.dart.hbs +1 -0
  186. package/templates/overlays/architecture/mvvm/lib/src/ui/auth/forgot_password_screen.dart.hbs +1 -0
  187. package/templates/overlays/architecture/mvvm/lib/src/ui/auth/login_screen.dart.hbs +1 -0
  188. package/templates/overlays/architecture/mvvm/lib/src/ui/auth/signup_screen.dart.hbs +1 -0
  189. package/templates/overlays/architecture/mvvm/lib/src/ui/home/home_page.dart.hbs +1 -0
  190. package/templates/overlays/architecture/mvvm/lib/src/ui/onboarding/onboarding_page.dart.hbs +1 -0
  191. package/templates/overlays/backend/appwrite/lib/src/services/(usesAppwriteAuth)@auth_service.dart.hbs +101 -0
  192. package/templates/overlays/backend/custom/lib/src/services/auth_service.dart.hbs +158 -0
  193. package/templates/overlays/backend/firebase/lib/src/services/(usesFirebaseAuth)@auth_service.dart.hbs +96 -0
  194. package/templates/overlays/backend/supabase/lib/src/services/(usesSupabaseAuth)@auth_service.dart.hbs +97 -0
  195. package/templates/overlays/device/app_version_update/lib/src/services/(usesAppVersionUpdate)@version_update_service.dart.hbs +118 -0
  196. package/templates/overlays/device/device_info/lib/src/services/(usesDeviceInfoPlus)@device_info_service.dart.hbs +54 -0
  197. package/templates/overlays/extras/dotenv/.env.hbs +33 -0
  198. package/templates/overlays/extras/flavors/lib/src/flavors.dart.hbs +15 -0
  199. package/templates/overlays/extras/localization/assets/translations/en.json.hbs +47 -0
  200. package/templates/overlays/media/lib/src/services/(usesImagePicker,usesFilePicker)@media_service.dart.hbs +148 -0
  201. package/templates/overlays/networking/cached_image/lib/src/shared/widgets/app_cached_image.dart.hbs +160 -0
  202. package/templates/overlays/networking/dio/lib/src/services/(usesDio)@dio_service.dart.hbs +50 -0
  203. package/templates/overlays/networking/http/lib/src/services/(usesHttp)@http_service.dart.hbs +82 -0
  204. package/templates/overlays/storage/hive/lib/src/services/(usesHive)@hive_service.dart.hbs +59 -0
  205. package/templates/overlays/storage/secure_storage/lib/src/services/(usesSecureStorage)@secure_storage_service.dart.hbs +39 -0
  206. package/templates/overlays/storage/shared_preferences/lib/src/services/(usesSharedPreferences)@storage_service.dart.hbs +53 -0
  207. package/templates/overlays/utilities/geolocator/lib/src/services/(usesGeolocator)@location_service.dart.hbs +78 -0
  208. package/templates/overlays/utilities/path_provider/lib/src/services/(usesPathProvider)@path_service.dart.hbs +30 -0
  209. package/templates/overlays/utilities/permission_handler/lib/src/services/(usesPermissionHandler)@permission_service.dart.hbs +28 -0
  210. package/templates/overlays/utilities/permission_handler/lib/src/shared/hooks/use_permission.dart.hbs +44 -0
  211. package/templates/overlays/utilities/share_plus/lib/src/services/(usesSharePlus)@share_service.dart.hbs +22 -0
  212. package/templates/overlays/utilities/share_plus/lib/src/shared/hooks/use_share.dart.hbs +26 -0
  213. package/templates/overlays/utilities/url_launcher/lib/src/services/(usesUrlLauncher)@url_launcher_service.dart.hbs +37 -0
  214. package/templates/overlays/utilities/url_launcher/lib/src/shared/hooks/use_launch_url.dart.hbs +52 -0
  215. package/templates/partials/features/auth/auth_logic.hbs +611 -0
  216. package/templates/partials/features/auth/auth_repository.hbs +32 -0
  217. package/templates/partials/features/auth/auth_repository_impl.hbs +116 -0
  218. package/templates/partials/features/auth/forgot_password_screen.hbs +368 -0
  219. package/templates/partials/features/auth/login_screen.hbs +540 -0
  220. package/templates/partials/features/auth/session_provider.hbs +437 -0
  221. package/templates/partials/features/auth/signup_screen.hbs +511 -0
  222. package/templates/partials/features/auth/user_model.hbs +23 -0
  223. package/templates/partials/features/home/home_page.hbs +156 -0
  224. package/templates/partials/features/onboarding/onboarding_page.hbs +264 -0
  225. package/templates/partials/llm/add-feature-workflow.hbs +38 -0
  226. package/templates/partials/llm/architecture-rules.hbs +92 -0
  227. package/templates/partials/llm/backend-rules.hbs +30 -0
  228. package/templates/partials/llm/build-runner-note.hbs +15 -0
  229. package/templates/partials/llm/design-quick-ref.hbs +14 -0
  230. package/templates/partials/llm/design-quick-reference.hbs +14 -0
  231. package/templates/partials/llm/navigation-rules.hbs +27 -0
  232. package/templates/partials/llm/networking-rules.hbs +16 -0
  233. package/templates/partials/llm/packages-list.hbs +26 -0
  234. package/templates/partials/llm/services-conventions.hbs +20 -0
  235. package/templates/partials/llm/stack-summary.hbs +22 -0
  236. package/templates/partials/llm/state-management-rules.hbs +40 -0
  237. package/templates/partials/state_label.hbs +1 -0
@@ -0,0 +1,33 @@
1
+ # ─────────────────────────────────────────────────────────────
2
+ # {{appName}} — Environment Configuration
3
+ # ─────────────────────────────────────────────────────────────
4
+ # ⚠️ Never commit this file to version control — add .env to your .gitignore
5
+ # Copy this file, fill in your actual values, and rename to .env
6
+
7
+ {{#if (eq backend.provider "firebase")}}
8
+ # ── Firebase ──────────────────────────────────────────────────
9
+ FIREBASE_API_KEY=your-api-key
10
+ FIREBASE_AUTH_DOMAIN=your-project.firebaseapp.com
11
+ FIREBASE_PROJECT_ID=your-project-id
12
+ FIREBASE_STORAGE_BUCKET=your-project.appspot.com
13
+ FIREBASE_MESSAGING_SENDER_ID=your-sender-id
14
+ FIREBASE_APP_ID=your-app-id
15
+ FIREBASE_MEASUREMENT_ID=your-measurement-id
16
+ {{/if}}
17
+ {{#if (eq backend.provider "supabase")}}
18
+ # ── Supabase ──────────────────────────────────────────────────
19
+ SUPABASE_URL=https://your-project.supabase.co
20
+ SUPABASE_ANON_KEY=your-anon-key
21
+ {{/if}}
22
+ {{#if (eq backend.provider "appwrite")}}
23
+ # ── Appwrite ──────────────────────────────────────────────────
24
+ APPWRITE_ENDPOINT=https://cloud.appwrite.io/v1
25
+ APPWRITE_PROJECT_ID=your-project-id
26
+ {{/if}}
27
+ {{#if (or flags.usesDio flags.usesHttp)}}
28
+ # ── API ───────────────────────────────────────────────────────
29
+ API_BASE_URL={{#if (eq backend.provider "customRest")}}{{backend.options.baseUrl}}{{else}}https://api.example.com{{/if}}
30
+ {{/if}}
31
+ # ── App ───────────────────────────────────────────────────────
32
+ APP_NAME={{appName}}
33
+ APP_ENV=development
@@ -0,0 +1,15 @@
1
+ enum Flavor { dev, staging, prod }
2
+
3
+ class FlavorConfig {
4
+ FlavorConfig(this.flavor);
5
+
6
+ final Flavor flavor;
7
+
8
+ static late FlavorConfig current;
9
+
10
+ static void load(Flavor flavor) {
11
+ current = FlavorConfig(flavor);
12
+ }
13
+
14
+ static String get name => current.flavor.name;
15
+ }
@@ -0,0 +1,47 @@
1
+ {
2
+ "shared": {
3
+ "get_started": "Get Started"
4
+ },
5
+ "onboarding": {
6
+ "onboarding_title_1": "Your Journey,\nPerfectly Planned",
7
+ "onboarding_subtitle_1": "Effortlessly create and organize your\ndream trips. Start exploring now!",
8
+ "onboarding_title_2": "Discover\nFriends Nearby",
9
+ "onboarding_subtitle_2": "See where your friends are traveling and\nexplore the world together.",
10
+ "onboarding_title_3": "Stay Updated\nwith Top Places",
11
+ "onboarding_subtitle_3": "Find trending destinations and must-see attractions,\nall tailored to enhance your travel plans."
12
+ },
13
+ "home": {
14
+ "home_title": "Home",
15
+ "welcome_home": "Welcome Home!",
16
+ "home_subtitle": "You have successfully completed the onboarding process."
17
+ },
18
+ "auth": {
19
+ "log_in": "Log in",
20
+ "log_in_subtitle": "Enter your email and password",
21
+ "email": "Email",
22
+ "email_required": "Email is required",
23
+ "email_invalid": "Enter a valid email",
24
+ "password": "Password",
25
+ "password_required": "Password is required",
26
+ "password_too_short": "Password must be at least 6 characters",
27
+ "remember_me": "Remember me",
28
+ "forgot_password": "Forgot Password",
29
+ "login_button": "Login",
30
+ "dont_have_account": "Don't have an account? ",
31
+ "sign_up": "Sign Up",
32
+ "create_account": "Create Account",
33
+ "create_account_subtitle": "Create a new account to get started.",
34
+ "name": "Name",
35
+ "name_required": "Name is required",
36
+ "confirm_password": "Confirm Password",
37
+ "confirm_password_required": "Confirm password is required",
38
+ "passwords_do_not_match": "Passwords do not match",
39
+ "create_account_button": "Create Account",
40
+ "already_have_account": "Already have an account? ",
41
+ "sign_in": "Sign In",
42
+ "or_continue_with": "Or Continue With Account",
43
+ "forgot_password_title": "Forgot Password",
44
+ "forgot_password_subtitle": "Enter your email address to receive a reset link\nand regain access to your account.",
45
+ "reset_link_sent": "Password reset link sent to your email."
46
+ }
47
+ }
@@ -0,0 +1,148 @@
1
+ import 'dart:io';
2
+ {{#if flags.usesImagePicker}}
3
+ import 'package:image_picker/image_picker.dart';
4
+ {{/if}}
5
+ {{#if flags.usesFilePicker}}
6
+ import 'package:file_picker/file_picker.dart';
7
+ {{/if}}
8
+ {{#if flags.usesPermissionHandler}}
9
+ import 'package:permission_handler/permission_handler.dart';
10
+ {{/if}}
11
+ import '../utils/utils.dart';
12
+
13
+ /// A service to handle media selection (images, videos, files).
14
+ class MediaService {
15
+ MediaService._();
16
+ static final MediaService instance = MediaService._();
17
+
18
+ {{#if flags.usesImagePicker}}
19
+ final ImagePicker _imagePicker = ImagePicker();
20
+
21
+ /// Pick an image from gallery or camera.
22
+ FutureEither<File?> pickImage({
23
+ required ImageSource source,
24
+ double? maxWidth,
25
+ double? maxHeight,
26
+ int? imageQuality,
27
+ }) async {
28
+ return runTask(() async {
29
+ {{#if flags.usesPermissionHandler}}
30
+ // Check permissions
31
+ if (source == ImageSource.camera) {
32
+ final status = await Permission.camera.request();
33
+ if (!status.isGranted) {
34
+ throw Exception('Camera permission denied');
35
+ }
36
+ } else {
37
+ if (Platform.isAndroid || Platform.isIOS) {
38
+ final status = await Permission.photos.request();
39
+ if (!status.isGranted && !status.isLimited) {
40
+ throw Exception('Photos permission denied');
41
+ }
42
+ }
43
+ }
44
+ {{/if}}
45
+
46
+ final XFile? file = await _imagePicker.pickImage(
47
+ source: source,
48
+ maxWidth: maxWidth,
49
+ maxHeight: maxHeight,
50
+ imageQuality: imageQuality,
51
+ );
52
+
53
+ return file != null ? File(file.path) : null;
54
+ });
55
+ }
56
+
57
+ /// Pick multiple images from gallery.
58
+ FutureEither<List<File>> pickMultiImage({
59
+ double? maxWidth,
60
+ double? maxHeight,
61
+ int? imageQuality,
62
+ }) async {
63
+ return runTask(() async {
64
+ {{#if flags.usesPermissionHandler}}
65
+ if (Platform.isAndroid || Platform.isIOS) {
66
+ final status = await Permission.photos.request();
67
+ if (!status.isGranted && !status.isLimited) {
68
+ throw Exception('Photos permission denied');
69
+ }
70
+ }
71
+ {{/if}}
72
+
73
+ final List<XFile> files = await _imagePicker.pickMultiImage(
74
+ maxWidth: maxWidth,
75
+ maxHeight: maxHeight,
76
+ imageQuality: imageQuality,
77
+ );
78
+
79
+ return files.map((file) => File(file.path)).toList();
80
+ });
81
+ }
82
+
83
+ /// Pick a video from gallery or camera.
84
+ FutureEither<File?> pickVideo({
85
+ required ImageSource source,
86
+ Duration? maxDuration,
87
+ }) async {
88
+ return runTask(() async {
89
+ {{#if flags.usesPermissionHandler}}
90
+ if (source == ImageSource.camera) {
91
+ final status = await Permission.camera.request();
92
+ if (!status.isGranted) {
93
+ throw Exception('Camera permission denied');
94
+ }
95
+ } else {
96
+ if (Platform.isAndroid || Platform.isIOS) {
97
+ final status = await Permission.photos.request();
98
+ if (!status.isGranted && !status.isLimited) {
99
+ throw Exception('Photos permission denied');
100
+ }
101
+ }
102
+ }
103
+ {{/if}}
104
+
105
+ final XFile? file = await _imagePicker.pickVideo(
106
+ source: source,
107
+ maxDuration: maxDuration,
108
+ );
109
+
110
+ return file != null ? File(file.path) : null;
111
+ });
112
+ }
113
+ {{/if}}
114
+
115
+ {{#if flags.usesFilePicker}}
116
+ /// Pick one or more files from the device.
117
+ FutureEither<List<File>> pickFiles({
118
+ FileType type = FileType.any,
119
+ List<String>? allowedExtensions,
120
+ bool allowMultiple = false,
121
+ }) async {
122
+ return runTask(() async {
123
+ {{#if flags.usesPermissionHandler}}
124
+ if (Platform.isAndroid) {
125
+ final status = await Permission.storage.request();
126
+ if (!status.isGranted) {
127
+ // Note: On Android 13+, storage permission might be handled differently (media-specific)
128
+ // but permission_handler usually handles the abstraction.
129
+ }
130
+ }
131
+ {{/if}}
132
+
133
+ final FilePickerResult? result = await FilePicker.platform.pickFiles(
134
+ type: type,
135
+ allowedExtensions: allowedExtensions,
136
+ allowMultiple: allowMultiple,
137
+ );
138
+
139
+ if (result == null || result.files.isEmpty) return [];
140
+
141
+ return result.paths
142
+ .where((path) => path != null)
143
+ .map((path) => File(path!))
144
+ .toList();
145
+ });
146
+ }
147
+ {{/if}}
148
+ }
@@ -0,0 +1,160 @@
1
+ import '../../imports/core_imports.dart';
2
+ import '../../imports/packages_imports.dart';
3
+
4
+
5
+ /// A premium, highly customizable wrapper around [CachedNetworkImage].
6
+ ///
7
+ /// This widget provides smooth transitions, specialized error handling,
8
+ /// and integrates with the project's design system.
9
+ class AppCachedImage extends StatelessWidget {
10
+ /// The URL of the image to display.
11
+ final String imageUrl;
12
+
13
+ /// Optional width for the image.
14
+ final double? width;
15
+
16
+ /// Optional height for the image.
17
+ final double? height;
18
+
19
+ /// How the image should be inscribed into the box.
20
+ final BoxFit fit;
21
+
22
+ /// Optional placeholder displayed while the image is loading.
23
+ /// If null, a shimmer or loading indicator is shown.
24
+ final Widget? placeholder;
25
+
26
+ /// Optional widget displayed if the image fails to load.
27
+ final Widget? errorWidget;
28
+
29
+ /// [Optional] color to be combined with the image.
30
+ final Color? color;
31
+
32
+ /// [Optional] blend mode for the [color].
33
+ final BlendMode? colorBlendMode;
34
+
35
+ /// The borderRadius of the image.
36
+ final BorderRadius? borderRadius;
37
+
38
+ /// The duration of the fade-in animation.
39
+ final Duration? fadeInDuration;
40
+
41
+ /// How to align the image within its bounds.
42
+ final Alignment alignment;
43
+
44
+ /// If true, the image will be wrapped in a [Skeletonizer] during loading.
45
+ final bool useSkeleton;
46
+
47
+ /// Optional key to use for caching.
48
+ final String? cacheKey;
49
+
50
+ const AppCachedImage({
51
+ super.key,
52
+ required this.imageUrl,
53
+ this.width,
54
+ this.height,
55
+ this.fit = BoxFit.cover,
56
+ this.placeholder,
57
+ this.errorWidget,
58
+ this.color,
59
+ this.colorBlendMode,
60
+ this.borderRadius,
61
+ this.fadeInDuration,
62
+ this.alignment = Alignment.center,
63
+ this.useSkeleton = true,
64
+ this.cacheKey,
65
+ });
66
+
67
+ @override
68
+ Widget build(BuildContext context) {
69
+ // Adjust sizing for screenutil if enabled
70
+ final double? adjustedWidth = {{#if flags.usesScreenutil}}width?.w{{else}}width{{/if}};
71
+ final double? adjustedHeight = {{#if flags.usesScreenutil}}height?.h{{else}}height{{/if}};
72
+
73
+ Widget imageContent = CachedNetworkImage(
74
+ imageUrl: imageUrl,
75
+ cacheKey: cacheKey,
76
+ width: adjustedWidth,
77
+ height: adjustedHeight,
78
+ fit: fit,
79
+ color: color,
80
+ colorBlendMode: colorBlendMode,
81
+ alignment: alignment,
82
+ fadeInDuration: fadeInDuration ?? const Duration(milliseconds: 500),
83
+ placeholder: (context, url) => placeholder ?? _buildDefaultPlaceholder(context),
84
+ errorWidget: (context, url, error) => errorWidget ?? _buildDefaultErrorWidget(context),
85
+ );
86
+
87
+ if (borderRadius != null) {
88
+ imageContent = ClipRRect(
89
+ borderRadius: borderRadius!,
90
+ child: imageContent,
91
+ );
92
+ }
93
+
94
+ return imageContent;
95
+ }
96
+
97
+ Widget _buildDefaultPlaceholder(BuildContext context) {
98
+ if (useSkeleton) {
99
+ {{#if flags.usesSkeletonizer}}
100
+ return Skeletonizer(
101
+ enabled: true,
102
+ child: Container(
103
+ width: width,
104
+ height: height,
105
+ decoration: BoxDecoration(
106
+ color: context.theme.colorScheme.surfaceContainerHighest,
107
+ borderRadius: borderRadius,
108
+ ),
109
+ ),
110
+ );
111
+ {{else}}
112
+ return _buildLoadingIndicator(context);
113
+ {{/if}}
114
+ }
115
+ return _buildLoadingIndicator(context);
116
+ }
117
+
118
+ Widget _buildLoadingIndicator(BuildContext context) {
119
+ return Container(
120
+ width: width,
121
+ height: height,
122
+ color: context.theme.colorScheme.surfaceContainerHighest .withValues(alpha: 0.9),
123
+ child: const Center(
124
+ child: CircularProgressIndicator(strokeWidth: 2),
125
+ ),
126
+ );
127
+ }
128
+
129
+ Widget _buildDefaultErrorWidget(BuildContext context) {
130
+ return Container(
131
+ width: width,
132
+ height: height,
133
+ color: context.theme.colorScheme.errorContainer .withValues(alpha: 0.9),
134
+ child: Center(
135
+ child: {{#if flags.usesHugeicons}}
136
+ HugeIcon(
137
+ icon: HugeIcons.strokeRoundedImageNotFound02,
138
+ color: context.theme.colorScheme.error,
139
+ size: 24,
140
+ )
141
+ {{else if flags.usesIconsaxPlus}}
142
+ Icon(
143
+ IconsaxPlusBold.image,
144
+ color: context.theme.colorScheme.error,
145
+ )
146
+ {{else if flags.usesFlutterRemix}}
147
+ Icon(
148
+ FlutterRemix.image_line,
149
+ color: context.theme.colorScheme.error,
150
+ )
151
+ {{else}}
152
+ Icon(
153
+ Icons.broken_image_outlined,
154
+ color: context.theme.colorScheme.error,
155
+ )
156
+ {{/if}},
157
+ ),
158
+ );
159
+ }
160
+ }
@@ -0,0 +1,50 @@
1
+ import 'package:dio/dio.dart';
2
+ import '../config/app_config.dart';
3
+ import '../utils/utils.dart';
4
+
5
+ /// A robust networking service powered by Dio.
6
+ class DioService {
7
+ DioService._();
8
+ static final DioService instance = DioService._();
9
+
10
+ // --- HTTP Methods ---
11
+
12
+ FutureEither<Response<dynamic>> get(
13
+ String path, {
14
+ Map<String, dynamic>? queryParameters,
15
+ }) {
16
+ return runTask(() => AppConfig.dio.get(path, queryParameters: queryParameters), requiresNetwork: true);
17
+ }
18
+
19
+ FutureEither<Response<dynamic>> post(
20
+ String path, {
21
+ dynamic data,
22
+ Map<String, dynamic>? queryParameters,
23
+ }) {
24
+ return runTask(() => AppConfig.dio.post(path, data: data, queryParameters: queryParameters), requiresNetwork: true);
25
+ }
26
+
27
+ FutureEither<Response<dynamic>> put(
28
+ String path, {
29
+ dynamic data,
30
+ Map<String, dynamic>? queryParameters,
31
+ }) {
32
+ return runTask(() => AppConfig.dio.put(path, data: data, queryParameters: queryParameters), requiresNetwork: true);
33
+ }
34
+
35
+ FutureEither<Response<dynamic>> patch(
36
+ String path, {
37
+ dynamic data,
38
+ Map<String, dynamic>? queryParameters,
39
+ }) {
40
+ return runTask(() => AppConfig.dio.patch(path, data: data, queryParameters: queryParameters), requiresNetwork: true);
41
+ }
42
+
43
+ FutureEither<Response<dynamic>> delete(
44
+ String path, {
45
+ dynamic data,
46
+ Map<String, dynamic>? queryParameters,
47
+ }) {
48
+ return runTask(() => AppConfig.dio.delete(path, data: data, queryParameters: queryParameters), requiresNetwork: true);
49
+ }
50
+ }
@@ -0,0 +1,82 @@
1
+ import 'dart:convert';
2
+ import 'package:http/http.dart' as http;
3
+ import '../config/app_config.dart';
4
+ import '../utils/utils.dart';
5
+
6
+ /// A lightweight networking service powered by the http package.
7
+ class HttpService {
8
+ HttpService._();
9
+ static final HttpService instance = HttpService._();
10
+
11
+ // --- HTTP Methods ---
12
+
13
+ FutureEither<http.Response> get(String path, {Map<String, String>? headers}) {
14
+ return runTask(() async {
15
+ final response = await AppConfig.httpClient.get(
16
+ Uri.parse('${AppConfig.baseUrl}$path'),
17
+ headers: {
18
+ 'Content-Type': 'application/json',
19
+ 'Accept': 'application/json',
20
+ ...?headers,
21
+ },
22
+ );
23
+ _logResponse(response);
24
+ return response;
25
+ }, requiresNetwork: true);
26
+ }
27
+
28
+ FutureEither<http.Response> post(String path, {dynamic body, Map<String, String>? headers}) {
29
+ return runTask(() async {
30
+ final response = await AppConfig.httpClient.post(
31
+ Uri.parse('${AppConfig.baseUrl}$path'),
32
+ headers: {
33
+ 'Content-Type': 'application/json',
34
+ 'Accept': 'application/json',
35
+ ...?headers,
36
+ },
37
+ body: jsonEncode(body),
38
+ );
39
+ _logResponse(response);
40
+ return response;
41
+ }, requiresNetwork: true);
42
+ }
43
+
44
+ FutureEither<http.Response> put(String path, {dynamic body, Map<String, String>? headers}) {
45
+ return runTask(() async {
46
+ final response = await AppConfig.httpClient.put(
47
+ Uri.parse('${AppConfig.baseUrl}$path'),
48
+ headers: {
49
+ 'Content-Type': 'application/json',
50
+ 'Accept': 'application/json',
51
+ ...?headers,
52
+ },
53
+ body: jsonEncode(body),
54
+ );
55
+ _logResponse(response);
56
+ return response;
57
+ }, requiresNetwork: true);
58
+ }
59
+
60
+ FutureEither<http.Response> delete(String path, {Map<String, String>? headers}) {
61
+ return runTask(() async {
62
+ final response = await AppConfig.httpClient.delete(
63
+ Uri.parse('${AppConfig.baseUrl}$path'),
64
+ headers: {
65
+ 'Content-Type': 'application/json',
66
+ 'Accept': 'application/json',
67
+ ...?headers,
68
+ },
69
+ );
70
+ _logResponse(response);
71
+ return response;
72
+ }, requiresNetwork: true);
73
+ }
74
+
75
+ void _logResponse(http.Response response) {
76
+ if (response.statusCode >= 200 && response.statusCode < 300) {
77
+ AppLogger.debug('✅ [HTTP] RESPONSE[${response.statusCode}] => ${response.request?.url}');
78
+ } else {
79
+ AppLogger.error('❌ [HTTP] RESPONSE[${response.statusCode}] => ${response.request?.url}');
80
+ }
81
+ }
82
+ }
@@ -0,0 +1,59 @@
1
+ import 'package:hive_ce_flutter/hive_ce_flutter.dart';
2
+ import '../utils/utils.dart';
3
+
4
+ /// A robust [Hive] storage service for local NoSQL persistence.
5
+ class HiveService {
6
+ HiveService._();
7
+ static final HiveService instance = HiveService._();
8
+
9
+ /// Initialize Hive and open a default box.
10
+ FutureEither<void> init() async {
11
+ return runTask(() async {
12
+ await Hive.initFlutter();
13
+ await Hive.openBox<dynamic>('app_box');
14
+ AppLogger.info('Hive initialized');
15
+ });
16
+ }
17
+
18
+ /// Get data from the default box.
19
+ T? get<T>(String key, {T? defaultValue}) {
20
+ try {
21
+ final box = Hive.box<dynamic>('app_box');
22
+ return box.get(key, defaultValue: defaultValue) as T?;
23
+ } catch (e) {
24
+ AppLogger.error('Hive get error', e);
25
+ return defaultValue;
26
+ }
27
+ }
28
+
29
+ /// Put data into the default box.
30
+ FutureEither<void> put(String key, dynamic value) async {
31
+ return runTask(() async {
32
+ final box = Hive.box<dynamic>('app_box');
33
+ await box.put(key, value);
34
+ });
35
+ }
36
+
37
+ /// Delete data from the default box.
38
+ FutureEither<void> delete(String key) async {
39
+ return runTask(() async {
40
+ final box = Hive.box<dynamic>('app_box');
41
+ await box.delete(key);
42
+ });
43
+ }
44
+
45
+ /// Clear all data from the default box.
46
+ FutureEither<void> clear() async {
47
+ return runTask(() async {
48
+ final box = Hive.box<dynamic>('app_box');
49
+ await box.clear();
50
+ });
51
+ }
52
+
53
+ /// Open a custom box for specialized storage.
54
+ FutureEither<Box<T>> openCustomBox<T>(String name) async {
55
+ return runTask(() async {
56
+ return await Hive.openBox<T>(name);
57
+ });
58
+ }
59
+ }
@@ -0,0 +1,39 @@
1
+ import 'package:flutter_secure_storage/flutter_secure_storage.dart';
2
+ import '../utils/utils.dart';
3
+
4
+ /// A service to securely store sensitive data like JWT tokens or API keys.
5
+ ///
6
+ /// Uses [FlutterSecureStorage] which utilizes Keychain (iOS) and Keystore (Android).
7
+ class SecureStorageService {
8
+ SecureStorageService._();
9
+ static final SecureStorageService instance = SecureStorageService._();
10
+
11
+ final _storage = const FlutterSecureStorage(
12
+ aOptions: AndroidOptions.defaultOptions,
13
+ );
14
+
15
+ /// Write a sensitive value to secure storage.
16
+ FutureEither<void> write(String key, String value) async {
17
+ return runTask(() => _storage.write(key: key, value: value));
18
+ }
19
+
20
+ /// Read a sensitive value from secure storage.
21
+ FutureEither<String?> read(String key) async {
22
+ return runTask(() => _storage.read(key: key));
23
+ }
24
+
25
+ /// Delete a specific key from secure storage.
26
+ FutureEither<void> delete(String key) async {
27
+ return runTask(() => _storage.delete(key: key));
28
+ }
29
+
30
+ /// Wipe all data from secure storage.
31
+ FutureEither<void> deleteAll() async {
32
+ return runTask(() => _storage.deleteAll());
33
+ }
34
+
35
+ /// Check if a key exists in secure storage.
36
+ FutureEither<bool> containsKey(String key) async {
37
+ return runTask(() => _storage.containsKey(key: key));
38
+ }
39
+ }
@@ -0,0 +1,53 @@
1
+ import 'package:shared_preferences/shared_preferences.dart';
2
+ import '../utils/utils.dart';
3
+
4
+ /// A wrapper around [SharedPreferences] for simple key-value persistence.
5
+ class StorageService {
6
+ StorageService._();
7
+ static final StorageService instance = StorageService._();
8
+
9
+ late final SharedPreferences _prefs;
10
+
11
+ /// Initialize SharedPreferences instance.
12
+ FutureEither<void> init() async {
13
+ return runTask(() async {
14
+ _prefs = await SharedPreferences.getInstance();
15
+ AppLogger.info('StorageService (SharedPreferences) initialized');
16
+ });
17
+ }
18
+
19
+ // --- SETTERS ---
20
+
21
+ FutureEither<bool> setString(String key, String value) async =>
22
+ runTask(() => _prefs.setString(key, value));
23
+
24
+ FutureEither<bool> setBool(String key, bool value) async =>
25
+ runTask(() => _prefs.setBool(key, value));
26
+
27
+ FutureEither<bool> setInt(String key, int value) async =>
28
+ runTask(() => _prefs.setInt(key, value));
29
+
30
+ FutureEither<bool> setDouble(String key, double value) async =>
31
+ runTask(() => _prefs.setDouble(key, value));
32
+
33
+ FutureEither<bool> setStringList(String key, List<String> value) async =>
34
+ runTask(() => _prefs.setStringList(key, value));
35
+
36
+ // --- GETTERS ---
37
+
38
+ String? getString(String key) => _prefs.getString(key);
39
+ bool? getBool(String key) => _prefs.getBool(key);
40
+ int? getInt(String key) => _prefs.getInt(key);
41
+ double? getDouble(String key) => _prefs.getDouble(key);
42
+ List<String>? getStringList(String key) => _prefs.getStringList(key);
43
+
44
+ // --- COMMON ---
45
+
46
+ bool containsKey(String key) => _prefs.containsKey(key);
47
+
48
+ FutureEither<bool> remove(String key) async =>
49
+ runTask(() => _prefs.remove(key));
50
+
51
+ FutureEither<bool> clear() async =>
52
+ runTask(() => _prefs.clear());
53
+ }