LogoSignals.dart
Copy Markdown
rodydavis/signals.dart 999999

Signal

Represents a mutable reactive state container that sits at the foundation of the reactivity system.

Represents a mutable reactive state container that sits at the foundation of the reactivity system.

Signals hold a single, mutable value that can be read or modified. When a signal's value is updated, any active computations (like Computed) or effects (like effect) that read the signal's value inside their execution context are automatically notified and scheduled to re-run.

Under the hood, this establishes a reactive dependency graph where reading a signal registers the reader as a "target", and updating a signal triggers direct, glitch-free propagation to all registered targets.

Accessing .value inside a reactive context (like effect or computed) registers a dependency. Reading a value outside a reactive context behaves like a standard getter without creating a subscription.

Example Usage#

1. Basic Reactive Flow

import 'package:preact_signals/preact_signals.dart';

void main() {
  final count = Signal(0);

  // The effect automatically subscribes to count.value
  effect(() {
    print('Count is: ${count.value}');
  });

  count.value = 5; // Triggers print: Count is: 5
}

2. Controlling Subscriptions via .peek()

If you need to read a signal's value without subscribing to updates, use the .peek() method:

final count = Signal(0);
final threshold = Signal(10);

effect(() {
  // Subscribes to count, but NOT to threshold
  if (count.value >= threshold.peek()) {
    print('Threshold reached!');
  }
});

Constructors#

View Constructors
Signal(this._internalValue, {String? name, void Function()? watched, void Function()? unwatched, ReadonlySignalOptions? options, SignalEquality? equality})

Creates a new Signal instance with the given initial value.

You can optionally provide:

  • A name for debugging/observer tracing.
  • watched/unwatched hooks triggered when the signal gains its first subscriber or loses its last subscriber.
  • equality checking callback to customize how value modifications are compared.
final count = Signal(0, name: 'counter_signal');
Signal.lazy({String? name, void Function()? watched, void Function()? unwatched, ReadonlySignalOptions? options, SignalEquality? equality})

Creates a new lazy Signal instance that is computed on-demand upon first read.

Reading a lazy signal before a value has been explicitly set or assigned via .value = ... or .set(...) will throw a runtime initialization exception.
final lazyUser = Signal<User>.lazy(name: 'lazy_user');

// Throws error:
// print(lazyUser.value);

lazyUser.value = User(id: 1, name: 'John'); // Initialized successfully
print(lazyUser.value); // Safe to read now

Properties#

View Properties
int globalId
String? name
void Function()? watched
void Function()? unwatched
int version

Version numbers should always be >= 0, because the special value -1 is used by Nodes to signify potentially unused but recyclable nodes.

Methods#

View Methods
SignalEquality equalityCheck

Get the active equality check

bool isInitialized

Check if the value is set and not a lazy signal

T internalValue
bool internalRefresh()
void subscribeToNode(Node node)
void unsubscribeFromNode(Node node)
void Function() subscribe(void Function(T value) fn)
T value

Gets the current value of the signal.

If read inside an active reactive context (e.g., an effect or computed signal), the calling context automatically subscribes to updates of this signal.

value(T val)

Sets the current value of the signal.

If the new value is not equal to the existing value (based on equalityCheck), the signal's version is incremented and all active downstream subscribers (computeds/effects) are synchronously notified to re-evaluate.

bool set(T val, {bool force = false})

Updates the signal's value by method call.

Under normal conditions, this only notifies subscribers if the new value is different from the current value.

Set force to true to bypass standard equality checks and notify downstream subscribers unconditionally. This is useful when working with mutable collections or class instances where properties change in-place but the object reference remains identical.

final numbers = Signal([1, 2, 3]);
numbers.value.add(4); // In-place modification
numbers.set(numbers.value, force: true); // Force notify downstream subscribers

ReadonlySignalOptions#

Configuration options for a ReadonlySignal.

Allows intercepting the signal's active subscription state changes via watched and unwatched callback event listeners. This is extremely useful for initiating or canceling active background fetching, web sockets, or timer loops.

Example Usage#

import 'package:preact_signals/preact_signals.dart';

final stockTicker = signal(
  0.0,
  options: ReadonlySignalOptions(
    name: 'stock-ticker',
    watched: () => print('Stock Ticker is actively being listened to!'),
    unwatched: () => print('No more listeners, sleeping the ticker.'),
  ),
);

Constructors#

View Constructors
ReadonlySignalOptions({super.name, this.watched, this.unwatched})

Creates a new ReadonlySignalOptions instance.

Properties#

View Properties
void Function()? watched

Callback called when the signal goes from 0 to >=1 listeners.

void Function()? unwatched

Callback called when the signal goes from >=1 to 0 listeners.

Methods#

View Methods
ReadonlySignalOptions copyWith({String? name, void Function()? watched, void Function()? unwatched})

Creates a copy of this options with custom overrides.

bool ==(Object other)
int hashCode

ComputedOptions#

Configuration options for a Computed signal.

Enables configuring debugging names and subscription state event listeners for computed derivations.

Example Usage#

import 'package:preact_signals/preact_signals.dart';

final count = signal(0);
final doubleCount = computed(
  () => count.value * 2,
  options: ComputedOptions(
    name: 'double-count',
    watched: () => print('Computed doubleCount is active'),
    unwatched: () => print('Computed doubleCount is inactive'),
  ),
);

Constructors#

View Constructors
ComputedOptions({super.name, super.watched, super.unwatched})

Creates a new ComputedOptions instance.

Methods#

View Methods
ComputedOptions copyWith({String? name, void Function()? watched, void Function()? unwatched})
bool ==(Object other)
int hashCode

SignalOptions#

Configuration options for a Signal.

Extends ReadonlySignalOptions to also support custom equality checkers, which control whether incoming values trigger update events.

Example Usage#

import 'package:preact_signals/preact_signals.dart';

final items = signal(
  [1, 2, 3],
  options: SignalOptions(
    name: 'item-list',
    equality: SignalEquality.deep(),
    watched: () => print('Items watch active'),
    unwatched: () => print('Items watch inactive'),
  ),
);

Constructors#

View Constructors
SignalOptions({super.name, super.watched, super.unwatched, SignalEquality? equality})

Creates a new SignalOptions instance.

Methods#

View Methods
SignalEquality equalityCheck

Get the active equality check

SignalOptions copyWith({String? name, void Function()? watched, void Function()? unwatched})
bool ==(Object other)
int hashCode

EffectOptions#

Configuration options for reactive Effects.

Permits naming the effect for debugging, performance profiling, and tracing within the signals developer tools.

Example Usage#

import 'package:preact_signals/preact_signals.dart';

final count = signal(0);

final logger = effect(
  () => print('Count changed to: ${count.value}'),
  options: const EffectOptions(name: 'counter-logger'),
);

Constructors#

View Constructors
EffectOptions({super.name})

Creates a new EffectOptions instance.

Methods#

View Methods
EffectOptions copyWith({String? name})

Creates a copy of this options with custom overrides.

bool ==(Object other)
int hashCode

signal#

Convenient global constructor for creating a mutable reactive state signal.

Example Usage#

import 'package:preact_signals/preact_signals.dart';

final count = signal(0);
final name = signal('Jane');

SignalOptionsBase#

Base configuration options for reactive components and signals.

Contains common options across all signals, computed values, and effects, such as the debug name.

Constructors#

View Constructors
SignalOptionsBase({this.name})

Creates a new SignalOptionsBase instance.

Properties#

View Properties
String? name

The name for debugging, tracing, and DevTools inspection.

Methods#

View Methods
bool ==(Object other)
int hashCode

SignalEffectException#

Error for when a effect fails to run the callback

Constructors#

View Constructors
SignalEffectException(this.error, [this.stackTrace])

Error for when a effect fails to run the callback

Properties#

View Properties
Object? error

Error during callback

StackTrace? stackTrace

StackTrace for where the error started

Methods#

View Methods
String toString()