138 lines
4.9 KiB
Dart
138 lines
4.9 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:lottie/lottie.dart';
|
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
|
import '/core/auth_provider.dart';
|
|
import '/models/invite.dart';
|
|
import '/features/auth/invite_pending_screen.dart';
|
|
|
|
class SplashScreen extends ConsumerStatefulWidget {
|
|
const SplashScreen({super.key});
|
|
@override
|
|
ConsumerState<SplashScreen> createState() => _SplashScreenState();
|
|
}
|
|
|
|
class _SplashScreenState extends ConsumerState<SplashScreen>
|
|
with SingleTickerProviderStateMixin {
|
|
late AnimationController _ctrl;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_ctrl = AnimationController(vsync: this, duration: const Duration(seconds: 2));
|
|
_ctrl.forward();
|
|
Future.delayed(const Duration(milliseconds: 2200), _navigate);
|
|
}
|
|
|
|
Future<void> _navigate() async {
|
|
if (!mounted) return;
|
|
try {
|
|
await ref.read(authNotifierProvider.future);
|
|
final session = await ref.read(currentSessionProvider.future);
|
|
if (!mounted) return;
|
|
|
|
if (session == null) { context.go('/login'); return; }
|
|
|
|
// ─── Verificar se o utilizador tem perfil
|
|
final supabase = Supabase.instance.client;
|
|
final profile = await supabase
|
|
.from('profiles')
|
|
.select()
|
|
.eq('user_id', session.user.id)
|
|
.maybeSingle();
|
|
|
|
// ─── Verificar convite pendente pelo email
|
|
final email = session.user.email ?? '';
|
|
final inviteData = await supabase
|
|
.from('invites')
|
|
.select()
|
|
.eq('email', email)
|
|
.eq('status', 'pending')
|
|
.gt('expires_at', DateTime.now().toIso8601String())
|
|
.order('created_at', ascending: false)
|
|
.limit(1)
|
|
.maybeSingle();
|
|
|
|
if (!mounted) return;
|
|
|
|
if (inviteData != null) {
|
|
final invite = Invite.fromMap(inviteData);
|
|
if (!invite.isExpired) {
|
|
// Mostrar ecrã de aceitação de convite
|
|
Navigator.of(context).pushReplacement(
|
|
MaterialPageRoute(builder: (_) => InvitePendingScreen(invite: invite)),
|
|
);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (profile == null) {
|
|
// Sem perfil e sem convite → ecrã de espera / registo incompleto
|
|
context.go('/login');
|
|
} else {
|
|
context.go('/home');
|
|
}
|
|
} catch (_) {
|
|
if (mounted) context.go('/login');
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() { _ctrl.dispose(); super.dispose(); }
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
backgroundColor: const Color(0xFF0D1117),
|
|
body: Stack(children: [
|
|
// Orbs
|
|
Positioned(top: -100, right: -80,
|
|
child: _orb(300, const Color(0xFF4FC3F7).withOpacity(0.08))),
|
|
Positioned(bottom: -80, left: -60,
|
|
child: _orb(250, const Color(0xFFA5D6A7).withOpacity(0.06))),
|
|
SafeArea(
|
|
child: Center(
|
|
child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
|
|
// Logo com glow
|
|
Container(
|
|
padding: const EdgeInsets.all(20),
|
|
decoration: BoxDecoration(
|
|
shape: BoxShape.circle,
|
|
gradient: RadialGradient(colors: [
|
|
const Color(0xFF4FC3F7).withOpacity(0.15),
|
|
Colors.transparent,
|
|
]),
|
|
border: Border.all(color: const Color(0xFF4FC3F7).withOpacity(0.2), width: 1.5),
|
|
),
|
|
child: Image.asset('assets/logo.png', height: 100,
|
|
errorBuilder: (_, __, ___) => const Icon(Icons.child_care, size: 80, color: Color(0xFF4FC3F7))),
|
|
),
|
|
const SizedBox(height: 24),
|
|
const Text('SEMENTES DO FUTURO',
|
|
style: TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.w900, letterSpacing: 2)),
|
|
const SizedBox(height: 6),
|
|
const Text('Diário do Candengue',
|
|
style: TextStyle(color: Color(0xFF4FC3F7), fontSize: 13, letterSpacing: 1.5)),
|
|
const SizedBox(height: 48),
|
|
Lottie.asset('assets/splash_animation.json',
|
|
controller: _ctrl, height: 80, repeat: false,
|
|
errorBuilder: (_, __, ___) => const SizedBox(
|
|
height: 40, width: 40,
|
|
child: CircularProgressIndicator(color: Color(0xFF4FC3F7), strokeWidth: 2),
|
|
)),
|
|
const SizedBox(height: 16),
|
|
Text('"Conforto, cuidado e aprendizagem"',
|
|
style: TextStyle(color: Colors.white.withOpacity(0.3), fontSize: 12, fontStyle: FontStyle.italic)),
|
|
]),
|
|
),
|
|
),
|
|
]),
|
|
);
|
|
}
|
|
|
|
Widget _orb(double size, Color color) => Container(width: size, height: size,
|
|
decoration: BoxDecoration(shape: BoxShape.circle, color: color,
|
|
boxShadow: [BoxShadow(color: color, blurRadius: size / 2)]));
|
|
}
|