Skip to main content

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

Build docs developers (and LLMs) love