Signal#
class & function |
Package: package:preact_signals
Class: Signal#
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.
.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!');
}
});
Members of Signal#
| Member | Type | Signature | Description |
|---|---|---|---|
| globalId | field |
dart int globalId |
|
| name | field |
dart String? name |
|
| watched | field |
dart void Function()? watched |
|
| unwatched | field |
dart void Function()? unwatched |
|
| equalityCheck | method |
dart SignalEquality |
Get the active equality check |
| isInitialized | method |
dart bool isInitialized |
Check if the value is set and not a lazy signal |
| internalValue | method |
dart T internalValue |
|
| Signal | constructor |
dart Signal(this._internalValue, {String? name, void Function()? watched, void Function()? unwatched, ReadonlySignalOptions
|
Creates a new Signal instance with the given initial value. |
| Signal.lazy | constructor |
dart Signal.lazy({String? name, void Function()? watched, void Function()? unwatched, ReadonlySignalOptions
|
Creates a new lazy Signal instance that is computed on-demand upon first read. |
| version | field |
dart int version |
Version numbers should always be >= 0, because the special value -1 is used |
| internalRefresh | method |
dart bool internalRefresh() |
|
| subscribeToNode | method |
dart void subscribeToNode(Node node) |
|
| unsubscribeFromNode | method |
dart void unsubscribeFromNode(Node node) |
|
| subscribe | method |
dart void Function() subscribe(void Function(T value) fn) |
|
| value | method |
dart T value |
Gets the current value of the signal. |
| value | method |
dart value(T val) |
Sets the current value of the signal. |
| set | method |
dart bool set(T val, {bool force = false}) |
Updates the signal's value by method call. |
Class: Signal#
Simple writeable signal
Members of Signal#
| Member | Type | Signature | Description |
|---|---|---|---|
| Signal | constructor |
dart Signal(super.internalValue, {SignalOptions
|
Simple writeable signal. |
| Signal.lazy | constructor |
dart Signal.lazy({SignalOptions
|
Lazy signal that can be created with type T that |
| debugLabel | method |
dart String? debugLabel |
|
| equalityCheck | method |
dart signals.SignalEquality |
Optional method to check if to values are the same |
| set | method |
dart bool set(T val, {bool force = false}) |
|
| value | method |
dart T value |
|
| readonly | method |
dart ReadonlySignal |
Returns a readonly signal |
| unsubscribeFromNode | method |
dart void unsubscribeFromNode(Node node) |
|
| overrideWith | method |
dart Signal |
Override the current signal with a new value as if it was created with it. |
Function: signal#
Signal<T> signal(T value, {SignalOptions<T>? options, @Deprecated('Use options: SignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: SignalOptions(name: ...) instead') String? debugLabel})
A Signal is a reactive container for a value that changes over time. It forms the bedrock of the reactive framework, allowing fine-grained, glitch-free propagation of state updates to dependent computeds and effects.
You can read a signal's current state, mutate it to dispatch updates, or subscribe to changes by accessing its
.value property inside any active reactive context.
Core Example#
import 'package:signals/signals.dart';
// Create a reactive signal holding an integer
final counter = signal(0);
// Read the value: prints 0
print(counter.value);
// Write to a signal: dispatches updates to all downstreams synchronously
counter.value = 1;
Key API Capabilities#
1. Reading & Writing via .value#
The .value property is the default way to interact with a signal.
-
Inside a Reactive Context: Accessing
.valueinside a computed block or effect callback automatically registers the signal as a dependency, establishing an active subscription. - Outside a Reactive Context: Acts as a standard getter and setter, allowing you to fetch or update the underlying state.
2. Non-reactive Reads via .peek()#
If you need to read a signal's current value without subscribing to its updates inside a reactive context, use the
.peek() method. This is invaluable when writing to another signal inside an effect based on the previous state, preventing infinite update loops (cycles).
final counter = signal(0);
final effectTriggerCount = signal(0);
effect(() {
// Subscribes to changes of `counter`
final current = counter.value;
print('Counter updated: $current');
// Read current count non-reactively and increment.
// The effect will NOT subscribe to `effectTriggerCount`.
effectTriggerCount.value = effectTriggerCount.peek() + 1;
});
3. Accessing the Previous State via .previousValue#
Signals automatically cache their immediately preceding value. Accessing .previousValue lets you perform diffing or historic analysis. Like
.peek(), reading .previousValue does not establish a reactive dependency.
final username = signal("initial_user");
effect(() {
print('Current Username: ${username.value}');
print('Previous Username: ${username.previousValue}');
});
username.value = "new_user";
// Prints:
// Current Username: new_user
// Previous Username: initial_user
4. Force Updates via .set()#
When dealing with mutable data types (e.g., custom class instances, collections), mutating properties directly does not change the instance reference. You can force an update using
.set(..., force: true) to skip standard equality checks and notify all downstreams.
final numbers = signal([1, 2, 3]);
// Modify the list in-place and force notify
numbers.value.add(4);
numbers.set(numbers.value, force: true);
Lifecycle & Resource Management#
Auto-Disposal#
If a signal is constructed with autoDispose: true, it will automatically destroy itself when it no longer has active reactive listeners (subscriptions). This prevents memory leaks by freeing resources as soon as they are out of scope.
final s = signal(0, options: SignalOptions(autoDispose: true));
s.onDispose(() => print('Signal has been disposed!'));
// Create active subscriber
final dispose = s.subscribe((_) {});
// Cancel subscription: s has no listeners, so it self-disposes
dispose();
// Prints: "Signal has been disposed!"
You can manually verify the lifecycle state using .disposed, or register custom clean-up routines via
.onDispose(callback).
Flutter Integration#
In Flutter applications, manage state and reactivity seamlessly by using SignalWidget
(for stateless widgets) or SignalStatefulWidget (for stateful widgets).
These widgets establish an implicit reactive context directly at the element layer. Any signal accessed via
.value inside the build method is automatically tracked, and the widget automatically rebuilds when they mutate.
Stateless Example with SignalWidget#
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
final counter = signal(0);
class CounterDisplay extends SignalWidget {
const CounterDisplay({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Count: ${counter.value}'),
ElevatedButton(
onPressed: () => counter.value++,
child: const Text('Increment'),
),
],
),
),
);
}
}
Stateful Example with SignalStatefulWidget#
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
class CounterDisplay extends SignalStatefulWidget {
const CounterDisplay({super.key});
@override
State<CounterDisplay> createState() => _CounterDisplayState();
}
class _CounterDisplayState extends State<CounterDisplay> {
// Local signal scoped to this widget state:
final counter = signal(0);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Count: ${counter.value}'),
ElevatedButton(
onPressed: () => counter.value++,
child: const Text('Increment'),
),
],
),
),
);
}
}
Testing Strategies#
1. Converting to Streams#
You can convert any reactive signal into a standard Dart Stream by calling .toStream(). This is highly beneficial for testing signal value sequences in order using test matchers.
test('emits sequential count updates in order', () async {
final counter = signal(0);
final stream = counter.toStream();
counter.value = 1;
counter.value = 2;
await expectLater(stream, emitsInOrder([0, 1, 2]));
});
2. Dependency Injection & Mock Overrides#
Global or lazy signals used across your application can be mocked or overridden during testing via .overrideWith(value). This returns a new signal sharing the same global identifier, helping you mock complex state dependencies seamlessly.
test('mocking global signals', () {
final apiToken = signal("production_token");
// Override with test mock token
apiToken.overrideWith("mock_test_token");
expect(apiToken.value, "mock_test_token");
});
Function: signal#
FlutterSignal<T> signal(T value, {core.SignalOptions<T>? options, @Deprecated('Use options: SignalOptions(name: ...) instead') String? debugLabel, @Deprecated('Use options: SignalOptions(autoDispose: ...) instead') bool? autoDispose, bool runCallbackOnListen = false})
Creates a mutable, reactive FlutterSignal initialized with the given value.
When the value changes, all registered builders, effects, and ValueNotifier listeners are automatically scheduled to update/rebuild.
Flutter Widget Example#
final count = signal(0);
class CounterView extends StatelessWidget {
const CounterView({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SignalBuilder(
builder: (context) => Text('Count: ${count.value}'),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => count.value++,
child: const Icon(Icons.add),
),
);
}
}
Function: signal#
Signal<T> signal(T value, [SignalOptions<T>? options])
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');
References#
The Signal type is referenced and used in the following pages:
- Persisted Signals (guides)
- Dependency Injection (guides)
- Bi-directional Data Flow (guides)
- ValueNotifier (guides)
- Untracked (signals_flutter/core)
- LinkedSignal (signals_flutter/core)
- Effect (signals_flutter/core)
- ReadonlySignal (signals_flutter/core)
- Signal (signals_flutter/core)
- Computed (signals_flutter/core)
- Action (signals_flutter/core)
- Batch (signals_flutter/core)
- MapSignalMixin (signals_flutter/mixins)
- TrackedSignalMixin (signals_flutter/mixins)
- SetSignalMixin (signals_flutter/mixins)
- ListSignalMixin (signals_flutter/mixins)
- ValueNotifierSignalMixin (signals_flutter/mixins)
- SinkSignalMixin (signals_flutter/mixins)
- SignalsMixin (signals_flutter/mixins)
- QueueSignalMixin (signals_flutter/mixins)
- IterableSignalMixin (signals_flutter/mixins)
- EventSinkSignalMixin (signals_flutter/mixins)
- StreamSignalMixin (signals_flutter/mixins)
- ChangeStackSignalMixin (signals_flutter/mixins)
- FlutterComputed (signals_flutter/signals)
- Ticker Signal (signals_flutter/signals)
- FlutterSignal (signals_flutter/signals)
- Flutter Readonly Signal (signals_flutter/signals)
- AsyncState (signals_flutter/async)
- FutureSignal (signals_flutter/async)
- AsyncSignal (signals_flutter/async)
- Computed (signals_flutter/async)
- Connect (signals_flutter/async)
- Stream (signals_flutter/async)
- MapSignal (signals_flutter/value)
- SetSignal (signals_flutter/value)
- ListSignal (signals_flutter/value)
- IterableSignal (signals_flutter/value)
- ChangeStackSignal (signals_flutter/value)
- RenderSignalBox (signals_flutter/render)
- Signal Value Listenable Utils (signals_flutter/extensions)
- Stateful Element Convert Extension (signals_flutter/extensions)
- SignalStatefulElement (signals_flutter/extensions)
- Stateful Widget Convert Widget Extension (signals_flutter/extensions)
- SignalCustomPainter (signals_flutter/extensions)
- Stateless Widget Convert Widget Extension (signals_flutter/extensions)
- Signal Value Notifier Utils (signals_flutter/extensions)
- SignalElement (signals_flutter/extensions)
- Value Listenable To Signal (signals_flutter/extensions)
- Stateless Element Convert Extension (signals_flutter/extensions)
- Value Notifier To Signal (signals_flutter/extensions)
- SignalStatefulWidget (signals_flutter/extensions)
- SignalEquality (signals_flutter/utilities)
- Model (signals_flutter/utilities)
- SignalContainer (signals_flutter/utilities)
- SignalsObserver (signals_flutter/utilities)
- PersistedSignal (signals_flutter/utilities)
- signals_flutter
- SignalEffect (signals_flutter/effects)
- SignalPainterWidget (signals_flutter/widgets)
- WatchBuilder (signals_flutter/widgets)
- Watch (signals_flutter/widgets)
- SignalWidget (signals_flutter/widgets)
- SignalBuilder (signals_flutter/widgets)
- SignalAnimatedBuilder (signals_flutter/widgets)
- SignalProvider (signals_flutter/widgets)
- Untracked (signals_core/core)
- LinkedSignal (signals_core/core)
- Effect (signals_core/core)
- ReadonlySignal (signals_core/core)
- Signal (signals_core/core)
- Computed (signals_core/core)
- Action (signals_core/core)
- Batch (signals_core/core)
- MapSignalMixin (signals_core/mixins)
- TrackedSignalMixin (signals_core/mixins)
- SetSignalMixin (signals_core/mixins)
- ListSignalMixin (signals_core/mixins)
- SinkSignalMixin (signals_core/mixins)
- QueueSignalMixin (signals_core/mixins)
- IterableSignalMixin (signals_core/mixins)
- EventSinkSignalMixin (signals_core/mixins)
- StreamSignalMixin (signals_core/mixins)
- ChangeStackSignalMixin (signals_core/mixins)
- AsyncState (signals_core/async)
- FutureSignal (signals_core/async)
- AsyncSignal (signals_core/async)
- Computed (signals_core/async)
- Connect (signals_core/async)
- Stream (signals_core/async)
- MapSignal (signals_core/value)
- SetSignal (signals_core/value)
- ListSignal (signals_core/value)
- IterableSignal (signals_core/value)
- ChangeStackSignal (signals_core/value)
- SignalEquality (signals_core/utilities)
- Model (signals_core/utilities)
- SignalContainer (signals_core/utilities)
- SignalsObserver (signals_core/utilities)
- PersistedSignal (signals_core/utilities)
- signals_core
- SignalsAvoidCreateInBuildMethod (signals_lint/flutter)
- SignalsPreferUnifiedOptions (signals_lint/flutter)
- signals_lint
- SignalsDevToolsExtension (signals_devtools_extension/flutter)
- signals_devtools_extension
- Untracked (signals/core)
- LinkedSignal (signals/core)
- Effect (signals/core)
- ReadonlySignal (signals/core)
- Signal (signals/core)
- Computed (signals/core)
- Action (signals/core)
- Batch (signals/core)
- MapSignalMixin (signals/mixins)
- TrackedSignalMixin (signals/mixins)
- SetSignalMixin (signals/mixins)
- ListSignalMixin (signals/mixins)
- ValueNotifierSignalMixin (signals/mixins)
- SinkSignalMixin (signals/mixins)
- SignalsMixin (signals/mixins)
- QueueSignalMixin (signals/mixins)
- IterableSignalMixin (signals/mixins)
- EventSinkSignalMixin (signals/mixins)
- StreamSignalMixin (signals/mixins)
- ChangeStackSignalMixin (signals/mixins)
- FlutterComputed (signals/signals)
- TickerSignal (signals/signals)
- FlutterSignal (signals/signals)
- Flutter Readonly Signal (signals/signals)
- AsyncState (signals/async)
- FutureSignal (signals/async)
- AsyncSignal (signals/async)
- Computed (signals/async)
- Connect (signals/async)
- Stream (signals/async)
- MapSignal (signals/value)
- SetSignal (signals/value)
- ListSignal (signals/value)
- IterableSignal (signals/value)
- ChangeStackSignal (signals/value)
- RenderSignalBox (signals/render)
- Signal Value Listenable Utils (signals/extensions)
- Stateful Element Convert Extension (signals/extensions)
- SignalStatefulElement (signals/extensions)
- Lazy Signal (signals/extensions)
- Stateful Widget Convert Widget Extension (signals/extensions)
- SignalCustomPainter (signals/extensions)
- Stateless Widget Convert Widget Extension (signals/extensions)
- Signal Value Notifier Utils (signals/extensions)
- SignalElement (signals/extensions)
- Value Listenable To Signal (signals/extensions)
- Stateless Element Convert Extension (signals/extensions)
- Value Notifier To Signal (signals/extensions)
- SignalStatefulWidget (signals/extensions)
- SignalEquality (signals/utilities)
- Model (signals/utilities)
- SignalContainer (signals/utilities)
- SignalsObserver (signals/utilities)
- PersistedSignal (signals/utilities)
- signals
- SignalEffect (signals/effects)
- SignalPainterWidget (signals/widgets)
- WatchBuilder (signals/widgets)
- Watch (signals/widgets)
- SignalWidget (signals/widgets)
- SignalBuilder (signals/widgets)
- SignalAnimatedBuilder (signals/widgets)
- SignalProvider (signals/widgets)
- signals_hooks
- useIterableSignal (signals_hooks/hooks)
- useLazySignal (signals_hooks/hooks)
- useConnect (signals_hooks/hooks)
- useValueNotifierToSignal (signals_hooks/hooks)
- useValueListenableToSignal (signals_hooks/hooks)
- useListSignal (signals_hooks/hooks)
- useQueueSignal (signals_hooks/hooks)
- useSignal (signals_hooks/hooks)
- useSignalValue (signals_hooks/hooks)
- useStreamSignal (signals_hooks/hooks)
- useSetSignal (signals_hooks/hooks)
- useLinkedSignal (signals_hooks/hooks)
- useChangeStackSignal (signals_hooks/hooks)
- useTrackedSignal (signals_hooks/hooks)
- useExistingSignal (signals_hooks/hooks)
- useAsyncSignal (signals_hooks/hooks)
- useComputed (signals_hooks/hooks)
- useSignalProvider (signals_hooks/hooks)
- useFutureSignal (signals_hooks/hooks)
- useAsyncComputed (signals_hooks/hooks)
- useSignalEffect (signals_hooks/hooks)
- useMapSignal (signals_hooks/hooks)
- SignalStatefulHookWidget (signals_hooks/widgets)
- SignalHookBuilder (signals_hooks/widgets)
- SignalHookWidget (signals_hooks/widgets)
- Untracked (preact_signals/core)
- Effect (preact_signals/core)
- ReadonlySignal (preact_signals/core)
- Signal (preact_signals/core)
- Computed (preact_signals/core)
- Action (preact_signals/core)
- Batch (preact_signals/core)
- SignalEquality (preact_signals/utilities)
- Model (preact_signals/utilities)
- preact_signals
- Overview (reference)
- AI Integration (reference)