A high-performance signal reactivity library for Dart and Flutter, based on alien-signals.
English | 简体中文
- ⚡ High Performance: Based on alien-signals, one of the fastest signal implementations
- 🎯 Zero Overhead Abstractions: Uses Dart extension types for zero-cost abstractions
- 🔄 Fine-Grained Reactivity: Only updates what actually changed
- 🧩 Minimal API: Just
signal(),computed(),effect()- that's it! - 📦 Flutter Ready: Seamless integration with Flutter widgets
- 🪝 Hooks Support: Optional flutter_hooks integration
- 🔮 AsyncValue: Riverpod-style sealed class for loading/data/error states
- ⏳ AsyncComputed: Async computed values with automatic dependency tracking
- 🌊 StreamComputed: Subscribe to streams with automatic lifecycle management
- 🔗 combineAsync: Combine multiple async values into one
- 🎯 SignalLifecycle:
onDispose,onCancel,onResumecallbacks - 🔒 KeepAliveLink: Prevent automatic disposal of signals
- 📡 SignalSubscription: Pause/resume subscriptions with missed update handling
- 🎛️ SubscriptionController: Manage multiple subscriptions together
- ✅ Result Type: Type-safe
Result<T>for operations that can fail - 🔄 Retry Logic: Exponential backoff with jitter for async operations
- 🛡️ runGuarded/runGuardedAsync: Safe execution with error capture
⚠️ SignalErrorHandler: Global error handler for signal operations
| Package | Description |
|---|---|
| void_signals | Core reactive primitives for Dart |
| void_signals_flutter | Flutter bindings and widgets |
| void_signals_hooks | Flutter hooks integration |
| void_signals_lint | Custom lint rules |
| void_signals_devtools_extension | DevTools extension |
dependencies:
void_signals: ^1.0.0
void_signals_flutter: ^1.0.0 # For Flutter
void_signals_hooks: ^1.0.0 # For flutter_hooks users
dev_dependencies:
void_signals_lint: ^1.0.0 # Custom lint rules
custom_lint: ^0.8.0 # Required for lint rulesimport 'package:void_signals/void_signals.dart';
void main() {
// Create a signal
final count = signal(0);
// Create a computed value
final doubled = computed((prev) => count() * 2);
// Create an effect
effect(() {
print('Count: ${count()}, Doubled: ${doubled()}');
});
count.value = 1; // Prints: Count: 1, Doubled: 2
count.value = 2; // Prints: Count: 2, Doubled: 4
}import 'package:flutter/material.dart';
import 'package:void_signals_flutter/void_signals_flutter.dart';
// Define signals at file top-level
final counter = signal(0);
class CounterWidget extends StatelessWidget {
const CounterWidget({super.key});
@override
Widget build(BuildContext context) {
return Watch(builder: (context, _) => Column(
children: [
Text('Count: ${counter.value}'),
ElevatedButton(
onPressed: () => counter.value++,
child: const Text('Increment'),
),
],
));
}
}A signal is a reactive value that notifies subscribers when it changes.
final name = signal('John');
print(name.value); // 'John'
name.value = 'Jane'; // Notifies all subscribersA computed value is derived from other signals and automatically updates when dependencies change.
final firstName = signal('John');
final lastName = signal('Doe');
final fullName = computed((prev) => '${firstName()} ${lastName()}');
print(fullName()); // 'John Doe'
firstName.value = 'Jane';
print(fullName()); // 'Jane Doe'An effect runs automatically when its dependencies change.
final count = signal(0);
final eff = effect(() {
print('Count changed to: ${count()}');
});
count.value = 1; // Prints: Count changed to: 1
eff.stop(); // Stop listening to changesBatch multiple updates to run effects only once.
final a = signal(1);
final b = signal(2);
effect(() {
print('Sum: ${a() + b()}');
});
batch(() {
a.value = 10;
b.value = 20;
});
// Prints only once: Sum: 30Group multiple effects together for easy cleanup.
final scope = effectScope(() {
effect(() { /* effect 1 */ });
effect(() { /* effect 2 */ });
});
scope.stop(); // Stops all effects in the scopeRead a signal without creating a dependency.
effect(() {
print(count()); // Creates dependency
untrack(() => otherSignal()); // Does not create dependency
});Handle async operations with automatic dependency tracking:
final userId = signal(1);
// AsyncComputed automatically tracks dependencies
final user = asyncComputed(() async {
final id = userId(); // Tracked synchronously
return await fetchUser(id);
});
// Use the async state
user().when(
loading: () => print('Loading...'),
data: (user) => print('User: ${user.name}'),
error: (e, _) => print('Error: $e'),
);
// When userId changes, user automatically refetches
userId.value = 2;Subscribe to streams with automatic lifecycle management:
final roomId = signal('room1');
final messages = streamComputed(() {
return chatService.messagesStream(roomId());
});
// Automatically resubscribes when roomId changes
roomId.value = 'room2';Riverpod-inspired lifecycle hooks:
final count = signal(0);
// Subscribe with pause/resume support
final sub = count.subscribe(
(prev, current) => print('Changed: $prev -> $current'),
);
sub.pause(); // Temporarily stop receiving updates
sub.resume(); // Resume receiving updates
sub.close(); // Stop listening permanentlyfinal config = RetryConfig(
maxAttempts: 3,
exponentialBackoff: true,
jitter: 0.1,
);
final result = await retry(
() => fetchData(),
config: config,
);For complete documentation, see the void_signals package README.
void_signals is built on alien-signals, which is one of the fastest signal implementations available. Key optimizations include:
- Extension types for zero-cost abstractions
- Lazy evaluation for computed values
- Efficient dependency tracking with O(1) operations
- Minimal memory allocations through object pooling
We run comprehensive benchmarks comparing void_signals against other popular reactive libraries. The benchmarks are automatically run on every push to the main branch.
📊 View Latest Benchmark Report
| Rank | Framework | Wins | Pass Rate |
|---|---|---|---|
| 🥇 | void_signals (1.0.0) | 26 | 100% |
| 🥈 | alien_signals (2.0.1) | 6 | 100% |
| 🥉 | state_beacon (1.0.1) | 2 | 100% |
| 4 | preact_signals (1.9.3) | 1 | 100% |
| 5 | mobx (2.5.0) | 0 | 100% |
| 6 | signals_core (6.2.0) | 0 | 100% |
| 7 | solidart (2.8.3) | 0 | 100% |
The benchmarks include tests for:
- Propagation patterns (deep, broad, diamond, triangle)
- Dynamic dependencies
- Cell-based reactivity
- Computed value chains
- Signal creation and updates
Contributions are welcome! Please read our contributing guidelines before submitting a PR.
MIT License - see LICENSE for details.