Skip to main content

Truck Profile

Last updated: July 15, 2026 | 5 minutes read

This example demonstrates how to create a Flutter app that displays a truck profile and calculates routes using Maps SDK for Flutter. Users can modify truck parameters and visualize routes on the map.

How it works

The example app demonstrates the following features:

  • Display a map.
  • Fill truck details in a truck profile panel.
  • Calculate a route based on the truck’s profile and visualize them on the map.
Truck navigation settings
Computed route

UI and Map Integration

class MyApp extends StatelessWidget {
const MyApp({super.key});


Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Truck Profile',
home: MyHomePage(),
);
}
}

class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});


State<MyHomePage> createState() => _MyHomePageState();
}

Main Screen with Truck Profile

This code sets up the main screen with a map and functionality for modifying the truck profile and calculating routes.

class _MyHomePageState extends State<MyHomePage> {
late GemMapController _mapController;
final TruckProfile _truckProfile = TruckProfile();

// We use the handler to cancel the route calculation.
TaskHandler? _routingHandler;

List<Route>? _routes;


void dispose() {
GemKit.release();
super.dispose();
}


Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.deepPurple[900],
title: const Text(
'Truck Profile',
style: TextStyle(color: Colors.white),
),
actions: [
// Routes are not built.
if (_routingHandler == null && _routes == null)
IconButton(
onPressed: () => _onBuildRouteButtonPressed(context),
icon: const Icon(Icons.route, color: Colors.white),
),
// Routes calculating is in progress.
if (_routingHandler != null)
IconButton(
onPressed: () => _onCancelRouteButtonPressed(),
icon: const Icon(Icons.stop, color: Colors.white),
),
// Routes calculating is finished.
if (_routes != null)
IconButton(
onPressed: () => _onClearRoutesButtonPressed(),
icon: const Icon(Icons.clear, color: Colors.white),
),
],
),
body: Stack(
alignment: AlignmentDirectional.bottomStart,
children: [
GemMap(
key: ValueKey("GemMap"),
onMapCreated: _onMapCreated,
appAuthorization: projectApiToken,
),
if (_routes == null)
Padding(
padding: const EdgeInsets.all(15.0),
child: Container(
decoration: BoxDecoration(
color: Colors.deepPurple[900],
shape: BoxShape.circle,
),
child: IconButton(
onPressed: () =>
showTruckProfileDialog(context, _truckProfile),
icon: Icon(Icons.settings),
color: Colors.white,
),
),
),
],
),
);
}

// The callback for when map is ready to use.
Future<void> _onMapCreated(GemMapController controller) async {
// Save controller for further usage.
_mapController = controller;

// Register route tap gesture callback.
await _registerRouteTapCallback();
}

Route Calculation

This code handles the route calculation based on the truck’s profile and updates the UI with the calculated routes.

void _onBuildRouteButtonPressed(BuildContext context) {
// Define the departure.
final departureLandmark = Landmark.withLatLng(
latitude: 48.87126,
longitude: 2.33787,
);

// Define the destination.
final destinationLandmark = Landmark.withLatLng(
latitude: 51.4739,
longitude: -0.0302,
);

// Define the route preferences with current truck profile.
final routePreferences = RoutePreferences(
truckProfile: _truckProfile,
transportMode:
RouteTransportMode.lorry, // <- This field is very important
);

_showSnackBar(context, message: "The route is being calculated.");

// Calling the calculateRoute SDK method.
// (err, results) - is a callback function that gets called when the route computing is finished.
// err is an error enum, results is a list of routes.

_routingHandler = RoutingService.calculateRoute(
[departureLandmark, destinationLandmark],
routePreferences,
(err, routes) {
// If the route calculation is finished, we don't have a progress listener anymore.
_routingHandler = null;
ScaffoldMessenger.of(context).clearSnackBars();

// If there aren't any errors, we display the routes.
if (err == GemError.success) {
// Get the routes collection from map preferences.
final routesMap = _mapController.preferences.routes;

// Display the routes on map.
for (final route in routes) {
routesMap.add(
route,
route == routes.first,
label: getMapLabel(route),
);
}

// Center the camera on routes.
_mapController.centerOnRoutes(routes: routes);
setState(() {
_routes = routes;
});
}
},
);

setState(() {});
}
warning

Version 2.26.0 introduces options for caravan routes, allowing you to specify vehicle dimensions without being limited to truck routes. The transportMode field is essential for distinguishing a caravan from a truck.

If RouteTransportMode is not set to lorry, the routing may direct you onto roads restricted for lorries.

Truck Profile Modification

This dialog allows users to modify the truck profile parameters and returns the updated profile.

truck_profile_dialog.dartView on Github
class TruckProfileDialog extends StatefulWidget {
// The truck profile data to be modified in the dialog.
final TruckProfile truckProfile;

const TruckProfileDialog({super.key, required this.truckProfile});


TruckProfileDialogState createState() => TruckProfileDialogState();
}

class TruckProfileDialogState extends State<TruckProfileDialog> {
late TruckProfile profile;

// Initializes the state of the dialog by copying the truck profile and setting initial values.

void initState() {
super.initState();
profile = widget.truckProfile;
}


Widget build(BuildContext context) {
return AlertDialog(
title: Text('Truck Profile'),
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
// Sliders to adjust various truck parameters.
_buildSlider(
'Height',
profile.height.toDouble() < 180 ? 180 : profile.height.toDouble(),
180,
400,
(value) {
setState(() {
profile.height = value.toInt();
});
},
"cm",
),
_buildSlider(
'Length',
profile.length.toDouble() < 500 ? 500 : profile.length.toDouble(),
500,
2000,
(value) {
setState(() {
profile.length = value.toInt();
});
},
"cm",
),
_buildSlider(
'Width',
profile.width.toDouble() < 200 ? 200 : profile.width.toDouble(),
200,
400,
(value) {
setState(() {
profile.width = value.toInt();
});
},
"cm",
),
_buildSlider(
'Axle Load',
profile.axleLoad.toDouble() < 1500
? 1500
: profile.axleLoad.toDouble(),
1500,
10000,
(value) {
setState(() {
profile.axleLoad = value.toInt();
});
},
"kg",
),
_buildSlider(
'Max Speed',
profile.maxSpeed < 60 ? 60 : profile.maxSpeed,
60,
250,
(value) {
setState(() {
profile.maxSpeed = value;
});
},
"km/h",
),

_buildSlider(
'Weight',
profile.mass.toDouble() < 3000 ? 3000 : profile.mass.toDouble(),
3000,
50000,
(value) {
setState(() {
profile.mass = value.toInt();
});
},
"kg",
),
],
),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(profile);
},
child: Text('Done'),
),
],
);
}