> ## 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.

# Route Planning

> Search for bus routes and display route options in the MND mobile app

The route planning feature allows users to search for optimal bus routes between campus locations, view multiple route options, and save favorites.

## RouteService

The `RouteService` handles all route-related API calls.

### Getting Available Nodes

Retrieve all available stops/locations:

```dart theme={null}
final RouteService _routeService = RouteService();

Future<void> _loadNodes() async {
  try {
    final nodes = await _routeService.getNodes();
    setState(() {
      _nodes = nodes;
    });
  } catch (e) {
    print('Failed to load locations: $e');
  }
}
```

Source: `lib/screens/home/home_screen.dart:38-53`

### Planning a Route

Search for routes between two locations:

```dart theme={null}
Future<void> _searchRoutes() async {
  try {
    final routes = await _routeService.planRoute(
      from: _fromNode!,
      to: _toNode!,
      time: _time,
    );
    setState(() {
      _routes = routes;
    });
  } catch (e) {
    print('Failed to find routes: $e');
  }
}
```

Source: `lib/screens/home/home_screen.dart:55-83`

### RouteService Implementation

```dart theme={null}
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`

## RouteOption Model

Route search results are represented by the `RouteOption` model:

```dart theme={null}
class RouteOption {
  final String label;
  final String category;
  final int totalTimeMin;
  final int totalCost;
  final int transfers;
  final int localTimeMin;
  final int localDistanceMeters;
  final List<RouteLeg> legs;

  RouteOption({
    required this.label,
    required this.category,
    required this.totalTimeMin,
    required this.totalCost,
    required this.transfers,
    required this.localTimeMin,
    required this.localDistanceMeters,
    required this.legs,
  });

  factory RouteOption.fromJson(Map<String, dynamic> json) {
    return RouteOption(
      label: json['label'],
      category: json['category'],
      totalTimeMin: json['totalTimeMin'],
      totalCost: json['totalCost'],
      transfers: json['transfers'],
      localTimeMin: json['localTimeMin'],
      localDistanceMeters: json['localDistanceMeters'],
      legs: (json['legs'] as List).map((leg) => RouteLeg.fromJson(leg)).toList(),
    );
  }
}
```

Source: `lib/models/route_option.dart`

### Route Categories

* `fastest` - Minimizes total travel time
* `cheapest` - Minimizes total cost
* Other categories as defined by the backend

## Route Search UI

The `HomeScreen` provides the route search interface.

### Search Form

```dart theme={null}
Form(
  key: _formKey,
  child: Column(
    children: [
      // From Dropdown
      DropdownButtonFormField<String>(
        decoration: const InputDecoration(
          labelText: 'From',
          border: OutlineInputBorder(),
          filled: true,
          fillColor: Colors.white,
        ),
        value: _fromNode,
        items: _nodes.map((node) {
          return DropdownMenuItem(
            value: node.id,
            child: Text(node.name),
          );
        }).toList(),
        onChanged: (value) => setState(() => _fromNode = value),
        validator: (value) => value == null ? 'Please select an origin' : null,
      ),
      
      // To Dropdown
      DropdownButtonFormField<String>(
        decoration: const InputDecoration(
          labelText: 'To',
          border: OutlineInputBorder(),
        ),
        value: _toNode,
        items: _nodes.map((node) {
          return DropdownMenuItem(
            value: node.id,
            child: Text(node.name),
          );
        }).toList(),
        onChanged: (value) => setState(() => _toNode = value),
      ),
      
      // Time Input
      TextFormField(
        readOnly: true,
        controller: TextEditingController(text: _time),
        decoration: const InputDecoration(
          labelText: 'Time',
          suffixIcon: Icon(Icons.access_time),
        ),
        onTap: _selectTime,
      ),
      
      // Search Button
      ElevatedButton(
        onPressed: _loading ? null : _searchRoutes,
        child: const Text('Find Routes'),
      ),
    ],
  ),
)
```

Source: `lib/screens/home/home_screen.dart:160-234`

### Time Picker

```dart theme={null}
Future<void> _selectTime() async {
  final TimeOfDay? picked = await showTimePicker(
    context: context,
    initialTime: TimeOfDay(
      hour: int.parse(_time.split(':')[0]),
      minute: int.parse(_time.split(':')[1]),
    ),
  );
  if (picked != null) {
    final formattedTime = '${picked.hour.toString().padLeft(2, '0')}:${picked.minute.toString().padLeft(2, '0')}';
    setState(() {
      _time = formattedTime;
    });
  }
}
```

Source: `lib/screens/home/home_screen.dart:127-142`

## RouteCard Widget

The `RouteCard` widget displays individual route options:

```dart theme={null}
class RouteCard extends StatelessWidget {
  final RouteOption route;
  final VoidCallback? onFavorite;

  const RouteCard({
    super.key,
    required this.route,
    this.onFavorite,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      margin: EdgeInsets.only(bottom: 16),
      elevation: 2,
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // Header with label and category badge
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text(
                  route.label,
                  style: TextStyle(
                    fontSize: 18,
                    fontWeight: FontWeight.bold,
                  ),
                ),
                // Map button
                IconButton(
                  icon: Icon(Icons.map_outlined, color: Colors.blue),
                  tooltip: 'View on Map',
                  onPressed: () => _openRouteMap(context),
                ),
                // Favorite button
                if (onFavorite != null)
                  IconButton(
                    icon: Icon(Icons.favorite_border),
                    onPressed: onFavorite,
                  ),
                // Category badge
                Container(
                  padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
                  decoration: BoxDecoration(
                    color: route.category == 'fastest'
                        ? Colors.green.withOpacity(0.2)
                        : Colors.blue.withOpacity(0.2),
                    borderRadius: BorderRadius.circular(12),
                  ),
                  child: Text(
                    route.category.toUpperCase(),
                    style: TextStyle(fontSize: 10, fontWeight: FontWeight.bold),
                  ),
                ),
              ],
            ),
            
            // Stats (time, cost, transfers)
            Row(
              children: [
                _buildStat(Icons.access_time, '${route.totalTimeMin} min'),
                SizedBox(width: 16),
                _buildStat(Icons.currency_rupee, '৳${route.totalCost}'),
                SizedBox(width: 16),
                _buildStat(Icons.transfer_within_a_station, '${route.transfers}'),
              ],
            ),
            
            // Walking/local time note
            if (route.localTimeMin > 0)
              Text(
                'Includes ${route.localTimeMin} min walking/local',
                style: TextStyle(color: Colors.grey, fontSize: 12),
              ),
            
            Divider(),
            
            // Route legs
            ...route.legs.map((leg) => Row(
              children: [
                Icon(
                  leg.mode == 'bus' ? Icons.directions_bus : Icons.directions_walk,
                  size: 20,
                  color: leg.mode == 'bus' ? Colors.blue : Colors.orange,
                ),
                SizedBox(width: 8),
                Expanded(
                  child: Text('${leg.from} → ${leg.to}'),
                ),
                if (leg.departure != null)
                  Text(
                    '${leg.departure} - ${leg.arrival}',
                    style: TextStyle(fontSize: 12, color: Colors.grey),
                  ),
              ],
            )),
          ],
        ),
      ),
    );
  }
}
```

Source: `lib/widgets/route_card.dart:24-124`

## Displaying Route Results

```dart theme={null}
Expanded(
  child: _routes.isEmpty
      ? Center(
          child: Text(
            _loading ? 'Searching...' : 'No routes found',
            style: const TextStyle(color: Colors.grey),
          ),
        )
      : ListView.builder(
          padding: const EdgeInsets.all(16),
          itemCount: _routes.length,
          itemBuilder: (context, index) {
            final route = _routes[index];
            return RouteCard(
              route: route,
              onFavorite: auth.isLoggedIn ? () => _addFavorite(route) : null,
            );
          },
        ),
)
```

Source: `lib/screens/home/home_screen.dart:238-257`

## Adding to Favorites

Users can save routes for quick access:

```dart theme={null}
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')),
    );
  } catch (e) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Failed to add favorite: $e')),
    );
  }
}
```

Source: `lib/screens/home/home_screen.dart:85-125`

## Opening Map View

Tap the map icon to visualize the route:

```dart theme={null}
void _openRouteMap(BuildContext context) {
  Navigator.of(context).push(
    MaterialPageRoute(
      builder: (context) => RouteMapScreen(routeOption: route),
    ),
  );
}
```

Source: `lib/widgets/route_card.dart:15-21`

## Example Usage

```dart theme={null}
class MyRouteScreen extends StatefulWidget {
  @override
  _MyRouteScreenState createState() => _MyRouteScreenState();
}

class _MyRouteScreenState extends State<MyRouteScreen> {
  final RouteService _routeService = RouteService();
  List<RouteOption> _routes = [];
  
  Future<void> searchRoutes() async {
    final routes = await _routeService.planRoute(
      from: 'CAMPUS',
      to: 'AMBARKHANA',
      time: '08:30',
    );
    
    setState(() {
      _routes = routes;
    });
  }
  
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: _routes.length,
      itemBuilder: (context, index) {
        return RouteCard(route: _routes[index]);
      },
    );
  }
}
```

## Error Handling

Handle network errors gracefully:

```dart theme={null}
try {
  final routes = await _routeService.planRoute(
    from: fromNode,
    to: toNode,
    time: time,
  );
  // Success
} catch (e) {
  // Network error, invalid input, or server error
  ScaffoldMessenger.of(context).showSnackBar(
    SnackBar(content: Text('Failed to find routes: $e')),
  );
}
```

## Next Steps

* [Maps Visualization](/mobile/maps-visualization)
* [User Favorites](/mobile/user-favorites)
* [Services Documentation](/mobile/services)
