Agent skill
signals-dart
Expert guidance for Signals.dart, a reactive state management library for Dart and Flutter. Use when (1) implementing reactive state with signals, computed values, or effects, (2) working with async signals (FutureSignal, StreamSignal, AsyncState), (3) using Flutter integration (Watch widget, SignalsMixin, hooks), (4) managing reactive collections (ListSignal, MapSignal, SetSignal), (5) migrating from ValueNotifier, Provider, Riverpod, or BLoC to Signals.dart. Triggers include signal, reactive state, computed, effect, Watch widget, SignalsMixin, futureSignal, streamSignal, AsyncState, ListSignal, signals.dart.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/signals-dart
SKILL.md
Signals.dart
Reactive state management using fine-grained reactive primitives. Works across Dart VM, WASM, Flutter, and server.
Installation
# Flutter
flutter pub add signals
# Dart
dart pub add signals
// Dart projects
import 'package:signals/signals.dart';
// Flutter projects (includes ValueNotifier compatibility)
import 'package:signals/signals_flutter.dart';
Core Primitives
Signal - Reactive State
final counter = signal(0);
print(counter.value); // Read
counter.value = 1; // Write (notifies dependents)
counter.peek(); // Read without tracking
counter.set(value, force: true); // Force update
Computed - Derived Values
final name = signal("Jane");
final surname = signal("Doe");
final fullName = computed(() => "${name.value} ${surname.value}");
Effect - Side Effects
final dispose = effect(() => print(counter.value));
dispose(); // Stop effect
// With cleanup
effect(() {
print(s.value);
return () => print('Cleanup');
});
Warning: Never mutate signals inside effects without untracked() - causes infinite loops.
Untracked and Batch
// Read without creating dependency
untracked(() => counter.value);
// Batch multiple updates
batch(() {
name.value = "Foo";
surname.value = "Bar";
}); // Single notification
Flutter Integration
Watch Widget (Recommended)
Watch((context) => Text('Count: ${counter.value}'));
// With child optimization
Watch.builder(
builder: (context, child) => Column(children: [
Text('Count: ${counter.value}'),
child!, // Doesn't rebuild
]),
child: ExpensiveWidget(),
);
SignalsMixin (Auto-disposal)
class _MyState extends State<MyWidget> with SignalsMixin {
late final count = createSignal(0);
late final isEven = createComputed(() => count.value.isEven);
@override
void initState() {
super.initState();
createEffect(() => print('Count: ${count.value}')); // Effects in initState!
}
}
Async Signals
FutureSignal
final data = futureSignal(() async => fetchData());
// With dependencies (re-executes on change)
final data = futureSignal(
() async => fetchDataFor(id.value),
dependencies: [id],
);
data.reset(); // Return to initial
data.refresh(); // Keep state, set loading
data.reload(); // Reset to loading with preserved value
AsyncState Pattern Matching
return switch (data.value) {
AsyncLoading() => CircularProgressIndicator(),
AsyncData(:final value) => Text('$value'),
AsyncError(:final error) => Text('Error: $error'),
};
// Or map()
data.value.map(
loading: () => CircularProgressIndicator(),
data: (value) => DataWidget(value),
error: (err, _) => ErrorWidget(err),
);
Collections
final list = listSignal([1, 2, 3]);
final map = mapSignal({'a': 1});
final set = setSignal({1, 2, 3});
list.add(4); // Triggers updates
map['b'] = 2; // Triggers updates
set.remove(1); // Triggers updates
Best Practices
- Use
Watchwidget instead of.watch(context) - Never mutate signals inside effects without
untracked() - Use
batch()for multiple updates - Put effects in
initState(), not as late fields - Access signal values BEFORE await in
computedAsync() - Prefer
computedFrom()overcomputedAsync()to avoid await timing issues - Always add
debugLabelto every signal — use the variable name (no class prefix):dartfinal counter = Signal<int>(0, debugLabel: 'counter'); final data = FutureSignal(() => fetch(), debugLabel: 'data'); final items = ListSignal([], debugLabel: 'items'); late final total = Computed(() => items.value.length, debugLabel: 'total');
Reference
For detailed API documentation including Flutter hooks, SignalProvider, ValueNotifier compatibility, async signals detail, collections API, advanced patterns (SignalsContainer, persisted signals, bi-directional flow, SignalsObserver), DI patterns, testing, and migration guides, see references/api_reference.md.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?