isolate_kit 1.0.2
isolate_kit: ^1.0.2 copied to clipboard
Isolate management for Flutter with task cancellation, zero-copy transfer, priority queue, progress tracking, and auto-dispose.
Isolate Kit ๐ #
A powerful and production-ready Flutter package for managing background tasks using Dart Isolates. It provides a robust architecture for running heavy computations with full support for cancellation, real-time progress tracking, task prioritization, and intelligent isolate pooling.

โจ Key Features #
- ๐ Efficient Backgrounding: Move heavy logic (JSON parsing, image processing, crypto) off the UI thread effortlessly.
- โก Intelligent Pooling: Scale your app with an Isolate Pool that automatically balances load across multiple workers.
- ๐ฏ Smart Prioritization: Use
TaskPriorityto ensure critical UI-blocking tasks run before background syncs. - ๐ Real-time Progress: Built-in
sendProgresscallback to update your UI with percentages and custom messages. - ๐ซ True Cancellation: Stop tasks mid-execution using
CancellationTokento save CPU and battery. - โฑ๏ธ Robust Timeout: Prevent "zombie" tasks with automatic timeout handling and isolate recovery.
- ๐ฅ Warmup Support: Eliminate first-run latency by pre-warming isolates or pools.
- โป๏ธ Auto Resource Management: Automatically dispose isolates when idle.
- ๐ฑ Lifecycle Aware: Responsive to application lifecycle changes (disposes on detach, pauses on idle).
- ๐จ Type-Safe & Generic: Fully typed API for both commands and results.
๐ฆ Installation #
Add to your pubspec.yaml:
dependencies:
isolate_kit: ^1.0.1
๐ Quick Start #
1. Define Your Task #
Extend IsolateTask<TCommand, TResult> to encapsulate your logic.
class MyComputeTask extends IsolateTask<int, int> {
final int iterations;
MyComputeTask(this.iterations);
@override
int get command => iterations;
@override
Map<String, dynamic> get payload => {'count': iterations};
@override
Future<int> execute({
void Function(TaskProgress)? sendProgress,
CancellationToken? cancellationToken,
}) async {
int sum = 0;
for (int i = 0; i < iterations; i++) {
// 1. Always check for cancellation in loops
cancellationToken?.throwIfCancelled();
sum += i;
// 2. Report progress
if (i % 100 == 0) {
sendProgress?.call(TaskProgress(
percentage: i / iterations,
message: 'Calculating $i/$iterations...',
));
}
}
return sum;
}
}
2. Setup & Registration #
Register your tasks in the IsolateTaskRegistry so the worker isolate knows how to recreate them.
final registry = IsolateTaskRegistry()
..register<MyComputeTask>(
'MyComputeTask',
(payload, _) => MyComputeTask(payload['count']),
);
final isolateKit = IsolateKit.instance(
name: 'main_worker',
taskRegistry: registry,
usePool: true,
poolSize: 2, // Dual-worker pool
);
// Optional: Warmup for better performance
await isolateKit.warmup();
3. Run and Monitor #
Use runTask to get a TaskHandle.
final handle = isolateKit.runTask(
MyComputeTask(1000000),
onProgress: (p) => print('Progress: ${(p.percentage * 100).toStringAsFixed(1)}%'),
);
try {
final result = await handle.future;
print('Result: $result');
} on TaskCancelledException {
print('Task was cancelled');
} on TaskTimeoutException {
print('Task timed out');
}
๐ Advanced Usage #
Isolate Pooling vs. Single Isolate #
- Single Isolate (
usePool: false): Best for occasional tasks. Uses less memory. - Isolate Pool (
usePool: true): Best for high-frequency tasks or multiple parallel operations. It automatically distributes tasks to the least busy worker.
// Run multiple tasks in parallel with a pool
final handles = [
isolateKit.runTask(task1),
isolateKit.runTask(task2),
isolateKit.runTask(task3),
isolateKit.runTask(task4),
];
final results = await Future.wait(
handles.map((h) => h.future),
);
Task Priority #
IsolateKit uses a priority queue. High-priority tasks will "jump" ahead of low-priority ones in the queue.
class CriticalTask extends IsolateTask<dynamic, String> {
@override
int get priority => TaskPriority.critical; // Will be executed first
}
class BackgroundSyncTask extends IsolateTask<dynamic, String> {
@override
int get priority => TaskPriority.low; // Deferred until others finish
}
| Priority | Value | Use Case |
|---|---|---|
TaskPriority.realtime |
20 | Extremely urgent, time-sensitive tasks |
TaskPriority.critical |
15 | Must be processed immediately |
TaskPriority.high |
10 | Important but not blocking |
TaskPriority.normal |
5 | Default priority |
TaskPriority.low |
0 | Background tasks that can be deferred |
Task Cancellation #
final handle = isolateKit.runTask(myTask);
// Cancel after 5 seconds
Future.delayed(Duration(seconds: 5), () => handle.cancel());
try {
final result = await handle.future;
} on TaskCancelledException {
print('Task was cancelled');
}
Global Cancellation #
Need to stop everything? Use cancelAll().
await isolateKit.cancelAll();
Combine Cancellation Tokens #
final userToken = CancellationToken();
final timeoutToken = CancellationToken();
// Task will be cancelled if ANY token is cancelled
final combined = CancellationToken.combine([userToken, timeoutToken]);
Real-time Progress Tracking #
class ImageProcessingTask extends IsolateTask<String, Uint8List> {
@override
Future<Uint8List> execute({
void Function(TaskProgress)? sendProgress,
CancellationToken? cancellationToken,
}) async {
sendProgress?.call(TaskProgress(percentage: 0.0, message: 'Loading image...'));
final bytes = await loadImage();
sendProgress?.call(TaskProgress(percentage: 0.5, message: 'Applying filters...'));
final processed = await processImage(bytes);
sendProgress?.call(TaskProgress(percentage: 1.0, message: 'Complete!'));
return processed;
}
}
Zero-Copy Data Transfer #
For very large Uint8List data, use transferables to avoid memory copying overhead.
class DataTransferTask extends IsolateTask<Uint8List, Uint8List> {
final Uint8List data;
DataTransferTask(this.data);
@override
List<TransferableTypedData>? get transferables => [
TransferableTypedData.fromList([data])
];
// ... implementation
}
โ๏ธ Configuration #
| Parameter | Default | Description |
|---|---|---|
maxConcurrentTasks |
3 |
Maximum tasks allowed to run simultaneously. |
usePool |
false |
Enable/Disable Isolate Pooling. |
poolSize |
2 |
Number of workers in the pool. |
idleTimeout |
5 min |
Time before an idle isolate is automatically killed. |
idleCheckInterval |
1 min |
How often to check for idle status. |
debugName |
'IsolateController' |
Label used in debug logs. |
๐ Monitoring & Status #
final status = isolateKit.getStatus();
print('Active tasks: ${status['activeTasks']}');
print('Queued tasks: ${status['queuedTasks']}');
print('Total completed: ${status['totalCompleted']}');
print('Pool size: ${status['poolStatus']?['poolSize']}');
// Monitor all instances at once
final allStatus = IsolateKit.getAllStatus();
print('Total instances: ${allStatus['totalInstances']}');
๐ ๏ธ Error Handling #
try {
final result = await handle.future;
} on TaskCancelledException catch (e) {
print('Task ${e.taskId} was cancelled');
} on TaskTimeoutException catch (e) {
print('Task ${e.taskId} timed out after ${e.timeout.inSeconds}s');
} catch (e, stackTrace) {
print('Unexpected error: $e');
print('Stack: $stackTrace');
}
๐งน Cleanup #
// Dispose a single instance
await isolateKit.dispose();
// Dispose by name
IsolateKit.disposeInstance('main_worker');
// Dispose all instances (e.g., on app exit)
IsolateKit.disposeAll();
// Reset and reinitialize
await isolateKit.reset();
๐ฑ Lifecycle Management #
IsolateKit is smart about system resources:
- App Paused: Waits 30 seconds, then disposes if no active tasks remain.
- App Detached: Force-disposes all resources immediately.
- App Resumed: Ready to receive new tasks, re-spawns isolates on demand.
โ ๏ธ Best Practices #
- Warmup for critical paths: Call
warmup()at app startup to eliminate first-run latency. - Use Pool for parallel work: Set
usePool: truewhen running multiple concurrent tasks. - Always check cancellation: Call
cancellationToken?.throwIfCancelled()inside long loops. - Never do heavy work on the UI thread: Pass only parameters to tasks, generate large data inside
execute(). - Set priority wisely: Reserve
realtime/criticalfor user-facing operations only. - Use realistic timeouts: Too-short timeouts cause unnecessary isolate kills and re-spawns.
- Track progress on long tasks: Use
sendProgressso users know the app is working.
๐ Support Me #
๐ค Contributing #
Found a bug or have a feature request? Open an issue or submit a PR ๐ GitHub Repo