Skip to content

void-signals/void_signals

Repository files navigation

void_signals logo

void_signals

A high-performance signal reactivity library for Dart and Flutter, based on alien-signals.

Pub Version License: MIT

English | 简体中文


Features

Core Reactivity

  • 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

Advanced Async Support

  • 🔮 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

Lifecycle Management (Riverpod-inspired)

  • 🎯 SignalLifecycle: onDispose, onCancel, onResume callbacks
  • 🔒 KeepAliveLink: Prevent automatic disposal of signals
  • 📡 SignalSubscription: Pause/resume subscriptions with missed update handling
  • 🎛️ SubscriptionController: Manage multiple subscriptions together

Error Handling & Retry

  • 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

Packages

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

Quick Start

Installation

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 rules

Basic Usage

import '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
}

Flutter Usage

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'),
        ),
      ],
    ));
  }
}

Core Concepts

Signal

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 subscribers

Computed

A 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'

Effect

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 changes

Batch

Batch 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: 30

Advanced Features

Effect Scope

Group multiple effects together for easy cleanup.

final scope = effectScope(() {
  effect(() { /* effect 1 */ });
  effect(() { /* effect 2 */ });
});

scope.stop();  // Stops all effects in the scope

Untrack

Read a signal without creating a dependency.

effect(() {
  print(count());  // Creates dependency
  untrack(() => otherSignal());  // Does not create dependency
});

Async Computed

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;

Stream Computed

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';

Lifecycle Management

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 permanently

Error Handling with Retry

final 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.

Performance

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

Benchmark Results

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

Contributing

Contributions are welcome! Please read our contributing guidelines before submitting a PR.

License

MIT License - see LICENSE for details.

About

A high-performance signal reactivity library for Dart and Flutter, based on alien-signals.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •