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,431 @@
1
+ import 'package:flutter/material.dart';
2
+ import 'package:flutter/cupertino.dart';
3
+
4
+ import 'text_theme.dart';
5
+ import 'color_schemes.dart';
6
+
7
+ Color _colorFromHex(String hex) {
8
+ final cleaned = hex.replaceFirst('#', '');
9
+ return Color(int.parse('ff$cleaned', radix: 16));
10
+ }
11
+
12
+ /// Custom theme extension for spacing and other design tokens
13
+ class AppDesignTokens extends ThemeExtension<AppDesignTokens> {
14
+ const AppDesignTokens({
15
+ required this.paddingSmall,
16
+ required this.paddingMedium,
17
+ required this.paddingLarge,
18
+ required this.borderRadiusSmall,
19
+ required this.borderRadiusMedium,
20
+ required this.borderRadiusLarge,
21
+ required this.cardElevation,
22
+ });
23
+
24
+ final double paddingSmall;
25
+ final double paddingMedium;
26
+ final double paddingLarge;
27
+ final double borderRadiusSmall;
28
+ final double borderRadiusMedium;
29
+ final double borderRadiusLarge;
30
+ final double cardElevation;
31
+
32
+ static const fallback = AppDesignTokens(
33
+ paddingSmall: 8,
34
+ paddingMedium: 16,
35
+ paddingLarge: 24,
36
+ borderRadiusSmall: 4,
37
+ borderRadiusMedium: 12,
38
+ borderRadiusLarge: 24,
39
+ cardElevation: 0,
40
+ );
41
+
42
+ @override
43
+ ThemeExtension<AppDesignTokens> copyWith({
44
+ double? paddingSmall,
45
+ double? paddingMedium,
46
+ double? paddingLarge,
47
+ double? borderRadiusSmall,
48
+ double? borderRadiusMedium,
49
+ double? borderRadiusLarge,
50
+ double? cardElevation,
51
+ }) {
52
+ return AppDesignTokens(
53
+ paddingSmall: paddingSmall ?? this.paddingSmall,
54
+ paddingMedium: paddingMedium ?? this.paddingMedium,
55
+ paddingLarge: paddingLarge ?? this.paddingLarge,
56
+ borderRadiusSmall: borderRadiusSmall ?? this.borderRadiusSmall,
57
+ borderRadiusMedium: borderRadiusMedium ?? this.borderRadiusMedium,
58
+ borderRadiusLarge: borderRadiusLarge ?? this.borderRadiusLarge,
59
+ cardElevation: cardElevation ?? this.cardElevation,
60
+ );
61
+ }
62
+
63
+ @override
64
+ ThemeExtension<AppDesignTokens> lerp(
65
+ covariant ThemeExtension<AppDesignTokens>? other,
66
+ double t,
67
+ ) {
68
+ if (other is! AppDesignTokens) return this;
69
+ return AppDesignTokens(
70
+ paddingSmall: lerpDouble(paddingSmall, other.paddingSmall, t)!,
71
+ paddingMedium: lerpDouble(paddingMedium, other.paddingMedium, t)!,
72
+ paddingLarge: lerpDouble(paddingLarge, other.paddingLarge, t)!,
73
+ borderRadiusSmall: lerpDouble(borderRadiusSmall, other.borderRadiusSmall, t)!,
74
+ borderRadiusMedium: lerpDouble(borderRadiusMedium, other.borderRadiusMedium, t)!,
75
+ borderRadiusLarge: lerpDouble(borderRadiusLarge, other.borderRadiusLarge, t)!,
76
+ cardElevation: lerpDouble(cardElevation, other.cardElevation, t)!,
77
+ );
78
+ }
79
+
80
+ static double? lerpDouble(double? a, double? b, double t) {
81
+ if (a == null && b == null) return null;
82
+ a ??= 0.0;
83
+ b ??= 0.0;
84
+ return a + (b - a) * t;
85
+ }
86
+ }
87
+
88
+ ThemeData _buildTheme(ColorScheme colorScheme, AppColorsExtension customColors) {
89
+ final textTheme = buildTextTheme();
90
+
91
+ return ThemeData(
92
+ useMaterial3: true,
93
+ primaryColor: colorScheme.primary,
94
+ colorScheme: colorScheme,
95
+ textTheme: textTheme,
96
+ {{#if flags.hasCustomFonts}}
97
+ fontFamily: '{{flags.primaryFontFamily}}',
98
+ {{/if}}
99
+ extensions: [
100
+ customColors,
101
+ AppDesignTokens.fallback,
102
+ ],
103
+
104
+ // --- Basic Elements ---
105
+ scaffoldBackgroundColor: colorScheme.surface,
106
+ dividerTheme: DividerThemeData(
107
+ color: colorScheme.outlineVariant,
108
+ thickness: 1,
109
+ space: 1,
110
+ ),
111
+ iconTheme: IconThemeData(
112
+ color: colorScheme.onSurface,
113
+ size: 24,
114
+ ),
115
+
116
+ // --- Widget Themes ---
117
+
118
+ // App Bar Theme
119
+ appBarTheme: AppBarTheme(
120
+ backgroundColor: colorScheme.surface,
121
+ foregroundColor: colorScheme.onSurface,
122
+ elevation: 0,
123
+ centerTitle: true,
124
+ titleTextStyle: textTheme.titleMedium?.copyWith(
125
+ fontWeight: FontWeight.w600,
126
+ color: colorScheme.onSurface,
127
+ ),
128
+ ),
129
+
130
+ // Button Themes
131
+ elevatedButtonTheme: ElevatedButtonThemeData(
132
+ style: ElevatedButton.styleFrom(
133
+ backgroundColor: colorScheme.primary,
134
+ foregroundColor: colorScheme.onPrimary,
135
+ minimumSize: const Size(88, 48),
136
+ padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
137
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
138
+ elevation: 0,
139
+ ).copyWith(
140
+ elevation: WidgetStateProperty.resolveWith((states) {
141
+ if (states.contains(WidgetState.hovered)) return 2;
142
+ return 0;
143
+ }),
144
+ ),
145
+ ),
146
+
147
+ filledButtonTheme: FilledButtonThemeData(
148
+ style: FilledButton.styleFrom(
149
+ minimumSize: const Size(88, 48),
150
+ padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
151
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
152
+ ),
153
+ ),
154
+
155
+ outlinedButtonTheme: OutlinedButtonThemeData(
156
+ style: OutlinedButton.styleFrom(
157
+ minimumSize: const Size(88, 48),
158
+ padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
159
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
160
+ side: BorderSide(color: colorScheme.outline, width: 1.5),
161
+ ),
162
+ ),
163
+
164
+ textButtonTheme: TextButtonThemeData(
165
+ style: TextButton.styleFrom(
166
+ minimumSize: const Size(88, 40),
167
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
168
+ ),
169
+ ),
170
+
171
+ // Card Theme
172
+ cardTheme: CardThemeData(
173
+ clipBehavior: Clip.antiAlias,
174
+ elevation: AppDesignTokens.fallback.cardElevation,
175
+ margin: EdgeInsets.zero,
176
+ shape: RoundedRectangleBorder(
177
+ side: BorderSide(color: colorScheme.outlineVariant, width: 1),
178
+ borderRadius: BorderRadius.circular(16),
179
+ ),
180
+ color: colorScheme.surfaceContainerLow,
181
+ ),
182
+
183
+ // Input Decoration Theme
184
+ inputDecorationTheme: InputDecorationTheme(
185
+ filled: true,
186
+ fillColor: colorScheme.surfaceContainerHighest.withValues(alpha: 0.5),
187
+ border: OutlineInputBorder(
188
+ borderRadius: BorderRadius.circular(12),
189
+
190
+ borderSide: BorderSide(color: colorScheme.outline),
191
+ ),
192
+ enabledBorder: OutlineInputBorder(
193
+ borderRadius: BorderRadius.circular(12),
194
+ borderSide: BorderSide(color: colorScheme.outline),
195
+ ),
196
+ focusedBorder: OutlineInputBorder(
197
+ borderRadius: BorderRadius.circular(12),
198
+ borderSide: BorderSide(color: colorScheme.primary, width: 2),
199
+ ),
200
+ errorBorder: OutlineInputBorder(
201
+ borderRadius: BorderRadius.circular(12),
202
+ borderSide: BorderSide(color: colorScheme.error),
203
+ ),
204
+ floatingLabelStyle: TextStyle(color: colorScheme.primary),
205
+ labelStyle: textTheme.labelMedium?.copyWith(color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5)),
206
+ hintStyle: textTheme.labelMedium?.copyWith(color: colorScheme.onSurfaceVariant.withValues(alpha: 0.5)),
207
+ ),
208
+
209
+ // Navigation Bar Theme
210
+ navigationBarTheme: NavigationBarThemeData(
211
+ backgroundColor: colorScheme.surface,
212
+ indicatorColor: colorScheme.secondaryContainer,
213
+ elevation: 8,
214
+ labelBehavior: NavigationDestinationLabelBehavior.alwaysShow,
215
+ height: 80,
216
+ labelTextStyle: WidgetStateProperty.resolveWith((states) {
217
+ if (states.contains(WidgetState.selected)) {
218
+ return textTheme.labelSmall?.copyWith(color: colorScheme.primary, fontWeight: FontWeight.bold);
219
+ }
220
+ return textTheme.labelSmall?.copyWith(color: colorScheme.onSurfaceVariant);
221
+ }),
222
+ ),
223
+
224
+ // Navigation Rail Theme
225
+ navigationRailTheme: NavigationRailThemeData(
226
+ backgroundColor: colorScheme.surface,
227
+ indicatorColor: colorScheme.secondaryContainer,
228
+ labelType: NavigationRailLabelType.all,
229
+ unselectedLabelTextStyle: textTheme.labelSmall?.copyWith(color: colorScheme.onSurfaceVariant),
230
+ selectedLabelTextStyle: textTheme.labelSmall?.copyWith(color: colorScheme.primary, fontWeight: FontWeight.bold),
231
+ ),
232
+
233
+ // Tab Bar Theme
234
+ tabBarTheme: TabBarThemeData(
235
+ labelColor: colorScheme.primary,
236
+ unselectedLabelColor: colorScheme.onSurfaceVariant,
237
+ indicatorColor: colorScheme.primary,
238
+ indicatorSize: TabBarIndicatorSize.label,
239
+ labelStyle: textTheme.titleSmall?.copyWith(fontWeight: FontWeight.bold),
240
+ unselectedLabelStyle: textTheme.titleSmall,
241
+ ),
242
+
243
+ // Floating Action Button Theme
244
+ floatingActionButtonTheme: FloatingActionButtonThemeData(
245
+ backgroundColor: colorScheme.primary,
246
+ foregroundColor: colorScheme.onPrimaryContainer,
247
+ elevation: 2,
248
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
249
+ ),
250
+
251
+ // Chip Theme
252
+ chipTheme: ChipThemeData(
253
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
254
+ side: BorderSide(color: colorScheme.outlineVariant),
255
+ backgroundColor: colorScheme.surfaceContainerLow,
256
+ padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
257
+ labelStyle: textTheme.labelMedium,
258
+ ),
259
+
260
+ // List Tile Theme
261
+ listTileTheme: ListTileThemeData(
262
+ contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
263
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
264
+ visualDensity: VisualDensity.comfortable,
265
+ titleTextStyle: textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600),
266
+ subtitleTextStyle: textTheme.bodyMedium?.copyWith(color: colorScheme.onSurfaceVariant),
267
+ ),
268
+
269
+ // Checkbox Theme
270
+ checkboxTheme: CheckboxThemeData(
271
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6)),
272
+ ),
273
+
274
+ // Switch Theme
275
+ switchTheme: SwitchThemeData(
276
+ thumbColor: WidgetStateProperty.resolveWith((states) {
277
+ if (states.contains(WidgetState.selected)) return colorScheme.primary;
278
+ return colorScheme.outline;
279
+ }),
280
+ trackColor: WidgetStateProperty.resolveWith((states) {
281
+ if (states.contains(WidgetState.selected)) return colorScheme.primaryContainer;
282
+ return colorScheme.surfaceContainerHighest;
283
+ }),
284
+ ),
285
+
286
+ // SnackBar Theme
287
+ snackBarTheme: SnackBarThemeData(
288
+ behavior: SnackBarBehavior.floating,
289
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
290
+ elevation: 4,
291
+ backgroundColor: colorScheme.inverseSurface,
292
+ contentTextStyle: textTheme.bodyMedium?.copyWith(color: colorScheme.onInverseSurface),
293
+ ),
294
+
295
+ // Dialog Theme
296
+ dialogTheme: DialogThemeData(
297
+ shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)),
298
+ elevation: 0,
299
+ backgroundColor: colorScheme.surface,
300
+ titleTextStyle: textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold),
301
+ contentTextStyle: textTheme.bodyMedium,
302
+ ),
303
+
304
+ // Bottom Sheet Theme
305
+ bottomSheetTheme: const BottomSheetThemeData(
306
+ showDragHandle: true,
307
+ elevation: 0,
308
+ shape: RoundedRectangleBorder(
309
+ borderRadius: BorderRadius.vertical(
310
+ top: Radius.circular(28),
311
+ ),
312
+ ),
313
+ ),
314
+
315
+ // Search Bar Theme
316
+ searchBarTheme: SearchBarThemeData(
317
+ elevation: WidgetStateProperty.all(0),
318
+ backgroundColor: WidgetStateProperty.all(colorScheme.surfaceContainerLow),
319
+ shape: WidgetStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(28))),
320
+ padding: WidgetStateProperty.all(const EdgeInsets.symmetric(horizontal: 16)),
321
+ hintStyle: WidgetStateProperty.all(textTheme.bodyLarge?.copyWith(color: colorScheme.onSurfaceVariant)),
322
+ ),
323
+
324
+ // Badge Theme
325
+ badgeTheme: BadgeThemeData(
326
+ backgroundColor: colorScheme.error,
327
+ textColor: colorScheme.onError,
328
+ textStyle: textTheme.labelSmall?.copyWith(fontWeight: FontWeight.bold),
329
+ ),
330
+
331
+ // Segmented Button Theme
332
+ segmentedButtonTheme: SegmentedButtonThemeData(
333
+ style: SegmentedButton.styleFrom(
334
+ selectedBackgroundColor: colorScheme.secondaryContainer,
335
+ selectedForegroundColor: colorScheme.onSecondaryContainer,
336
+ side: BorderSide(color: colorScheme.outline),
337
+ ),
338
+ ),
339
+
340
+ // Tooltip Theme
341
+ tooltipTheme: TooltipThemeData(
342
+ decoration: BoxDecoration(
343
+ color: colorScheme.inverseSurface.withValues(alpha: 0.9),
344
+ borderRadius: BorderRadius.circular(8),
345
+ ),
346
+ textStyle: textTheme.labelSmall?.copyWith(color: colorScheme.onInverseSurface),
347
+ ),
348
+ );
349
+ }
350
+
351
+ ThemeData buildLightTheme({required String primaryColorHex}) {
352
+ final seed = _colorFromHex(primaryColorHex.isNotEmpty ? primaryColorHex : '#6750A4');
353
+ final colorScheme = ColorScheme.fromSeed(
354
+ seedColor: seed,
355
+ brightness: Brightness.light,
356
+ );
357
+ return _buildTheme(colorScheme, AppPalettes.light);
358
+ }
359
+
360
+ ThemeData buildDarkTheme({required String primaryColorHex}) {
361
+ final seed = _colorFromHex(primaryColorHex.isNotEmpty ? primaryColorHex : '#6750A4');
362
+ final colorScheme = ColorScheme.fromSeed(
363
+ seedColor: seed,
364
+ brightness: Brightness.dark,
365
+ );
366
+ return _buildTheme(colorScheme, AppPalettes.dark);
367
+ }
368
+
369
+ CupertinoThemeData buildCupertinoTheme({required String primaryColorHex}) {
370
+ final seed = _colorFromHex(primaryColorHex.isNotEmpty ? primaryColorHex : '#007AFF');
371
+
372
+ return CupertinoThemeData(
373
+ applyThemeToAll: true,
374
+ primaryColor: seed,
375
+ primaryContrastingColor: CupertinoColors.white,
376
+ {{#if flags.hasDarkMode}}
377
+ {{#if theme.darkMode.system}}
378
+ brightness: null, // Allow system-wide dark mode support
379
+ {{else}}
380
+ brightness: Brightness.dark,
381
+ {{/if}}
382
+ {{else}}
383
+ brightness: Brightness.light,
384
+ {{/if}}
385
+ scaffoldBackgroundColor: CupertinoColors.systemBackground,
386
+ barBackgroundColor: CupertinoColors.systemGrey6,
387
+ textTheme: CupertinoTextThemeData(
388
+ primaryColor: seed,
389
+ textStyle: const TextStyle(
390
+ {{#if flags.hasCustomFonts}}fontFamily: '{{flags.primaryFontFamily}}',{{/if}}
391
+ fontSize: 17,
392
+ letterSpacing: -0.41,
393
+ ),
394
+ actionTextStyle: TextStyle(
395
+ {{#if flags.hasCustomFonts}}fontFamily: '{{flags.primaryFontFamily}}',{{/if}}
396
+ color: seed,
397
+ fontSize: 17,
398
+ fontWeight: FontWeight.w400,
399
+ ),
400
+ navTitleTextStyle: const TextStyle(
401
+ {{#if flags.hasCustomFonts}}fontFamily: '{{flags.primaryFontFamily}}',{{/if}}
402
+ fontWeight: FontWeight.w600,
403
+ fontSize: 17,
404
+ letterSpacing: -0.41,
405
+ ),
406
+ navLargeTitleTextStyle: const TextStyle(
407
+ {{#if flags.hasCustomFonts}}fontFamily: '{{flags.primaryFontFamily}}',{{/if}}
408
+ fontWeight: FontWeight.bold,
409
+ fontSize: 34,
410
+ letterSpacing: 0.41,
411
+ ),
412
+ tabLabelTextStyle: const TextStyle(
413
+ {{#if flags.hasCustomFonts}}fontFamily: '{{flags.primaryFontFamily}}',{{/if}}
414
+ fontSize: 10,
415
+ fontWeight: FontWeight.w500,
416
+ letterSpacing: -0.24,
417
+ ),
418
+ pickerTextStyle: const TextStyle(
419
+ {{#if flags.hasCustomFonts}}fontFamily: '{{flags.primaryFontFamily}}',{{/if}}
420
+ fontSize: 21,
421
+ letterSpacing: -0.41,
422
+ ),
423
+ dateTimePickerTextStyle: const TextStyle(
424
+ {{#if flags.hasCustomFonts}}fontFamily: '{{flags.primaryFontFamily}}',{{/if}}
425
+ fontSize: 21,
426
+ letterSpacing: -0.41,
427
+ ),
428
+ ),
429
+ );
430
+ }
431
+
@@ -0,0 +1,20 @@
1
+ /// Barrel export for all theme constants.
2
+ ///
3
+ /// Import this single file to access all design foundation values:
4
+ /// ```dart
5
+ /// import 'package:{{appSlug}}/src/theme/theme_constants.dart';
6
+ ///
7
+ /// SizedBox(height: AppSpacing.md)
8
+ /// Container(decoration: BoxDecoration(borderRadius: AppBorders.card))
9
+ /// AnimatedContainer(duration: AppDurations.normal, curve: AppCurves.standard)
10
+ /// ```
11
+ library;
12
+
13
+ export 'app_spacing.dart';
14
+ export 'app_borders.dart';
15
+ export 'app_shadows.dart';
16
+ export 'app_durations.dart';
17
+ export 'app_curves.dart';
18
+ export 'color_schemes.dart';
19
+ export 'text_theme.dart';
20
+ export 'theme.dart';
@@ -0,0 +1,46 @@
1
+ class AppUtils {
2
+ /// Checks if data is null.
3
+ static bool isNull(dynamic value) => value == null;
4
+
5
+ /// Checks if data is null or blank (empty or only contains whitespace).
6
+ static bool isBlank(dynamic value) {
7
+ if (value == null) return true;
8
+ if (value is String) {
9
+ return value.trim().isEmpty;
10
+ }
11
+ if (value is Iterable || value is Map) {
12
+ return value.isEmpty;
13
+ }
14
+ return false;
15
+ }
16
+
17
+ /// Uppercase first letter inside string and let the others lowercase.
18
+ /// Example: your name => Your name
19
+ static String? capitalizeFirst(String s) {
20
+ if (isBlank(s)) return s;
21
+ return s[0].toUpperCase() + s.substring(1).toLowerCase();
22
+ }
23
+
24
+ /// Checks if string is phone number.
25
+ static bool isPhoneNumber(String s) {
26
+ if (s.length > 16 || s.length < 9) return false;
27
+ return hasMatch(s, r'^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s\./0-9]*$');
28
+ }
29
+
30
+ static bool hasMatch(String? value, String pattern) {
31
+ return (value == null) ? false : RegExp(pattern).hasMatch(value);
32
+ }
33
+
34
+ static bool isValidEmail(String s) {
35
+ final emailRegExp = RegExp(
36
+ r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9]+\.[a-zA-Z]+",
37
+ );
38
+ return emailRegExp.hasMatch(s);
39
+ }
40
+
41
+ /// Checks if string is URL.
42
+ static bool isURL(String s) => hasMatch(
43
+ s,
44
+ r"^((((H|h)(T|t)|(F|f))(T|t)(P|p)((S|s)?))\\://)?(www.|[a-zA-Z0-9].)[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,6}(\:[0-9]{1,5})*(/($|[a-zA-Z0-9\.\,\;\?\'\\\+&%\$#\=~_\-]+))*$",
45
+ );
46
+ }
@@ -0,0 +1,31 @@
1
+ import 'dart:async';
2
+
3
+ class Debouncer {
4
+ final Duration delay;
5
+ Timer? _timer;
6
+
7
+ Debouncer({this.delay = const Duration(milliseconds: 500)});
8
+
9
+ void run(void Function() action) {
10
+ _timer?.cancel();
11
+ _timer = Timer(delay, action);
12
+ }
13
+
14
+ void dispose() {
15
+ _timer?.cancel();
16
+ }
17
+ }
18
+
19
+ class Throttler {
20
+ final Duration delay;
21
+ bool _isThrottling = false;
22
+
23
+ Throttler({this.delay = const Duration(milliseconds: 500)});
24
+
25
+ void run(void Function() action) {
26
+ if (_isThrottling) return;
27
+ action();
28
+ _isThrottling = true;
29
+ Timer(delay, () => _isThrottling = false);
30
+ }
31
+ }
@@ -0,0 +1,14 @@
1
+ class AppErrorHandler {
2
+ static String format(dynamic error) {
3
+ if (error is String) return error;
4
+
5
+ // Add logic for common exceptions (e.g., DioException if using Dio)
6
+ // For now, basic error message extraction
7
+ try {
8
+ if (error?.message != null) return error.message;
9
+ if (error?.toString() != null) return error.toString();
10
+ } catch (_) {}
11
+
12
+ return 'An unexpected error occurred';
13
+ }
14
+ }
@@ -0,0 +1,30 @@
1
+ import 'package:equatable/equatable.dart';
2
+
3
+ abstract class Failure extends Equatable {
4
+ final String message;
5
+ final dynamic error;
6
+
7
+ const Failure(this.message, {this.error});
8
+
9
+ @override
10
+ List<Object?> get props => [message, error];
11
+
12
+ @override
13
+ String toString() => message;
14
+ }
15
+
16
+ class ServerFailure extends Failure {
17
+ const ServerFailure(super.message, {super.error});
18
+ }
19
+
20
+ class CacheFailure extends Failure {
21
+ const CacheFailure(super.message, {super.error});
22
+ }
23
+
24
+ class NetworkFailure extends Failure {
25
+ const NetworkFailure(super.message, {super.error});
26
+ }
27
+
28
+ class UnknownFailure extends Failure {
29
+ const UnknownFailure(super.message, {super.error});
30
+ }
@@ -0,0 +1,27 @@
1
+ import 'package:flutter/services.dart';
2
+
3
+ class FormCardFormatter extends TextInputFormatter {
4
+ @override
5
+ TextEditingValue formatEditUpdate(
6
+ TextEditingValue oldValue,
7
+ TextEditingValue newValue,
8
+ ) {
9
+ final text = newValue.text;
10
+ if (newValue.selection.baseOffset == 0) return newValue;
11
+
12
+ final buffer = StringBuffer();
13
+ for (int i = 0; i < text.length; i++) {
14
+ buffer.write(text[i]);
15
+ final nonZeroIndex = i + 1;
16
+ if (nonZeroIndex % 4 == 0 && nonZeroIndex != text.length) {
17
+ buffer.write(' '); // Add space every 4 digits
18
+ }
19
+ }
20
+
21
+ final string = buffer.toString();
22
+ return newValue.copyWith(
23
+ text: string,
24
+ selection: TextSelection.collapsed(offset: string.length),
25
+ );
26
+ }
27
+ }
@@ -0,0 +1,37 @@
1
+ import 'dart:developer' as developer;
2
+ import 'package:flutter/foundation.dart';
3
+
4
+ class AppLogger {
5
+ static void info(String message) {
6
+ _log('\x1B[34m$message\x1B[0m', name: 'INFO');
7
+ }
8
+
9
+ static void success(String message) {
10
+ _log('\x1B[32m$message\x1B[0m', name: 'SUCCESS');
11
+ }
12
+
13
+ static void warning(String message) {
14
+ _log('\x1B[33m$message\x1B[0m', name: 'WARNING');
15
+ }
16
+
17
+ static void error(String message, [Object? error, StackTrace? stackTrace]) {
18
+ _log('\x1B[31m$message\x1B[0m', name: 'ERROR', error: error, stackTrace: stackTrace);
19
+ }
20
+
21
+ static void _log(String message, {String name = '', Object? error, StackTrace? stackTrace}) {
22
+ if (kDebugMode) {
23
+ developer.log(
24
+ message,
25
+ name: name,
26
+ error: error,
27
+ stackTrace: stackTrace,
28
+ );
29
+ }
30
+ }
31
+ }
32
+
33
+ // Global helper functions as requested by the user
34
+ void logInfo(String msg) => AppLogger.info(msg);
35
+ void logSuccess(String msg) => AppLogger.success(msg);
36
+ void logWarning(String msg) => AppLogger.warning(msg);
37
+ void logError(String msg) => AppLogger.error(msg);
@@ -0,0 +1,13 @@
1
+ import 'package:flutter/foundation.dart';
2
+
3
+ class PlatformInfo {
4
+ static bool get isWeb => kIsWeb;
5
+ static bool get isAndroid => defaultTargetPlatform == TargetPlatform.android;
6
+ static bool get isIOS => defaultTargetPlatform == TargetPlatform.iOS;
7
+ static bool get isMacOS => defaultTargetPlatform == TargetPlatform.macOS;
8
+ static bool get isWindows => defaultTargetPlatform == TargetPlatform.windows;
9
+ static bool get isLinux => defaultTargetPlatform == TargetPlatform.linux;
10
+
11
+ static bool get isMobile => isAndroid || isIOS;
12
+ static bool get isDesktop => isMacOS || isWindows || isLinux;
13
+ }
@@ -0,0 +1,42 @@
1
+ import 'package:fpdart/fpdart.dart';
2
+
3
+ import '../imports/core_imports.dart';
4
+
5
+ /// A reusable generic function to handle potential exceptions in async tasks
6
+ /// and map them to the [Either] type matching [FutureEither<T>].
7
+ ///
8
+ /// If [requiresNetwork] is `true` and [isNetworkAvailable] returns `false`,
9
+ /// the [action] will not be executed and a [NetworkFailure] will be returned.
10
+ FutureEither<T> runTask<T>(
11
+ Future<T> Function() action, {
12
+ bool requiresNetwork = false,
13
+ }) async {
14
+ if (requiresNetwork) {
15
+ final hasNetwork = await InternetConnectionService().hasConnection();
16
+
17
+ if (!hasNetwork) {
18
+ AppLogger.warning('Network unavailable for task');
19
+ showGlobalToast(
20
+ message:
21
+ 'No internet connection. Please check your connection and try again.',
22
+ status: 'warning',
23
+ );
24
+ return left(
25
+ const NetworkFailure(
26
+ 'No internet connection. Please check your connection and try again.',
27
+ ),
28
+ );
29
+ }
30
+ }
31
+
32
+ try {
33
+ final result = await action();
34
+ return right(result);
35
+ } catch (error, stackTrace) {
36
+ AppLogger.error('Task execution failed $error', [error, stackTrace]);
37
+ final errorMessage = AppErrorHandler.format(error);
38
+
39
+ // Depending on logic, map error strings/types to specific Failure variants
40
+ return left(ServerFailure(errorMessage, error: error));
41
+ }
42
+ }