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<T>? options, SignalEquality<T>? 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<T>? options, SignalEquality<T>? 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 batchSnapshotVersion

@internal The global batch snapshot version tracked during mutation cycles.

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<T> equalityCheck

Get the active equality check

bool isInitialized

Check if the value is set and not a lazy signal

isInitialized(bool val)

@internal Set if the signal is initialized.

T internalValue
internalValue(T value)

@internal Set the internal raw value of the signal.

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
void internalSetValue(T val)

@internal Sets the internal value of the signal during batch updates.


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