update
This commit is contained in:
179
.agents/skills/flutter-handling-concurrency/SKILL.md
Normal file
179
.agents/skills/flutter-handling-concurrency/SKILL.md
Normal file
@@ -0,0 +1,179 @@
|
||||
---
|
||||
name: "flutter-handling-concurrency"
|
||||
description: "Executes long-running tasks in background isolates to keep the UI responsive. Use when performing heavy computations or parsing large datasets."
|
||||
metadata:
|
||||
model: "models/gemini-3.1-pro-preview"
|
||||
last_modified: "Thu, 12 Mar 2026 22:23:14 GMT"
|
||||
|
||||
---
|
||||
# Managing Dart Concurrency and Isolates
|
||||
|
||||
## Contents
|
||||
- [Core Concepts](#core-concepts)
|
||||
- [Decision Matrix: Async vs. Isolates](#decision-matrix-async-vs-isolates)
|
||||
- [Workflows](#workflows)
|
||||
- [Implementing Standard Asynchronous UI](#implementing-standard-asynchronous-ui)
|
||||
- [Offloading Short-Lived Heavy Computation](#offloading-short-lived-heavy-computation)
|
||||
- [Establishing Long-Lived Worker Isolates](#establishing-long-lived-worker-isolates)
|
||||
- [Examples](#examples)
|
||||
|
||||
## Core Concepts
|
||||
|
||||
Dart utilizes a single-threaded execution model driven by an Event Loop (comparable to the iOS main loop). By default, all Flutter application code runs on the Main Isolate.
|
||||
|
||||
* **Asynchronous Operations (`async`/`await`):** Use for non-blocking I/O tasks (network requests, file access). The Event Loop continues processing other events while waiting for the `Future` to complete.
|
||||
* **Isolates:** Dart's implementation of lightweight threads. Isolates possess their own isolated memory and do not share state. They communicate exclusively via message passing.
|
||||
* **Main Isolate:** The default thread where UI rendering and event handling occur. Blocking this isolate causes UI freezing (jank).
|
||||
* **Worker Isolate:** A spawned isolate used to offload CPU-bound tasks (e.g., decoding large JSON blobs) to prevent Main Isolate blockage.
|
||||
|
||||
## Decision Matrix: Async vs. Isolates
|
||||
|
||||
Apply the following conditional logic to determine the correct concurrency approach:
|
||||
|
||||
* **If** the task is I/O bound (e.g., HTTP request, database read) -> **Use `async`/`await`** on the Main Isolate.
|
||||
* **If** the task is CPU-bound but executes quickly (< 16ms) -> **Use `async`/`await`** on the Main Isolate.
|
||||
* **If** the task is CPU-bound, takes significant time, and runs once (e.g., parsing a massive JSON payload) -> **Use `Isolate.run()`**.
|
||||
* **If** the task requires continuous or repeated background processing with multiple messages passed over time -> **Use `Isolate.spawn()` with `ReceivePort` and `SendPort`**.
|
||||
|
||||
## Workflows
|
||||
|
||||
### Implementing Standard Asynchronous UI
|
||||
|
||||
Use this workflow to fetch and display non-blocking asynchronous data.
|
||||
|
||||
**Task Progress:**
|
||||
- [ ] Mark the data-fetching function with the `async` keyword.
|
||||
- [ ] Return a `Future<T>` from the function.
|
||||
- [ ] Use the `await` keyword to yield execution until the operation completes.
|
||||
- [ ] Wrap the UI component in a `FutureBuilder<T>` (or `StreamBuilder` for streams).
|
||||
- [ ] Handle `ConnectionState.waiting`, `hasError`, and `hasData` states within the builder.
|
||||
- [ ] Run validator -> review UI for loading indicators -> fix missing states.
|
||||
|
||||
### Offloading Short-Lived Heavy Computation
|
||||
|
||||
Use this workflow for one-off, CPU-intensive tasks using Dart 2.19+.
|
||||
|
||||
**Task Progress:**
|
||||
- [ ] Identify the CPU-bound operation blocking the Main Isolate.
|
||||
- [ ] Extract the computation into a standalone callback function.
|
||||
- [ ] Ensure the callback function signature accepts exactly one required, unnamed argument (as per specific architectural constraints).
|
||||
- [ ] Invoke `Isolate.run()` passing the callback.
|
||||
- [ ] `await` the result of `Isolate.run()` in the Main Isolate.
|
||||
- [ ] Assign the returned value to the application state.
|
||||
|
||||
### Establishing Long-Lived Worker Isolates
|
||||
|
||||
Use this workflow for persistent background processes requiring continuous bidirectional communication.
|
||||
|
||||
**Task Progress:**
|
||||
- [ ] Instantiate a `ReceivePort` on the Main Isolate to listen for messages.
|
||||
- [ ] Spawn the worker isolate using `Isolate.spawn()`, passing the `ReceivePort.sendPort` as the initial message.
|
||||
- [ ] In the worker isolate, instantiate its own `ReceivePort`.
|
||||
- [ ] Send the worker's `SendPort` back to the Main Isolate via the initial port.
|
||||
- [ ] Store the worker's `SendPort` in the Main Isolate for future message dispatching.
|
||||
- [ ] Implement listeners on both `ReceivePort` instances to handle incoming messages.
|
||||
- [ ] Run validator -> review memory leaks -> ensure ports are closed when the isolate is no longer needed.
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Asynchronous UI with FutureBuilder
|
||||
|
||||
```dart
|
||||
// 1. Define the async operation
|
||||
Future<String> fetchUserData() async {
|
||||
await Future.delayed(const Duration(seconds: 2)); // Simulate network I/O
|
||||
return "User Data Loaded";
|
||||
}
|
||||
|
||||
// 2. Consume in the UI
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<String>(
|
||||
future: fetchUserData(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const CircularProgressIndicator();
|
||||
} else if (snapshot.hasError) {
|
||||
return Text('Error: ${snapshot.error}');
|
||||
} else {
|
||||
return Text('Result: ${snapshot.data}');
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Example 2: Short-Lived Isolate (`Isolate.run`)
|
||||
|
||||
```dart
|
||||
import 'dart:isolate';
|
||||
import 'dart:convert';
|
||||
|
||||
// 1. Define the heavy computation callback
|
||||
// Note: Adhering to the strict single-argument signature requirement.
|
||||
List<dynamic> decodeHeavyJson(String jsonString) {
|
||||
return jsonDecode(jsonString) as List<dynamic>;
|
||||
}
|
||||
|
||||
// 2. Offload to a worker isolate
|
||||
Future<List<dynamic>> processDataInBackground(String rawJson) async {
|
||||
// Isolate.run spawns the isolate, runs the computation, returns the value, and exits.
|
||||
final result = await Isolate.run(() => decodeHeavyJson(rawJson));
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### Example 3: Long-Lived Isolate (`ReceivePort` / `SendPort`)
|
||||
|
||||
```dart
|
||||
import 'dart:isolate';
|
||||
|
||||
class WorkerManager {
|
||||
late SendPort _workerSendPort;
|
||||
final ReceivePort _mainReceivePort = ReceivePort();
|
||||
Isolate? _isolate;
|
||||
|
||||
Future<void> initialize() async {
|
||||
// 1. Spawn isolate and pass the Main Isolate's SendPort
|
||||
_isolate = await Isolate.spawn(_workerEntry, _mainReceivePort.sendPort);
|
||||
|
||||
// 2. Listen for messages from the Worker Isolate
|
||||
_mainReceivePort.listen((message) {
|
||||
if (message is SendPort) {
|
||||
// First message is the Worker's SendPort
|
||||
_workerSendPort = message;
|
||||
_startCommunication();
|
||||
} else {
|
||||
// Subsequent messages are data payloads
|
||||
print('Main Isolate received: $message');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _startCommunication() {
|
||||
// Send data to the worker
|
||||
_workerSendPort.send("Process this data");
|
||||
}
|
||||
|
||||
// 3. Worker Isolate Entry Point
|
||||
static void _workerEntry(SendPort mainSendPort) {
|
||||
final workerReceivePort = ReceivePort();
|
||||
|
||||
// Send the Worker's SendPort back to the Main Isolate
|
||||
mainSendPort.send(workerReceivePort.sendPort);
|
||||
|
||||
// Listen for incoming tasks
|
||||
workerReceivePort.listen((message) {
|
||||
print('Worker Isolate received: $message');
|
||||
|
||||
// Perform work and send result back
|
||||
final result = "Processed: $message";
|
||||
mainSendPort.send(result);
|
||||
});
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_mainReceivePort.close();
|
||||
_isolate?.kill();
|
||||
}
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user