Agrego frontend app

This commit is contained in:
Pablo
2026-08-22 19:08:19 -03:00
parent 824e092b29
commit 2d0797e627
254 changed files with 37231 additions and 0 deletions
@@ -0,0 +1,98 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:gimnasio_soma/core/widgets/soma_toast.dart';
import 'package:gimnasio_soma/features/auth/presentation/providers/auth_provider.dart';
import 'package:gimnasio_soma/features/auth/presentation/widgets/login_desktop.dart';
import 'package:gimnasio_soma/features/auth/presentation/widgets/login_mobile.dart';
/// Breakpoint para cambiar entre mobile y desktop layout.
const _kDesktopBreakpoint = 800.0;
class LoginScreen extends ConsumerStatefulWidget {
const LoginScreen({super.key});
@override
ConsumerState<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends ConsumerState<LoginScreen>
with SingleTickerProviderStateMixin {
late final AnimationController _animController;
late final Animation<double> _fadeIn;
late final Animation<Offset> _slideMobile;
late final Animation<Offset> _slideDesktop;
@override
void initState() {
super.initState();
_animController = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 800),
);
_fadeIn = CurvedAnimation(
parent: _animController,
curve: Curves.easeOut,
);
// Mobile: form slides up
_slideMobile = Tween<Offset>(
begin: const Offset(0, 0.15),
end: Offset.zero,
).animate(CurvedAnimation(
parent: _animController,
curve: Curves.easeOutCubic,
));
// Desktop: form slides in from right
_slideDesktop = Tween<Offset>(
begin: const Offset(0.08, 0),
end: Offset.zero,
).animate(CurvedAnimation(
parent: _animController,
curve: Curves.easeOutCubic,
));
_animController.forward();
}
@override
void dispose() {
_animController.dispose();
super.dispose();
}
Future<void> _handleLogin(String dni, String password) async {
final error = await ref
.read(authStateProvider.notifier)
.login(dni, password)
.timeout(
const Duration(seconds: 15),
onTimeout: () => 'Tiempo de espera agotado. Verificá tu conexión.',
);
if (!mounted || error == null) return;
SomaToast.show(context, message: error, type: ToastType.error);
}
@override
Widget build(BuildContext context) {
final width = MediaQuery.of(context).size.width;
final isDesktop = width >= _kDesktopBreakpoint;
return Scaffold(
body: isDesktop
? LoginDesktop(
onSubmit: _handleLogin,
fadeIn: _fadeIn,
slideIn: _slideDesktop,
)
: LoginMobile(
onSubmit: _handleLogin,
fadeIn: _fadeIn,
slideUp: _slideMobile,
),
);
}
}
@@ -0,0 +1,59 @@
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:gimnasio_soma/core/theme/soma_colors.dart';
import 'package:gimnasio_soma/core/widgets/soma_logo.dart';
import 'package:gimnasio_soma/features/auth/presentation/providers/auth_provider.dart';
class SplashScreen extends ConsumerStatefulWidget {
const SplashScreen({super.key});
@override
ConsumerState<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends ConsumerState<SplashScreen> {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) => _checkSession());
}
Future<void> _checkSession() async {
try {
await ref.read(authStateProvider.notifier).checkSession();
} catch (_) {
// Error en la validación, ir a login
}
if (!mounted) return;
final user = ref.read(authStateProvider).valueOrNull;
if (user == null) {
context.go('/login');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const SomaLogo(width: 160),
const SizedBox(height: 32),
SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2.5,
color: SomaColors.primary,
),
),
],
),
),
);
}
}