Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion templates/flutter/base/lib/src/app.dart.hbs
Original file line number Diff line number Diff line change
@@ -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});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
49 changes: 6 additions & 43 deletions templates/flutter/base/lib/src/routing/app_router.dart.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -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<GetPage> get getPages => [
GetPage(
static List<GetPage<dynamic>> get getPages => [
GetPage<dynamic>(
name: AppRoutes.onboarding,
page: () => const OnboardingPage(),
),
GetPage(
GetPage<dynamic>(
name: AppRoutes.login,
page: () => const LoginScreen(),
),
GetPage(
GetPage<dynamic>(
name: AppRoutes.signup,
page: () => const SignupScreen(),
),
GetPage(
GetPage<dynamic>(
name: AppRoutes.forgotPassword,
page: () => const ForgotPasswordScreen(),
),
GetPage(
GetPage<dynamic>(
name: AppRoutes.home,
page: () => const HomePage(),
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,9 @@ class _SessionListenerWrapperState extends State<SessionListenerWrapper> {
FlutterNativeSplash.remove();
{{/if}}
if (status == SessionStatus.authenticated) {
Get.offAllNamed(AppRoutes.home);
Get.offAllNamed<dynamic>(AppRoutes.home);
} else if (status == SessionStatus.unauthenticated) {
Get.offAllNamed(AppRoutes.onboarding);
Get.offAllNamed<dynamic>(AppRoutes.onboarding);
}
}
}, condition: () => session.status.value != SessionStatus.unknown);
Expand Down
2 changes: 2 additions & 0 deletions templates/flutter/base/pubspec.yaml.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
37 changes: 34 additions & 3 deletions templates/flutter/partials/features/auth/auth_logic.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,37 @@ class AuthController extends GetxController {

final isLoading = false.obs;

// Login controllers & states
final loginFormKey = GlobalKey<FormState>();
final emailController = TextEditingController();
final passwordController = TextEditingController();
final obscurePassword = true.obs;

// SignUp controllers & states
final signUpFormKey = GlobalKey<FormState>();
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<FormState>();
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;

Expand All @@ -464,7 +495,7 @@ class AuthController extends GetxController {
}
},
(user) {
Get.offAllNamed(AppRoutes.home);
Get.offAllNamed<dynamic>(AppRoutes.home);
},
);
}
Expand All @@ -482,7 +513,7 @@ class AuthController extends GetxController {
}
},
(user) {
Get.offAllNamed(AppRoutes.home);
Get.offAllNamed<dynamic>(AppRoutes.home);
},
);
}
Expand All @@ -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<dynamic>(AppRoutes.login);
},
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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<AuthController> {
const ForgotPasswordScreen({super.key});

@override
Widget build(BuildContext context) {
final cs = context.theme.colorScheme;
final tt = context.theme.textTheme;

Future<void> 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});
Expand Down Expand Up @@ -274,6 +306,7 @@ class _ForgotPasswordScreenState extends {{#if flags.isRiverpod}}ConsumerState<F
}
}
{{/if}}
{{/if}}

class _ForgotPasswordView extends StatelessWidget {
const _ForgotPasswordView({
Expand Down
43 changes: 40 additions & 3 deletions templates/flutter/partials/features/auth/login_screen.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -22,6 +22,42 @@ import 'package:{{flags.appSnake}}/src/{{#if (eq architecture "layer-first")}}pr
{{#if (eq flags.routerPackage "auto_route")}}
@RoutePage()
{{/if}}
{{#if flags.isGetX}}
class LoginScreen extends GetView<AuthController> {
const LoginScreen({super.key});

@override
Widget build(BuildContext context) {
final cs = context.theme.colorScheme;
final tt = context.theme.textTheme;

Future<void> 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});
Expand Down Expand Up @@ -300,6 +336,7 @@ class _LoginScreenState extends {{#if flags.isRiverpod}}ConsumerState<LoginScree
}
}
{{/if}}
{{/if}}

class _LoginView extends StatelessWidget {
const _LoginView({
Expand Down Expand Up @@ -418,7 +455,7 @@ class _LoginView extends StatelessWidget {
{{else if (eq flags.routerPackage "auto_route")}}
context.pushRoute(const ForgotPasswordRoute());
{{else if flags.isGetX}}
Get.toNamed(AppRoutes.forgotPassword);
Get.toNamed<dynamic>(AppRoutes.forgotPassword);
{{else}}
Navigator.pushNamed(context, AppRoutes.forgotPassword);
{{/if}}
Expand Down Expand Up @@ -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<dynamic>(AppRoutes.signup);
{{else}}
Navigator.pushNamed(context, AppRoutes.signup);
{{/if}}
Expand Down
46 changes: 44 additions & 2 deletions templates/flutter/partials/features/auth/signup_screen.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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<AuthController> {
const SignupScreen({super.key});

@override
Widget build(BuildContext context) {
final cs = context.theme.colorScheme;
final tt = context.theme.textTheme;

Future<void> 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});
Expand Down Expand Up @@ -332,6 +373,7 @@ class _SignupScreenState extends {{#if flags.isRiverpod}}ConsumerState<SignupScr
}
}
{{/if}}
{{/if}}

class _SignupView extends StatelessWidget {
const _SignupView({
Expand Down Expand Up @@ -479,7 +521,7 @@ class _SignupView extends StatelessWidget {
{{else if (eq flags.routerPackage "auto_route")}}
context.pushRoute(const LoginRoute());
{{else if flags.isGetX}}
Get.toNamed(AppRoutes.login);
Get.toNamed<dynamic>(AppRoutes.login);
{{else}}
Navigator.pushNamed(context, AppRoutes.login);
{{/if}}
Expand Down