diff --git a/templates/flutter/base/lib/src/app.dart.hbs b/templates/flutter/base/lib/src/app.dart.hbs index ff77c93..2fb3ac9 100644 --- a/templates/flutter/base/lib/src/app.dart.hbs +++ b/templates/flutter/base/lib/src/app.dart.hbs @@ -1,4 +1,4 @@ -import 'package:{{flags.appSnake}}/src/imports/core_imports.dart'; +import 'package:{{flags.appSnake}}/src/imports/imports.dart'; class App extends StatelessWidget { const App({super.key}); diff --git a/templates/flutter/base/lib/src/imports/packages_imports.dart.hbs b/templates/flutter/base/lib/src/imports/packages_imports.dart.hbs index a2b0e06..a3ba1a8 100644 --- a/templates/flutter/base/lib/src/imports/packages_imports.dart.hbs +++ b/templates/flutter/base/lib/src/imports/packages_imports.dart.hbs @@ -22,14 +22,7 @@ export 'package:provider/provider.dart' hide Dispose; export 'package:flutter_bloc/flutter_bloc.dart'; {{/if}} {{#if (or flags.isGetX (eq flags.routerPackage "getx"))}} -// For Navigation -export 'package:get/get_navigation/get_navigation.dart' hide GetContextExtensions; -// For State Management (.obs, GetxController, Obx) -{{#if flags.isGetX}} -export 'package:get/get_state_manager/get_state_manager.dart'; -// For Dependency Management (Get.put, Get.find) -export 'package:get/get_instance/get_instance.dart'; -{{/if}} +export 'package:get/get.dart' hide ContextExtensionss, Trans, StringExtension; {{/if}} {{#if flags.isMobX}} export 'package:mobx/mobx.dart' hide version, StringExtension, Action, Listener, Listenable, Interceptor, Interceptors; diff --git a/templates/flutter/base/lib/src/routing/(isGetX)@app_bindings.dart.hbs b/templates/flutter/base/lib/src/routing/(isGetX)@app_bindings.dart.hbs index 6c5b3e6..d9d9726 100644 --- a/templates/flutter/base/lib/src/routing/(isGetX)@app_bindings.dart.hbs +++ b/templates/flutter/base/lib/src/routing/(isGetX)@app_bindings.dart.hbs @@ -8,7 +8,7 @@ import 'package:{{flags.appSnake}}/src/controllers/auth/auth_controller.dart'; {{else if (eq architecture "mvvm")}} import 'package:{{flags.appSnake}}/src/ui/auth/controllers/auth_controller.dart'; {{else}} -import 'package:{{flags.appSnake}}/src/features/auth/presentation/controllers/auth_controller.dart'; +import 'package:{{flags.appSnake}}/src/features/auth/presentation/providers/auth_controller.dart'; {{/if}} class AppBindings implements Bindings { diff --git a/templates/flutter/base/lib/src/routing/app_router.dart.hbs b/templates/flutter/base/lib/src/routing/app_router.dart.hbs index 835311d..f3281f2 100644 --- a/templates/flutter/base/lib/src/routing/app_router.dart.hbs +++ b/templates/flutter/base/lib/src/routing/app_router.dart.hbs @@ -108,63 +108,26 @@ class AppRouter extends RootStackRouter { } {{else if (eq flags.routerPackage "getx")}} import 'package:{{flags.appSnake}}/src/imports/imports.dart'; -import 'package:{{flags.appSnake}}/src/routing/app_routes.dart'; - -{{#if (eq architecture "layer-first")}} -import 'package:{{flags.appSnake}}/src/presentation/screens/auth/login_screen.dart'; -import 'package:{{flags.appSnake}}/src/presentation/screens/auth/signup_screen.dart'; -import 'package:{{flags.appSnake}}/src/presentation/screens/auth/forgot_password_screen.dart'; -{{else if (eq architecture "mvc")}} -import 'package:{{flags.appSnake}}/src/views/auth/login_screen.dart'; -import 'package:{{flags.appSnake}}/src/views/auth/signup_screen.dart'; -import 'package:{{flags.appSnake}}/src/views/auth/forgot_password_screen.dart'; -{{else if (eq architecture "mvvm")}} -import 'package:{{flags.appSnake}}/src/ui/auth/login_screen.dart'; -import 'package:{{flags.appSnake}}/src/ui/auth/signup_screen.dart'; -import 'package:{{flags.appSnake}}/src/ui/auth/forgot_password_screen.dart'; -{{else}} -import 'package:{{flags.appSnake}}/src/features/auth/presentation/screens/login_screen.dart'; -import 'package:{{flags.appSnake}}/src/features/auth/presentation/screens/signup_screen.dart'; -import 'package:{{flags.appSnake}}/src/features/auth/presentation/screens/forgot_password_screen.dart'; -{{/if}} - -{{#if (eq architecture "layer-first")}} -import 'package:{{flags.appSnake}}/src/presentation/screens/home/home_page.dart'; -import 'package:{{flags.appSnake}}/src/presentation/screens/onboarding/onboarding_page.dart'; - -{{else if (eq architecture "mvc")}} -import 'package:{{flags.appSnake}}/src/views/home/home_page.dart'; -import 'package:{{flags.appSnake}}/src/views/onboarding/onboarding_page.dart'; - -{{else if (eq architecture "mvvm")}} -import 'package:{{flags.appSnake}}/src/ui/home/home_page.dart'; -import 'package:{{flags.appSnake}}/src/ui/onboarding/onboarding_page.dart'; - -{{else}} -import 'package:{{flags.appSnake}}/src/features/home/presentation/screens/home_page.dart'; -import 'package:{{flags.appSnake}}/src/features/onboarding/presentation/screens/onboarding_page.dart'; - -{{/if}} class AppRouter { - static List get getPages => [ - GetPage( + static List> get getPages => [ + GetPage( name: AppRoutes.onboarding, page: () => const OnboardingPage(), ), - GetPage( + GetPage( name: AppRoutes.login, page: () => const LoginScreen(), ), - GetPage( + GetPage( name: AppRoutes.signup, page: () => const SignupScreen(), ), - GetPage( + GetPage( name: AppRoutes.forgotPassword, page: () => const ForgotPasswordScreen(), ), - GetPage( + GetPage( name: AppRoutes.home, page: () => const HomePage(), ), diff --git a/templates/flutter/base/lib/src/shared/wrappers/session_listener_wrapper.dart.hbs b/templates/flutter/base/lib/src/shared/wrappers/session_listener_wrapper.dart.hbs index 6c35c75..c7063d7 100644 --- a/templates/flutter/base/lib/src/shared/wrappers/session_listener_wrapper.dart.hbs +++ b/templates/flutter/base/lib/src/shared/wrappers/session_listener_wrapper.dart.hbs @@ -116,9 +116,9 @@ class _SessionListenerWrapperState extends State { FlutterNativeSplash.remove(); {{/if}} if (status == SessionStatus.authenticated) { - Get.offAllNamed(AppRoutes.home); + Get.offAllNamed(AppRoutes.home); } else if (status == SessionStatus.unauthenticated) { - Get.offAllNamed(AppRoutes.onboarding); + Get.offAllNamed(AppRoutes.onboarding); } } }, condition: () => session.status.value != SessionStatus.unknown); diff --git a/templates/flutter/base/pubspec.yaml.hbs b/templates/flutter/base/pubspec.yaml.hbs index 90b5e8a..d802437 100644 --- a/templates/flutter/base/pubspec.yaml.hbs +++ b/templates/flutter/base/pubspec.yaml.hbs @@ -45,7 +45,9 @@ dependencies: flutter_bloc: ^9.1.1 {{/if}} {{#if flags.isGetX}} + {{#unless (eq flags.routerPackage "getx")}} get: ^4.7.3 + {{/unless}} {{/if}} {{#if flags.isProvider}} provider: ^6.1.5+1 diff --git a/templates/flutter/partials/features/auth/auth_logic.hbs b/templates/flutter/partials/features/auth/auth_logic.hbs index 584552f..28e8287 100644 --- a/templates/flutter/partials/features/auth/auth_logic.hbs +++ b/templates/flutter/partials/features/auth/auth_logic.hbs @@ -451,6 +451,37 @@ class AuthController extends GetxController { final isLoading = false.obs; + // Login controllers & states + final loginFormKey = GlobalKey(); + final emailController = TextEditingController(); + final passwordController = TextEditingController(); + final obscurePassword = true.obs; + + // SignUp controllers & states + final signUpFormKey = GlobalKey(); + final nameController = TextEditingController(); + final signUpEmailController = TextEditingController(); + final signUpPasswordController = TextEditingController(); + final signUpConfirmPasswordController = TextEditingController(); + final signUpObscurePassword = true.obs; + final signUpObscureConfirmPassword = true.obs; + + // ForgotPassword controllers & states + final forgotPasswordFormKey = GlobalKey(); + final forgotPasswordEmailController = TextEditingController(); + + @override + void onClose() { + emailController.dispose(); + passwordController.dispose(); + nameController.dispose(); + signUpEmailController.dispose(); + signUpPasswordController.dispose(); + signUpConfirmPasswordController.dispose(); + forgotPasswordEmailController.dispose(); + super.onClose(); + } + void login({required BuildContext context, required String email, required String password}) async { isLoading.value = true; @@ -464,7 +495,7 @@ class AuthController extends GetxController { } }, (user) { - Get.offAllNamed(AppRoutes.home); + Get.offAllNamed(AppRoutes.home); }, ); } @@ -482,7 +513,7 @@ class AuthController extends GetxController { } }, (user) { - Get.offAllNamed(AppRoutes.home); + Get.offAllNamed(AppRoutes.home); }, ); } @@ -503,7 +534,7 @@ class AuthController extends GetxController { if (context.mounted) { showToast(context, message: 'Password reset link sent successfully', status: 'success'); } - Get.offNamed(AppRoutes.login); + Get.offNamed(AppRoutes.login); }, ); } diff --git a/templates/flutter/partials/features/auth/forgot_password_screen.hbs b/templates/flutter/partials/features/auth/forgot_password_screen.hbs index 992af62..b418ad6 100644 --- a/templates/flutter/partials/features/auth/forgot_password_screen.hbs +++ b/templates/flutter/partials/features/auth/forgot_password_screen.hbs @@ -8,7 +8,7 @@ import 'package:{{flags.appSnake}}/src/{{#if (eq architecture "layer-first")}}pr {{else if flags.isProvider}} import 'package:{{flags.appSnake}}/src/{{#if (eq architecture "layer-first")}}presentation/providers/{{else if (eq architecture "mvc")}}controllers/auth/{{else if (eq architecture "mvvm")}}ui/auth/providers/{{else}}features/auth/presentation/providers/{{/if}}auth_provider.dart'; {{else if flags.isGetX}} -import 'package:{{flags.appSnake}}/src/{{#if (eq architecture "layer-first")}}presentation/controllers/auth/{{else if (eq architecture "mvc")}}controllers/auth/{{else if (eq architecture "mvvm")}}ui/auth/controllers/{{else}}features/auth/presentation/controllers/{{/if}}auth_controller.dart'; +import 'package:{{flags.appSnake}}/src/{{#if (eq architecture "layer-first")}}presentation/controllers/auth/{{else if (eq architecture "mvc")}}controllers/auth/{{else if (eq architecture "mvvm")}}ui/auth/controllers/{{else}}features/auth/presentation/providers/{{/if}}auth_controller.dart'; {{else if flags.isMobX}} {{#if flags.usesFlutterHooks}} import 'package:{{flags.appSnake}}/src/{{#if (eq architecture "layer-first")}}presentation/providers/{{else if (eq architecture "mvc")}}controllers/auth/{{else if (eq architecture "mvvm")}}ui/auth/stores/{{else}}features/auth/presentation/providers/{{/if}}auth_store.dart'; @@ -22,6 +22,38 @@ import 'package:{{flags.appSnake}}/src/{{#if (eq architecture "layer-first")}}pr {{#if (eq flags.routerPackage "auto_route")}} @RoutePage() {{/if}} +{{#if flags.isGetX}} +class ForgotPasswordScreen extends GetView { + const ForgotPasswordScreen({super.key}); + + @override + Widget build(BuildContext context) { + final cs = context.theme.colorScheme; + final tt = context.theme.textTheme; + + Future handleForgotPassword() async { + if (!(controller.forgotPasswordFormKey.currentState?.validate() ?? false)) return; + + controller.forgotPassword( + context: context, + email: controller.forgotPasswordEmailController.text, + ); + } + + return Obx(() { + final isLoading = controller.isLoading.value; + return _ForgotPasswordView( + formKey: controller.forgotPasswordFormKey, + emailController: controller.forgotPasswordEmailController, + isLoading: isLoading, + onForgotPassword: handleForgotPassword, + cs: cs, + tt: tt, + ); + }); + } +} +{{else}} {{#if flags.usesFlutterHooks}} class ForgotPasswordScreen extends {{#if flags.isRiverpod}}HookConsumerWidget{{else}}HookWidget{{/if}} { const ForgotPasswordScreen({super.key}); @@ -274,6 +306,7 @@ class _ForgotPasswordScreenState extends {{#if flags.isRiverpod}}ConsumerState { + const LoginScreen({super.key}); + + @override + Widget build(BuildContext context) { + final cs = context.theme.colorScheme; + final tt = context.theme.textTheme; + + Future handleLogin() async { + if (!(controller.loginFormKey.currentState?.validate() ?? false)) return; + + controller.login( + context: context, + email: controller.emailController.text, + password: controller.passwordController.text, + ); + } + + return Obx(() { + final isLoading = controller.isLoading.value; + return _LoginView( + formKey: controller.loginFormKey, + emailController: controller.emailController, + passwordController: controller.passwordController, + obscurePassword: controller.obscurePassword.value, + isLoading: isLoading, + onToggleObscure: () => controller.obscurePassword.toggle(), + onLogin: handleLogin, + cs: cs, + tt: tt, + ); + }); + } +} +{{else}} {{#if flags.usesFlutterHooks}} class LoginScreen extends {{#if flags.isRiverpod}}HookConsumerWidget{{else}}HookWidget{{/if}} { const LoginScreen({super.key}); @@ -300,6 +336,7 @@ class _LoginScreenState extends {{#if flags.isRiverpod}}ConsumerState(AppRoutes.forgotPassword); {{else}} Navigator.pushNamed(context, AppRoutes.forgotPassword); {{/if}} @@ -509,7 +546,7 @@ class _LoginView extends StatelessWidget { {{else if (eq flags.routerPackage "auto_route")}} context.pushRoute(const SignupRoute()); {{else if flags.isGetX}} - Get.toNamed(AppRoutes.signup); + Get.toNamed(AppRoutes.signup); {{else}} Navigator.pushNamed(context, AppRoutes.signup); {{/if}} diff --git a/templates/flutter/partials/features/auth/signup_screen.hbs b/templates/flutter/partials/features/auth/signup_screen.hbs index df4970e..572201a 100644 --- a/templates/flutter/partials/features/auth/signup_screen.hbs +++ b/templates/flutter/partials/features/auth/signup_screen.hbs @@ -8,7 +8,7 @@ import 'package:{{flags.appSnake}}/src/{{#if (eq architecture "layer-first")}}pr {{else if flags.isProvider}} import 'package:{{flags.appSnake}}/src/{{#if (eq architecture "layer-first")}}presentation/providers/{{else if (eq architecture "mvc")}}controllers/auth/{{else if (eq architecture "mvvm")}}ui/auth/providers/{{else}}features/auth/presentation/providers/{{/if}}auth_provider.dart'; {{else if flags.isGetX}} -import 'package:{{flags.appSnake}}/src/{{#if (eq architecture "layer-first")}}presentation/controllers/auth/{{else if (eq architecture "mvc")}}controllers/auth/{{else if (eq architecture "mvvm")}}ui/auth/controllers/{{else}}features/auth/presentation/controllers/{{/if}}auth_controller.dart'; +import 'package:{{flags.appSnake}}/src/{{#if (eq architecture "layer-first")}}presentation/controllers/auth/{{else if (eq architecture "mvc")}}controllers/auth/{{else if (eq architecture "mvvm")}}ui/auth/controllers/{{else}}features/auth/presentation/providers/{{/if}}auth_controller.dart'; {{else if flags.isMobX}} {{#if flags.usesFlutterHooks}} import 'package:{{flags.appSnake}}/src/{{#if (eq architecture "layer-first")}}presentation/providers/{{else if (eq architecture "mvc")}}controllers/auth/{{else if (eq architecture "mvvm")}}ui/auth/stores/{{else}}features/auth/presentation/providers/{{/if}}auth_store.dart'; @@ -22,6 +22,47 @@ import 'package:{{flags.appSnake}}/src/{{#if (eq architecture "layer-first")}}pr {{#if (eq flags.routerPackage "auto_route")}} @RoutePage() {{/if}} +{{#if flags.isGetX}} +class SignupScreen extends GetView { + const SignupScreen({super.key}); + + @override + Widget build(BuildContext context) { + final cs = context.theme.colorScheme; + final tt = context.theme.textTheme; + + Future handleSignup() async { + if (!(controller.signUpFormKey.currentState?.validate() ?? false)) return; + + controller.signUp( + context: context, + name: controller.nameController.text, + email: controller.signUpEmailController.text, + password: controller.signUpPasswordController.text, + ); + } + + return Obx(() { + final isLoading = controller.isLoading.value; + return _SignupView( + formKey: controller.signUpFormKey, + nameController: controller.nameController, + emailController: controller.signUpEmailController, + passwordController: controller.signUpPasswordController, + confirmPasswordController: controller.signUpConfirmPasswordController, + obscurePassword: controller.signUpObscurePassword.value, + obscureConfirmPassword: controller.signUpObscureConfirmPassword.value, + isLoading: isLoading, + onToggleObscure: () => controller.signUpObscurePassword.toggle(), + onToggleConfirmObscure: () => controller.signUpObscureConfirmPassword.toggle(), + onSignup: handleSignup, + cs: cs, + tt: tt, + ); + }); + } +} +{{else}} {{#if flags.usesFlutterHooks}} class SignupScreen extends {{#if flags.isRiverpod}}HookConsumerWidget{{else}}HookWidget{{/if}} { const SignupScreen({super.key}); @@ -332,6 +373,7 @@ class _SignupScreenState extends {{#if flags.isRiverpod}}ConsumerState(AppRoutes.login); {{else}} Navigator.pushNamed(context, AppRoutes.login); {{/if}}