Documentation Index
Fetch the complete documentation index at: https://mintlify.com/ihfaz297/MND/llms.txt
Use this file to discover all available pages before exploring further.
The favorites feature allows authenticated users to save frequently used routes for quick access.
FavoriteService
The FavoriteService handles all favorites-related API calls.
Implementation
class FavoriteService {
final ApiService _api = ApiService();
Future<List<Favorite>> getFavorites() async {
final data = await _api.get('/favorites', requireAuth: true);
return (data['favorites'] as List)
.map((fav) => Favorite.fromJson(fav))
.toList();
}
Future<Favorite> addFavorite(Favorite favorite) async {
final data = await _api.post('/favorites', favorite.toJson(), requireAuth: true);
return Favorite.fromJson(data['favorite']);
}
Future<void> deleteFavorite(String id) async {
await _api.delete('/favorites/$id', requireAuth: true);
}
}
Source: lib/services/favorite_service.dart
Authentication Required
All favorite operations require user authentication (requireAuth: true).
Favorite Model
Favorite data is represented by the Favorite model:
class Favorite {
final String id;
final String label;
final String from;
final String to;
final String defaultTime;
Favorite({
required this.id,
required this.label,
required this.from,
required this.to,
required this.defaultTime,
});
factory Favorite.fromJson(Map<String, dynamic> json) {
return Favorite(
id: json['id'],
label: json['label'],
from: json['from'],
to: json['to'],
defaultTime: json['defaultTime'],
);
}
Map<String, dynamic> toJson() {
return {
'label': label,
'from': from,
'to': to,
'defaultTime': defaultTime,
};
}
}
Source: lib/models/favorite.dart
Model Fields
id: Server-assigned unique identifier
label: User-friendly display name
from: Origin stop name
to: Destination stop name
defaultTime: Preferred departure time
Adding Favorites
Users can save routes from the route search results:
Future<void> _addFavorite(RouteOption route) async {
final auth = Provider.of<AuthProvider>(context, listen: false);
// Require authentication
if (!auth.isLoggedIn) {
await Navigator.push(
context,
MaterialPageRoute(builder: (_) => const LoginScreen()),
);
if (!mounted || !auth.isLoggedIn) return;
}
try {
final favorite = Favorite(
id: 'temp', // Server will assign ID
label: route.label,
from: _nodes.firstWhere((n) => n.id == _fromNode).name,
to: _nodes.firstWhere((n) => n.id == _toNode).name,
defaultTime: _time,
);
await _favoriteService.addFavorite(favorite);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Added "${route.label}" to favorites'),
action: SnackBarAction(
label: 'View',
onPressed: () {
// Navigate to favorites screen
},
),
),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to add favorite: $e')),
);
}
}
Source: lib/screens/home/home_screen.dart:85-125
FavoritesScreen
The FavoritesScreen displays all saved routes.
Screen Structure
class FavoritesScreen extends StatefulWidget {
@override
_FavoritesScreenState createState() => _FavoritesScreenState();
}
class _FavoritesScreenState extends State<FavoritesScreen> {
final FavoriteService _favoriteService = FavoriteService();
List<Favorite> _favorites = [];
bool _loading = true;
String? _error;
@override
void initState() {
super.initState();
_loadFavorites();
}
}
Source: lib/screens/favorites/favorites_screen.dart:8-23
Loading Favorites
Future<void> _loadFavorites() async {
final auth = Provider.of<AuthProvider>(context, listen: false);
if (!auth.isLoggedIn) {
setState(() {
_loading = false;
_error = 'Please login to see your favorites';
});
return;
}
setState(() {
_loading = true;
_error = null;
});
try {
final favorites = await _favoriteService.getFavorites();
setState(() {
_favorites = favorites;
_loading = false;
});
} catch (e) {
setState(() {
_error = e.toString();
_loading = false;
});
}
}
Source: lib/screens/favorites/favorites_screen.dart:25-53
Deleting Favorites
Future<void> _deleteFavorite(Favorite favorite) async {
final confirm = await showDialog<bool>(
context: context,
builder: (ctx) => AlertDialog(
title: Text('Delete Favorite?'),
content: Text('Remove "${favorite.label}" from favorites?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx, false),
child: Text('Cancel'),
),
TextButton(
onPressed: () => Navigator.pop(ctx, true),
child: Text('Delete', style: TextStyle(color: Colors.red)),
),
],
),
);
if (confirm == true) {
try {
await _favoriteService.deleteFavorite(favorite.id);
_loadFavorites();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Favorite removed')),
);
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Failed to delete: $e')),
);
}
}
}
Source: lib/screens/favorites/favorites_screen.dart:55-87
Favorites List Display
Widget _buildList() {
return RefreshIndicator(
onRefresh: _loadFavorites,
child: ListView.builder(
padding: EdgeInsets.all(16),
itemCount: _favorites.length,
itemBuilder: (ctx, index) {
final favorite = _favorites[index];
return Card(
margin: EdgeInsets.only(bottom: 12),
child: ListTile(
leading: CircleAvatar(
backgroundColor: Theme.of(context).primaryColor,
child: Icon(Icons.favorite, color: Colors.white),
),
title: Text(
favorite.label,
style: TextStyle(fontWeight: FontWeight.bold),
),
subtitle: Text('${favorite.from} → ${favorite.to}'),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
favorite.defaultTime,
style: TextStyle(color: Colors.grey),
),
IconButton(
icon: Icon(Icons.delete_outline, color: Colors.red),
onPressed: () => _deleteFavorite(favorite),
),
],
),
onTap: () {
// Navigate to route search with pre-filled from/to
Navigator.pushNamed(
context,
'/route-search',
arguments: {
'from': favorite.from,
'to': favorite.to,
},
);
},
),
);
},
),
);
}
Source: lib/screens/favorites/favorites_screen.dart:190-231
Authentication States
Login Prompt
Show login prompt for unauthenticated users:
Widget _buildLoginPrompt() {
return Center(
child: Padding(
padding: EdgeInsets.all(24),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.favorite_border, size: 80, color: Colors.grey),
SizedBox(height: 16),
Text(
'Login to Save Routes',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
'Save your frequent routes for quick access',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey),
),
SizedBox(height: 24),
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => LoginScreen()),
).then((_) => _loadFavorites());
},
child: Text('Login'),
),
],
),
),
);
}
Source: lib/screens/favorites/favorites_screen.dart:115-148
Empty State
Widget _buildEmpty() {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(Icons.favorite_border, size: 80, color: Colors.grey),
SizedBox(height: 16),
Text(
'No Saved Routes',
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
),
SizedBox(height: 8),
Text(
'Search for routes and tap the heart to save them',
textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey),
),
],
),
);
}
Source: lib/screens/favorites/favorites_screen.dart:168-188
Example Usage
class MyFavoritesWidget extends StatefulWidget {
@override
_MyFavoritesWidgetState createState() => _MyFavoritesWidgetState();
}
class _MyFavoritesWidgetState extends State<MyFavoritesWidget> {
final FavoriteService _favoriteService = FavoriteService();
List<Favorite> _favorites = [];
Future<void> loadFavorites() async {
try {
final favorites = await _favoriteService.getFavorites();
setState(() {
_favorites = favorites;
});
} catch (e) {
print('Error: $e');
}
}
Future<void> addFavorite() async {
final favorite = Favorite(
id: '',
label: 'Campus to Ambarkhana',
from: 'Campus',
to: 'Ambarkhana',
defaultTime: '08:30',
);
await _favoriteService.addFavorite(favorite);
await loadFavorites();
}
Future<void> deleteFavorite(String id) async {
await _favoriteService.deleteFavorite(id);
await loadFavorites();
}
}
Error Handling
try {
final favorites = await _favoriteService.getFavorites();
// Success
} catch (e) {
if (e.toString().contains('Session expired')) {
// Redirect to login
} else {
// Show error message
setState(() {
_error = e.toString();
});
}
}
Pull to Refresh
Users can refresh favorites by pulling down:
RefreshIndicator(
onRefresh: _loadFavorites,
child: ListView.builder(
// ... favorites list
),
)
Source: lib/screens/favorites/favorites_screen.dart:191-193
UI Features
- Authentication-gated access
- Pull-to-refresh functionality
- Delete confirmation dialog
- Empty and login states
- Error handling with retry
- Visual feedback (SnackBars)
Next Steps