Real-Time GPS Tracking with Flutter and WebSockets
Real-time location tracking is a core feature of delivery, fleet management, ride-sharing, logistics, and field-service applications. Polling an API every few seconds can waste battery and still introduce delays.
In this tutorial, we will build the architecture for real-time GPS tracking with Flutter and WebSockets. The Flutter app will collect location updates, send them through a WebSocket connection, and receive live updates from other clients.
The Architecture
A typical production architecture looks like this:
- Flutter obtains GPS coordinates.
- Flutter opens a WebSocket connection.
- The client sends location events to the backend.
- The backend validates and broadcasts updates.
- Other connected clients receive the new coordinates.
- The UI updates the marker without polling.
This approach is useful for delivery drivers, fleet dashboards, courier apps, and live tracking systems.
Prerequisites
You will need:
- Flutter SDK
- Dart
- A WebSocket-capable backend
- Location permissions
- A physical device for realistic GPS testing
Add Dependencies
Add a location package and a WebSocket client to pubspec.yaml.
dependencies:
flutter:
sdk: flutter
geolocator: ^latest
web_socket_channel: ^latest
Use the current compatible package versions when creating the project.
Configure Location Permissions
For Android, declare location permissions in AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
Background tracking may require additional platform-specific configuration and user permission. Follow the requirements of your target Android and iOS versions.
Create a WebSocket Service
Keep networking outside your widgets.
import 'dart:convert';
import 'package:web_socket_channel/web_socket_channel.dart';
class LocationSocket {
final WebSocketChannel channel;
LocationSocket(String url)
: channel = WebSocketChannel.connect(Uri.parse(url));
void sendLocation({
required double latitude,
required double longitude,
}) {
channel.sink.add(jsonEncode({
'type': 'location_update',
'latitude': latitude,
'longitude': longitude,
}));
}
Stream<dynamic> get messages => channel.stream;
Future<void> dispose() async {
await channel.sink.close();
}
}
Listen to GPS Updates
Use Geolocator.getPositionStream() to receive location changes.
final positionStream = Geolocator.getPositionStream(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.high,
distanceFilter: 10,
),
);
positionStream.listen((position) {
socket.sendLocation(
latitude: position.latitude,
longitude: position.longitude,
);
});
The distanceFilter prevents the app from sending an update for every tiny movement.
Receive Live Locations
Other clients can listen for incoming WebSocket events:
socket.messages.listen((message) {
final data = jsonDecode(message);
if (data['type'] == 'location_update') {
final latitude = data['latitude'];
final longitude = data['longitude'];
// Update your map marker here.
}
});
Connect this stream to your state-management layer instead of directly changing complex UI state.
Display the Location
Your map layer can consume a model such as:
class DriverLocation {
final String driverId;
final double latitude;
final double longitude;
const DriverLocation({
required this.driverId,
required this.latitude,
required this.longitude,
});
}
When a new event arrives, update the corresponding marker.
Handle Connection Loss
Mobile networks change frequently. A production WebSocket client should handle:
- Connection failures
- Temporary network loss
- Reconnection
- Authentication expiration
- Duplicate events
A simple reconnection strategy can use increasing delays:
Future<void> reconnect() async {
for (var attempt = 1; attempt <= 5; attempt++) {
try {
// Create a new WebSocket connection.
return;
} catch (_) {
await Future.delayed(
Duration(seconds: attempt * 2),
);
}
}
}
In production, also add jitter and avoid reconnecting continuously when the device is offline.
Secure the Connection
Always use wss:// in production.
wss://api.example.com/location
Authenticate the WebSocket connection with a short-lived token. Never hard-code backend secrets in the Flutter application.
Optimize Battery Usage
Real-time GPS can consume significant battery power.
Consider:
- Increasing the distance filter.
- Reducing update frequency when the device is stationary.
- Using appropriate location accuracy.
- Stopping tracking when a delivery or trip ends.
- Moving processing away from the main UI thread.
Production Architecture
For a larger application, separate responsibilities:
Flutter UI
↓
State Management
↓
Location Repository
↓
GPS Service + WebSocket Service
↓
Backend
↓
Database / Message Broker
This makes the application easier to test and maintain.
Conclusion
WebSockets and Flutter provide a strong foundation for real-time location tracking. By combining a GPS stream with a persistent WebSocket connection, you can deliver location updates with much less overhead than frequent HTTP polling.
For production systems, focus on authentication, reconnection, battery consumption, background-location rules, and server-side validation. These details are just as important as drawing the location marker on the map.
Stay tuned for more advanced Flutter development tutorials!
Useful Links
SDK Flutter: https://github.com/v-modal/vmodal_sdk_flutter
SDK Android: https://github.com/v-modal/vmodal_sdk_android
Discord: https://discord.gg/K72z28KUx
Top comments (0)