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:
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',
),
),
);
}
}
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:
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
| Parameter | Type | Description | Required |
|---|---|---|---|
apiKey | String | Your DoorstepAI API key | ✅ Required |
notificationTitle | String? | Title for tracking notifications | ✅ Required |
notificationText | String? | Description for tracking notifications | ✅ Required |
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 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 aftermarkDropoffis called. Ifnull, the SDK falls back to remote config.manualForeground(Android only) — whentrue, 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');
}
}
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
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
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,
);
Recommended Geofence Settings
- 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');
}
}
Execute stopDelivery(...) when the driver exits the delivery geofence surrounding the building. Do not stop inside the building.
SDK Methods Reference
Authentication
| Method | Parameters | Description |
|---|---|---|
setApiKey | String key | Set your DoorstepAI API key |
await DoorstepAI.setApiKey('your-api-key');
Delivery Management
| Method | Parameters | Description |
|---|---|---|
startDeliveryByPlaceID | String placeID, String deliveryId, double? timeoutSeconds | Start delivery tracking using Google Place ID |
startDeliveryByPlusCode | String plusCode, String deliveryId, double? timeoutSeconds | Start delivery tracking using Plus Code |
startDeliveryByAddress | AddressType address, String deliveryId, double? timeoutSeconds | Start delivery tracking using address components |
startDeliveryByAddressString | String address, String deliveryId, double? timeoutSeconds | Start delivery tracking using a single address string |
startDeliveryByLatLng | double latitude, double longitude, String? subUnit, String deliveryId, double? timeoutSeconds | Start delivery tracking using latitude/longitude |
markDropoff | String deliveryId, DropoffType dropoffType | Mark a POD or non-POD dropoff |
newEvent | String eventName, String deliveryId | Send custom event for active delivery |
stopDelivery | String deliveryId | Stop 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_LOCATIONpermission (API 29+)
Next Steps
- 💡 View Examples - See complete implementation examples
- 🛠️ Troubleshooting Guide - Solve common integration and runtime issues