Skip to main content

Get Started

The Flutter Tracking SDK has three parts: a Dart dependency and wrapper, native Android setup, and native iOS setup. All three are required - the Dart layer has nothing to call into until the native side is wired up.


Prerequisites and requirements​

  • Access Key: an API key issued during onboarding - see Contact us to request access.
  • Tenant, username and password: the Tracking SDK sends tracking logs for a tracked object to a specific tenant and requires authentication to do so - see Contact us to request access.
  • Flutter: a recent stable Flutter SDK.
  • Completed native prerequisites for Android and iOS.

Exchange your credentials for a token​

curl -X POST 'https://api-gw.sovereignsolutions.com/gateway/authen/oauth/token?api-key=$API_KEY' \
-d 'grant_type=password' \
-d 'username=$USERNAME' \
-d 'password=$PASSWORD'
{
"access_token": "<JWT>",
"token_type": "bearer",
"expires_in": 3600,
"refresh_token": "<token>"
}

Keep the access_token. Every other endpoint needs it.

Full details: Authentication reference


1. Add Dart dependencies​

In pubspec.yaml:

dependencies:
flutter:
sdk: flutter

permission_handler: ^11.3.1
shared_preferences: ^2.3.3
flutter pub get

2. Add the MethodChannel wrapper​

Create lib/tracking_sdk.dart:

import 'package:flutter/services.dart';

class TrackingSdk {
static const MethodChannel _channel = MethodChannel('TrackingSdk');

static Future<String> initAndStartTracking({
required String baseUrl,
required String apiKey,
required String username,
required String clientId,
}) async {
final result = await _channel.invokeMethod<dynamic>(
'initAndStartTracking',
{
'baseUrl': baseUrl,
'apiKey': apiKey,
'username': username,
'clientId': clientId,
},
);

return _messageFromResult(result, fallback: 'Tracking started');
}

static Future<String> stopTracking() async {
final result = await _channel.invokeMethod<dynamic>('stopTracking');
return _messageFromResult(result, fallback: 'Tracking stopped');
}

static Future<bool> getTrackingState() async {
final result = await _channel.invokeMethod<dynamic>('getTrackingState');

if (result is bool) return result;
if (result is Map) return result['enabled'] == true;
return false;
}

static Future<Map<String, dynamic>> getSavedTrackingStatus() async {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>(
'getSavedTrackingStatus',
);
return Map<String, dynamic>.from(result ?? {});
}

static Future<Map<String, dynamic>> sync() async {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>('sync');
return Map<String, dynamic>.from(result ?? {});
}

static Future<Map<String, dynamic>> refreshAccessToken() async {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>(
'refreshAccessToken',
);
return Map<String, dynamic>.from(result ?? {});
}

static Future<Map<String, dynamic>> destroy() async {
final result = await _channel.invokeMethod<Map<dynamic, dynamic>>('destroy');
return Map<String, dynamic>.from(result ?? {});
}

static String _messageFromResult(dynamic result, {required String fallback}) {
if (result == null) return fallback;
if (result is String) return result;
if (result is Map) {
final message = result['message'];
if (message != null && message.toString().trim().isNotEmpty) {
return message.toString();
}
}
return fallback;
}
}

3. Native Android setup​

Add the native SDK dependency, permissions and MethodChannel handler exactly as documented on the Android Tracking SDK page, then register the channel in MainActivity.kt:

class MainActivity : FlutterActivity() {
companion object {
private const val CHANNEL = "TrackingSdk"
}

override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)

MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
.setMethodCallHandler { call, result ->
when (call.method) {
"initAndStartTracking" -> { /* forward to SsBackgroundGeolocation.initSDK + startTracking */ }
"stopTracking" -> { /* forward to SsBackgroundGeolocation.stopTracking */ }
"getTrackingState" -> { /* forward to SsBackgroundGeolocation.getState */ }
"getSavedTrackingStatus" -> { /* read from SharedPreferences */ }
else -> result.notImplemented()
}
}
}
}

See Android Usage for the full SsBackgroundGeolocation call signatures each case should forward to.

4. Native iOS setup​

Add the SDK pod, Info.plist permissions and Background Modes exactly as documented on the iOS Tracking SDK page. Because Flutter iOS projects using multi-window support register through SceneDelegate.swift rather than AppDelegate.swift, register the same channel name there:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
private let channelName = "TrackingSdk"

func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
registerTrackingSdkChannel()
}

private func registerTrackingSdkChannel() {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
guard let controller = self.window?.rootViewController as? FlutterViewController else {
return
}

let channel = FlutterMethodChannel(name: self.channelName,
binaryMessenger: controller.binaryMessenger)

channel.setMethodCallHandler { call, result in
switch call.method {
case "initAndStartTracking":
// forward to SSBackgroundGeolocation.shared.initSDK + startTracking
break
case "stopTracking":
// forward to SSBackgroundGeolocation.shared.stopTracking
break
case "getTrackingState":
// forward to SSBackgroundGeolocation.shared.getState
break
case "sync", "refreshAccessToken", "destroy":
// forward to the matching SSBackgroundGeolocation.shared method
break
default:
result(FlutterMethodNotImplemented)
}
}
}
}
}

See iOS Usage for the full SSBackgroundGeolocation call signatures each case should forward to.

Rebuild after native changes

Changes to MainActivity.kt or SceneDelegate.swift need a full rebuild - uninstall the app and run again. Hot reload does not pick up native channel registration changes.


Next steps​


Looking for the map rendering SDK instead? See the Flutter Map SDK.