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.

Services handle all business logic and API communication in the MND mobile app. Each service encapsulates specific functionality and uses the base ApiService for HTTP requests.

ApiService

The ApiService is the base HTTP client used by all other services.

Implementation

class ApiService {
  final http.Client _client = http.Client();
  
  /// Get auth token from storage
  Future<String?> _getAuthToken() async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getString('auth_token');
  }

  /// Build headers with optional auth
  Future<Map<String, String>> _buildHeaders({bool requireAuth = false}) async {
    final headers = <String, String>{
      'Content-Type': 'application/json',
    };
    
    final token = await _getAuthToken();
    if (token != null) {
      headers['Authorization'] = 'Bearer $token';
    } else if (requireAuth) {
      throw Exception('Authentication required');
    }
    
    return headers;
  }
}
Source: lib/services/api_service.dart:6-29

GET Requests

Future<Map<String, dynamic>> get(
  String endpoint, {
  Map<String, String>? params,
  bool requireAuth = false,
}) async {
  final uri = Uri.parse('${ApiConfig.baseUrl}$endpoint')
      .replace(queryParameters: params);
  
  try {
    final headers = await _buildHeaders(requireAuth: requireAuth);
    final response = await _client.get(uri, headers: headers)
        .timeout(ApiConfig.timeout);
    
    if (response.statusCode == 200) {
      return json.decode(response.body);
    } else if (response.statusCode == 401) {
      throw Exception('Session expired. Please login again.');
    } else {
      final error = json.decode(response.body);
      throw Exception(error['message'] ?? 'Request failed: ${response.statusCode}');
    }
  } catch (e) {
    throw Exception('Network error: $e');
  }
}
Source: lib/services/api_service.dart:31-55

POST Requests

Future<Map<String, dynamic>> post(
  String endpoint,
  Map<String, dynamic> body, {
  bool requireAuth = false,
}) async {
  final uri = Uri.parse('${ApiConfig.baseUrl}$endpoint');
  
  try {
    final headers = await _buildHeaders(requireAuth: requireAuth);
    final response = await _client.post(
      uri,
      headers: headers,
      body: json.encode(body),
    ).timeout(ApiConfig.timeout);
    
    if (response.statusCode == 200 || response.statusCode == 201) {
      return json.decode(response.body);
    } else if (response.statusCode == 401) {
      throw Exception('Session expired. Please login again.');
    } else {
      final error = json.decode(response.body);
      throw Exception(error['message'] ?? 'Request failed: ${response.statusCode}');
    }
  } catch (e) {
    throw Exception('Network error: $e');
  }
}
Source: lib/services/api_service.dart:57-83

DELETE Requests

Future<void> delete(String endpoint, {bool requireAuth = true}) async {
  final uri = Uri.parse('${ApiConfig.baseUrl}$endpoint');
  
  try {
    final headers = await _buildHeaders(requireAuth: requireAuth);
    final response = await _client.delete(uri, headers: headers)
        .timeout(ApiConfig.timeout);
    
    if (response.statusCode != 200 && response.statusCode != 204) {
      if (response.statusCode == 401) {
        throw Exception('Session expired. Please login again.');
      }
      final error = json.decode(response.body);
      throw Exception(error['message'] ?? 'Delete failed: ${response.statusCode}');
    }
  } catch (e) {
    throw Exception('Network error: $e');
  }
}
Source: lib/services/api_service.dart:85-103

RouteService

Handles route planning and node retrieval.
class RouteService {
  final ApiService _api = ApiService();

  Future<List<Node>> getNodes() async {
    final data = await _api.get('/nodes');
    return (data['nodes'] as List).map((node) => Node.fromJson(node)).toList();
  }

  Future<List<RouteOption>> planRoute({
    required String from,
    required String to,
    required String time,
  }) async {
    final data = await _api.get('/routes', params: {
      'from': from,
      'to': to,
      'time': time,
    });
    
    return (data['options'] as List)
        .map((option) => RouteOption.fromJson(option))
        .toList();
  }
}
Source: lib/services/route_service.dart

Methods

  • getNodes(): Fetch all available stops/locations
  • planRoute(): Search for routes between two points

Example Usage

final routeService = RouteService();

// Get all nodes
final nodes = await routeService.getNodes();

// Plan a route
final routes = await routeService.planRoute(
  from: 'CAMPUS',
  to: 'AMBARKHANA',
  time: '08:30',
);

BusService

Handles bus schedule retrieval.
class BusService {
  final ApiService _api = ApiService();

  Future<List<BusSchedule>> getUpcomingBuses(String from, {int limit = 5}) async {
    final data = await _api.get('/buses/upcoming', params: {
      'from': from,
      'limit': limit.toString(),
    });
    
    return (data['buses'] as List)
        .map((bus) => BusSchedule.fromJson(bus))
        .toList();
  }
}
Source: lib/services/bus_service.dart

Methods

  • getUpcomingBuses(): Fetch upcoming departures at a stop

Example Usage

final busService = BusService();

final buses = await busService.getUpcomingBuses(
  'CAMPUS',
  limit: 10,
);

AuthService

Handles authentication and user session management.
class AuthService {
  static const String _tokenKey = 'auth_token';
  static const String _userKey = 'user_data';
  
  String? _authToken;
  User? _currentUser;

  String? get authToken => _authToken;
  User? get currentUser => _currentUser;
  bool get isLoggedIn => _authToken != null;

  /// Initialize auth state from local storage
  Future<void> init() async {
    final prefs = await SharedPreferences.getInstance();
    _authToken = prefs.getString(_tokenKey);
    
    final userData = prefs.getString(_userKey);
    if (userData != null) {
      _currentUser = User.fromJson(json.decode(userData));
    }
  }
}
Source: lib/services/auth_service.dart:7-27
Future<Map<String, dynamic>> sendMagicLink(String email) async {
  final uri = Uri.parse('${ApiConfig.baseUrl}/auth/send-link');
  
  final response = await http.post(
    uri,
    headers: {'Content-Type': 'application/json'},
    body: json.encode({'email': email}),
  ).timeout(ApiConfig.timeout);

  if (response.statusCode == 200) {
    return json.decode(response.body);
  } else {
    final error = json.decode(response.body);
    throw Exception(error['message'] ?? 'Failed to send magic link');
  }
}
Source: lib/services/auth_service.dart:29-45
Future<User> verifyMagicLink(String token) async {
  final uri = Uri.parse('${ApiConfig.baseUrl}/auth/verify?token=$token');
  
  final response = await http.get(uri).timeout(ApiConfig.timeout);

  if (response.statusCode == 200) {
    final data = json.decode(response.body);
    
    _authToken = data['authToken'];
    _currentUser = User.fromJson(data['user']);
    
    // Save to local storage
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(_tokenKey, _authToken!);
    await prefs.setString(_userKey, json.encode(data['user']));
    
    return _currentUser!;
  } else {
    final error = json.decode(response.body);
    throw Exception(error['message'] ?? 'Verification failed');
  }
}
Source: lib/services/auth_service.dart:47-69

Logout

Future<void> logout() async {
  if (_authToken != null) {
    try {
      final uri = Uri.parse('${ApiConfig.baseUrl}/auth/logout');
      await http.post(
        uri,
        headers: {'Authorization': 'Bearer $_authToken'},
      );
    } catch (_) {}
  }
  
  _authToken = null;
  _currentUser = null;
  
  final prefs = await SharedPreferences.getInstance();
  await prefs.remove(_tokenKey);
  await prefs.remove(_userKey);
}
Source: lib/services/auth_service.dart:94-112

Example Usage

final authService = AuthService();

// Initialize from storage
await authService.init();

// Send magic link
await authService.sendMagicLink('[email protected]');

// Verify token
final user = await authService.verifyMagicLink(token);

// Check login status
if (authService.isLoggedIn) {
  print('User: ${authService.currentUser?.email}');
}

// Logout
await authService.logout();

FavoriteService

Handles user favorites management.
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

Methods

  • getFavorites(): Fetch all user favorites (requires auth)
  • addFavorite(): Save a new favorite (requires auth)
  • deleteFavorite(): Remove a favorite (requires auth)

Example Usage

final favoriteService = FavoriteService();

// Get favorites
final favorites = await favoriteService.getFavorites();

// Add favorite
final favorite = Favorite(
  id: '',
  label: 'Campus to Ambarkhana',
  from: 'Campus',
  to: 'Ambarkhana',
  defaultTime: '08:30',
);
await favoriteService.addFavorite(favorite);

// Delete favorite
await favoriteService.deleteFavorite(favoriteId);

DirectionsService

Fetches directions from Google Directions API.
class DirectionsService {
  static const String _baseUrl =
      'https://maps.googleapis.com/maps/api/directions/json';

  static Future<DirectionsResult?> getDirections({
    required String origin,
    required String destination,
  }) async {
    final url = Uri.parse(
      '$_baseUrl?origin=${Uri.encodeComponent(origin)}'
      '&destination=${Uri.encodeComponent(destination)}'
      '&mode=driving'
      '&key=${ApiConfig.googleMapsApiKey}',
    );

    final response = await http.get(url);

    if (response.statusCode != 200) {
      return null;
    }

    final data = json.decode(response.body);

    if (data['status'] != 'OK') {
      return null;
    }

    final route = data['routes'][0];
    final leg = route['legs'][0];

    // Decode polyline
    final polylinePoints =
        _decodePolyline(route['overview_polyline']['points']);

    // Extract bounds
    final bounds = route['bounds'];
    final northeast = LatLng(
      bounds['northeast']['lat'].toDouble(),
      bounds['northeast']['lng'].toDouble(),
    );
    final southwest = LatLng(
      bounds['southwest']['lat'].toDouble(),
      bounds['southwest']['lng'].toDouble(),
    );

    return DirectionsResult(
      polylinePoints: polylinePoints,
      distanceMeters: leg['distance']['value'],
      distanceText: leg['distance']['text'],
      durationSeconds: leg['duration']['value'],
      durationText: leg['duration']['text'],
      startLocation: LatLng(
        leg['start_location']['lat'].toDouble(),
        leg['start_location']['lng'].toDouble(),
      ),
      endLocation: LatLng(
        leg['end_location']['lat'].toDouble(),
        leg['end_location']['lng'].toDouble(),
      ),
      bounds: LatLngBounds(southwest: southwest, northeast: northeast),
    );
  }
}
Source: lib/services/directions_service.dart:7-80

Example Usage

final directions = await DirectionsService.getDirections(
  origin: 'Tilagor, Sylhet, Bangladesh',
  destination: 'Ambarkhana, Sylhet, Bangladesh',
);

if (directions != null) {
  print('Distance: ${directions.distanceText}');
  print('Duration: ${directions.durationText}');
  // Use polylinePoints for map visualization
}

Error Handling

All services throw exceptions with descriptive messages:
try {
  final routes = await routeService.planRoute(
    from: 'CAMPUS',
    to: 'AMBARKHANA',
    time: '08:30',
  );
} catch (e) {
  if (e.toString().contains('Session expired')) {
    // Redirect to login
  } else if (e.toString().contains('Network error')) {
    // Show network error message
  } else {
    // Generic error handling
    print('Error: $e');
  }
}

Common Error Types

  • Session expired. Please login again. - 401 Unauthorized
  • Network error: <details> - Connection/timeout issues
  • Request failed: <status> - Server errors (4xx, 5xx)
  • Authentication required - Missing auth token for protected endpoint

HTTP Status Codes

  • 200 OK - Successful GET/DELETE
  • 201 Created - Successful POST
  • 204 No Content - Successful DELETE (alternative)
  • 401 Unauthorized - Invalid/expired auth token
  • 4xx - Client errors
  • 5xx - Server errors

Timeout Configuration

All requests use a configurable timeout:
final response = await _client.get(uri, headers: headers)
    .timeout(ApiConfig.timeout);
Default: 30 seconds (configured in ApiConfig.timeout) Source: lib/services/api_service.dart:41-42

Authentication Headers

Authenticated requests include a Bearer token:
headers['Authorization'] = 'Bearer $token';
Source: lib/services/api_service.dart:23

Local Storage

Services use SharedPreferences for persistent storage:
final prefs = await SharedPreferences.getInstance();
final token = prefs.getString('auth_token');
await prefs.setString('auth_token', newToken);
await prefs.remove('auth_token');

Next Steps

Build docs developers (and LLMs) love