Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.
Quelldateien prüfen
Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
Add optimisticCommand for blocking one-time actions with immediate UI updates and automatic rollback on failure
Add Optimistic Command
This skill adds optimisticCommand for one-time actions with immediate UI updates and automatic rollback on failure.
What This Skill Does
Implements optimistic updates for blocking, one-time operations like:
Creating items (add todo, post message)
Deleting items
Form submissions
File uploads
Payments
The UI updates immediately, the server operation runs, and on failure the UI rolls back.
Instructions
Step 1: Identify the Operation
Use optimisticCommand when:
The action should run once (not coalesce rapid calls)
UI should update immediately for responsiveness
Failure should revert the change
The operation is a discrete command, not a continuous sync
Step 2: Implement optimisticCommand
import 'package:bloc_superpowers/bloc_superpowers.dart';
class TodoCubit extends Cubit<TodoState> {
TodoCubit() : super(const TodoState());
void addTodo(Todo newTodo) => optimisticCommand(
key: (AddTodo, newTodo.id),
// 1. Return the optimistic value (new list with item added)
optimisticValue: () => state.todoList.add(newTodo),
// 2. Extract the value from current state
getValueFromState: (state) => state.todoList,
// 3. Apply a value back to state
applyValueToState: (state, value) =>
state.copyWith(todoList: value as List<Todo>),
// 4. Send the command to the server
sendCommandToServer: (optimisticValue) async {
await api.saveTodo(newTodo);
return null; // Return server response if needed
},
);
}
Step 3: Understand the Flow
Non-reentrant check: Prevents duplicate execution
Capture initial state: For potential rollback
Apply optimistic update: UI updates immediately
Execute server command: Async operation
On success: Optionally apply server response
On failure: Rollback to initial state (if safe)
Rollback Safety: Rollback only occurs if the current state value still matches the optimistic value. This prevents overwriting concurrent changes made by other operations. If another update changed the value while the command was running, rollback is skipped to preserve that newer change.
Required Parameters
Parameter
Purpose
key
Identifies the operation for state tracking and non-reentrant
optimisticValue
Returns the immediate UI value
getValueFromState
Extracts the relevant value from state
applyValueToState
Applies a value back to state
sendCommandToServer
Executes the server operation
Optional Parameters
Apply Server Response
When the server returns data that should update the state:
void addTodo(Todo newTodo) => optimisticCommand(
key: (AddTodo, newTodo.id),
optimisticValue: () => state.todoList.add(newTodo),
getValueFromState: (state) => state.todoList,
applyValueToState: (state, value) =>
state.copyWith(todoList: value as List<Todo>),
sendCommandToServer: (optimisticValue) async {
final savedTodo = await api.saveTodo(newTodo);
return savedTodo; // Return server response
},
// Replace client-side todo with server-side todo
applyServerResponseToState: (state, serverResponse) {
final savedTodo = serverResponse as Todo;
return state.copyWith(
todoList: state.todoList
.where((t) => t.id != newTodo.id)
.toList()
..add(savedTodo),
);
},
);
Reload from Server
Reload data after success or failure:
void deleteTodo(String todoId) => optimisticCommand(
key: (DeleteTodo, todoId),
optimisticValue: () =>
state.todoList.where((t) => t.id != todoId).toList(),
getValueFromState: (state) => state.todoList,
applyValueToState: (state, value) =>
state.copyWith(todoList: value as List<Todo>),
sendCommandToServer: (optimisticValue) async {
await api.deleteTodo(todoId);
return null;
},
// Reload after operation
reloadFromServer: () async {
return await api.loadTodoList();
},
// Control when to reload
shouldReload: ({
required currentValue,
required lastAppliedValue,
required optimisticValue,
required rollbackValue,
required error,
}) => error != null, // Only reload on failure
);
Custom Rollback
Customize how rollback works:
void addTodo(Todo newTodo) => optimisticCommand(
key: (AddTodo, newTodo.id),
// ... required params ...
// Control whether to rollback
shouldRollback: ({
required currentValue,
required initialValue,
required optimisticValue,
required error,
}) => true, // Default: always rollback on error
// Custom rollback state (e.g., show failed status instead of removing)
rollbackState: ({
required state,
required initialValue,
required optimisticValue,
required error,
}) => state.copyWith(
todoList: state.todoList.map((t) =>
t.id == newTodo.id ? t.copyWith(status: TodoStatus.failed) : t
).toList(),
),
);
Separate Non-Reentrant Key
Track loading state globally but prevent duplicates per item:
void addTodo(Todo newTodo) => optimisticCommand(
key: AddTodo, // isWaiting(AddTodo) shows any add in progress
nonReentrantKey: (AddTodo, newTodo.id), // Prevents duplicate for same todo
// ... rest of params ...
);
// Widget
if (context.isWaiting(AddTodo)) {
// Any add operation is in progress
}