Task Customization
Advanced task configuration with constraints, input data, and management
Configure background tasks with constraints, input data, and advanced management options.
Input Data
Pass data to your background tasks and access it in the callback:
// Schedule task with input data
Workmanager().registerOneOffTask(
"upload-task",
"file_upload",
inputData: {
'fileName': 'document.pdf',
'uploadUrl': 'https://api.example.com/upload',
'retryCount': 3,
'userId': 12345,
},
);
// Access input data in your task
@pragma('vm:entry-point')
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) async {
print('Task: $task');
print('Input: $inputData');
// Extract specific values
String? fileName = inputData?['fileName'];
String? uploadUrl = inputData?['uploadUrl'];
int retryCount = inputData?['retryCount'] ?? 0;
int userId = inputData?['userId'] ?? 0;
// Use the data in your task logic
await uploadFile(fileName, uploadUrl, userId);
return Future.value(true);
});
}
Input Data Types: You can pass basic JSON-serializable types: String
, int
, double
, bool
, List
, Map
. Complex objects need to be serialized first.
Task Constraints
Control when tasks should run based on device conditions:
Workmanager().registerOneOffTask(
"sync-task",
"data_sync",
constraints: Constraints(
networkType: NetworkType.connected, // Require internet connection
requiresBatteryNotLow: true, // Don't run when battery is low
requiresCharging: false, // Can run when not charging
requiresDeviceIdle: false, // Can run when device is active
requiresStorageNotLow: true, // Don't run when storage is low
),
);
Network Constraints
// Different network requirements
NetworkType.connected // Any internet connection
NetworkType.unmetered // WiFi or unlimited data only
NetworkType.not_required // Can run without internet
Battery and Charging
constraints: Constraints(
requiresBatteryNotLow: true, // Wait for adequate battery
requiresCharging: true, // Only run when plugged in
)
Platform Differences: Some constraints are Android-only. iOS background tasks have different system-level constraints that cannot be configured directly.
Tagging Tasks
Group related tasks with tags for easier management:
// Tag multiple related tasks
Workmanager().registerOneOffTask(
"sync-photos",
"photo_sync",
tag: "sync-tasks",
);
Workmanager().registerOneOffTask(
"sync-documents",
"document_sync",
tag: "sync-tasks",
);
// Cancel all tasks with a specific tag
Workmanager().cancelByTag("sync-tasks");
Canceling Tasks
// Cancel a specific task by unique name
Workmanager().cancelByUniqueName("sync-photos");
// Cancel tasks by tag
Workmanager().cancelByTag("sync-tasks");
// Cancel all scheduled tasks
Workmanager().cancelAll();
Task Scheduling Options
// One-time task with delay
Workmanager().registerOneOffTask(
"delayed-task",
"cleanup",
initialDelay: Duration(minutes: 30),
inputData: {'cleanupType': 'cache'}
);
// Periodic task with custom frequency
Workmanager().registerPeriodicTask(
"hourly-sync",
"data_sync",
frequency: Duration(hours: 1), // Android: minimum 15 minutes
initialDelay: Duration(minutes: 5), // Wait before first execution
inputData: {'syncType': 'incremental'}
);
Task Identification
Use meaningful, unique task names to avoid conflicts:
// Good: Specific and unique
Workmanager().registerOneOffTask(
"user-${userId}-photo-upload-${timestamp}",
"upload_task",
inputData: {'userId': userId, 'type': 'photo'}
);
// Avoid: Generic names that might conflict
Workmanager().registerOneOffTask(
"task1",
"upload",
// ...
);
Task Types and Names
// Use descriptive task type names in your callback
@pragma('vm:entry-point')
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) async {
switch (task) {
case 'photo_upload':
return await handlePhotoUpload(inputData);
case 'data_sync':
return await handleDataSync(inputData);
case 'cache_cleanup':
return await handleCacheCleanup(inputData);
case 'notification_check':
return await handleNotificationCheck(inputData);
default:
print('Unknown task: $task');
return Future.value(false);
}
});
}
Error Handling and Retries
@pragma('vm:entry-point')
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) async {
int retryCount = inputData?['retryCount'] ?? 0;
try {
// Your task logic
await performTask(inputData);
return Future.value(true);
} catch (e) {
print('Task failed: $e');
// Decide whether to retry
if (retryCount < 3 && isRetryableError(e)) {
print('Retrying task (attempt ${retryCount + 1})');
return Future.value(false); // Tell system to retry
} else {
print('Task failed permanently');
return Future.value(true); // Don't retry
}
}
});
}
bool isRetryableError(dynamic error) {
// Network errors, temporary server issues, etc.
return error.toString().contains('network') ||
error.toString().contains('timeout');
}
Efficient Task Design
// Good: Quick, focused tasks
@pragma('vm:entry-point')
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) async {
// Fast operation
await syncCriticalData();
return Future.value(true);
});
}
// Avoid: Long-running operations
@pragma('vm:entry-point')
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) async {
// This might timeout on iOS (30-second limit)
await processLargeDataset(); // ❌ Too slow
return Future.value(true);
});
}
Resource Management
@pragma('vm:entry-point')
void callbackDispatcher() {
Workmanager().executeTask((task, inputData) async {
HttpClient? client;
try {
// Initialize resources in the background isolate
client = HttpClient();
// Perform task
await performNetworkOperation(client);
return Future.value(true);
} finally {
// Clean up resources
client?.close();
}
});
}
Pro Tip: Always initialize dependencies inside your background task callback since it runs in a separate isolate from your main app.