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
Send Magic Link
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
Verify Magic Link
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
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