Added a lobbyScreen to generate a unique Code and invite other Players and the possibility to workout together, by using the pocketbase realtime feature.
270 lines
8.4 KiB
Dart
270 lines
8.4 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:go_router/go_router.dart';
|
|
import 'package:slrpg_app/src/features/multiplayer/presentation/screens/leaderboard_screen.dart';
|
|
import 'package:slrpg_app/src/features/multiplayer/presentation/screens/lobby_screen.dart';
|
|
|
|
import '../../features/authentication/presentation/screens/login_screen.dart';
|
|
import '../../features/authentication/presentation/screens/profile_screen.dart';
|
|
import '../../features/authentication/presentation/screens/register_screen.dart';
|
|
import '../../features/gamification/presentation/screens/quest_log.dart';
|
|
import '../../features/onboarding/presentation/screens/avatar_setup_screen.dart';
|
|
import '../../features/onboarding/presentation/screens/welcome_screen.dart';
|
|
import '../../features/onboarding/presentation/screens/bodyweight_input_screen.dart';
|
|
import '../../features/onboarding/presentation/screens/strength_test_screen.dart';
|
|
import '../../features/onboarding/presentation/screens/inventory_setup_screen.dart';
|
|
import '../../features/dashboard/presentation/screens/hub_screen.dart';
|
|
import '../../features/workout_runner/presentation/screens/battle_screen.dart';
|
|
import '../../features/inventory/presentation/screens/inventory_screen.dart';
|
|
import '../../features/history/presentation/screens/history_screen.dart';
|
|
import '../../shared/data/repositories/user_repository.dart';
|
|
import '../../features/stats/presentation/screens/stats_screen.dart';
|
|
import '../constants/asset_paths.dart';
|
|
import '../../features/gamification/presentation/screens/codex_screen.dart';
|
|
|
|
final routerProvider = Provider<GoRouter>((ref) {
|
|
final userRepo = ref.watch(userRepositoryProvider);
|
|
|
|
return GoRouter(
|
|
initialLocation: '/splash',
|
|
redirect: (context, state) async {
|
|
final user = await userRepo.getLocalUser();
|
|
final isAuthenticated = user != null;
|
|
|
|
final isOnAuthPage = state.matchedLocation == '/login' ||
|
|
state.matchedLocation == '/register' ||
|
|
state.matchedLocation.startsWith('/onboarding');
|
|
|
|
if (!isAuthenticated &&
|
|
!isOnAuthPage &&
|
|
state.matchedLocation != '/splash') {
|
|
return '/login';
|
|
}
|
|
|
|
return null;
|
|
},
|
|
routes: [
|
|
// Splash / Initial Route
|
|
GoRoute(
|
|
path: '/splash',
|
|
builder: (context, state) => const SplashScreen(),
|
|
),
|
|
|
|
// Authentication
|
|
GoRoute(
|
|
path: '/login',
|
|
name: 'login',
|
|
builder: (context, state) => const LoginScreen(),
|
|
),
|
|
GoRoute(
|
|
path: '/register',
|
|
name: 'register',
|
|
builder: (context, state) => const RegisterScreen(),
|
|
),
|
|
|
|
// Onboarding Flow
|
|
GoRoute(
|
|
path: '/onboarding/welcome',
|
|
name: 'welcome',
|
|
builder: (context, state) => const WelcomeScreen(),
|
|
),
|
|
GoRoute(
|
|
path: '/onboarding/bodyweight',
|
|
name: 'bodyweight',
|
|
builder: (context, state) => const BodyweightInputScreen(),
|
|
),
|
|
GoRoute(
|
|
path: '/onboarding/strength-test',
|
|
name: 'strength-test',
|
|
builder: (context, state) => const StrengthTestScreen(),
|
|
),
|
|
GoRoute(
|
|
path: '/onboarding/inventory',
|
|
name: 'inventory-setup',
|
|
builder: (context, state) => const InventorySetupScreen(),
|
|
),
|
|
GoRoute(
|
|
path: '/onboarding/avatar',
|
|
name: 'avatar-setup',
|
|
builder: (context, state) => const AvatarSetupScreen(),
|
|
),
|
|
|
|
// Main App
|
|
GoRoute(
|
|
path: '/hub',
|
|
name: 'hub',
|
|
builder: (context, state) => const HubScreen(),
|
|
),
|
|
GoRoute(
|
|
path: '/battle',
|
|
name: 'battle',
|
|
builder: (context, state) {
|
|
final extra = state.extra as Map<String, dynamic>?;
|
|
return BattleScreen(
|
|
week: extra?['week'] ?? 1,
|
|
day: extra?['day'] ?? 1,
|
|
workoutId: extra?['workoutId'],
|
|
);
|
|
},
|
|
),
|
|
GoRoute(
|
|
path: '/inventory',
|
|
name: 'inventory',
|
|
builder: (context, state) => const InventoryScreen(),
|
|
),
|
|
GoRoute(
|
|
path: '/history',
|
|
name: 'history',
|
|
builder: (context, state) => const HistoryScreen(),
|
|
),
|
|
GoRoute(
|
|
path: '/stats',
|
|
name: 'stats',
|
|
builder: (context, state) => const StatsScreen(),
|
|
),
|
|
GoRoute(
|
|
path: '/profile',
|
|
name: 'profile',
|
|
builder: (context, state) => const ProfileScreen(),
|
|
),
|
|
GoRoute(
|
|
path: '/codex',
|
|
name: 'codex',
|
|
builder: (context, state) => const CodexScreen(),
|
|
),
|
|
GoRoute(
|
|
path: '/quests',
|
|
name: 'quests',
|
|
builder: (context, state) => const QuestLogScreen(),
|
|
),
|
|
GoRoute(
|
|
path: '/leaderboard',
|
|
builder: (context, state) => const LeaderboardScreen(),
|
|
),
|
|
GoRoute(
|
|
path: '/lobby/:partyId',
|
|
builder: (context, state) {
|
|
final partyId = state.pathParameters['partyId']!;
|
|
return LobbyScreen(partyId: partyId);
|
|
},
|
|
),
|
|
GoRoute(
|
|
path: '/battle/:partyId',
|
|
builder: (context, state) {
|
|
final partyId = state.pathParameters['partyId']!;
|
|
|
|
final extra = state.extra as Map<String, dynamic>?;
|
|
return BattleScreen(
|
|
week: extra?['week'] ?? 1,
|
|
day: extra?['day'] ?? 1,
|
|
workoutId: extra?['workoutId'],
|
|
partyId: partyId);
|
|
},
|
|
),
|
|
],
|
|
);
|
|
});
|
|
|
|
class SplashScreen extends ConsumerStatefulWidget {
|
|
const SplashScreen({super.key});
|
|
|
|
@override
|
|
ConsumerState<SplashScreen> createState() => _SplashScreenState();
|
|
}
|
|
|
|
class _SplashScreenState extends ConsumerState<SplashScreen> {
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_checkInitialRoute();
|
|
}
|
|
|
|
Future<void> _checkInitialRoute() async {
|
|
await Future.delayed(const Duration(seconds: 1));
|
|
|
|
if (!mounted) return;
|
|
|
|
final userRepo = ref.read(userRepositoryProvider);
|
|
final user = await userRepo.getLocalUser();
|
|
|
|
if (user == null) {
|
|
context.go('/login');
|
|
} else {
|
|
context.go('/hub');
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Scaffold(
|
|
body: Stack(
|
|
children: [
|
|
Positioned.fill(
|
|
child: Image.asset(
|
|
AssetPaths.bgSplash,
|
|
fit: BoxFit.cover,
|
|
),
|
|
),
|
|
Positioned.fill(
|
|
child: Container(
|
|
color: Colors.black.withValues(alpha: 0.5),
|
|
),
|
|
),
|
|
Center(
|
|
child: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
Container(
|
|
width: 120,
|
|
height: 120,
|
|
decoration: BoxDecoration(
|
|
color: const Color(0xFF00E5FF).withValues(alpha: 0.9),
|
|
borderRadius: BorderRadius.circular(24),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: const Color(0xFF00E5FF).withValues(alpha: 0.6),
|
|
blurRadius: 20,
|
|
spreadRadius: 5,
|
|
),
|
|
],
|
|
),
|
|
child: const Icon(
|
|
Icons.fitness_center,
|
|
size: 64,
|
|
color: Colors.black,
|
|
),
|
|
),
|
|
const SizedBox(height: 24),
|
|
Text(
|
|
'S.L.R.P.G.',
|
|
style: Theme.of(context).textTheme.displayLarge?.copyWith(
|
|
color: Colors.white,
|
|
shadows: [
|
|
const Shadow(
|
|
color: Colors.black,
|
|
blurRadius: 10,
|
|
offset: Offset(0, 4)),
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(height: 8),
|
|
Text(
|
|
'Streetlifting RPG',
|
|
style: Theme.of(context).textTheme.bodyLarge?.copyWith(
|
|
color: Colors.white,
|
|
fontWeight: FontWeight.bold,
|
|
letterSpacing: 1.2,
|
|
),
|
|
),
|
|
const SizedBox(height: 48),
|
|
const CircularProgressIndicator(
|
|
color: Color(0xFF00E5FF),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|