Skip to main content

Flutter SDK Usage & Integration

Learn how to initialize the DoorstepAI SDK and integrate delivery tracking into your Flutter application.

SDK Initialization

Basic Setup with DoorstepAiView Widget

The easiest way to initialize the SDK is using the DoorstepAiView widget, which handles initialization and permission requests automatically:

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',
),
),
);
}
}
API Key Security

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

Manual Initialization

For more control over the initialization process, you can initialize the SDK manually:

delivery_manager.dart
import 'package:flutter/foundation.dart';
import 'package:permission_handler/permission_handler.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
if (defaultTargetPlatform == TargetPlatform.android) {
await _requestAndroidPermissions();
}
}

static Future<void> _requestAndroidPermissions() async {
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');
}
}
}

Initialization Parameters

ParameterTypeDescriptionRequired
apiKeyStringYour DoorstepAI API keyRequired
notificationTitleString?Title for tracking notificationsRequired
notificationTextString?Description for tracking notificationsRequired
Notification Customization

The notification title and text are used for the foreground service notification that appears when the SDK is actively tracking deliveries. These parameters are required for proper SDK initialization.

Core Functionality

Delivery IDs

Delivery IDs are designed to be unique identifiers for each delivery session. Use meaningful, unique identifiers that help you track and manage your deliveries effectively.

Starting Delivery Tracking

The SDK provides multiple methods to start delivery tracking based on different address formats.

Every start method accepts these optional knobs:

  • timeoutSeconds — auto-stops tracking after the given duration, as a backstop in case an exit geofence is missed.
  • autoStopAfterDropoffSeconds — auto-stops tracking the given number of seconds after markDropoff is called. If null, the SDK falls back to remote config.
  • manualForeground (Android only) — when true, the SDK does not promote its tracking service to the foreground; the host app must already be running its own foreground service.

The address and addressString variants additionally accept coordinates: LatLngObject? so you can pair a textual address with the lat/lng you already resolved upstream.

1. Start by Place ID

import 'package:doorstepai_dropoff_sdk/doorstepai_dropoff_sdk.dart';

Future<void> startDeliveryByPlaceID() async {
try {
await DoorstepAI.startDeliveryByPlaceID(
placeID: 'some_place_id',
deliveryId: 'delivery_12345',
timeoutSeconds: 1200, // optional
autoStopAfterDropoffSeconds: 600, // optional
manualForeground: false, // optional, Android-only
);
print('✅ Delivery started successfully');
} catch (error) {
print('❌ Failed to start delivery: $error');
}
}
Geofence Start Requirement

Execute startDelivery... when the driver enters the delivery geofence around the target building. Do not start inside the building.

2. Start by Address Components

Future<void> startDeliveryByAddress() async {
try {
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',
coordinates: LatLngObject(lat: 37.7749, lng: -122.4194), // optional
timeoutSeconds: 1200, // optional
autoStopAfterDropoffSeconds: 600, // optional
manualForeground: false, // optional, Android-only
);
print('✅ Address delivery started');
} catch (error) {
print('❌ Address delivery failed: $error');
}
}

3. Start by Address String

Future<void> startDeliveryByAddressString() async {
try {
await DoorstepAI.startDeliveryByAddressString(
address: '123 Main St, Apt 4B, San Francisco, CA 94102',
deliveryId: 'delivery_12345',
coordinates: LatLngObject(lat: 37.7749, lng: -122.4194), // optional
timeoutSeconds: 1200, // optional
autoStopAfterDropoffSeconds: 600, // optional
manualForeground: false, // optional, Android-only
);
print('✅ Address string delivery started');
} catch (error) {
print('❌ Address string delivery failed: $error');
}
}

Deprecated: Start by Plus Code

Deprecated

startDeliveryByPlusCode is deprecated. Use startDeliveryByAddressString (or startDeliveryByAddress) and pass coordinates if you have them.

// Deprecated — kept for backwards compatibility.
await DoorstepAI.startDeliveryByPlusCode(
plusCode: 'some_plus_code',
deliveryId: 'delivery_12345',
timeoutSeconds: 1200,
);

Deprecated: Start by Lat/Lng

Deprecated

startDeliveryByLatLng is deprecated. Use startDeliveryByAddressString or startDeliveryByAddress with the coordinates parameter instead.

// Deprecated — kept for backwards compatibility.
await DoorstepAI.startDeliveryByLatLng(
latitude: 37.7749,
longitude: -122.4194,
subUnit: 'Apt 4B',
deliveryId: 'delivery_12345',
timeoutSeconds: 1200,
);
  • Use both an enter and exit geofence to bound the delivery session.
  • A radius of 250m or larger typically yields the best results.

Delivery Events

Track important delivery milestones using event reporting:

Mark Dropoff (POD or non-POD)

try {
await DoorstepAI.markDropoff(
deliveryId: 'delivery_12345',
dropoffType: DropoffType.pod, // or DropoffType.nonPod
);
print('✅ Dropoff marked');
} catch (error) {
print('❌ Dropoff mark failed: $error');
}
// NOTE: The previous methods of marking a dropoff still work as expected.
try {
await DoorstepAI.newEvent(
eventName: 'taking_pod',
deliveryId: 'delivery_12345',
);
print('Event sent');
} catch (error) {
print('Event send failed: $error');
}

Stop Delivery Tracking

Future<void> stopDelivery(String deliveryId) async {
try {
await DoorstepAI.stopDelivery(deliveryId: deliveryId);
print('✅ Delivery stopped successfully');
} catch (error) {
print('❌ Failed to stop delivery: $error');
}
}
Geofence Stop Requirement

Execute stopDelivery(...) when the driver exits the delivery geofence surrounding the building. Do not stop inside the building.

SDK Methods Reference

Authentication

MethodParametersDescription
setApiKeyString keySet your DoorstepAI API key
await DoorstepAI.setApiKey('your-api-key');

Delivery Management

MethodParametersDescription
startDeliveryByPlaceIDString placeID, String deliveryId, double? timeoutSecondsStart delivery tracking using Google Place ID
startDeliveryByPlusCodeString plusCode, String deliveryId, double? timeoutSecondsStart delivery tracking using Plus Code
startDeliveryByAddressAddressType address, String deliveryId, double? timeoutSecondsStart delivery tracking using address components
startDeliveryByAddressStringString address, String deliveryId, double? timeoutSecondsStart delivery tracking using a single address string
startDeliveryByLatLngdouble latitude, double longitude, String? subUnit, String deliveryId, double? timeoutSecondsStart delivery tracking using latitude/longitude
markDropoffString deliveryId, DropoffType dropoffTypeMark a POD or non-POD dropoff
newEventString eventName, String deliveryIdSend custom event for active delivery
stopDeliveryString deliveryIdStop delivery tracking

Address Type

class AddressType {
final String streetNumber;
final String route;
final String? subPremise;
final String locality;
final String administrativeAreaLevel1;
final String postalCode;

AddressType({
required this.streetNumber,
required this.route,
this.subPremise,
required this.locality,
required this.administrativeAreaLevel1,
required this.postalCode,
});
}

LatLngObject

Pair a textual address with lat/lng you already resolved upstream by passing it as coordinates on startDeliveryByAddress or startDeliveryByAddressString.

class LatLngObject {
final double lat;
final double lng;

LatLngObject({required this.lat, required this.lng});
}

DoorstepAiView Widget

The DoorstepAiView widget provides a Flutter wrapper for certain initialization functions. In this latest update, it is not expliticly required anymore, as long as initialization happens manually elsewhere in the application.

DoorstepAiView(
apiKey: 'your-api-key',
notificationTitle: 'Tracking...',
notificationText: 'Tracking your delivery',
)

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);
}

Retrying GNSS After Permission Grants (Android)

If the user grants location permissions after SDK init, retry GNSS callback registration so raw GNSS data starts flowing:

if (defaultTargetPlatform == TargetPlatform.android) {
await DoorstepAI.retryGnssCallbacks();
}

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,
);

Dev Mode

Validate the API token and enable dev mode if it's authorized:

final enabled = await DoorstepAI.enableDevMode(apiKey: 'YOUR_API_KEY');
if (enabled) {
print('✅ Dev mode active');
}

Best Practices

1. 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');
}
}

2. Lifecycle Management

Handle app lifecycle changes appropriately:

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;
}
}
}

3. Permission Handling

The DoorstepAiView component automatically handles permission requests, 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');
}
}

Platform-Specific Notes

iOS

  • Permissions are handled automatically by the system, although requesting them in advance is sometimes recommended.

Android

  • Manual permission requests are required
  • Background location requires ACCESS_BACKGROUND_LOCATION permission (API 29+)

Next Steps

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