Files
2025-11-16 22:40:55 +01:00

82 lines
2.2 KiB
Dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../../main.dart';
import 'package:go_router/go_router.dart';
class SplashScreen extends ConsumerStatefulWidget {
const SplashScreen({super.key});
@override
ConsumerState<SplashScreen> createState() => _SplashState();
}
class _SplashState extends ConsumerState<SplashScreen>
with SingleTickerProviderStateMixin {
late final AnimationController _ac = AnimationController(
vsync: this, duration: const Duration(milliseconds: 600))
..forward();
@override
void initState() {
super.initState();
_boot();
}
Future<void> _boot() async {
final tokens = ref.read(tokenStorageProvider);
final access = await tokens.access;
var authed = _isJwtValid(access);
if (!authed) {
await tokens.refreshAccess();
authed = _isJwtValid(await tokens.access);
}
if (!mounted) return;
await Future.delayed(
const Duration(milliseconds: 300)); // kleines Fade-Finish
if (!mounted) return;
context.go(authed ? '/igel' : '/login');
}
bool _isJwtValid(String? jwt) {
if (jwt == null || jwt.isEmpty) return false;
try {
final parts = jwt.split('.');
if (parts.length != 3) return false;
final payload = jsonDecode(
utf8.decode(base64Url.decode(base64Url.normalize(parts[1]))))
as Map<String, dynamic>;
final exp = (payload['exp'] as num?)?.toInt();
final now = DateTime.now().millisecondsSinceEpoch ~/ 1000;
return exp == null ? true : now < exp - 15;
} catch (_) {
return false;
}
}
@override
void dispose() {
_ac.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final t = CurvedAnimation(parent: _ac, curve: Curves.easeOut);
return Scaffold(
body: FadeTransition(
opacity: t,
child: const Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.pets, size: 96),
SizedBox(height: 12),
CircularProgressIndicator(),
],
),
),
),
);
}
}