Skip to main content

Flutter SDK Usage & Integration

The DoorstepAI SDK has four touchpoints in your driver app. Wire up each one and you're done. Everything else on this page is reference detail for those four calls.

#When this happens in the driver appWhat you call
1App launchesInitialize SDK
2Driver enters the ≥250 m delivery geofenceStart Tracking
3Driver takes POD or confirms drop-off in-appMark Drop-off
4Driver exits the ≥250 m delivery geofenceStop Tracking
Permissions

Tracking needs runtime location permission on Android. Request it during init (see Manual Initialization and Permission Handling). On iOS the system prompts automatically.


1. Initialize SDK

Initialize the SDK once, early in your app's lifecycle. You can let the DoorstepAiView widget handle it, or initialize manually for more control.

Option A: DoorstepAiView widget

The widget initializes the SDK and requests permissions automatically. It is no longer required as of the latest release (manual init works just as well), but it's the quickest path:

main.dart
import 'package:flutter/material.dart';
import 'package:doorstepai_dropoff_sdk/doorstepai_dropoff_sdk.dart';
import 'package:doorstepai_dropoff_sdk/doorstep_ai_view.dart';

void main() {
runApp(const MyApp());
}

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

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: DoorstepAiView(
apiKey: 'your-api-key',
notificationTitle: 'Tracking...',
notificationText: 'Tracking your delivery',
),
),
);
}
}

Option B: Manual initialization

For more control, initialize the SDK and set the API key yourself, then request permissions:

delivery_manager.dart
import 'package:flutter/foundation.dart';
import 'package:doorstepai_dropoff_sdk/doorstepai_dropoff_sdk.dart';

class DeliveryManager {
static Future<void> initializeSDK() async {
// Initialize SDK first
await DoorstepAI.init(
notificationTitle: 'Tracking...',
notificationText: 'Tracking your delivery',
);

// Set API key after initialization
await DoorstepAI.setApiKey('your-api-key');

// Request permissions on Android (see requestPermissions() below)
if (defaultTargetPlatform == TargetPlatform.android) {
await requestPermissions();
}
}
}

See Permission Handling below for the requestPermissions() implementation.

Initialization parameters

ParameterTypeRequiredDescription
apiKeyStringYesYour DoorstepAI API key
notificationTitleString?NoTitle on the foreground-service notification shown while tracking
notificationTextString?NoDescription on that notification
API Key Security

Store your API key securely using environment variables, secure storage, or a configuration service. Never hardcode API keys in production builds.


2. Start Tracking

Call startDelivery… when the driver enters the delivery geofence around the target building, not once inside. Use a unique deliveryId per session: a meaningful identifier you can correlate on your side. Wrap calls in try/catch to handle failures such as an invalid key or denied permissions.

Pick whichever address format you have (startDeliveryByPlaceID, startDeliveryByAddress, or startDeliveryByAddressString):

import 'package:doorstepai_dropoff_sdk/doorstepai_dropoff_sdk.dart';

// By Google Place ID
await DoorstepAI.startDeliveryByPlaceID(
placeID: 'some_place_id',
deliveryId: 'delivery_12345',
);

// By address components
final address = AddressType(
streetNumber: '123',
route: 'Main Street',
subPremise: 'Apt 4B',
locality: 'San Francisco',
administrativeAreaLevel1: 'CA',
postalCode: '94102',
);
await DoorstepAI.startDeliveryByAddress(
address: address,
deliveryId: 'delivery_12345',
);

// By single address string, with optional coordinates and knobs
await DoorstepAI.startDeliveryByAddressString(
address: '123 Main St, Apt 4B, San Francisco, CA 94102',
deliveryId: 'delivery_12345',
coordinates: LatLngObject(lat: 37.7749, lng: -122.4194),
timeoutSeconds: 1200,
);

Start parameters

Every startDelivery* method takes a deliveryId plus these optional knobs:

ParameterTypeDescription
deliveryIdStringRequired. Unique per session; correlate it on your side
timeoutSecondsint?Auto-stops tracking after this duration, a backstop if the exit geofence is missed
autoStopAfterDropoffSecondsint?Auto-stops this many seconds after markDropoff. Falls back to remote config if omitted
manualForegroundbool?(Android only) When true, the SDK won't promote its tracking service to the foreground; your app must already run its own
coordinatesLatLngObject?(address / addressString variants only) pairs a textual address with a lat/lng you resolved upstream
Deprecated start methods

startDeliveryByPlusCode and startDeliveryByLatLng are deprecated. Use startDeliveryByAddressString / startDeliveryByAddress with coordinates instead. They remain for backwards compatibility:

await DoorstepAI.startDeliveryByPlusCode(
plusCode: 'some_plus_code',
deliveryId: 'delivery_12345',
);

await DoorstepAI.startDeliveryByLatLng(
latitude: 37.7749,
longitude: -122.4194,
subUnit: 'Apt 4B',
deliveryId: 'delivery_12345',
);

3. Mark Drop-off

When the driver completes the delivery (takes a POD or confirms drop-off in-app), mark it. markDropoff is the preferred API:

await DoorstepAI.markDropoff(
deliveryId: 'delivery_12345',
dropoffType: DropoffType.pod, // or DropoffType.nonPod
);

markDropoff parameters

ParameterTypeDescription
deliveryIdStringRequired. The session you're marking
dropoffTypeDropoffTypepod (proof of delivery captured) or nonPod

Custom Events

Use newEvent to record a custom event on a delivery:

await DoorstepAI.newEvent(
eventName: 'taking_pod',
deliveryId: 'delivery_12345',
);

4. Stop Tracking

Call stopDelivery when the driver exits the delivery geofence:

await DoorstepAI.stopDelivery(deliveryId: 'delivery_12345');

That covers the full lifecycle. The sections below are reference for the calls above.


DoorstepAiView Widget

See Option A: DoorstepAiView widget above.


Advanced APIs

Requesting Permissions Up-Front (iOS)

Trigger the iOS Motion/Fitness + Location permission prompts before the first delivery:

import 'package:flutter/foundation.dart';

if (defaultTargetPlatform == TargetPlatform.iOS) {
// Defaults to "Always" location authorization. Pass false for when-in-use.
await DoorstepAI.requestAllPermissions(requestAlwaysLocation: true);
}

Remote Logging

Stream SDK logs to DoorstepAI for support investigations:

await DoorstepAI.configureRemoteLogging(
enabled: true,
minLevel: SDKLogLevel.warning, // debug | info | warning | error
flushInterval: 30, // seconds
batchSize: 50,
maxQueueSize: 1000,
);

Best Practices

Error Handling

Always implement proper error handling for SDK methods:

Future<void> startDeliveryWithErrorHandling() async {
try {
await DoorstepAI.startDeliveryByPlaceID(
placeID: 'your-place-id',
deliveryId: 'unique-delivery-id',
);
// Handle success
} catch (error) {
// Handle error
print('Error: $error');
}
}

Lifecycle Management

The SDK keeps tracking in the background, so you usually don't need to react to app-state changes. If you want to observe them, use a WidgetsBindingObserver:

import 'package:flutter/material.dart';

class DeliveryManager extends StatefulWidget {
@override
_DeliveryManagerState createState() => _DeliveryManagerState();
}

class _DeliveryManagerState extends State<DeliveryManager> with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}

@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}

@override
void didChangeAppLifecycleState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.paused:
print('App paused - SDK continues tracking in background');
break;
case AppLifecycleState.resumed:
print('App resumed');
break;
default:
break;
}
}
}

Permission Handling

The DoorstepAiView widget handles permission requests automatically, but you can also handle them manually:

import 'package:permission_handler/permission_handler.dart';
import 'package:flutter/foundation.dart';

Future<void> requestPermissions() async {
if (defaultTargetPlatform == TargetPlatform.android) {
final permissions = {
Permission.location: 'Location',
Permission.activityRecognition: 'Activity Recognition',
};

// Request permissions sequentially
Map<Permission, PermissionStatus> results = {};
for (var entry in permissions.entries) {
final permission = entry.key;
final name = entry.value;
final status = await permission.request();
results[permission] = status;
}

if (results.values.every((status) => status.isGranted)) {
print('Required Android permissions granted');
} else {
print('One or more required Android permissions denied');
}
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
print('iOS: Ensure location and motion usage descriptions are in Info.plist');
}
}

Note: background location requires the ACCESS_BACKGROUND_LOCATION permission on Android 10+ (API 29+).


Next Steps

  1. 💡 View Examples: complete implementation examples
  2. 🛠️ Troubleshooting Guide: common integration and runtime issues