Flutter Daily: Isolates for Background Processing
Isolates for Background Processing
What You’ll Learn
Today we’ll explore Dart isolates—Dart’s approach to true parallel execution. Unlike async/await which runs code concurrently on a single thread, isolates let you run computationally expensive tasks on separate threads without blocking your UI.
Why Isolates Matter
Flutter apps run on a single thread by default. Heavy operations like JSON parsing from large files, image processing, or complex calculations can freeze your UI. Isolates solve this by running code in complete isolation from the main thread.
Example: Background JSON Processing
Here’s a practical example of parsing large JSON data without blocking the UI:
import 'dart:isolate';
import 'dart:convert';
// This function runs in a separate isolate
Future<List<User>> parseUsers(String jsonString) async {
final parsed = json.decode(jsonString) as List;
return parsed.map((json) => User.fromJson(json)).toList();
}
// In your widget or service class:
class UserService {
Future<List<User>> loadUsersInBackground(String jsonString) async {
// Spawn an isolate to parse JSON
final result = await Isolate.run(() => parseUsers(jsonString));
return result;
}
}
// Using it in a widget:
class UserListScreen extends StatefulWidget {
@override
State<UserListScreen> createState() => _UserListScreenState();
}
class _UserListScreenState extends State<UserListScreen> {
List<User> users = [];
bool isLoading = true;
@override
void initState() {
super.initState();
_loadUsers();
}
Future<void> _loadUsers() async {
final jsonString = await loadLargeJsonFile(); // Your loading logic
// This runs on a separate thread - UI stays responsive!
final parsedUsers = await UserService().loadUsersInBackground(jsonString);
setState(() {
users = parsedUsers;
isLoading = false;
});
}
@override
Widget build(BuildContext context) {
if (isLoading) return CircularProgressIndicator();
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) => UserTile(users[index]),
);
}
}
How It Works
- Isolate.run() is the modern way (Flutter 3.7+) to spawn isolates for one-time computations
- The function you pass runs completely separately from your main thread
- Data is copied between isolates (not shared) for safety
- The result is automatically sent back when the computation completes
When to Use Isolates
✅ Use isolates for:
- Parsing large JSON/XML files (>100KB)
- Image processing or compression
- Encryption/decryption operations
- Heavy mathematical calculations
- Database operations on large datasets
❌ Don’t use isolates for:
- Small, quick operations (overhead isn’t worth it)
- UI updates (isolates can’t access widgets)
- Operations that need to access platform plugins directly
Try It Yourself
Create a simple app that compresses an image in the background:
Future<Uint8List> compressImage(Uint8List imageBytes) async {
return await Isolate.run(() {
// Simulate heavy processing
final compressed = /* your image compression logic */;
return compressed;
});
}
Challenge: Add a progress indicator while the isolate is working, and display the compressed image when done. Notice how smooth your UI remains during compression!
Tip of the Day
For long-running background tasks that need bidirectional communication (like progress updates), use Isolate.spawn() with SendPort and ReceivePort instead of Isolate.run(). This gives you a persistent isolate that can communicate back and forth with your main thread—perfect for download managers or real-time data processing.