import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'core/constants.dart'; import 'providers/auth_provider.dart'; import 'providers/settings_provider.dart'; import 'screens/auth/login_screen.dart'; import 'screens/chat/chat_screen.dart'; import 'screens/chat/conversations_list_screen.dart'; import 'screens/notes/note_detail_screen.dart'; import 'screens/notes/note_edit_screen.dart'; import 'screens/notes/notes_list_screen.dart'; import 'screens/quick_capture/quick_capture_screen.dart'; import 'screens/settings/settings_screen.dart'; import 'screens/setup/setup_screen.dart'; import 'screens/splash/splash_screen.dart'; import 'screens/tasks/task_edit_screen.dart'; import 'screens/tasks/tasks_list_screen.dart'; // ChangeNotifier that fires when auth or server URL changes, // used as GoRouter.refreshListenable so the router re-evaluates redirects // without being recreated. class _RouterNotifier extends ChangeNotifier { _RouterNotifier(Ref ref) { ref.listen(authProvider, (_, _) => notifyListeners()); ref.listen(serverUrlProvider, (_, _) => notifyListeners()); } } final routerProvider = Provider((ref) { final notifier = _RouterNotifier(ref); return GoRouter( refreshListenable: notifier, initialLocation: Routes.splash, redirect: (context, state) { final location = state.matchedLocation; final serverUrl = ref.read(serverUrlProvider); final authStatus = ref.read(authProvider); if (serverUrl == null || serverUrl.isEmpty) { if (location != Routes.setup) return Routes.setup; return null; } if (authStatus == AuthStatus.unauthenticated) { if (location != Routes.login && location != Routes.setup) { return Routes.login; } return null; } return null; }, routes: [ GoRoute( path: Routes.splash, builder: (_, _) => const SplashScreen(), ), GoRoute( path: Routes.setup, builder: (_, _) => const SetupScreen(), ), GoRoute( path: Routes.login, builder: (_, _) => const LoginScreen(), ), GoRoute( path: Routes.quickCapture, builder: (_, _) => const QuickCaptureScreen(), ), GoRoute( path: Routes.settings, builder: (_, _) => const SettingsScreen(), ), GoRoute( path: Routes.noteNew, builder: (_, _) => const NoteEditScreen(), ), GoRoute( path: Routes.noteDetail, builder: (_, state) => NoteDetailScreen( noteId: int.parse(state.pathParameters['id']!), ), ), GoRoute( path: Routes.noteEdit, builder: (_, state) => NoteEditScreen( noteId: int.parse(state.pathParameters['id']!), ), ), GoRoute( path: Routes.taskNew, builder: (_, _) => const TaskEditScreen(), ), GoRoute( path: Routes.taskEdit, builder: (_, state) => TaskEditScreen( taskId: int.parse(state.pathParameters['id']!), ), ), GoRoute( path: Routes.chat, builder: (_, state) => ChatScreen( conversationId: int.parse(state.pathParameters['id']!), ), ), ShellRoute( builder: (context, state, child) => _Shell(child: child), routes: [ GoRoute( path: Routes.notes, builder: (_, _) => const NotesListScreen(), ), GoRoute( path: Routes.tasks, builder: (_, _) => const TasksListScreen(), ), GoRoute( path: Routes.conversations, builder: (_, _) => const ConversationsListScreen(), ), ], ), ], ); }); class _Shell extends ConsumerWidget { final Widget child; const _Shell({required this.child}); static const _tabs = [Routes.notes, Routes.tasks, Routes.conversations]; int _tabIndex(String location) { for (var i = 0; i < _tabs.length; i++) { if (location.startsWith(_tabs[i])) return i; } return 0; } @override Widget build(BuildContext context, WidgetRef ref) { final location = GoRouterState.of(context).matchedLocation; final index = _tabIndex(location); return Scaffold( body: child, bottomNavigationBar: NavigationBar( selectedIndex: index, onDestinationSelected: (i) => context.go(_tabs[i]), destinations: const [ NavigationDestination(icon: Icon(Icons.note), label: 'Notes'), NavigationDestination(icon: Icon(Icons.check_box), label: 'Tasks'), NavigationDestination( icon: Icon(Icons.chat_bubble), label: 'Chat'), ], ), floatingActionButton: FloatingActionButton( heroTag: 'shell_fab', onPressed: () => context.push(Routes.quickCapture), child: const Icon(Icons.add), ), floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked, ); } } class FabledApp extends ConsumerWidget { const FabledApp({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final router = ref.watch(routerProvider); return MaterialApp.router( title: 'Fabled', theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo), useMaterial3: true, ), darkTheme: ThemeData( colorScheme: ColorScheme.fromSeed( seedColor: Colors.indigo, brightness: Brightness.dark, ), useMaterial3: true, ), routerConfig: router, ); } }