Signal<T> class
abstract
The signal
function creates a new signal. A signal is a container for a value that can change over time. You can read a signal's value or subscribe to value updates by accessing its .value
property.
import 'package:signals/signals.dart';
final counter = signal(0);
// Read value from signal, logs: 0
print(counter.value);
// Write to a signal
counter.value = 1;
Signals can be created globally, inside classes or functions. It's up to you how you want to structure your app.
It is not recommended to create signals inside effects or computed, as this will create a new signal every time the effect or computed is triggered. This can lead to unexpected behavior.
In Flutter do not create signals inside build
methods, as this will create a new signal every time the widget is rebuilt.
Writing to a signal
Writing to a signal is done by setting its .value
property. Changing a signal's value synchronously updates every computed and effect that depends on that signal, ensuring your app state is always consistent.
.peek()
In the rare instance that you have an effect that should write to another signal based on the previous value, but you don't want the effect to be subscribed to that signal, you can read a signals's previous value via signal.peek()
.
final counter = signal(0);
final effectCount = signal(0);
effect(() {
print(counter.value);
// Whenever this effect is triggered, increase `effectCount`.
// But we don't want this signal to react to `effectCount`
effectCount.value = effectCount.peek() + 1;
});
Note that you should only use signal.peek()
if you really need it. Reading a signal's value via signal.value
is the preferred way in most scenarios.
.value
The .value
property of a signal is used to read or write to the signal. If used inside an effect or computed, it will subscribe to the signal and trigger the effect or computed whenever the signal's value changes.
final counter = signal(0);
effect(() {
print(counter.value);
});
counter.value = 1;
.previousValue
The .previousValue
property of a signal is used to read the previous value of the signal. If used inside an effect or computed, it will not subscribe to the signal and not trigger the effect or computed whenever the signal's value changes.
final counter = signal(0);
effect(() {
print('Current value: ${counter.value}');
print('Previous value: ${counter.previousValue}');
});
counter.value = 1;
Force Update
If you want to force an update for a signal, you can call the .set(..., force: true)
method. This will trigger all effects and mark all computed as dirty.
final counter = signal(0);
counter.set(1, force: true);
Disposing
Auto Dispose
If a signal is created with autoDispose set to true, it will automatically dispose itself when there are no more listeners.
final s = signal(0, autoDispose: true);
s.onDispose(() => print('Signal destroyed'));
final dispose = s.subscribe((_) {});
dispose();
final value = s.value; // 0
// prints: Signal destroyed
A auto disposing signal does not require its dependencies to be auto disposing. When it is disposed it will freeze its value and stop tracking its dependencies.
final s = signal(0);
s.dispose();
final c = computed(() => s.value);
// c will not react to changes in s
You can check if a signal is disposed by calling the .disposed
method.
final s = signal(0);
print(s.disposed); // false
s.dispose();
print(s.disposed); // true
On Dispose Callback
You can attach a callback to a signal that will be called when the signal is destroyed.
final s = signal(0);
s.onDispose(() => print('Signal destroyed'));
s.dispose();
Testing
Testing signals is possible by converting a signal to a stream and testing it like any other stream in Dart.
test('test as stream', () {
final s = signal(0);
final stream = s.toStream(); // create a stream of values
s.value = 1;
s.value = 2;
s.value = 3;
expect(stream, emitsInOrder([0, 1, 2, 3]));
});
emitsInOrder
is a matcher that will check if the stream emits the values in the correct order which in this case is each value after a signal is updated.
You can also override the initial value of a signal when testing. This is is useful for mocking and testing specific value implementations.
test('test with override', () {
final s = signal(0).overrideWith(-1);
final stream = s.toStream();
s.value = 1;
s.value = 2;
s.value = 3;
expect(stream, emitsInOrder([-1, 1, 2, 3]));
});
overrideWith
returns a new signal with the same global id sets the value as if it was created with it. This can be useful when using async signals or global signals used for dependency injection.
@link https://dartsignals.dev/dart/core/signal
- Implemented types
- Available extensions
Constructors
- Signal()
Properties
- autoDispose → bool
-
Throws and error if read after dispose and can be
disposed on last unsubscribe.
no setterinherited
- debugLabel → String?
-
Debug label for Debug Mode
no setterinherited
- disposed → bool
-
Returns true if dispose has been called and will throw and
error on value read
no setterinherited
- globalId → int
-
Global ID of the signal
no setterinherited
- hashCode → int
-
The hash code for this object.
no setterinherited
- runtimeType → Type
-
A representation of the runtime type of the object.
no setterinherited
- value ↔ T
-
Compute the current value
getter/setter pairinherited-getter
Methods
-
call(
) → T -
Return the value when invoked
inherited
-
dispose(
) → void -
Dispose the signal
inherited
-
get(
) → T -
Get the current value
inherited
-
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent method or property is accessed.
inherited
-
onDispose(
void cleanup()) → EffectCleanup -
Add a cleanup function to be called when the signal is disposed
inherited
-
overrideWith(
T value) → Signal< T> - Override the current signal with a new value as if it was created with it
-
peek(
) → T -
In the rare instance that you have an effect that should write to
another signal based on the previous value, but you don't want the
effect to be subscribed to that signal, you can read a signals's
previous value via
signal.peek()
.inherited -
readonly(
) → ReadonlySignal< T> - Returns a readonly signal
-
set(
T value, {bool force = false}) → void - Set the current value
-
subscribe(
void fn(T value)) → EffectCleanup -
Subscribe to value changes
inherited
-
toJson(
) → T -
Convert value to JSON
inherited
-
toString(
) → String -
A string representation of this object.
inherited
Operators
-
operator ==(
Object other) → bool -
The equality operator.
inherited