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

SetSignalExtension#

Helper extensions for [Signal<Set>] to perform mutation operations that automatically notify downstreams.

Under the hood, these methods mutate the underlying set and call set(..., force: true) to trigger all listeners and computations.

import 'package:signals_core/signals_core.dart';

final tags = <String>{}.$;

effect(() {
  print('Tags: ${tags.value}');
});

tags.add('dart'); // Automatically prints: Tags: {dart}
tags.addAll(['flutter', 'signals']); // Automatically prints: Tags: {dart, flutter, signals}

Methods#

View Methods
bool add(E value)
void addAll(Iterable elements)
void clear()
bool remove(Object? value)
void removeAll(Iterable<Object?> elements)
void removeWhere(bool Function(E element) test)
void retainAll(Iterable<Object?> elements)
void retainWhere(bool Function(E element) test)

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

BoolSignalExtension#

Helper extensions for ReadonlySignal, enabling direct reactive logical conjunction (&), disjunction (|), and exclusive or (^) operations.

import 'package:signals_core/signals_core.dart';

final a = true.$;
final b = false.$;
final andResult = a & b.value; // false
final orResult = a | b.value; // true

Methods#

View Methods
bool &(bool other)

The logical conjunction ("and") of this and other.

Returns true if both this and other are true, and false otherwise.

bool |(bool other)

The logical disjunction ("inclusive or") of this and other.

Returns true if either this or other is true, and false otherwise.

bool ^(bool other)

The logical exclusive disjunction ("exclusive or") of this and other.

Returns whether this and other are neither both true nor both false.


NumSignalExtension#

Helper extensions for ReadonlySignal, providing convenient reactive math and comparison operations without needing to manually unwrap .value.

import 'package:signals_core/signals_core.dart';

final a = 5.0.$;
final sum = a + 3; // 8.0 (evaluates reactively)
final isGreater = a > 4; // true

Methods#

View Methods
num +(num other)

Adds other to this number.

The result is an int, as described by [int.+], if both this number and other is an integer, otherwise the result is a double.

num -(num other)

Subtracts other from this number.

The result is an int, as described by int.-, if both this number and other is an integer, otherwise the result is a double.

num *(num other)

Multiplies this number by other.

The result is an int, as described by [int.*], if both this number and other are integers, otherwise the result is a double.

num %(num other)

Euclidean modulo of this number by other.

Returns the remainder of the Euclidean division. The Euclidean division of two integers a and b yields two integers q and r such that a == b * q + r and 0 <= r < b.abs().

The Euclidean division is only defined for integers, but can be easily extended to work with doubles. In that case, q is still an integer, but r may have a non-integer value that still satisfies 0 <= r < |b|.

The sign of the returned value r is always positive.

See remainder for the remainder of the truncating division.

The result is an int, as described by [int.%], if both this number and other are integers, otherwise the result is a double.

Example:

print(5 % 3); // 2
print(-5 % 3); // 1
print(5 % -3); // 2
print(-5 % -3); // 1
double /(num other)

Divides this number by other.

int ~/(num other)

Truncating division operator.

Performs truncating division of this number by other. Truncating division is division where a fractional result is converted to an integer by rounding towards zero.

If both operands are ints, then other must not be zero. Then a ~/ b corresponds to a.remainder(b) such that a == (a ~/ b) * b + a.remainder(b).

If either operand is a double, then the other operand is converted to a double before performing the division and truncation of the result. Then a ~/ b is equivalent to (a / b).truncate(). This means that the intermediate result of the double division must be a finite integer (not an infinity or double.nan).

num -()

The negation of this value.

The negation of a number is a number of the same kind (int or double) representing the negation of the numbers numerical value (the result of subtracting the number from zero), if that value exists.

Negating a double gives a number with the same magnitude as the original value (number.abs() == (-number).abs()), and the opposite sign (-(number.sign) == (-number).sign).

Negating an integer, -number, is equivalent to subtracting it from zero, 0 - number.

(Both properties generally also hold for the other type, but with a few edge case exceptions).

num remainder(num other)

The remainder of the truncating division of this by other.

The result r of this operation satisfies: this == (this ~/ other) * other + r. As a consequence, the remainder r has the same sign as the dividend this.

The result is an int, as described by int.remainder, if both this number and other are integers, otherwise the result is a double.

Example:

print(5.remainder(3)); // 2
print(-5.remainder(3)); // -2
print(5.remainder(-3)); // 2
print(-5.remainder(-3)); // -2
bool <(num other)

Whether this number is numerically smaller than other.

Returns true if this number is smaller than other. Returns false if this number is greater than or equal to other or if either value is a NaN value like double.nan.

bool <=(num other)

Whether this number is numerically smaller than or equal to other.

Returns true if this number is smaller than or equal to other. Returns false if this number is greater than other or if either value is a NaN value like double.nan.

bool >(num other)

Whether this number is numerically greater than other.

Returns true if this number is greater than other. Returns false if this number is smaller than or equal to other or if either value is a NaN value like double.nan.

bool >=(num other)

Whether this number is numerically greater than or equal to other.

Returns true if this number is greater than or equal to other. Returns false if this number is smaller than other or if either value is a NaN value like double.nan.

bool isNaN

Whether this number is a Not-a-Number value.

Is true if this number is the double.nan value or any other of the possible double NaN values. Is false if this number is an integer, a finite double or an infinite double (double.infinity or double.negativeInfinity).

All numbers satisfy exactly one of isInfinite, isFinite and isNaN.

bool isNegative

Whether this number is negative.

A number is negative if it's smaller than zero, or if it is the double -0.0. This precludes a NaN value like double.nan from being negative.

bool isInfinite

Whether this number is positive infinity or negative infinity.

Only satisfied by double.infinity and double.negativeInfinity.

All numbers satisfy exactly one of isInfinite, isFinite and isNaN.

bool isFinite

Whether this number is finite.

The only non-finite numbers are NaN values, positive infinity, and negative infinity. All integers are finite.

All numbers satisfy exactly one of isInfinite, isFinite and isNaN.

num abs()

The absolute value of this number.

The absolute value is the value itself, if the value is non-negative, and -value if the value is negative.

Integer overflow may cause the result of -value to stay negative.

print((2).abs()); // 2
print((-2.5).abs()); // 2.5
num sign

Negative one, zero or positive one depending on the sign and numerical value of this number.

The value minus one if this number is less than zero, plus one if this number is greater than zero, and zero if this number is equal to zero.

Returns NaN if this number is a double NaN value.

Returns a number of the same type as this number. For doubles, (-0.0).sign is -0.0.

The result satisfies:

n == n.sign * n.abs()

for all numbers n (except NaN, because NaN isn't == to itself).

int round()

The integer closest to this number.

Rounds away from zero when there is no closest integer: (3.5).round() == 4 and (-3.5).round() == -4.

The number must be finite (see isFinite).

If the value is greater than the highest representable positive integer, the result is that highest positive integer. If the value is smaller than the highest representable negative integer, the result is that highest negative integer.

int floor()

The greatest integer no greater than this number.

Rounds fractional values towards negative infinity.

The number must be finite (see isFinite).

If the value is greater than the highest representable positive integer, the result is that highest positive integer. If the value is smaller than the highest representable negative integer, the result is that highest negative integer.

int ceil()

The least integer no smaller than this.

Rounds fractional values towards positive infinity.

The number must be finite (see isFinite).

If the value is greater than the highest representable positive integer, the result is that highest positive integer. If the value is smaller than the highest representable negative integer, the result is that highest negative integer.

int truncate()

The integer obtained by discarding any fractional digits from this.

Rounds fractional values towards zero.

The number must be finite (see isFinite).

If the value is greater than the highest representable positive integer, the result is that highest positive integer. If the value is smaller than the highest representable negative integer, the result is that highest negative integer.

double roundToDouble()

The double integer value closest to this value.

Rounds away from zero when there is no closest integer: (3.5).roundToDouble() == 4 and (-3.5).roundToDouble() == -4.

If this is already an integer valued double, including -0.0, or it is a non-finite double value, the value is returned unmodified.

For the purpose of rounding, -0.0 is considered to be below 0.0, and -0.0 is therefore considered closer to negative numbers than 0.0. This means that for a value d in the range -0.5 < d < 0.0, the result is -0.0.

double floorToDouble()

Returns the greatest double integer value no greater than this.

If this is already an integer valued double, including -0.0, or it is a non-finite double value, the value is returned unmodified.

For the purpose of rounding, -0.0 is considered to be below 0.0. A number d in the range 0.0 < d < 1.0 will return 0.0.

double ceilToDouble()

Returns the least double integer value no smaller than this.

If this is already an integer valued double, including -0.0, or it is a non-finite double value, the value is returned unmodified.

For the purpose of rounding, -0.0 is considered to be below 0.0. A number d in the range -1.0 < d < 0.0 will return -0.0.

double truncateToDouble()

Returns the double integer value obtained by discarding any fractional digits from the double value of this.

If this is already an integer valued double, including -0.0, or it is a non-finite double value, the value is returned unmodified.

For the purpose of rounding, -0.0 is considered to be below 0.0. A number d in the range -1.0 < d < 0.0 will return -0.0, and in the range 0.0 < d < 1.0 it will return 0.0.

num clamp(num lowerLimit, num upperLimit)

Returns this num clamped to be in the range lowerLimit-upperLimit.

The comparison is done using compareTo and therefore takes -0.0 into account. This also implies that double.nan is treated as the maximal double value.

The arguments lowerLimit and upperLimit must form a valid range where lowerLimit.compareTo(upperLimit) <= 0.

Example:

var result = 10.5.clamp(5, 10.0); // 10.0
result = 0.75.clamp(5, 10.0); // 5
result = (-10).clamp(-5, 5.0); // -5
result = (-0.0).clamp(-5, 5.0); // -0.0
int toInt()

Truncates this num to an integer and returns the result as an int.

Equivalent to truncate.

double toDouble()

This number as a double.

If an integer number is not precisely representable as a double, an approximation is returned.

String toStringAsFixed(int fractionDigits)

A decimal-point string-representation of this number.

Converts this number to a double before computing the string representation, as by toDouble.

If the absolute value of this is greater than or equal to 10^21, then this methods returns an exponential representation computed by this.toStringAsExponential(). Otherwise the result is the closest string representation with exactly fractionDigits digits after the decimal point. If fractionDigits equals 0, then the decimal point is omitted.

The parameter fractionDigits must be an integer satisfying: 0 <= fractionDigits <= 20.

Examples:

1.toStringAsFixed(3);  // 1.000
(4321.12345678).toStringAsFixed(3);  // 4321.123
(4321.12345678).toStringAsFixed(5);  // 4321.12346
123456789012345.toStringAsFixed(3);  // 123456789012345.000
10000000000000000.toStringAsFixed(4); // 10000000000000000.0000
5.25.toStringAsFixed(0); // 5
String toStringAsExponential([int? fractionDigits])

An exponential string-representation of this number.

Converts this number to a double before computing the string representation.

If fractionDigits is given, then it must be an integer satisfying: 0 <= fractionDigits <= 20. In this case the string contains exactly fractionDigits after the decimal point. Otherwise, without the parameter, the returned string uses the shortest number of digits that accurately represent this number.

If fractionDigits equals 0, then the decimal point is omitted. Examples:

1.toStringAsExponential();       // 1e+0
1.toStringAsExponential(3);      // 1.000e+0
123456.toStringAsExponential();  // 1.23456e+5
123456.toStringAsExponential(3); // 1.235e+5
123.toStringAsExponential(0);    // 1e+2
String toStringAsPrecision(int precision)

A string representation with precision significant digits.

Converts this number to a double and returns a string representation of that value with exactly precision significant digits.

The parameter precision must be an integer satisfying: 1 <= precision <= 21.

Examples:

1.toStringAsPrecision(2);       // 1.0
1e15.toStringAsPrecision(3);    // 1.00e+15
1234567.toStringAsPrecision(3); // 1.23e+6
1234567.toStringAsPrecision(9); // 1234567.00
12345678901234567890.toStringAsPrecision(20); // 12345678901234567168
12345678901234567890.toStringAsPrecision(14); // 1.2345678901235e+19
0.00000012345.toStringAsPrecision(15); // 1.23450000000000e-7
0.0000012345.toStringAsPrecision(15);  // 0.00000123450000000000

DoubleSignalExtension#

Helper extensions for ReadonlySignal, enabling direct reactive arithmetic and rounding operations on double signals.

import 'package:signals_core/signals_core.dart';

final doubleSignal = 2.5.$;
final rounded = doubleSignal.round(); // 3
final negated = -doubleSignal; // -2.5

Methods#

View Methods
double remainder(num other)

Returns the remainder of this value divided by other.

double +(num other)

Returns the sum of this value and other.

double -(num other)

Returns the difference of this value and other.

double *(num other)

Returns the product of this value and other.

double %(num other)

Returns the modulo of this value and other.

double /(num other)

Returns the division of this value and other.

int ~/(num other)

Returns the truncating division of this value and other.

double -()

Returns the negation of this value.

double abs()

Returns the absolute value of this value.

double sign

The sign of the double's numerical value.

Returns -1.0 if the value is less than zero, +1.0 if the value is greater than zero, and the value itself if it is -0.0, 0.0 or NaN.

int round()

Returns the integer closest to this number.

Rounds away from zero when there is no closest integer: (3.5).round() == 4 and (-3.5).round() == -4.

Throws an UnsupportedError if this number is not finite (NaN or an infinity).

print(3.0.round()); // 3
print(3.25.round()); // 3
print(3.5.round()); // 4
print(3.75.round()); // 4
print((-3.5).round()); // -4
int floor()

Returns the greatest integer no greater than this number.

Rounds the number towards negative infinity.

Throws an UnsupportedError if this number is not finite (NaN or infinity).

print(1.99999.floor()); // 1
print(2.0.floor()); // 2
print(2.99999.floor()); // 2
print((-1.99999).floor()); // -2
print((-2.0).floor()); // -2
print((-2.00001).floor()); // -3
int ceil()

Returns the least integer that is not smaller than this number.

Rounds the number towards infinity.

Throws an UnsupportedError if this number is not finite (NaN or an infinity).

print(1.99999.ceil()); // 2
print(2.0.ceil()); // 2
print(2.00001.ceil()); // 3
print((-1.99999).ceil()); // -1
print((-2.0).ceil()); // -2
print((-2.00001).ceil()); // -2
int truncate()

Returns the integer obtained by discarding any fractional part of this number.

Rounds the number towards zero.

Throws an UnsupportedError if this number is not finite (NaN or an infinity).

print(2.00001.truncate()); // 2
print(1.99999.truncate()); // 1
print(0.5.truncate()); // 0
print((-0.5).truncate()); // 0
print((-1.5).truncate()); // -1
print((-2.5).truncate()); // -2
double roundToDouble()

Returns the integer double value closest to this.

Rounds away from zero when there is no closest integer: (3.5).roundToDouble() == 4 and (-3.5).roundToDouble() == -4.

If this is already an integer valued double, including -0.0, or it is not a finite value, the value is returned unmodified.

For the purpose of rounding, -0.0 is considered to be below 0.0, and -0.0 is therefore considered closer to negative numbers than 0.0. This means that for a value d in the range -0.5 < d < 0.0, the result is -0.0.

print(3.0.roundToDouble()); // 3.0
print(3.25.roundToDouble()); // 3.0
print(3.5.roundToDouble()); // 4.0
print(3.75.roundToDouble()); // 4.0
print((-3.5).roundToDouble()); // -4.0
double floorToDouble()

Returns the greatest integer double value no greater than this.

If this is already an integer valued double, including -0.0, or it is not a finite value, the value is returned unmodified.

For the purpose of rounding, -0.0 is considered to be below 0.0. A number d in the range 0.0 < d < 1.0 will return 0.0.

print(1.99999.floorToDouble()); // 1.0
print(2.0.floorToDouble()); // 2.0
print(2.99999.floorToDouble()); // 2.0
print((-1.99999).floorToDouble()); // -2.0
print((-2.0).floorToDouble()); // -2.0
print((-2.00001).floorToDouble()); // -3.0
double ceilToDouble()

Returns the least integer double value no smaller than this.

If this is already an integer valued double, including -0.0, or it is not a finite value, the value is returned unmodified.

For the purpose of rounding, -0.0 is considered to be below 0.0. A number d in the range -1.0 < d < 0.0 will return -0.0.

print(1.99999.ceilToDouble()); // 2.0
print(2.0.ceilToDouble()); // 2.0
print(2.00001.ceilToDouble()); // 3.0
print((-1.99999).ceilToDouble()); // -1.0
print((-2.0).ceilToDouble()); // -2.0
print((-2.00001).ceilToDouble()); // -2.0
double truncateToDouble()

Returns the integer double value obtained by discarding any fractional digits from this.

If this is already an integer valued double, including -0.0, or it is not a finite value, the value is returned unmodified.

For the purpose of rounding, -0.0 is considered to be below 0.0. A number d in the range -1.0 < d < 0.0 will return -0.0, and in the range 0.0 < d < 1.0 it will return 0.0.

print(2.5.truncateToDouble()); // 2.0
print(2.00001.truncateToDouble()); // 2.0
print(1.99999.truncateToDouble()); // 1.0
print(0.5.truncateToDouble()); // 0.0
print((-0.5).truncateToDouble()); // -0.0
print((-1.5).truncateToDouble()); // -1.0
print((-2.5).truncateToDouble()); // -2.0

ReadonlySetSignalExtension#

Helper extensions for [ReadonlySignal<Set>], providing delegators to compute set operations reactively.

import 'package:signals_core/signals_core.dart';

final setA = {1, 2, 3}.$;
final setB = {3, 4, 5}.$;
final diff = computed(() => setA.difference(setB.value)); // {1, 2}

Methods#

View Methods
Set cast()
bool containsAll(Iterable<Object?> other)
Set difference(Set<Object?> other)
Set intersection(Set<Object?> other)
E? lookup(Object? object)
Set union(Set other)

SignalStreamUtils#

Extension on Stream to provide convenient utilities to convert streams into reactive signals.

import 'package:signals_core/signals_core.dart';

final myStream = Stream.periodic(Duration(seconds: 1), (x) => x).take(5);
final mySignal = myStream.toStreamSignal();

Methods#

View Methods
StreamSignal toStreamSignal({bool? cancelOnError, T? initialValue, bool lazy = true, List<ReadonlySignal> dependencies = const [], void Function()? onDone, AsyncSignalOptions? options})

Convert a stream to a signal

import 'package:signals/signals.dart';

Stream<int> createStream() async* {
    yield 1;
    yield 2;
    yield 3;
}
final stream = createStream();
final signal = stream.toSignal();

For returning a signal with the value that can be accessed sync use stream.toSyncSignal instead.

ReadonlySignal toSyncSignal(T initialData)

Convert a Stream to a synchronous ReadonlySignal and provide an initial value.

This is different from toStreamSignal() because it directly feeds the stream's values into a standard Signal, allowing you to read the bare, synchronous values directly instead of wrapping them in an AsyncState.

import 'package:signals_core/signals_core.dart';

final stream = Stream.value(42);
final syncSignal = stream.toSyncSignal(0);
print(syncSignal.value); // 0 (initially)
// After the stream emits:
// print(syncSignal.value); // 42

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

ReadonlySignalUtils#

Utility extensions on ReadonlySignal to bridge reactive programming with asynchronous streams and select sub-states.

Methods#

View Methods
Stream toStream()

Convert a signal to a Stream to be consumed as a read only stream.

Computed select(R Function(ReadonlySignal) selector, [ComputedOptions? options])

Select a sub-state value from this signal and return a computed signal that only notifies when that specific sub-state changes.

This is highly useful for nesting or destructuring complex objects or maps without triggering downstream updates on changes to unrelated fields.

import 'package:signals_core/signals_core.dart';

final user = signal({'name': 'John', 'age': 30});
final name = user.select((val) => val()['name'] as String);

effect(() => print('Name changed: ${name.value}'));

// Unrelated field update: does NOT trigger the name effect!
user.value = {'name': 'John', 'age': 31};

// Related field update: triggers the name effect!
user.value = {'name': 'Jane', 'age': 31};

WriteableSignalUtils#

Utility extensions on Signal providing functional programming wrappers like React-style hooks destructuring.

Methods#

View Methods
(T Function(), void Function(T)) hooks

Easy destructure to get and set the value

final counter = signal(0);
...
final (getCount, setCount) = counter.hooks;

SignalFunctionExtensions#

Utility extension on a getter function T Function() to instantly convert it into a Computed signal.

Methods#

View Methods
Computed $

Return a cached, derived Computed signal from this getter function.

import 'package:signals_core/signals_core.dart';

final count = signal(0);
final doubleCount = (() => count.value * 2).$;

print(doubleCount.value); // 0
count.value = 5;
print(doubleCount.value); // 10

EffectCycleDetectionError#

Cycle detection usually means you have updated a signal inside an effect and are reading by value.


SignalDoubleExtensions#

Utility extension on double to easily lift a double into a reactive Signal.

Methods#

View Methods
Signal $

Lift a primitive double into a reactive Signal.

import 'package:signals_core/signals_core.dart';

final doubleSignal = 3.14.$;
print(doubleSignal.value); // 3.14

SignalBoolExtensions#

Utility extension on bool to easily lift a boolean into a reactive Signal.

Methods#

View Methods
Signal $

Lift a primitive bool into a reactive Signal.

import 'package:signals_core/signals_core.dart';

final isEnabled = true.$;
print(isEnabled.value); // true

SignalIterableUtils#

Utility extension methods on Iterable to convert them to IterableSignals.

Methods#

View Methods
IterableSignal toSignal({IterableSignalOptions? options, @Deprecated('Use options: IterableSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: IterableSignalOptions(name: ...) instead') String? debugLabel})

Convert an existing Iterable to an IterableSignal.

This returns an IterableSignal initialized with the current collection.

import 'package:signals/signals.dart';

final numbers = [1, 2, 3];
final signal = numbers.toSignal();

SignalNumExtensions#

Utility extension on num to easily lift a number into a reactive Signal.

Methods#

View Methods
Signal $

Lift a primitive num into a reactive Signal.

import 'package:signals_core/signals_core.dart';

final counter = 10.$;
print(counter.value); // 10

SignalSetExtensions#

Utility extension on Set to easily lift a set into a reactive Signal.

Methods#

View Methods
Signal<Set> $

Lift a primitive Set into a reactive [Signal<Set>].

import 'package:signals_core/signals_core.dart';

final tags = {'sports', 'news'}.$;
print(tags.value); // {'sports', 'news'}

SignalListUtils#

Utility extension methods on List to convert them to ListSignals.

Methods#

View Methods
ListSignal toSignal({ListSignalOptions? options, @Deprecated('Use options: ListSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: ListSignalOptions(name: ...) instead') String? debugLabel})

Convert this existing List to a reactive ListSignal.

import 'package:signals/signals.dart';

final myList = [1, 2, 3];
final signal = myList.toSignal();

SignalMapUtils#

Utility extension methods on Map to convert them to MapSignals.

Methods#

View Methods
MapSignal<K, V> toSignal({MapSignalOptions<K, V>? options, @Deprecated('Use options: MapSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: MapSignalOptions(name: ...) instead') String? debugLabel})

Convert this existing Map to a reactive MapSignal.

import 'package:signals/signals.dart';

final myMap = {'key': 'value'};
final signal = myMap.toSignal();

SignalSetUtils#

Utility extension methods on Set to convert them to SetSignals.

Methods#

View Methods
SetSignal toSignal({SetSignalOptions? options, @Deprecated('Use options: SetSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: SetSignalOptions(name: ...) instead') String? debugLabel})

Convert this existing Set to a reactive SetSignal.

import 'package:signals/signals.dart';

final mySet = {1, 2, 3};
final signal = mySet.toSignal();

SignalsWriteAfterDisposeError#

Error to throw if a signal is written to after it is disposed

Constructors#

View Constructors
SignalsWriteAfterDisposeError(ReadonlySignal instance)

Error to throw if a signal is written to after it is disposed


SignalFutureUtils#

Extension on future to provide helpful methods for signals

Methods#

View Methods
FutureSignal toFutureSignal({Duration? timeout, T? initialValue, bool lazy = true, List<ReadonlySignal> dependencies = const [], AsyncSignalOptions? options})

Convert an existing future to FutureSignal

import 'package:signals/signals.dart';

final future = Future(() => 1);
final signal = future.toSignal();

ReadonlySignalMixin#

Readonly signal mixin for adding addition helper methods

Methods#

View Methods
bool isInitialized

Check if a signal value is set (does not subscribe)

String? debugLabel

Debug label for Debug Mode Debug label for Debug Mode

T value
T peek()

SignalsReadAfterDisposeError#

Error to throw if a signal is read after it is disposed

Constructors#

View Constructors
SignalsReadAfterDisposeError(ReadonlySignal instance)

Error to throw if a signal is read after it is disposed


ComparableSignalExtension#

Helper extensions for [ReadonlySignal<Comparable>]

Methods#

View Methods
int compareTo(T other)

Compares this object to another object.

Returns a value like a Comparator when comparing this to other. That is, it returns a negative integer if this is ordered before other, a positive integer if this is ordered after other, and zero if this and other are ordered together.

The other argument must be a value that is comparable to this object.


LazySignalInitializationError#

Lazy signal must value value set before it is read

Constructors#

View Constructors
LazySignalInitializationError(ReadonlySignal instance)

Lazy signal must value value set before it is read


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()

ReadonlyIterableSignalExtension#

Helper extensions for ReadonlySignal

Methods#

View Methods
bool any(bool Function(E element) test)
Iterable cast()
bool contains(Object? value)
E elementAt(int index)
bool every(bool Function(E element) test)
Iterable expand(Iterable Function(E element) toElements)
E first
E firstWhere(bool Function(E element) test, {E Function()? orElse})
R fold(R initialValue, R Function(R previousValue, E element) combine)
Iterable followedBy(Iterable other)
bool isEmpty
bool isNotEmpty
Iterator iterator
String join([String separator = ""])
E last
E lastWhere(bool Function(E element) test, {E Function()? orElse})
int length
Iterable map(R Function(E e) toElement)
E reduce(E Function(E value, E element) combine)
E single
E singleWhere(bool Function(E element) test, {E Function()? orElse})
Iterable skip(int count)
Iterable skipWhile(bool Function(E value) test)
Iterable take(int count)
Iterable takeWhile(bool Function(E value) test)
List toList({bool growable = true})
Set toSet()
Iterable where(bool Function(E element) test)
Iterable whereType()
void forEach(void Function(E element) action)

ChangeSignalOptions#

Configuration options for a ChangeStackSignal.

Constructors#

View Constructors
ChangeSignalOptions({this.limit, super.name, super.autoDispose, super.watched, super.unwatched})

Creates a new ChangeSignalOptions instance.

Properties#

View Properties
int? limit

The limit of changes to keep in the undo/redo stack.

Methods#

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

PatternSignalExtension#

Helper extensions for ReadonlySignal

Methods#

View Methods
Iterable allMatches(String string, [int start = 0])

Matches this pattern against the string repeatedly.

If start is provided, matching will start at that index.

The returned iterable lazily finds non-overlapping matches of the pattern in the string. If a user only requests the first match, this function should not compute all possible matches.

The matches are found by repeatedly finding the first match of the pattern in the string, initially starting from start, and then from the end of the previous match (but always at least one position later than the start of the previous match, in case the pattern matches an empty substring).

RegExp exp = RegExp(r'(\w+)');
var str = 'Dash is a bird';
Iterable<Match> matches = exp.allMatches(str, 8);
for (final Match m in matches) {
  String match = m[0]!;
  print(match);
}

The output of the example is:

a
bird
Match? matchAsPrefix(String string, [int start = 0])

Matches this pattern against the start of string.

Returns a match if the pattern matches a substring of string starting at start, and null if the pattern doesn't match at that point.

The start must be non-negative and no greater than string.length.

final string = 'Dash is a bird';

var regExp = RegExp(r'bird');
var match = regExp.matchAsPrefix(string, 10); // Match found.

regExp = RegExp(r'bird');
match = regExp.matchAsPrefix(string); // null

StringSignalExtension#

Helper extensions for ReadonlySignal

Methods#

View Methods
String [](int index)

The character (as a single-code-unit String) at the given index.

The returned string represents exactly one UTF-16 code unit, which may be half of a surrogate pair. A single member of a surrogate pair is an invalid UTF-16 string:

var clef = '\u{1D11E}';
// These represent invalid UTF-16 strings.
clef[0].codeUnits;      // [0xD834]
clef[1].codeUnits;      // [0xDD1E]

This method is equivalent to String.fromCharCode(this.codeUnitAt(index)).

int codeUnitAt(int index)

Returns the 16-bit UTF-16 code unit at the given index.

int length

The length of the string.

Returns the number of UTF-16 code units in this string. The number of runes might be fewer if the string contains characters outside the Basic Multilingual Plane (plane 0):

'Dart'.length;          // 4
'Dart'.runes.length;    // 4

var clef = '\u{1D11E}';
clef.length;            // 2
clef.runes.length;      // 1
bool endsWith(String other)

Whether this string ends with other.

For example:

const string = 'Dart is open source';
print(string.endsWith('urce')); // true
bool startsWith(Pattern pattern, [int index = 0])

Whether this string starts with a match of pattern.

const string = 'Dart is open source';
print(string.startsWith('Dar')); // true
print(string.startsWith(RegExp(r'[A-Z][a-z]'))); // true

If index is provided, this method checks if the substring starting at that index starts with a match of pattern:

const string = 'Dart';
print(string.startsWith('art', 0)); // false
print(string.startsWith('art', 1)); // true
print(string.startsWith(RegExp(r'\w{3}'), 2)); // false

index must not be negative or greater than length.

A RegExp containing '^' does not match if the index is greater than zero and the regexp is not multi-line. The pattern works on the string as a whole, and does not extract a substring starting at index first:

const string = 'Dart';
print(string.startsWith(RegExp(r'^art'), 1)); // false
print(string.startsWith(RegExp(r'art'), 1)); // true
int indexOf(Pattern pattern, [int start = 0])

Returns the position of the first match of pattern in this string, starting at start, inclusive:

const string = 'Dartisans';
print(string.indexOf('art')); // 1
print(string.indexOf(RegExp(r'[A-Z][a-z]'))); // 0

Returns -1 if no match is found:

const string = 'Dartisans';
string.indexOf(RegExp(r'dart')); // -1

The start must be non-negative and not greater than length.

int lastIndexOf(Pattern pattern, [int? start])

The starting position of the last match pattern in this string.

Finds a match of pattern by searching backward starting at start:

const string = 'Dartisans';
print(string.lastIndexOf('a')); // 6
print(string.lastIndexOf(RegExp(r'a(r|n)'))); // 6

Returns -1 if pattern could not be found in this string.

const string = 'Dartisans';
print(string.lastIndexOf(RegExp(r'DART'))); // -1

If start is omitted, search starts from the end of the string. If supplied, start must be non-negative and not greater than length.

bool isEmpty

Whether this string is empty.

bool isNotEmpty

Whether this string is not empty.

String +(String other)

Creates a new string by concatenating this string with other.

Example:

const string = 'dart' + 'lang'; // 'dartlang'
String substring(int start, [int? end])

The substring of this string from start, inclusive, to end, exclusive.

Example:

const string = 'dartlang';
var result = string.substring(1); // 'artlang'
result = string.substring(1, 4); // 'art'

Both start and end must be non-negative and no greater than length; end, if provided, must be greater than or equal to start.

String trim()

The string without any leading and trailing whitespace.

If the string contains leading or trailing whitespace, a new string with no leading and no trailing whitespace is returned:

final trimmed = '\tDart is fun\n'.trim();
print(trimmed); // 'Dart is fun'

Otherwise, the original string itself is returned:

const string1 = 'Dart';
final string2 = string1.trim(); // 'Dart'
print(identical(string1, string2)); // true

Whitespace is defined by the Unicode White_Space property (as defined in version 6.2 or later) and the BOM character, 0xFEFF.

Here is the list of trimmed characters according to Unicode version 6.3:

    0009..000D    ; White_Space # Cc   <control-0009>..<control-000D>
    0020          ; White_Space # Zs   SPACE
    0085          ; White_Space # Cc   <control-0085>
    00A0          ; White_Space # Zs   NO-BREAK SPACE
    1680          ; White_Space # Zs   OGHAM SPACE MARK
    2000..200A    ; White_Space # Zs   EN QUAD..HAIR SPACE
    2028          ; White_Space # Zl   LINE SEPARATOR
    2029          ; White_Space # Zp   PARAGRAPH SEPARATOR
    202F          ; White_Space # Zs   NARROW NO-BREAK SPACE
    205F          ; White_Space # Zs   MEDIUM MATHEMATICAL SPACE
    3000          ; White_Space # Zs   IDEOGRAPHIC SPACE

    FEFF          ; BOM                ZERO WIDTH NO_BREAK SPACE

Some later versions of Unicode do not include U+0085 as a whitespace character. Whether it is trimmed depends on the Unicode version used by the system.

String trimLeft()

The string without any leading whitespace.

As trim, but only removes leading whitespace.

final string = ' Dart '.trimLeft();
print(string); // 'Dart '
String trimRight()

The string without any trailing whitespace.

As trim, but only removes trailing whitespace.

final string = ' Dart '.trimRight();
print(string); // ' Dart'
String *(int times)

Creates a new string by concatenating this string with itself a number of times.

The result of str * n is equivalent to str + str + ...(n times)... + str.

const string = 'Dart';
final multiplied = string * 3;
print(multiplied); // 'DartDartDart'

Returns an empty string if times is zero or negative.

String padLeft(int width, [String padding = ' '])

Pads this string on the left if it is shorter than width.

Returns a new string that prepends padding onto this string one time for each position the length is less than width.

const string = 'D';
print(string.padLeft(4)); // '   D'
print(string.padLeft(2, 'x')); // 'xD'
print(string.padLeft(4, 'y')); // 'yyyD'
print(string.padLeft(4, '>>')); // '>>>>>>D'

If width is already smaller than or equal to this.length, no padding is added. A negative width is treated as zero.

If padding has length different from 1, the result will not have length width. This may be useful for cases where the padding is a longer string representing a single character, like " " or "\u{10002}". In that case, the user should make sure that this.length is the correct measure of the string's length.

String padRight(int width, [String padding = ' '])

Pads this string on the right if it is shorter than width.

Returns a new string that appends padding after this string one time for each position the length is less than width.

const string = 'D';
print(string.padRight(4)); // 'D    '
print(string.padRight(2, 'x')); // 'Dx'
print(string.padRight(4, 'y')); // 'Dyyy'
print(string.padRight(4, '>>')); // 'D>>>>>>'

If width is already smaller than or equal to this.length, no padding is added. A negative width is treated as zero.

If padding has length different from 1, the result will not have length width. This may be useful for cases where the padding is a longer string representing a single character, like " " or "\u{10002}". In that case, the user should make sure that this.length is the correct measure of the string's length.

bool contains(Pattern other, [int startIndex = 0])

Whether this string contains a match of other.

Example:

const string = 'Dart strings';
final containsD = string.contains('D'); // true
final containsUpperCase = string.contains(RegExp(r'[A-Z]')); // true

If startIndex is provided, this method matches only at or after that index:

const string = 'Dart strings';
final containsD = string.contains(RegExp('D'), 0); // true
final caseSensitive = string.contains(RegExp(r'[A-Z]'), 1); // false

The startIndex must not be negative or greater than length.

String replaceFirst(Pattern from, String to, [int startIndex = 0])

Creates a new string with the first occurrence of from replaced by to.

Finds the first match of from in this string, starting from startIndex, and creates a new string where that match is replaced with the to string.

Example:

'0.0001'.replaceFirst(RegExp(r'0'), ''); // '.0001'
'0.0001'.replaceFirst(RegExp(r'0'), '7', 1); // '0.7001'
String replaceFirstMapped(Pattern from, String Function(Match match) replace, [int startIndex = 0])

Replace the first occurrence of from in this string.

const string = 'Dart is fun';
print(string.replaceFirstMapped(
    'fun', (m) => 'open source')); // Dart is open source

print(string.replaceFirstMapped(
    RegExp(r'\w(\w*)'), (m) => '<${m[0]}-${m[1]}>')); // <Dart-art> is fun

Returns a new string, which is this string except that the first match of from, starting from startIndex, is replaced by the result of calling replace with the match object.

The startIndex must be non-negative and no greater than length.

String replaceAll(Pattern from, String replace)

Replaces all substrings that match from with replace.

Creates a new string in which the non-overlapping substrings matching from (the ones iterated by from.allMatches(thisString)) are replaced by the literal string replace.

'resume'.replaceAll(RegExp(r'e'), 'é'); // 'résumé'

Notice that the replace string is not interpreted. If the replacement depends on the match (for example, on a RegExp's capture groups), use the replaceAllMapped method instead.

String replaceAllMapped(Pattern from, String Function(Match match) replace)

Replace all substrings that match from by a computed string.

Creates a new string in which the non-overlapping substrings that match from (the ones iterated by from.allMatches(thisString)) are replaced by the result of calling replace on the corresponding Match object.

This can be used to replace matches with new content that depends on the match, unlike replaceAll where the replacement string is always the same.

The replace function is called with the Match generated by the pattern, and its result is used as replacement.

The function defined below converts each word in a string to simplified 'pig latin' using replaceAllMapped:

String pigLatin(String words) => words.replaceAllMapped(
    RegExp(r'\b(\w*?)([aeiou]\w*)', caseSensitive: false),
    (Match m) => "${m[2]}${m[1]}${m[1]!.isEmpty ? 'way' : 'ay'}");

final result = pigLatin('I have a secret now!');
print(result); // 'Iway avehay away ecretsay ownay!'
String replaceRange(int start, int? end, String replacement)

Replaces the substring from start to end with replacement.

Creates a new string equivalent to:

this.substring(0, start) + replacement + this.substring(end)

Example:

const string = 'Dart is fun';
final result = string.replaceRange(8, null, 'open source');
print(result); // Dart is open source

The start and end indices must specify a valid range of this string. That is 0 <= start <= end <= this.length. If end is null, it defaults to length.

List split(Pattern pattern)

Splits the string at matches of pattern and returns a list of substrings.

Finds all the matches of pattern in this string, as by using Pattern.allMatches, and returns the list of the substrings between the matches, before the first match, and after the last match.

const string = 'Hello world!';
final splitted = string.split(' ');
print(splitted); // [Hello, world!];

If the pattern doesn't match this string at all, the result is always a list containing only the original string.

If the pattern is a String, then it's always the case that:

string.split(pattern).join(pattern) == string

If the first match is an empty match at the start of the string, the empty substring before it is not included in the result. If the last match is an empty match at the end of the string, the empty substring after it is not included in the result. If a match is empty, and it immediately follows a previous match (it starts at the position where the previous match ended), then the empty substring between the two matches is not included in the result.

const string = 'abba';
final re = RegExp(r'b*');
// re.allMatches(string) will find four matches:
// * empty match before first "a".
// * match of "bb"
// * empty match after "bb", before second "a"
// * empty match after second "a".
print(string.split(re)); // [a, a]

A non-empty match at the start or end of the string, or after another match, is not treated specially, and will introduce empty substrings in the result:

const string = 'abbaa';
final splitted = string.split('a'); // ['', 'bb', '', '']

If this string is the empty string, the result is an empty list if pattern matches the empty string, since the empty string before and after the first-and-last empty match are not included. (It is still a list containing the original empty string [""] if the pattern doesn't match).

const string = '';
print(string.split('')); // []
print(string.split('a')); // []

Splitting with an empty pattern splits the string into single-code unit strings.

const string = 'Pub';
print(string.split('')); // [P, u, b]

// Same as:
var codeUnitStrings = [
  for (final unit in string.codeUnits) String.fromCharCode(unit)
];
print(codeUnitStrings); // [P, u, b]

Splitting happens at UTF-16 code unit boundaries, and not at rune (Unicode code point) boundaries:

// String made up of two code units, but one rune.
const string = '\u{1D11E}';
final splitted = string.split('');
print(splitted); // ['\ud834', '\udd1e'] - 2 unpaired surrogate values

To get a list of strings containing the individual runes of a string, you should not use split. You can instead get a string for each rune as follows:

const string = '\u{1F642}';
for (final rune in string.runes) {
  print(String.fromCharCode(rune));
}
String splitMapJoin(Pattern pattern, {String Function(Match)? onMatch, String Function(String)? onNonMatch})

Splits the string, converts its parts, and combines them into a new string.

The pattern is used to split the string into parts and separating matches. Each match of Pattern.allMatches of pattern on this string is used as a match, and the substrings between the end of one match (or the start of the string) and the start of the next match (or the end of the string) is treated as a non-matched part. (There is no omission of leading or trailing empty matchs, like in split, all matches and parts between the are included.)

Each match is converted to a string by calling onMatch. If onMatch is omitted, the matched substring is used.

Each non-matched part is converted to a string by a call to onNonMatch. If onNonMatch is omitted, the non-matching substring itself is used.

Then all the converted parts are concatenated into the resulting string.

final result = 'Eats shoots leaves'.splitMapJoin(RegExp(r'shoots'),
    onMatch: (m) => '${m[0]}', // (or no onMatch at all)
    onNonMatch: (n) => '*');
print(result); // *shoots*
List codeUnits

An unmodifiable list of the UTF-16 code units of this string.

Runes runes

An Iterable of Unicode code-points of this string.

If the string contains surrogate pairs, they are combined and returned as one integer by this iterator. Unmatched surrogate halves are treated like valid 16-bit code-units.

String toLowerCase()

Converts all characters in this string to lower case.

If the string is already in all lower case, this method returns this.

'ALPHABET'.toLowerCase(); // 'alphabet'
'abc'.toLowerCase(); // 'abc'

This function uses the language independent Unicode mapping and thus only works in some languages.

String toUpperCase()

Converts all characters in this string to upper case.

If the string is already in all upper case, this method returns this.

'alphabet'.toUpperCase(); // 'ALPHABET'
'ABC'.toUpperCase(); // 'ABC'

This function uses the language independent Unicode mapping and thus only works in some languages.


IterableSignalOptions#

Configuration options for a IterableSignal.

Constructors#

View Constructors
IterableSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched, super.equality = const SignalDeepEquality()})

Creates a new IterableSignalOptions instance.

Methods#

View Methods
IterableSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, SignalEquality<Iterable>? equality})
bool ==(Object other)
int hashCode

EnumSignalExtension#

Helper extensions for ReadonlySignal

Methods#

View Methods
int index

A numeric identifier for the enumerated value.

The values of a single enumeration are numbered consecutively from zero to one less than the number of values. This is also the index of the value in the enumerated type's static values list.

String name

The name of the enum value.

The name is a string containing the source identifier used to declare the enum value.

For example, given a declaration like:

enum MyEnum {
  value1,
  value2
}

the result of MyEnum.value1.name is the string "value1".


ReadonlyListSignalExtension#

Helper extensions for ReadonlySignal

Methods#

View Methods
List cast()
E last
List +(List other)
E [](int index)
Map<int, E> asMap()
Iterable expand(Iterable Function(E element) toElements)
E firstWhere(bool Function(E element) test, {E Function()? orElse})
R fold(R initialValue, R Function(R previousValue, E element) combine)
Iterable followedBy(Iterable other)
Iterable getRange(int start, int end)
int indexOf(E element, [int start = 0])
int indexWhere(bool Function(E element) test, [int start = 0])
int lastIndexOf(E element, [int? start])
int lastIndexWhere(bool Function(E element) test, [int? start])
Iterable reversed
List sorted([int Function(E a, E b)? compare])

Return a new array that is sorted by the compare function

List sublist(int start, [int? end])

ReadonlyMapSignalExtension#

Helper extensions for ReadonlySignal

Methods#

View Methods
V? [](Object? key)
Map<RK, RV> cast()
bool containsKey(Object? key)
bool containsValue(Object? value)
Iterable<MapEntry<K, V>> entries
bool isEmpty
bool isNotEmpty
Iterable keys
int length
Map<K2, V2> map(MapEntry<K2, V2> Function(K key, V value) convert)
Iterable values

IntSignalExtension#

Helper extensions for ReadonlySignal

Methods#

View Methods
int &(int other)

Bit-wise and operator.

Treating both this and other as sufficiently large two's component integers, the result is a number with only the bits set that are set in both this and other

If both operands are negative, the result is negative, otherwise the result is non-negative.

print((2 & 1).toRadixString(2)); // 0010 & 0001 -> 0000
print((3 & 1).toRadixString(2)); // 0011 & 0001 -> 0001
print((10 & 2).toRadixString(2)); // 1010 & 0010 -> 0010
int |(int other)

Bit-wise or operator.

Treating both this and other as sufficiently large two's component integers, the result is a number with the bits set that are set in either of this and other

If both operands are non-negative, the result is non-negative, otherwise the result is negative.

Example:

print((2 | 1).toRadixString(2)); // 0010 | 0001 -> 0011
print((3 | 1).toRadixString(2)); // 0011 | 0001 -> 0011
print((10 | 2).toRadixString(2)); // 1010 | 0010 -> 1010
int ^(int other)

Bit-wise exclusive-or operator.

Treating both this and other as sufficiently large two's component integers, the result is a number with the bits set that are set in one, but not both, of this and other

If the operands have the same sign, the result is non-negative, otherwise the result is negative.

Example:

print((2 ^ 1).toRadixString(2)); //  0010 ^ 0001 -> 0011
print((3 ^ 1).toRadixString(2)); //  0011 ^ 0001 -> 0010
print((10 ^ 2).toRadixString(2)); //  1010 ^ 0010 -> 1000
int ~()

The bit-wise negate operator.

Treating this as a sufficiently large two's component integer, the result is a number with the opposite bits set.

This maps any integer x to -x - 1.

int <<(int shiftAmount)

Shift the bits of this integer to the left by shiftAmount.

Shifting to the left makes the number larger, effectively multiplying the number by pow(2, shiftAmount).

There is no limit on the size of the result. It may be relevant to limit intermediate values by using the "and" operator with a suitable mask.

It is an error if shiftAmount is negative.

Example:

print((3 << 1).toRadixString(2)); // 0011 -> 0110
print((9 << 2).toRadixString(2)); // 1001 -> 100100
print((10 << 3).toRadixString(2)); // 1010 -> 1010000
int >>(int shiftAmount)

Shift the bits of this integer to the right by shiftAmount.

Shifting to the right makes the number smaller and drops the least significant bits, effectively doing an integer division by pow(2, shiftAmount).

It is an error if shiftAmount is negative.

Example:

print((3 >> 1).toRadixString(2)); // 0011 -> 0001
print((9 >> 2).toRadixString(2)); // 1001 -> 0010
print((10 >> 3).toRadixString(2)); // 1010 -> 0001
print((-6 >> 2).toRadixString); // 111...1010 -> 111...1110 == -2
print((-85 >> 3).toRadixString); // 111...10101011 -> 111...11110101 == -11
int >>>(int shiftAmount)

Bitwise unsigned right shift by shiftAmount bits.

The least significant shiftAmount bits are dropped, the remaining bits (if any) are shifted down, and zero-bits are shifted in as the new most significant bits.

The shiftAmount must be non-negative.

Example:

print((3 >>> 1).toRadixString(2)); // 0011 -> 0001
print((9 >>> 2).toRadixString(2)); // 1001 -> 0010
print(((-9) >>> 2).toRadixString(2)); // 111...1011 -> 001...1110 (> 0)
int modPow(int exponent, int modulus)

Returns this integer to the power of exponent modulo modulus.

The exponent must be non-negative and modulus must be positive.

int modInverse(int modulus)

Returns the modular multiplicative inverse of this integer modulo modulus.

The modulus must be positive.

It is an error if no modular inverse exists.

int gcd(int other)

Returns the greatest common divisor of this integer and other.

If either number is non-zero, the result is the numerically greatest integer dividing both this and other.

The greatest common divisor is independent of the order, so x.gcd(y) is always the same as y.gcd(x).

For any integer x, x.gcd(x) is x.abs().

If both this and other is zero, the result is also zero.

Example:

print(4.gcd(2)); // 2
print(8.gcd(4)); // 4
print(10.gcd(12)); // 2
print(10.gcd(0)); // 10
print((-2).gcd(-3)); // 1
bool isEven

Returns true if and only if this integer is even.

bool isOdd

Returns true if and only if this integer is odd.

int bitLength

Returns the minimum number of bits required to store this integer.

The number of bits excludes the sign bit, which gives the natural length for non-negative (unsigned) values. Negative values are complemented to return the bit position of the first bit that differs from the sign bit.

To find the number of bits needed to store the value as a signed value, add one, i.e. use x.bitLength + 1.

x.bitLength == (-x-1).bitLength;

3.bitLength == 2;     // 00000011
2.bitLength == 2;     // 00000010
1.bitLength == 1;     // 00000001
0.bitLength == 0;     // 00000000
(-1).bitLength == 0;  // 11111111
(-2).bitLength == 1;  // 11111110
(-3).bitLength == 2;  // 11111101
(-4).bitLength == 2;  // 11111100
int toUnsigned(int width)

Returns the least significant width bits of this integer as a non-negative number (i.e. unsigned representation). The returned value has zeros in all bit positions higher than width.

(-1).toUnsigned(5) == 31   // 11111111  ->  00011111

This operation can be used to simulate arithmetic from low level languages. For example, to increment an 8 bit quantity:

q = (q + 1).toUnsigned(8);

q will count from 0 up to 255 and then wrap around to 0.

If the input fits in width bits without truncation, the result is the same as the input. The minimum width needed to avoid truncation of x is given by x.bitLength, i.e.

x == x.toUnsigned(x.bitLength);
int toSigned(int width)

Returns the least significant width bits of this integer, extending the highest retained bit to the sign. This is the same as truncating the value to fit in width bits using an signed 2-s complement representation. The returned value has the same bit value in all positions higher than width.

                         //     V--sign bit-V
16.toSigned(5) == -16;   //  00010000 -> 11110000
239.toSigned(5) == 15;   //  11101111 -> 00001111
                         //     ^           ^

This operation can be used to simulate arithmetic from low level languages. For example, to increment an 8 bit signed quantity:

q = (q + 1).toSigned(8);

q will count from 0 up to 127, wrap to -128 and count back up to 127.

If the input value fits in width bits without truncation, the result is the same as the input. The minimum width needed to avoid truncation of x is x.bitLength + 1, i.e.

x == x.toSigned(x.bitLength + 1);
int -()

Return the negative value of this integer.

The result of negating an integer always has the opposite sign, except for zero, which is its own negation.

int abs()

Returns the absolute value of this integer.

For any integer value, the result is the same as value < 0 ? -value : value.

Integer overflow may cause the result of -value to stay negative.

int sign

Returns the sign of this integer.

Returns 0 for zero, -1 for values less than zero and +1 for values greater than zero.

int round()

Returns this.

int floor()

Returns this.

int ceil()

Returns this.

int truncate()

Returns this.

double roundToDouble()

Returns this.toDouble().

double floorToDouble()

Returns this.toDouble().

double ceilToDouble()

Returns this.toDouble().

double truncateToDouble()

Returns this.toDouble().

String toRadixString(int radix)

Converts this int to a string representation in the given radix.

In the string representation, lower-case letters are used for digits above '9', with 'a' being 10 and 'z' being 35.

The radix argument must be an integer in the range 2 to 36.

Example:

// Binary (base 2).
print(12.toRadixString(2)); // 1100
print(31.toRadixString(2)); // 11111
print(2021.toRadixString(2)); // 11111100101
print((-12).toRadixString(2)); // -1100
// Octal (base 8).
print(12.toRadixString(8)); // 14
print(31.toRadixString(8)); // 37
print(2021.toRadixString(8)); // 3745
// Hexadecimal (base 16).
print(12.toRadixString(16)); // c
print(31.toRadixString(16)); // 1f
print(2021.toRadixString(16)); // 7e5
// Base 36.
print((35 * 36 + 1).toRadixString(36)); // z1

ListSignalOptions#

Configuration options for a ListSignal.

Constructors#

View Constructors
ListSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched, super.equality = const SignalDeepEquality()})

Creates a new ListSignalOptions instance.

Methods#

View Methods
ListSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, SignalEquality<List>? equality})
bool ==(Object other)
int hashCode

MapSignalOptions#

Configuration options for a MapSignal.

Constructors#

View Constructors
MapSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched, super.equality = const SignalDeepEquality()})

Creates a new MapSignalOptions instance.

Methods#

View Methods
MapSignalOptions<K, V> copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, SignalEquality<Map<K, V>>? equality})
bool ==(Object other)
int hashCode

SetSignalOptions#

Configuration options for a SetSignal.

Constructors#

View Constructors
SetSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched, super.equality = const SignalDeepEquality()})

Creates a new SetSignalOptions instance.

Methods#

View Methods
SetSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, SignalEquality<Set>? equality})
bool ==(Object other)
int hashCode

SignalsAutoDisposeMixin#

Mixin to enable autodispose on a signal

Properties#

View Properties
bool autoDispose

Throws and error if read after dispose and can be disposed on last unsubscribe.

Methods#

View Methods
bool disposed

Check if the effect is disposed

void Function() onDispose(void Function() cleanup)

Add a cleanup function to be called when the signal is disposed

final counter = signal(0);
final effectCount = signal(0);

final cleanup = counter.onDispose(() {
 print('Counter has been disposed');
});

// Remove the cleanup function
cleanup();
disposed(bool value)

Force a signal to be disposed

void dispose()

Dispose the signal


LinkedSignalOptions#

Options for creating a LinkedSignal.

Constructors#

View Constructors
LinkedSignalOptions({this.computation, this.sourceEquality, super.name, super.autoDispose})

Creates LinkedSignalOptions.

Properties#

View Properties
T Function(S source, LinkedSignalPreviousState<T, S>? previous)? computation

Custom computation logic that runs when the source changes.

bool Function(S a, S b)? sourceEquality

Optional equality check for the source values.

Methods#

View Methods
LinkedSignalOptions<T, S> copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, T Function(S source, LinkedSignalPreviousState<T, S>? previous)? computation, bool Function(S a, S b)? sourceEquality})
bool ==(Object other)
int hashCode

ListSignalExtension#

Helper extensions for Signal

Methods#

View Methods
first(E val)
last(E val)
length(int value)
void []=(int index, E value)
void add(E value)
void addAll(Iterable iterable)
void clear()
void fillRange(int start, int end, [E? fillValue])
void insert(int index, E element)
void insertAll(int index, Iterable iterable)
bool remove(Object? value)
E removeAt(int index)
E removeLast()
void removeRange(int start, int end)
void removeWhere(bool Function(E element) test)
void replaceRange(int start, int end, Iterable replacements)
void retainWhere(bool Function(E element) test)
void setAll(int index, Iterable iterable)
void setRange(int start, int end, Iterable iterable, [int skipCount = 0])
void shuffle([Random? random])
void sort([int Function(E a, E b)? compare])

MapSignalExtension#

Helper extensions for Signal

Methods#

View Methods
void []=(K key, V value)
void addAll(Map<K, V> other)
void addEntries(Iterable<MapEntry<K, V>> newEntries)
void clear()
void forEach(void Function(K key, V value) action)
V putIfAbsent(K key, V Function() ifAbsent)
V? remove(Object? key)
void removeWhere(bool Function(K key, V value) test)
V update(K key, V Function(V value) update, {V Function()? ifAbsent})
void updateAll(V Function(K key, V value) update)

SignalObjectUtils#

Connivent methods for signal values

Methods#

View Methods
Signal $

Convert an existing Object to Signal


SignalComparableExtensions#

Extensions for Comparable

Methods#

View Methods
Signal<Comparable> $

Return a signal from a Comparable value


SignalIterableExtensions#

Extensions for Iterable

Methods#

View Methods
Signal<Iterable> $

Return a signal from a Iterable value


SignalListExtensions#

Extensions for List

Methods#

View Methods
Signal<List> $

Return a signal from a List value


SignalPatternExtensions#

Extensions for Pattern

Methods#

View Methods
Signal $

Return a signal from a Pattern value


SignalMapExtensions#

Extensions for Map

Methods#

View Methods
Signal<Map<K, V>> $

Return a signal from a Map value


SignalStringExtensions#

Extensions for String

Methods#

View Methods
Signal $

Return a signal from a String value


SignalEnumExtensions#

Extensions for Enum

Methods#

View Methods
Signal $

Return a signal from a Enum value


SignalIntExtensions#

Extensions for int

Methods#

View Methods
Signal $

Return a signal from a int value


SignalsError#

Signal usage error

Constructors#

View Constructors
SignalsError(this.message)

Signal usage error

Properties#

View Properties
String message

Signals error pretty print message

Methods#

View Methods
String toString()