Files
SIB/old/lib/services/app_state_service.dart

207 lines
5.8 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import '../models/route_model.dart';
import './transportation_service.dart';
/// Global app state service to manage selected route across the entire app
class AppStateService extends ChangeNotifier {
static final AppStateService _instance = AppStateService._internal();
factory AppStateService() => _instance;
AppStateService._internal();
final TransportationService _transportationService = TransportationService();
// Global state
String? _selectedRouteId;
String? _selectedRouteName;
List<RouteModel> _allRoutes = [];
bool _isLoadingRoutes = false;
String? _error;
// Getters
String? get selectedRouteId => _selectedRouteId;
String? get selectedRouteName => _selectedRouteName;
List<RouteModel> get allRoutes => List.unmodifiable(_allRoutes);
bool get isLoadingRoutes => _isLoadingRoutes;
String? get error => _error;
bool get hasSelectedRoute =>
_selectedRouteId != null && _selectedRouteName != null;
/// Initialize app state - call this on app start
Future<void> initialize() async {
await loadRoutes();
}
/// Load all routes from Supabase
Future<void> loadRoutes() async {
if (_isLoadingRoutes) return;
_isLoadingRoutes = true;
notifyListeners();
try {
final result = await _transportationService.getRoutesWithCount();
final routes = result['data'] as List<RouteModel>;
final count = result['count'] as int;
final error = result['error'] as String?;
if (error != null) {
print('Error loading routes: $error');
// Handle error but continue with empty list
_allRoutes = [];
} else {
_allRoutes = routes;
// Auto-select first route if none selected and routes available
if (routes.isNotEmpty && _selectedRouteId == null) {
await selectRoute(routes.first.id);
}
}
_isLoadingRoutes = false;
notifyListeners();
// Show toast if no routes found as specified in requirements
if (_allRoutes.isEmpty) {
_showNoRoutesToast();
}
} catch (e) {
print('Exception loading routes: $e');
_allRoutes = [];
_isLoadingRoutes = false;
notifyListeners();
// Show error toast for exceptions
Fluttertoast.showToast(
msg: "Error loading routes: ${e.toString()}",
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.CENTER,
backgroundColor: Colors.red,
textColor: Colors.white,
fontSize: 16.0,
);
}
}
void _showNoRoutesToast() {
Fluttertoast.showToast(
msg: "No routes found",
toastLength: Toast.LENGTH_LONG,
gravity: ToastGravity.CENTER,
backgroundColor: Colors.orange,
textColor: Colors.white,
fontSize: 16.0,
);
}
/// Select a route and update global state
Future<void> selectRoute(String routeId) async {
if (routeId == _selectedRouteId) return;
try {
// Find the route in our cached routes
final route = _allRoutes.firstWhere(
(r) => r.id == routeId,
orElse: () => throw Exception('Route not found: $routeId'),
);
_selectedRouteId = routeId;
_selectedRouteName = route.displayName;
_error = null;
debugPrint('✅ Route selected: $routeId - $_selectedRouteName');
notifyListeners();
} catch (e) {
_error = 'Error selecting route: $e';
debugPrint('❌ Error selecting route: $e');
notifyListeners();
}
}
/// Clear selected route
void clearSelectedRoute() {
_selectedRouteId = null;
_selectedRouteName = null;
notifyListeners();
}
/// Get selected route model
RouteModel? getSelectedRoute() {
if (_selectedRouteId == null) return null;
try {
return _allRoutes.firstWhere((r) => r.id == _selectedRouteId!);
} catch (e) {
return null;
}
}
/// Private method to select first available route with stops
Future<void> _selectFirstAvailableRoute() async {
for (final route in _allRoutes) {
try {
final stops =
await _transportationService.getRouteStopsOrderedBySeq(route.id);
if (stops.isNotEmpty) {
_selectedRouteId = route.id;
_selectedRouteName = route.displayName;
debugPrint(
'✅ Auto-selected route: ${route.id} - ${route.displayName}');
return;
}
} catch (e) {
debugPrint('⚠️ Error checking stops for route ${route.id}: $e');
}
}
// Fallback: select first route even if no stops
if (_allRoutes.isNotEmpty) {
final firstRoute = _allRoutes.first;
_selectedRouteId = firstRoute.id;
_selectedRouteName = firstRoute.displayName;
debugPrint(
'✅ Fallback: selected first route: ${firstRoute.id} - ${firstRoute.displayName}');
}
}
/// Refresh routes and maintain selection if possible
Future<void> refreshRoutes() async {
final previousSelectedId = _selectedRouteId;
await loadRoutes();
// Try to restore previous selection
if (previousSelectedId != null &&
_allRoutes.any((r) => r.id == previousSelectedId)) {
await selectRoute(previousSelectedId);
}
}
/// Get route by ID
RouteModel? getRouteById(String routeId) {
try {
return _allRoutes.firstWhere((r) => r.id == routeId);
} catch (e) {
return null;
}
}
/// Check if route exists
bool hasRoute(String routeId) {
return _allRoutes.any((r) => r.id == routeId);
}
/// Show toast message for no routes found
void showNoRoutesToast(BuildContext context) {
if (_allRoutes.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('No routes found'),
backgroundColor: Colors.orange,
duration: Duration(seconds: 3),
),
);
}
}
}