# Signals.dart - Full Reference Manual
> This document concatenates all manuals, guides, and API references for package:signals.dart.
---
## Page: Signals.dart
Url: https://dartsignals.dev/
Description: Fine-grained reactive programming framework for Dart and Flutter.
---
> **Do more by doing less.** A highly optimized, native Dart reactive programming framework based on the Preact Signals model.
Ready to get started? Check out the Installation Guide or try it directly in your browser using the DartPad below.
---
## Why Signals?
- **📖 Fine-Grained Reactivity**: Automatically tracks read dependencies and surgically updates only the exact parts of the application that changed.
- **⚡️ 100% Native Dart**: Compatible with VM, CLI, Server (Shelf), Web (JS), and Flutter (Mobile, Desktop, Web). Use signals in any Dart project!
- **📐 Lazy & Memoized**: Values are computed lazily only when requested, and derived computations (computeds) cache their results until upstream dependencies mutate.
- **🧩 Small & Composable API**: Minimal surface area that is incredibly easy to learn, integrate, and scale.
- **🚀 Surgical Rebuilds**: Integrates with Flutter to allow surgical UI rebuilds, only marking the exact mounted widgets as dirty.
---
## Interactive Playground
Take a look at signals in action with this interactive DartPad:
---
## Packages & Reference
We offer specialized packages for all layers of your stack:
- [preact_signals](/packages/preact_signals): The raw, low-level reactivity engine.
- [signals_core](/packages/signals_core): Pure Dart reactive collections, mixins, and async primitives.
- [signals_flutter](/packages/signals_flutter): Surgical UI bindings and GPU-optimized rendering.
- [signals_hooks](/packages/signals_hooks): Hooks integration for standard Flutter Hook projects.
- [signals_lint](/packages/signals_lint): Static analysis rules and IDE assists.
- [signals_devtools_extension](/packages/signals_devtools_extension): Visual inspector in DevTools.
- [signals](/packages/signals): The unified umbrella package for most apps.
---
## Page: Overview
Url: https://dartsignals.dev/reference/overview
Description: An overview of the signals library
---
Signals are not new and have been around for a long time. They are also known as [signals](https://en.wikipedia.org/wiki/Signals_and_slots) or [observables](https://en.wikipedia.org/wiki/Observable_pattern).
Many popular JavaScript frameworks now include signals as part of their core library. Each of the implementations have their own unique features and APIs. Signals.dart is a port of the [Preact signals library](https://preactjs.com/blog/introducing-signals/) and is designed to be as close to the original API as possible in the core API.
VIDEO
Signals in preact started off by being implemented with dependencies tracked using a set but was later changed to use a linked list. The linked list implementation is more performant by taking advantage of [signal boosting](https://preactjs.com/blog/signal-boosting/) and is the implementation used in Signals.dart.
There is also a [DartPad](https://dartpad.dev/?id=d5f16f6be22e716d90419e41d10f281a) playground with some of the core methods that you can use to experiment!
If you are coming from the JS world and are comfortable with signals this should feel very familiar. If you are looking for a state management library in Flutter that can be used in the JS world and outside of Dart then look no further!
## Minimal Updates
An advantage with signals is the computation you get to save. **If you never read a signal it never gets computed.** That means that if you have a chain of computed values and never read the value of the last one then none of the callbacks would be called.
```dart
import 'package:signals/signals.dart';
final a = signal(0);
final b = computed(() => a.value + 1);
final c = computed(() => b.value + 1);
final d = computed(() => c.value + 1);
// if you never read `d` then none of the callbacks will be called
// All the callbacks will be called
print(d.value); // 3
// None of the callbacks will be called because the
// value is cached at each node
print(d.value); // 3
```
Signals also are a pull based state management library unlike most push based systems. This means that just because you update a signal value it does not mean that it will propagate (i.e. notifyListeners) to its targets.
Computed is also a special signal that keeps track of its dependencies and caches its value so it will only recompute on read and when the dependencies change. This can be pretty extensive and you can have a chain of computed signals and each of them are optimizing for the minimal amount of updates.
```dart
import 'package:signals/signals.dart';
final a = signal(0);
final b = signal(0);
final c = computed(() => a.value + b.value);
final d = computed(() => c.value + 1);
final e = computed(() => d.value + 1);
// All the callbacks will be called
print(e.value); // 2
// None of the callbacks will be called because the
// value is cached at each node
print(e.value); // 2
// Only the callbacks that need to be updated
// will be called
b.value = 1;
print(e.value); // 3
```
## Further reading
- https://signia.tldraw.dev/docs/what-are-signals
- https://www.solidjs.com/guides/reactivity
- https://angular.io/guide/signals
- https://vuejs.org/guide/extras/reactivity-in-depth.html
---
## Page: Installing
Url: https://dartsignals.dev/reference/install
Description: Learn how to install and configure Signals for pure Dart, Flutter, and developer tooling.
---
Signals is built with zero external dependencies and runs anywhere Dart can execute—including the Dart VM, Flutter (iOS, Android, macOS, Windows, Linux, Web), Web (WASM, JS), and server-side environments.
Unified Package : The standard signals package is a unified library that automatically selects the correct Dart-only or Flutter-native bindings depending on your target platform. In most cases, this is the only dependency you need!
---
## Quick Start
Add the stable release of Signals to your project using the command line:
**For pure Dart projects:**
```bash
dart pub add signals
```
**For Flutter projects:**
```bash
flutter pub add signals
```
---
## Package Directory
For modular development, the ecosystem is divided into specific packages depending on your architecture:
| Package | Purpose | Target | Pub Badge |
| :--- | :--- | :--- | :--- |
| **signals** | Full reactive framework (includes Flutter bindings) | Dart & Flutter | [](https://pub.dev/packages/signals) |
| **signals_flutter** | UI-bound reactive components and .watch(context) extensions | Flutter Only | [](https://pub.dev/packages/signals_flutter) |
| **signals_core** | Zero-dependency, ultra-fast reactive primitives | Pure Dart / Web | [](https://pub.dev/packages/signals_core) |
| **signals_lint** | Real-time static analysis and IDE quick-fixes | Dev Tooling | [](https://pub.dev/packages/signals_lint) |
| **preact_signals** | Direct low-overhead Dart port of Preact.js Signals | Core Engine | [](https://pub.dev/packages/preact_signals) |
---
## Manual Configuration (pubspec.yaml)
If you prefer to configure dependencies manually, add the following to your pubspec.yaml file:
### Stable Release (Recommended)
```yaml
dependencies:
signals: ^7.0.0
```
### Development Release (Direct from Git)
To track the latest updates on the main branch of the repository:
```yaml
dependencies:
signals:
git:
url: https://github.com/rodydavis/signals.dart
ref: main
path: packages/signals
```
---
## Linter Integration (signals_lint)
To guarantee best practices and catch performance gotchas before they hit production, we highly recommend integrating signals_lint into your development environment.
It automatically scans your code in real-time to alert you of common mistakes (such as creating signals inside Flutter build methods or reactive effect blocks) and provides automatic IDE quick-fixes.
### 1. Add Developer Dependency
Add the linter package under dev_dependencies in your pubspec.yaml:
```yaml
dev_dependencies:
signals_lint: ^7.0.0
```
### 2. Enable Analyzer Plugin
Activate the plugin in your project's analysis_options.yaml file:
```yaml
analyzer:
plugins:
- signals_lint
```
Once added, restart your editor's Dart Analysis server to start receiving real-time warnings and quick-fixes directly in your IDE!
---
## Page: AI Integration
Url: https://dartsignals.dev/reference/ai
Description: Enable LLMs and AI coding assistants to build high-performance reactive systems using Signals.dart.
---
The Signals ecosystem is designed to be highly accessible for Large Language Models (LLMs) and AI coding assistants. We provide unified context streams and modular developer **Skills** to give AI assistants full mastery over the framework's reactive paradigms.
---
## Unified Context Endpoint
Instead of manually feeding individual files into your AI chat or workspace, you can reference our single-source-of-truth endpoints. They contain the entire documentation set concatenated into a structured Markdown stream:
| Endpoint | Contents | Best Use Case |
| :--- | :--- | :--- |
| **https://dartsignals.dev/llms.txt** | Full user guides and core reference documentation. | Adding project context to lightweight assistants. |
| **https://dartsignals.dev/llms-full.txt** | Complete, comprehensive reference manual (expanded details). | In-depth code generation and technical codebases. |
### Adding to Local Workspace Context
If your AI coding assistant is scoped to your local project directory, you can download the endpoint directly to your workspace:
```bash
curl https://dartsignals.dev/llms.txt > signals.md
```
Once downloaded to your project root, you can reference the file (e.g. using @signals.md in cursor/copilot) to supply instant, highly relevant context.
---
## Prepackaged AI Developer Skills
To enable AI agents and coding assistants to write clean, idiomatic code for version 7 of the reactive framework, we package modular, high-fidelity developer **Skills** in the repository under the /skills folder.
These skills act as a system prompt extension that teaches the AI best practices, common gotchas (like the async tracking gap), and lifecycle optimization tracks:
* **signals-preact-dart**: Deep knowledge of the core reactive primitives (signal, computed, effect, batch, untracked).
* **signals-dart**: Best practices for async signal operations (FutureSignal, StreamSignal), reactive collections, writable computed signals (linkedSignal), and value semantics.
* **signals-flutter**: Comprehensive guides on element-level reactive UI integration, high-frequency GPU rendering bypasses, lifecycle side-effects, and .watch(context) extensions.
* **signals-migration-6-to-7**: Detailed instructions, patterns, and refactoring techniques to safely upgrade applications from signals v6.x to v7.0.0.
### One-Command Skill Installation
You can instantly install all available signals skills into your local AI workspace using the unified skills.sh registry CLI:
```bash
npx skills add rodydavis/signals.dart
```
This makes the skills immediately discoverable and usable by any editor-based or terminal-based AI assistant.
---
## Page: Persisted Signals
Url: https://dartsignals.dev/guides/persisted-signals
Description: How to save and restore signals.
---
When you need to store the state of your signals between app launches you can create a PersistedSignal from this example code.
You need to have a store that can be [SharedPreferences](https://pub.dev/packages/shared_preferences), [SQLite](https://pub.dev/packages/sqlite3), in memory, or any other storage solution. The store just needs to be able to save and restore the data.
```dart
abstract class KeyValueStore {
Future setItem(String key, String value);
Future getItem(String key);
Future removeItem(String key);
}
```
You can create an in-memory store for testing:
```dart
class InMemoryStore implements KeyValueStore {
final Map _store = {};
@override
Future getItem(String key) async {
return _store[key];
}
@override
Future removeItem(String key) async {
_store.remove(key);
}
@override
Future setItem(String key, String value) async {
_store[key] = value;
}
}
```
For this example we are going to be using SharedPreferences:
```dart
class SharedPreferencesStore implements KeyValueStore {
SharedPreferencesStore();
SharedPreferences? prefs;
Future init() async {
prefs ??= await SharedPreferences.getInstance();
return prefs!;
}
@override
Future getItem(String key) async {
final prefs = await init();
return prefs.getString(key);
}
@override
Future removeItem(String key) async {
final prefs = await init();
prefs.remove(key);
}
@override
Future setItem(String key, String value) async {
final prefs = await init();
prefs.setString(key, value);
}
}
```
The SharedPreferences will lazy load the instance when it is needed but you can also initialize before and pass it in the constructor.
By default we can encode and decode the value to and from JSON:
```dart
abstract class PersistedSignal extends FlutterSignal
with PersistedSignalMixin {
PersistedSignal(
super.internalValue, {
super.autoDispose,
super.debugLabel,
required this.key,
required this.store,
});
@override
final String key;
@override
final KeyValueStore store;
}
mixin PersistedSignalMixin on Signal {
String get key;
KeyValueStore get store;
bool loaded = false;
Future init() async {
try {
final val = await load();
super.value = val;
} catch (e) {
debugPrint('Error loading persisted signal: $e');
} finally {
loaded = true;
}
}
@override
T get value {
if (!loaded) init().ignore();
return super.value;
}
@override
set value(T value) {
super.value = value;
save(value).ignore();
}
Future load() async {
final val = await store.getItem(key);
if (val == null) return value;
return decode(val);
}
Future save(T value) async {
final str = encode(value);
await store.setItem(key, str);
}
T decode(String value) => jsonDecode(value);
String encode(T value) => jsonEncode(value);
}
```
We create a mixin so we can use it in other custom signals and share logic between signals_core and signals_flutter.
This can work in a lot of cases, but we might want to handle specific cases like enums:
```dart
class EnumSignal extends PersistedSignal {
EnumSignal(super.val, String key, this.values)
: super(
key: key,
store: SharedPreferencesStore(),
);
final List values;
@override
T decode(String value) => values.firstWhere((e) => e.name == value);
@override
String encode(T value) => value.name;
}
```
Or if you are in Flutter we can persist color values:
```dart
class ColorSignal extends PersistedSignal {
ColorSignal(super.val, String key)
: super(
key: key,
store: SharedPreferencesStore(),
);
@override
String encode(Color value) => value.value.toString();
@override
Color decode(String value) => Color(int.parse(value));
}
```
## Example
```dart
class AppTheme {
final sourceColor = ColorSignal(
Colors.blue,
'sourceColor',
);
final themeMode = EnumSignal(
ThemeMode.system,
'themeMode',
ThemeMode.values,
);
static AppTheme instance = AppTheme();
Future init() async {
await Future.wait([
sourceColor.init(),
themeMode.init(),
]);
}
}
void main() async{
final theme = AppTheme.instance;
// We need to init before running the app to prevent the theme from flickering
await theme.init();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = AppTheme.instance;
return MaterialApp(
theme: ThemeData.light().copyWith(
colorScheme: ColorScheme.fromSeed(
seedColor: theme.sourceColor.watch(context),
brightness: Brightness.light,
),
),
darkTheme: ThemeData.dark().copyWith(
colorScheme: ColorScheme.fromSeed(
seedColor: theme.sourceColor.watch(context),
brightness: Brightness.dark,
),
),
themeMode: theme.themeMode.watch(context),
home: Scaffold(
appBar: AppBar(
title: Text('Persisted Signals'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
theme.sourceColor.value = Colors.red;
},
child: Text('Change Color'),
),
ElevatedButton(
onPressed: () {
theme.themeMode.value = ThemeMode.dark;
},
child: Text('Change Theme'),
),
],
),
),
),
);
}
}
```
Now when we run the app and make changes, if we close the app and reopen it, the changes will persist offline.
---
## Page: Dependency Injection
Url: https://dartsignals.dev/guides/dependency-injection
Description: How is Signals can be used with any DI solution or none at all
---
Signals is a new **core primitive reactivity library** and not a framework which means it can be used with any dependency injection solution or none at all.
This library aims to adapt to any application architecture and you decide how you want to manage your signals.
This guide will show you how to use Signals with popular DI solutions.
## Provider
[Provider](https://pub.dev/packages/provider) is a simple way to provide objects to your widgets.
```dart
import 'package:signals/signals_flutter.dart';
import 'package:provider/provider.dart';
import 'package:flutter/material.dart';
void main() {
runApp(
Provider(
create: (_) => signal(0),
dispose: (_, instance) => instance.dispose(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = context.read>();
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Signals with Provider'),
),
body: Center(
child: Watch((context) => Text('Value: $signal')),
),
floatingActionButton: FloatingActionButton(
onPressed: () => counter.value++,
child: Icon(Icons.add),
),
),
);
}
}
```
> Note: Consumer can also be used instead of Watch.
## GetIt
[GetIt](https://pub.dev/packages/get_it) is a simple service locator that can be used in any Dart or Flutter project.
```dart
import 'package:signals/signals_flutter.dart';
import 'package:get_it/get_it.dart';
import 'package:flutter/material.dart';
void main() {
GetIt.I.registerSingleton>(signal(0));
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = GetIt.I.get>();
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Signals with GetIt'),
),
body: Center(
child: Watch((context) => Text('Value: $signal')),
),
floatingActionButton: FloatingActionButton(
onPressed: () => counter.value++,
child: Icon(Icons.add),
),
),
);
}
}
```
## Riverpod
[Riverpod](https://pub.dev/packages/riverpod) is a data-binding and reactive caching framework for Flutter and Dart.
```dart
import 'package:signals/signals_flutter.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:flutter/material.dart';
part 'main.g.dart';
@riverpod
Signal counter() => signal(0);
void main() {
runApp(ProviderScope(child: MyApp()));
}
class MyApp extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final counter = ref.read(counterProvider);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Signals with Riverpod'),
),
body: Center(
child: Watch((context) => Text('Value: $counter')),
),
floatingActionButton: FloatingActionButton(
onPressed: () => counter.value++,
child: Icon(Icons.add),
),
),
);
}
}
```
## InheritedWidget
InheritedWidget is a simple built-in way to provide objects to your widgets.
```dart
import 'package:signals/signals_flutter.dart';
import 'package:flutter/material.dart';
void main() {
runApp(
MaterialApp(
home: SignalProvider(
create: () => signal(0),
child: MyApp(),
),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = SignalProvider.of>(context)!;
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Signals with InheritedWidget'),
),
body: Center(
child: Watch((context) => Text('Value: $counter')),
),
floatingActionButton: FloatingActionButton(
onPressed: () => counter.value++,
child: Icon(Icons.add),
),
),
);
}
}
```
If you want to define multiple signals with the same type, then you will need to create custom classes for the container.
```dart
class Counter extends Signal {
Counter(int value) : super(value);
}
...
home: SignalProvider(
create: () => Counter(0),
child: MyApp(),
),
...
final counter = SignalProvider.of(context)!;
counter.value++;
```
## Lite Ref
[Lite Ref](https://pub.dev/packages/lite_ref) is a simple way to provide disposable objects to your widgets.
```dart
final counterRef = Ref.scoped(
(_) => signal(0),
dispose: (instance) => instance.dispose(),
);
void main() {
runApp(LiteRefScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = counterRef.of(context);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Signals with Zones'),
),
body: Center(
child: Watch((context) => Text('Value: $counter')),
),
floatingActionButton: FloatingActionButton(
onPressed: () => counter.value++,
child: Icon(Icons.add),
),
),
);
}
}
```
You can use any class that implements Disposable and it will be disposed when the widget is removed from the widget tree. You also don't need to provide a dispose function for the ScopedRef.
```dart
class Counter implements Disposable {
final value = signal(0);
final doubled = computed(() => value.value * 2);
@override
void dispose() {
value.dispose();
doubled.dispose();
}
}
final counterRef = Ref.scoped((_) => Counter());
```
## Zones
Zones are another built in way to provide objects to your application via Dart [Zones](https://dart.dev/articles/archive/zones).
[Scoped Deps](https://pub.dev/packages/scoped_deps) is a package that easily integrates Zones with Dart.
```dart
import 'package:signals/signals_flutter.dart';
import 'package:scoped_deps/scoped_deps.dart';
import 'package:flutter/material.dart';
final counter = create(() => signal(0));
void main() {
runScoped(() => MyApp(), values: {counter});
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = read(counter);
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Signals with Zones'),
),
body: Center(
child: Watch((context) => Text('Value: $counter')),
),
floatingActionButton: FloatingActionButton(
onPressed: () => counter.value++,
child: Icon(Icons.add),
),
),
);
}
}
```
## Global Signals
Global signals are a simple way to provide objects to your widgets.
This requires you to manage the lifecycle of the signal and dispose it when no longer needed.
> Note: This is not recommended for large applications and useful for select use cases like logging, analytics, auth, etc.
```dart
import 'package:signals/signals_flutter.dart';
import 'package:flutter/material.dart';
final counter = signal(0);
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Signals with Global Signal'),
),
body: Center(
child: Watch((context) => Text('Value: $counter')),
),
floatingActionButton: FloatingActionButton(
onPressed: () => counter.value++,
child: Icon(Icons.add),
),
),
);
}
}
```
---
## Page: Bi-directional Data Flow
Url: https://dartsignals.dev/guides/bi-directional-data-flow
Description: By default, Signals are uni-directional but can be used in a bi-directional way if needed.
---
By default, Signals are uni-directional but can be used in a bi-directional way if needed.
Bi-directional data flow should only be used when necessary as it can lead to infinite loops if not used correctly.
Consider the following example:
```dart
final a = signal(0);
final b = signal(0);
effect(() {
b.value = a.value + 1;
});
effect(() {
a.value = b.value + 1;
});
```
In this example, a and b are two signals that are dependent on each other. When a changes, b should update, and when b changes, a should update.
This however can lead to an infinite loop and will throw a EffectCycleDetectionError. To prevent this, you can use the untracked method to prevent the signal from updating itself.
```dart
final a = signal(0);
final b = signal(0);
effect(() {
b.value = untracked(() => a.value + 1);
});
effect(() {
a.value = untracked(() => b.value + 1);
});
```
This will prevent the infinite loop and allow the signals to update each other without causing an error.
Signals are synchronous and will update immediately when the value is set. This means that the value will be updated before the next effect is run. This allows you to create bi-directional data flow in a predictable way.
---
## Page: ValueNotifier
Url: https://dartsignals.dev/guides/value-notifier
Description: How is Signals different than using ValueNotifier?
---
As of Signals 6.0.0, Signal and Computed created with the flutter import implement ValueListenable and ValueNotifier by default.
```dart
import 'package:signals/signals_flutter.dart';
final count = signal(0);
assert(count is ValueListenable);
assert(count is Signal);
final isEven = computed(() => count.value.isEven);
assert(isEven is ValueListenable);
assert(isEven is Computed);
```
You can also use the [ValueListenableSignalMixin](/mixins/value-listenable) and [ValueNotifierSignalMixin](/mixins/value-notifier) to add the methods to custom signals.
## Signal
You may be thinking **"How is Signals different than using ValueNotifier?"** and that is a valid question when first coming to signals because at a glance they look very familiar.
```dart
// Value Notifier
final count = ValueNotifier(0);
// Signals
final count = signal(0);
```
But there is more to reactive programming than just the containers for the data. We still need to react to when the data changes which requires us to add listeners. This gets even more complicated the more we add.
```dart
class MyWidget extends ... {
final count1 = ValueNotifier(0);
final count2 = ValueNotifier(0);
// React to count 1 changing
count1.addListener(() {
if (mounted) setState(() {});
});
// React to count 2 changing
count2.addListener(() {
if (mounted) setState(() {});
});
@override
void dispose() {
super.dispose();
count1.dispose();
count2.dispose();
}
@override
Widget build(BuildContext context) {
// If using setState
return Text('${count1.value} - ${count2.value}');
// Or if you are using with ValueListenableBuilder
return ValueListenableBuilder(
valueListenable: count1,
builder: (context, count1Val, child) {
// React when count 1 changes
return ValueListenableBuilder(
valueListenable: count2,
builder: (context, count2Val, child) {
// React when count 2 changes
return Text('$count1Val - $count2Val');
},
);
},
);
}
}
```
As you can see there is a lot to keep track of mentally and you are writing more boilerplate than domain logic.
This also only works for Flutter and not in pure dart applications since [ValueNotifier](https://api.flutter.dev/flutter/foundation/ValueNotifier-class.html) is tied to the Flutter SDK.
The same example above for signals would be the following:
```dart
import 'package:signals/signals_flutter.dart';
class MyWidget extends ... {
final count1 = signal(0);
final count2 = signal(0);
@override
Widget build(BuildContext context) {
// If using setState
return Text('${count1.watch(context)} - ${count2.watch(context)}');
// Or if you are using with Watch
return Watch((context) => Text('$count1 - $count2'));
}
}
```
Lines of code are not everything, but this dramatically reduces the boilerplate needed to achieve the same result.
## Computed
State is not just about the values updated directly but often the derived state needed for any one screen.
In the example above we had two count values, but what if we had a third that was the total result and checked if it was even or odd.
With ValueNotifier you would have to calculate that directly or create a class with ChangeNotifier and start calling notifyListeners.
```dart
class MyWidget extends ... {
final count1 = ValueNotifier(0);
final count2 = ValueNotifier(0);
// React to count 1 changing
count1.addListener(() {
if (mounted) setState(() {});
});
// React to count 2 changing
count2.addListener(() {
if (mounted) setState(() {});
});
int get total => count1.value + count2.value;
int get isEven => total.isEven;
int get isOdd => total.isOdd;
@override
void dispose() {
super.dispose();
count1.dispose();
count2.dispose();
}
@override
Widget build(BuildContext context) {
// If using setState
return Text('$total even=$isEven odd=$isOdd');
// Or if you are using with ValueListenableBuilder
return ValueListenableBuilder(
valueListenable: count1,
builder: (context, count1Val, child) {
// React when count 1 changes
return ValueListenableBuilder(
valueListenable: count2,
builder: (context, count2Val, child) {
// React when count 2 changes
return Text('$total even=$isEven odd=$isOdd');
},
);
},
);
}
}
```
This still is possible but not efficient. What we care about is the total and isEven/isOdd result, not the count values themselves. Yet we have to still need to react to them when they change to trigger each computation.
It can be easy to miss an addListener or ValueListenableBuilder if you are unaware of a dependency in the chain.
Of course you could break it out with ChangeNotifier but then you are not using ValueNotifier anymore.
```dart
class Counter extends ChangeNotifier {
int _count1 = 0;
int get count1 => _count1;
set count1(int value) {
_count1 = value;
notifyListeners();
}
int _count2 = 0;
int get count2 => _count2;
set count2(int value) {
_count2 = value;
notifyListeners();
}
int get total => count1 + count2;
int get isEven => total.isEven;
int get isOdd => total.isOdd;
}
```
This still recalculates everything on every update. Total/isEven/isOdd are always computed regardless if the value has changed.
But how would this be possible with signals?
```dart
import 'package:signals/signals_flutter.dart';
class MyWidget extends ... {
final count1 = signal(0);
final count2 = signal(0);
final total = computed(() => count1.value + count2.value);
final isEven = computed(() => total.value.isEven);
final isOdd = computed(() => total.value.isOdd);
@override
Widget build(BuildContext context) {
// If using setState
return Text('${total.watch(context)} even=${isEven.watch(context)} odd=${isOdd.watch(context)}');
// Or if you are using with Watch
return Watch((context) {
return Text('$total even=$isEven odd=$isOdd');
});
}
}
```
There some special things happening here that I want to call out.
Total/isEven/isOdd is only called when the values it depends on change. Each computed signal will store the value and cache it until dependencies change.
If the value is never read the computed callbacks are never called. That means you only calculate the state you use when you use it.
Also the UI logic does not need to care about count1/count2 and only the values you want to read. This leads to fewer mistakes and simpler code.
## Incremental Migration
If you have value notifiers you cannot update because they come from a library you can convert them to a signal.
```dart
final notifier = ValueNotifier(0);
final ValueListenable listenable = ...;
final notifierSignal = notifier.toSignal();
// Will update notifier when the value is set
notifierSignal.value = 1; // calls notifier.value = 1;
// React to changes to listenable
final listenableSignal = listenable.toSignal();
```
You can also provide a signal as a ValueListenable or ValueNotifier depending on if the signal is read-only or not.
```dart
import 'package:signals/signals_flutter.dart';
final count = signal(0);
final isEven = computed(() => count.value.isEven);
final ValueNotifier countNotifier = count;
// Will call count.value when countNotifier is set
countNotifier.value = 1; // calls count.value = 1;
// React to changes from the host signal
final ValueListenable countListenable = isEven;
```
These extensions will also dispose the ValueNotifier and ValueListenable when the signal is disposed.
## Outside of Flutter
Signals can be used in pure dart applications. This means you can have the same logic for server side, flutter, CLIs, html web apps and more.
```dart
import 'package:signals/signals.dart';
void main() {
final count1 = signal(0);
final count2 = signal(0);
final total = computed(() => count1.value + count2.value);
final isEven = computed(() => total.value.isEven);
final isOdd = computed(() => total.value.isOdd);
effect(() {
print('$total even=$isEven odd=$isOdd');
});
}
```
It really is that simple.
All the other signals in the package are syntax sugar for core types or helper methods to connect to Flutter specifics.
With Signals 0.6.0 you can also create a signal that extends both Signal, ValueNotifier and Stream.
```dart
import 'package:signals/signals_flutter.dart';
class CustomSignal extends Signal with
ValueNotifierSignalMixin,
SinkSignalMixin,
StreamSignalMixin {
CustomSignal(T value) : super(value);
}
class Counter extends CustomSignal {
Counter(int value) : super(value);
}
void main() {
final counter = Counter(0);
assert(counter is Signal);
assert(counter is ValueNotifier);
assert(counter is Stream);
// Listen to the stream
counter.listen((value) {
print('stream: $value');
});
// Subscribe in an effect
effect(() {
print('effect: $counter');
});
counter.add(1);
print(counter.value); // 1
counter.value = 2;
print(counter.value); // 2
counter.close();
print(counter.disposed); // true
}
```
---
## Page: Untracked
Url: https://dartsignals.dev/packages/signals_flutter/core/untracked
Description: Runs a callback function fn that can read signal values without establishing a reactive subscription.
---
Runs a callback function **fn** that can read signal values without establishing a reactive subscription.
Normally, reading a signal's value (via .value or ()) inside an [effect](/types/effect) or a [computed](/types/computed) callback
automatically subscribes the surrounding context to that signal. If the signal changes, the context is
re-executed.
In some scenarios, you want to read a signal's current value inside a reactive context but *avoid* creating
a subscription. This is where [untracked](/types/untracked) is useful. It temporarily suspends active tracking, executes **fn**,
and then restores tracking.
Parameters:
- **fn**: The callback function to execute. Any signal read inside this callback will not register a dependency.
Returns:
- The value returned by the callback function **fn**.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
void main() {
final counter = signal(0);
final loggingThreshold = signal(5);
effect(() {
final currentCount = counter.value; // Establishing a subscription to `counter`
// We want to read `loggingThreshold` but we do NOT want this effect to
// trigger whenever `loggingThreshold` changes.
final threshold = untracked(() => loggingThreshold.value);
if (currentCount > threshold) {
print("Counter ($currentCount) has exceeded the threshold ($threshold)!");
}
});
counter.value = 6; // Prints: "Counter (6) has exceeded the threshold (5)!"
// Updating the threshold will NOT trigger the effect, because it was read inside `untracked`
loggingThreshold.value = 10;
}
```
untracked is particularly useful inside event handlers, conditional logging, or when you are performing
a write to a signal based on another signal's value inside an effect to prevent infinite dependency cycles.
Be cautious when using untracked , as it bypasses the dependency tracking system. If the values read inside
untracked change, your reactive side effects or computed derivations will not automatically re-run.
---
## Page: LinkedSignal
Url: https://dartsignals.dev/packages/signals_flutter/core/linked-signal
Description: A highly powerful, mutable computed signal that derives its default value from an underlying source,.
---
A highly powerful, mutable computed signal that derives its default value from an underlying source,
but allows manual write overrides. Crucially, **whenever the underlying source value changes, the signal**
**automatically discards any local manual overrides and resets back to the newly computed default.**
This hybrid behavior is the perfect solution for synchronizing local edit state with external remote state.
### 1. Real-World Use Case: Profile Form Editor
Imagine you are building a profile editor where the user can modify their username:
- The initial/remote username is fetched from a database and held in a source signal.
- The text input field is bound to a local signal.
- The user should be able to edit the field locally (overriding the remote default).
- If the selected user changes (e.g., they switch to a different profile in a list), the text field must automatically discard any local changes and reset to the new user's username.
```dart
// The remote/source state
final selectedUser = signal(User(id: 1, name: 'Alice'));
// The local editable state linked to the remote source
final username = linkedSignal(() => selectedUser.value.name);
print(username.value); // 'Alice'
// User edits the text field:
username.value = 'Bob';
print(username.value); // 'Bob' (local override active)
// Switch remote profile:
selectedUser.value = User(id: 2, name: 'Charlie');
// Local overrides are discarded and reset to the new source:
print(username.value); // 'Charlie'
```
### 2. Custom Computations using LinkedSignalOptions
By default, a linked signal directly passes the source value through. You can customize this mapping using a custom computation function that has access to both the current source value and the previous state:
```dart
final counter = signal(10);
final doubled = linkedSignal(
() => counter.value,
options: LinkedSignalOptions(
computation: (sourceVal, prev) {
print('Source changed to $sourceVal. Previous value was: ${prev?.value}');
return sourceVal * 2;
},
),
);
```
### 3. Custom Source Equality
To prevent unnecessary resets, you can supply a custom sourceEquality callback. The signal will only reset when the equality check returns false:
```dart
final selectedUser = signal(User(id: 1, name: 'Alice'));
final username = linkedSignal(
() => selectedUser.value,
options: LinkedSignalOptions(
// Only reset when the user ID actually changes:
sourceEquality: (a, b) => a.id == b.id,
),
);
```
Always use LinkedSignal rather than manual effect listeners to synchronize local editable values with remote defaults. It is simpler, avoids race conditions, and consumes significantly less memory.
### Constructors
View Constructors
##### `LinkedSignal({required S Function() source, LinkedSignalOptions? options})`
Creates a new [LinkedSignal](/types/linkedsignal).
### Methods
View Methods
##### `bool set(T val, {bool force = false})`
##### `T value`
##### `value(T val)`
##### `void dispose()`
---
## linkedSignal
{@macro linked_signal}
---
## Page: Effect
Url: https://dartsignals.dev/packages/signals_flutter/core/effect
Description: Represents a passive observer that runs arbitrary side-effect code in response to signal changes.
---
Represents a passive observer that runs arbitrary side-effect code in response to signal changes.
An [Effect](/types/effect) tracks which signals are accessed within its callback function,
and automatically schedules itself to re-run whenever those dependencies change.
Under the hood, the reactivity engine tracks reads on .value inside the active effect block.
Once the block completes, a subscription is registered for each accessed signal. When any of those signals
mutate, the effect is added to the microtask queue and executed synchronously during the next tick.
Do not modify a tracked signal directly inside an effect callback, as this will trigger another execution
of the same effect, causing an infinite loop (cycle) and throwing a cycle detection error.
To read a signal non-reactively, use .peek().
### Example Usage
#### 1. Standard Side-Effect
```dart
import 'package:preact_signals/preact_signals.dart';
final count = signal(0);
void main() {
// Creates and immediately starts the effect
final logger = Effect(() {
print('Active count is: ${count.value}');
});
count.value = 1; // Prints: "Active count is: 1"
logger.dispose();
}
```
#### 2. Effect Cleanup Callback
If your effect returns a function, that function is registered as a **cleanup callback**.
The cleanup callback is executed right before the next effect run, or when the effect is disposed.
This is highly useful for cleaning up timers, controllers, or other subscriptions:
```dart
final query = signal('search_term');
final searchEffect = Effect(() {
final currentQuery = query.value;
print('Initiating search for: $currentQuery');
final timer = Timer(Duration(milliseconds: 500), () {
print('Search completed for: $currentQuery');
});
// Return cleanup callback
return () {
print('Cancelling previous search timer');
timer.cancel();
};
});
```
### Constructors
View Constructors
##### `Effect(this.fn, {String? name, EffectOptions? options})`
Creates a new [Effect](/types/effect) instance with the passive side-effect callback **fn**.
You can optionally provide:
- A **name** for debugging/observer tracing.
```dart
final effectObj = Effect(() => print(count.value), name: 'count_logger');
```
### Properties
View Properties
##### `int globalId`
##### `int flags`
##### `String? name`
The name of the effect for debugging.
### Methods
View Methods
##### `void notify()`
##### `void dispose()`
Disposes of the effect, stopping future callback executions,
executing any registered cleanup routines, and unsubscribing from all dependency signals.
##### `void Function() call()`
Activates/Runs the effect immediately.
Returns a bound disposer function that can be called to stop the effect.
---
## effect
Creates and immediately executes a new reactive [Effect](/types/effect).
Returns a bound disposer function that can be called to stop the effect and unsubscribe
it from all tracked signals.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
final count = signal(0);
final dispose = effect(() {
print('Count is: ${count.value}');
return () => print('Cleaning up!');
});
void main() {
count.value = 10; // Prints: "Cleaning up!" then "Count is: 10"
dispose(); // Stops the effect and unsubscribes
}
```
---
## Page: ReadonlySignal
Url: https://dartsignals.dev/packages/signals_flutter/core/readonly
Description: An interface for read-only signals.
---
An interface for read-only signals.
A [ReadonlySignal](/types/readonlysignal) is a reactive container whose value can be read but not directly mutated.
Under the hood, any [Signal](/types/signal) implements or can be cast/exposed as a [ReadonlySignal](/types/readonlysignal). This
is a core architectural pattern for encapsulating state: classes can modify state internally
using a private mutable Signal, while exposing a public ReadonlySignal to consumers to
enforce unidirectional data flow.
Whenever the underlying value changes, any active [effect](/types/effect) or [computed](/types/computed) signal that reads this
signal's value will automatically be re-evaluated.
### Example Usage
````dart
import 'package:preact_signals/preact_signals.dart';
class CounterController {
// Keep the mutable state private to the controller
final _counter = signal(0);
// Expose a public read-only signal to external consumers
ReadonlySignal get counter => _counter;
void increment() {
_counter.value++;
}
void decrement() {
_counter.value--;
}
}
void main() {
final controller = CounterController();
// React to updates from the read-only signal
final dispose = effect(() {
print("The current count is: ${controller.counter.value}");
});
// controller.counter.value = 10; // Error: Cannot mutate a ReadonlySignal!
controller.increment(); // Prints: "The current count is: 1"
controller.increment(); // Prints: "The current count is: 2"
dispose();
}
````
Use [ReadonlySignal] to prevent consumers of your stores or controllers from modifying state
bypassing the controller's methods. This ensures consistent, predictable, and traceable mutations
throughout your application.
### Methods
View Methods
##### `int globalId`
Global ID of the signal
##### `T value`
Compute the current value
##### `String? name`
The name of the signal for debugging purposes.
##### `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.
##### `String toString()`
##### `dynamic toJson()`
Convert value to JSON
##### `T call()`
Return the value when invoked
##### `T get()`
Helper method to get the current value
##### `T 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().
```dart
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.
##### `void Function() subscribe(void Function(T value) fn)`
Subscribe to value changes with a dispose function
---
## readonly
Creates a new read-only signal initialized with **value**.
This function returns a [ReadonlySignal](/types/readonlysignal) containing **value**. Under the hood, a mutable [Signal](/types/signal)
is created, but it is returned under the [ReadonlySignal](/types/readonlysignal) interface to prevent modification by clients.
This is particularly useful when you need to expose a constant reactive value, or bridge some external,
immutable value source into the signals reactivity system.
Parameters:
- **value**: The initial value held by the read-only signal.
- **options**: Optional configuration options (e.g., custom debug name or lifecycle callbacks like watched/unwatched).
Returns:
- A [ReadonlySignal](/types/readonlysignal) containing the initial value.
### Example Usage
````dart
import 'package:preact_signals/preact_signals.dart';
final configUrl = readonly('https://api.example.com');
void main() {
effect(() {
print("Connecting to: ${configUrl.value}");
});
}
````
If you are trying to derive a value from other signals, do not use [readonly]. Use [computed] instead
to ensure the derived signal automatically re-evaluates when its source signals change.
---
## Page: Signal
Url: https://dartsignals.dev/packages/signals_flutter/core/signal
Description: 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](/types/computed)) or effects (like [effect](/types/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
```dart
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:
```dart
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](/types/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.
```dart
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](/types/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.
```dart
final lazyUser = Signal.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](/types/effect) or [computed](/types/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.
```dart
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](/types/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
```dart
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](/types/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>] 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.
```dart
import 'package:signals_core/signals_core.dart';
final tags = {}.$;
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 elements)`
##### `void removeWhere(bool Function(E element) test)`
##### `void retainAll(Iterable elements)`
##### `void retainWhere(bool Function(E element) test)`
---
## ComputedOptions
Configuration options for a [Computed](/types/computed) signal.
Enables configuring debugging names and subscription state event listeners
for computed derivations.
### Example Usage
```dart
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](/types/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](/types/signal).
Extends [ReadonlySignalOptions](/types/readonlysignaloptions) to also support custom **equality** checkers,
which control whether incoming values trigger update events.
### Example Usage
```dart
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](/types/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 [Effect](/types/effect)s.
Permits naming the effect for debugging, performance profiling,
and tracing within the signals developer tools.
### Example Usage
```dart
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](/types/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](/types/readonlysignal), enabling direct reactive logical conjunction (&), disjunction (|), and exclusive or (^) operations.
```dart
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](/types/readonlysignal), providing convenient reactive math and comparison operations without needing to manually unwrap .value.
```dart
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:
```dart
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 **int**s, 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:
```dart
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.
```dart
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:
```dart
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:
```dart
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:
```dart
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:
```dart
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:
```dart
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](/types/readonlysignal), enabling direct reactive arithmetic and rounding operations on double signals.
```dart
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).
```dart
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).
```dart
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).
```dart
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).
```dart
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.
```dart
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.
```dart
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.
```dart
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.
```dart
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>], providing delegators to compute set operations reactively.
```dart
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 other)`
##### `Set difference(Set other)`
##### `Set intersection(Set other)`
##### `E? lookup(Object? object)`
##### `Set union(Set other)`
---
## SignalStreamUtils
Extension on **Stream** to provide convenient utilities to convert streams into reactive signals.
```dart
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> dependencies = const [], void Function()? onDone, AsyncSignalOptions? options})`
Convert a stream to a signal
```dart
import 'package:signals/signals.dart';
Stream 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](/types/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](/types/asyncstate).
```dart
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
```dart
import 'package:preact_signals/preact_signals.dart';
final count = signal(0);
final name = signal('Jane');
```
---
## lazySignal
Lazy signal that can be created with type T that
the value will be assigned later.
```dart
final db = lazySignal();
...
db.value = DatabaseConnect(...);
```
---
## 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](/types/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](/types/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.
```dart
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](/types/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
```dart
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](/types/computed) signal.
### Methods
View Methods
##### `Computed $`
Return a cached, derived [Computed](/types/computed) signal from this getter function.
```dart
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](/types/signal).
### Methods
View Methods
##### `Signal $`
Lift a primitive **double** into a reactive [Signal](/types/signal).
```dart
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](/types/signal).
### Methods
View Methods
##### `Signal $`
Lift a primitive **bool** into a reactive [Signal](/types/signal).
```dart
import 'package:signals_core/signals_core.dart';
final isEnabled = true.$;
print(isEnabled.value); // true
```
---
## SignalIterableUtils
Utility extension methods on **Iterable** to convert them to [IterableSignal](/types/iterablesignal)s.
### 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](/types/iterablesignal).
This returns an [IterableSignal](/types/iterablesignal) initialized with the current collection.
```dart
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](/types/signal).
### Methods
View Methods
##### `Signal $`
Lift a primitive **num** into a reactive [Signal](/types/signal).
```dart
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](/types/signal).
### Methods
View Methods
##### `Signal> $`
Lift a primitive **Set** into a reactive [Signal>].
```dart
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 [ListSignal](/types/listsignal)s.
### 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](/types/listsignal).
```dart
import 'package:signals/signals.dart';
final myList = [1, 2, 3];
final signal = myList.toSignal();
```
---
## SignalMapUtils
Utility extension methods on **Map** to convert them to [MapSignal](/types/mapsignal)s.
### Methods
View Methods
##### `MapSignal toSignal({MapSignalOptions? 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](/types/mapsignal).
```dart
import 'package:signals/signals.dart';
final myMap = {'key': 'value'};
final signal = myMap.toSignal();
```
---
## SignalSetUtils
Utility extension methods on **Set** to convert them to [SetSignal](/types/setsignal)s.
### 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](/types/setsignal).
```dart
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> dependencies = const [], AsyncSignalOptions? options})`
Convert an existing future to [FutureSignal](/types/futuresignal)
```dart
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>]
### 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](/types/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](/types/changestacksignal).
### Constructors
View Constructors
##### `ChangeSignalOptions({this.limit, super.name, super.autoDispose, super.watched, super.unwatched})`
Creates a new [ChangeSignalOptions](/types/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](/types/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).
```dart
RegExp exp = RegExp(r'(\w+)');
var str = 'Dash is a bird';
Iterable 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.
```dart
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](/types/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:
```dart
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
'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:
```dart
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**.
```dart
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**:
```dart
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:
```dart
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:
```dart
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:
```dart
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**:
```dart
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.
```dart
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:
```dart
const string = 'dart' + 'lang'; // 'dartlang'
```
##### `String substring(int start, [int? end])`
The substring of this string from **start**, inclusive, to **end**, exclusive.
Example:
```dart
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:
```dart
final trimmed = '\tDart is fun\n'.trim();
print(trimmed); // 'Dart is fun'
```
Otherwise, the original string itself is returned:
```dart
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:
```plaintext
0009..000D ; White_Space # Cc ..
0020 ; White_Space # Zs SPACE
0085 ; White_Space # Cc
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.
```dart
final string = ' Dart '.trimLeft();
print(string); // 'Dart '
```
##### `String trimRight()`
The string without any trailing whitespace.
As **trim**, but only removes trailing whitespace.
```dart
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.
```dart
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**.
```dart
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**.
```dart
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:
```dart
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:
```dart
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:
```dart
'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.
```dart
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]}>')); // 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**.
```dart
'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**:
```dart
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:
```dart
this.substring(0, start) + replacement + this.substring(end)
```
Example:
```dart
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.
```dart
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:
```dart
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.
```dart
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:
```dart
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).
```dart
const string = '';
print(string.split('')); // []
print(string.split('a')); // []
```
Splitting with an empty pattern splits the string into single-code unit
strings.
```dart
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:
```dart
// 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:
```dart
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.
```dart
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.
```dart
'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.
```dart
'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](/types/iterablesignal).
### Constructors
View Constructors
##### `IterableSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched, super.equality = const SignalDeepEquality()})`
Creates a new [IterableSignalOptions](/types/iterablesignaloptions) instance.
### Methods
View Methods
##### `IterableSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, SignalEquality>? equality})`
##### `bool ==(Object other)`
##### `int hashCode`
---
## EnumSignalExtension
Helper extensions for [ReadonlySignal](/types/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:
```dart
enum MyEnum {
value1,
value2
}
```
the result of MyEnum.value1.name is the string "value1".
---
## ReadonlyListSignalExtension
Helper extensions for [ReadonlySignal](/types/readonlysignal)
### Methods
View Methods
##### `List cast()`
##### `E last`
##### `List +(List other)`
##### `E [](int index)`
##### `Map 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])`
---
## IntSignalExtension
Helper extensions for [ReadonlySignal](/types/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.
```dart
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:
```dart
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:
```dart
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:
```dart
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:
```dart
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:
```dart
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:
```dart
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.
```dart
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**.
```dart
(-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:
```dart
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.
```dart
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**.
```dart
// 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:
```dart
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.
```dart
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:
```dart
// 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
```
---
## ReadonlyMapSignalExtension
Helper extensions for [ReadonlySignal](/types/readonlysignal)
### Methods
View Methods
##### `V? [](Object? key)`
##### `Map cast()`
##### `bool containsKey(Object? key)`
##### `bool containsValue(Object? value)`
##### `Iterable> entries`
##### `bool isEmpty`
##### `bool isNotEmpty`
##### `Iterable keys`
##### `int length`
##### `Map map(MapEntry Function(K key, V value) convert)`
##### `Iterable values`
---
## ListSignalOptions
Configuration options for a [ListSignal](/types/listsignal).
### Constructors
View Constructors
##### `ListSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched, super.equality = const SignalDeepEquality()})`
Creates a new [ListSignalOptions](/types/listsignaloptions) instance.
### Methods
View Methods
##### `ListSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, SignalEquality>? equality})`
##### `bool ==(Object other)`
##### `int hashCode`
---
## MapSignalOptions
Configuration options for a [MapSignal](/types/mapsignal).
### Constructors
View Constructors
##### `MapSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched, super.equality = const SignalDeepEquality()})`
Creates a new [MapSignalOptions](/types/mapsignaloptions) instance.
### Methods
View Methods
##### `MapSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, SignalEquality>? equality})`
##### `bool ==(Object other)`
##### `int hashCode`
---
## SetSignalOptions
Configuration options for a [SetSignal](/types/setsignal).
### Constructors
View Constructors
##### `SetSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched, super.equality = const SignalDeepEquality()})`
Creates a new [SetSignalOptions](/types/setsignaloptions) instance.
### Methods
View Methods
##### `SetSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, SignalEquality>? 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
```dart
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](/types/linkedsignal).
### Constructors
View Constructors
##### `LinkedSignalOptions({this.computation, this.sourceEquality, super.name, super.autoDispose})`
Creates [LinkedSignalOptions](/types/linkedsignaloptions).
### Properties
View Properties
##### `T Function(S source, LinkedSignalPreviousState? 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 copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, T Function(S source, LinkedSignalPreviousState? previous)? computation, bool Function(S a, S b)? sourceEquality})`
##### `bool ==(Object other)`
##### `int hashCode`
---
## ListSignalExtension
Helper extensions for [Signal](/types/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](/types/signal)
### Methods
View Methods
##### `void []=(K key, V value)`
##### `void addAll(Map other)`
##### `void addEntries(Iterable> 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](/types/signal)
---
## SignalComparableExtensions
Extensions for **Comparable**
### Methods
View Methods
##### `Signal> $`
Return a signal from a Comparable value
---
## SignalIterableExtensions
Extensions for **Iterable**
### Methods
View Methods
##### `Signal> $`
Return a signal from a Iterable value
---
## SignalListExtensions
Extensions for **List**
### Methods
View Methods
##### `Signal> $`
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> $`
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()`
---
## Page: Computed
Url: https://dartsignals.dev/packages/signals_flutter/core/computed
Description: Represents a derived, read-only reactive state value computed from one or more other signals.
---
Represents a derived, read-only reactive state value computed from one or more other signals.
Computed signals are **lazily evaluated** and **memoized (cached)**. Their callback function **fn**
is only executed when its value is read *and* one of its upstream dependencies has mutated since the
last calculation. If none of the dependencies have changed, the cached value is returned directly.
Under the hood, a Computed signal tracks its sources dynamically. If a conditional branch inside
the computation changes such that certain signals are no longer read, those signals are automatically pruned
from the dependency list, preventing redundant triggers.
The computation callback fn should be pure and side-effect free. Writing to other signals or
performing network/database operations inside a computed callback is a critical anti-pattern that can lead to
infinite loops (cycles) or unpredictable state transitions.
### Example Usage
#### 1. Basic Derived State
```dart
import 'package:preact_signals/preact_signals.dart';
void main() {
final firstName = Signal('Jane');
final lastName = Signal('Doe');
// Computed automatically tracks both firstName and lastName
final fullName = Computed(() => '${firstName.value} ${lastName.value}');
print(fullName.value); // Jane Doe
lastName.value = 'Smith';
print(fullName.value); // Jane Smith
}
```
#### 2. Dynamic Dependency Tracking (Branching)
```dart
final showFull = Signal(false);
final detailedInfo = Signal('High Latency Alert');
final briefInfo = Signal('Alert');
final message = Computed(() {
if (showFull.value) {
return detailedInfo.value; // Subscribes to detailedInfo
} else {
return briefInfo.value; // Subscribes to briefInfo
}
});
```
### Constructors
View Constructors
##### `Computed(this.fn, {String? name, void Function()? watched, void Function()? unwatched, ComputedOptions? options})`
Creates a new [Computed](/types/computed) signal instance with the derivation callback **fn**.
You can optionally provide:
- A **name** for debugging/observer tracing.
- **watched**/**unwatched** hooks triggered when the computed gains its first subscriber or loses its last subscriber.
```dart
final doubleCount = Computed(() => count.value * 2, name: 'double_counter');
```
### Properties
View Properties
##### `int globalId`
##### `String? name`
##### `void Function()? watched`
##### `void Function()? unwatched`
##### `int flags`
##### `int version`
### Methods
View Methods
##### `bool isInitialized`
Check if the value has been computed
##### `T internalValue`
##### `bool internalRefresh()`
##### `void subscribeToNode(Node node)`
##### `void unsubscribeFromNode(Node node)`
##### `void notify()`
##### `T value`
##### `void Function() subscribe(void Function(T value) fn)`
---
## computed
Convenient global constructor for creating a derived computed signal.
Computed signals are lazily evaluated and cached (memoized). Their values
automatically update when any dependency signals accessed inside the callback function change.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
final firstName = signal('Jane');
final lastName = signal('Doe');
final fullName = computed(() => '${firstName.value} ${lastName.value}');
void main() {
print(fullName.value); // Prints: Jane Doe
}
```
---
## Page: Action
Url: https://dartsignals.dev/packages/signals_flutter/core/action
Description: Wraps a callback function into a reusable, batched, and untracked action.
---
Wraps a callback function into a reusable, batched, and untracked action.
An **action** is a higher-order function that takes a callback and returns a new function
with the exact same signature. When the returned function is executed, it runs the original
callback inside both a [batch](/types/batch) and an [untracked](/types/untracked) block.
### Why use action instead of batch?
1. **Reusability**: batch(fn) executes the callback immediately. In contrast, action(fn)
returns a *reusable function* that you can store, pass around, and invoke multiple times
to perform batch transactions on demand.
2. **Untracked Execution**: The callback runs inside untracked. If you invoke the action
from within an effect or a computed signal, the outer reactive context **will not**
establish subscriptions to any signals read inside the action.
---
### Example: Comparing Normal Updates vs. Action Batching
#### Without Actions (Standard Sequential Updates)
Every signal write immediately notifies active subscribers. This causes transient states
and redundant, intermediate executions:
```dart
import 'package:preact_signals/preact_signals.dart';
final a = signal('a');
final b = signal('b');
void main() {
// Set up a subscriber effect
effect(() => print('${a.value} ${b.value}'));
// Prints immediately: "a b"
a.value = 'aa'; // Prints: "aa b"
b.value = 'bb'; // Prints: "aa bb"
}
```
Total prints: **3** (initial execution + 2 updates).
#### With Actions (Coalesced Transaction)
By wrapping the state-mutating function in [action](/types/action), all updates are postponed and flushed
in a single notification block once the function completes:
```dart
import 'package:preact_signals/preact_signals.dart';
final a = signal('a');
final b = signal('b');
// Create a reusable action
final updateFields = action((String nextA, String nextB) {
a.value = nextA;
b.value = nextB;
});
void main() {
effect(() => print('${a.value} ${b.value}'));
// Prints immediately: "a b"
updateFields('aa', 'bb');
// The effect is deferred during execution and triggers exactly once at the end.
// Prints: "aa bb"
}
```
Total prints: **2** (initial execution + 1 coalesced update).
---
### Type-Safety & Extensions
While action accepts any generic Function, Dart's static analysis benefits greatly from
type-safe variants or extensions.
- **Type-safe functions**: Use action0 through action10 (e.g. action2(...) for 2 arguments) to preserve type arguments.
- **Extensions**: Call .action directly on any Dart function (e.g., myFunction.action).
---
## Type-Safe Variants & Extensions
To ensure complete type safety and optimize static analysis in Dart, the package exposes distinct variants and extension methods corresponding to the number of arguments (from 0 up to 10):
| Variant / Extension | Description |
| --- | --- |
| `action0` | Wraps a 0-argument callback function in a type-safe action. `signature` |
| `action1` | Wraps a 1-argument callback function in a type-safe action. `signature` |
| `action2` | Wraps a 2-argument callback function in a type-safe action. `signature` |
| `action3` | Wraps a 3-argument callback function in a type-safe action. `signature` |
| `action4` | Wraps a 4-argument callback function in a type-safe action. `signature` |
| `action5` | Wraps a 5-argument callback function in a type-safe action. `signature` |
| `action6` | Wraps a 6-argument callback function in a type-safe action. `signature` |
| `action7` | Wraps a 7-argument callback function in a type-safe action. `signature` |
| `action8` | Wraps an 8-argument callback function in a type-safe action. `signature` |
| `action9` | Wraps a 9-argument callback function in a type-safe action. `signature` |
| `action10` | Wraps a 10-argument callback function in a type-safe action. `signature` |
| `ActionExt0` | Extension on a 0-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt1` | Extension on a 1-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt2` | Extension on a 2-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt3` | Extension on a 3-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt4` | Extension on a 4-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt5` | Extension on a 5-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt6` | Extension on a 6-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt7` | Extension on a 7-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt8` | Extension on an 8-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt9` | Extension on a 9-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt10` | Extension on a 10-argument function to wrap it in a type-safe action. `signature` |
Show Full API Signatures & Examples
### action0
Wraps a 0-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
### Example Usage
```dart
final increment = action0(() {
count.value++;
clicks.value++;
});
```
---
### action1
Wraps a 1-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
### Example Usage
```dart
final setName = action1((String newName) {
name.value = newName;
updatedAt.value = DateTime.now();
});
```
---
### action2
Wraps a 2-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
### Example Usage
```dart
final updateProfile = action2((String newName, int newAge) {
name.value = newName;
age.value = newAge;
});
```
---
### action3
Wraps a 3-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
### Example Usage
```dart
final setCoordinates = action3((double lat, double lng, String label) {
latitude.value = lat;
longitude.value = lng;
locationName.value = label;
});
```
---
### action4
Wraps a 4-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
### Example Usage
```dart
final updateUserData = action4((String name, int age, double score, bool active) {
userName.value = name;
userAge.value = age;
userScore.value = score;
userActive.value = active;
});
```
---
### action5
Wraps a 5-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
### Example Usage
```dart
final setConfig = action5((int w, int h, String title, bool dark, double opacity) {
width.value = w;
height.value = h;
appTitle.value = title;
themeDark.value = dark;
bgOpacity.value = opacity;
});
```
---
### action6
Wraps a 6-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
---
### action7
Wraps a 7-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
---
### action8
Wraps an 8-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
---
### action9
Wraps a 9-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
---
### action10
Wraps a 10-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
---
### ActionExt0
Extension on a 0-argument function to wrap it in a type-safe action.
Enables calling .action directly on any 0-argument function to wrap it.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
final count = signal(0);
final clicks = signal(0);
void incrementCount() {
count.value++;
clicks.value++;
}
// Create a batched, untracked action from the function
final increment = incrementCount.action;
void main() {
effect(() => print('Count: ${count.value}, Clicks: ${clicks.value}'));
// Prints: "Count: 0, Clicks: 0"
increment();
// Updates both count and clicks inside a batch.
// Triggers the effect exactly once.
// Prints: "Count: 1, Clicks: 1"
}
```
### Methods
View Methods
##### `R Function() action`
Wraps the 0-argument function in a type-safe action.
---
### ActionExt1
Extension on a 1-argument function to wrap it in a type-safe action.
Enables calling .action directly on any 1-argument function to wrap it.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
final name = signal('Jane');
final clicks = signal(0);
void updateName(String newName) {
name.value = newName;
clicks.value++;
}
// Create a batched, untracked action from the function
final setName = updateName.action;
void main() {
effect(() => print('Name: ${name.value}, Clicks: ${clicks.value}'));
// Prints: "Name: Jane, Clicks: 0"
setName('John');
// Updates both name and clicks inside a batch.
// Triggers the effect exactly once.
// Prints: "Name: John, Clicks: 1"
}
```
### Methods
View Methods
##### `R Function(A) action`
Wraps the 1-argument function in a type-safe action.
---
### ActionExt2
Extension on a 2-argument function to wrap it in a type-safe action.
Enables calling .action directly on any 2-argument function to wrap it.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
final name = signal('Jane');
final age = signal(25);
void updateProfile(String newName, int newAge) {
name.value = newName;
age.value = newAge;
}
// Create a batched, untracked action from the function
final setProfile = updateProfile.action;
void main() {
effect(() => print('Name: ${name.value}, Age: ${age.value}'));
// Prints: "Name: Jane, Age: 25"
setProfile('John', 30);
// Updates both name and age inside a batch.
// Triggers the effect exactly once.
// Prints: "Name: John, Age: 30"
}
```
### Methods
View Methods
##### `R Function(A, B) action`
Wraps the 2-argument function in a type-safe action.
---
### ActionExt3
Extension on a 3-argument function to wrap it in a type-safe action.
Enables calling .action directly on any 3-argument function to wrap it.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
final latitude = signal(0.0);
final longitude = signal(0.0);
final locationName = signal('Unknown');
void setCoordinates(double lat, double lng, String label) {
latitude.value = lat;
longitude.value = lng;
locationName.value = label;
}
// Create a batched, untracked action from the function
final setCoords = setCoordinates.action;
void main() {
effect(() => print('${locationName.value}: (${latitude.value}, ${longitude.value})'));
// Prints: "Unknown: (0.0, 0.0)"
setCoords(37.7749, -122.4194, 'San Francisco');
// Updates latitude, longitude, and locationName inside a batch.
// Triggers the effect exactly once.
// Prints: "San Francisco: (37.7749, -122.4194)"
}
```
### Methods
View Methods
##### `R Function(A, B, C) action`
Wraps the 3-argument function in a type-safe action.
---
### ActionExt4
Extension on a 4-argument function to wrap it in a type-safe action.
### Methods
View Methods
##### `R Function(A, B, C, D) action`
Wraps the 4-argument function in a type-safe action.
---
### ActionExt5
Extension on a 5-argument function to wrap it in a type-safe action.
### Methods
View Methods
##### `R Function(A, B, C, D, E) action`
Wraps the 5-argument function in a type-safe action.
---
### ActionExt6
Extension on a 6-argument function to wrap it in a type-safe action.
### Methods
View Methods
##### `R Function(A, B, C, D, E, F) action`
Wraps the 6-argument function in a type-safe action.
---
### ActionExt7
Extension on a 7-argument function to wrap it in a type-safe action.
### Methods
View Methods
##### `R Function(A, B, C, D, E, F, G) action`
Wraps the 7-argument function in a type-safe action.
---
### ActionExt8
Extension on an 8-argument function to wrap it in a type-safe action.
### Methods
View Methods
##### `R Function(A, B, C, D, E, F, G, H) action`
Wraps the 8-argument function in a type-safe action.
---
### ActionExt9
Extension on a 9-argument function to wrap it in a type-safe action.
### Methods
View Methods
##### `R Function(A, B, C, D, E, F, G, H, I) action`
Wraps the 9-argument function in a type-safe action.
---
### ActionExt10
Extension on a 10-argument function to wrap it in a type-safe action.
### Methods
View Methods
##### `R Function(A, B, C, D, E, F, G, H, I, J) action`
Wraps the 10-argument function in a type-safe action.
---
---
## Page: Batch
Url: https://dartsignals.dev/packages/signals_flutter/core/batch
Description: Combines multiple signal writes into a single update transaction that is flushed only after the callback completes.
---
Combines multiple signal writes into a single update transaction that is flushed only after the callback completes.
Under normal circumstances, writing to a signal immediately notifies all of its active subscribers
(effects and computed signals), which can cause multiple redundant updates or temporary inconsistent/glitchy states
if you are updating several related signals sequentially.
By wrapping your mutations in [batch](/types/batch), notification events are deferred. Subscribed [effect](/types/effect)s and [computed](/types/computed)
signals will only run once at the very end of the batch callback block.
Always use batch when performing multiple state transitions together. This avoids flickering UI, unnecessary
rebuilds, and transient states where some dependencies are updated but others are not.
### Nested Batches
Batches can be nested. Updates are only flushed when the *outermost* batch callback completes.
### Mid-Batch Reads
If you read a mutated signal *inside* the batch callback, or access a computed signal that depends on a
mutated signal, that signal is immediately computed and updated inline to ensure your code always operates
on consistent, up-to-date data. However, other independent signals and effects are still deferred until
the batch finishes.
Parameters:
- **fn**: The callback function containing the signal write operations to be batched.
Returns:
- The value returned by the callback function **fn**.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
void main() {
final name = signal("Jane");
final surname = signal("Doe");
final fullName = computed(() => "${name.value} ${surname.value}");
// Set up an effect that reacts to changes
effect(() => print("Name changed to: ${fullName.value}"));
// Batching mutations ensures the effect runs only once
batch(() {
name.value = "John";
surname.value = "Smith";
});
// Prints: "Name changed to: John Smith" (Only once, not twice!)
}
```
---
## Page: MapSignalMixin
Url: https://dartsignals.dev/packages/signals_flutter/mixins/map
Description: A mixin that adds reactive Map methods and operators directly to a Signal.
---
A mixin that adds reactive Map methods and operators directly to a [Signal](/types/signal).
This mixin delegates all standard **Map** operations (such as mutations like []=, clear,
remove, and lookups like containsKey, isEmpty, keys, values) to the underlying
map value.
Every mutating operation automatically updates the signal and notifies its observers
(by forcing a change notification using force: true).
### Simple Example
```dart
class MyMapSignal extends Signal>
with MapSignalMixin> {
MyMapSignal(super.value);
}
final cart = MyMapSignal({'apple': 1});
// Register an effect reacting to cart changes
effect(() {
print('Cart length: ${cart.length}');
});
// Treating it as a standard Map triggers updates automatically!
cart['banana'] = 3; // Prints: Cart length: 2
cart.remove('apple'); // Prints: Cart length: 1
```
### Methods
View Methods
##### `V? [](Object? key)`
##### `void []=(K key, V value)`
##### `void addAll(Map other)`
##### `void addEntries(Iterable> newEntries)`
##### `Map cast()`
##### `void clear()`
##### `bool containsKey(Object? key)`
##### `bool containsValue(Object? value)`
##### `Iterable> entries`
##### `void forEach(void Function(K key, V value) action)`
##### `bool isEmpty`
##### `bool isNotEmpty`
##### `Iterable keys`
##### `int length`
##### `Map map(MapEntry Function(K key, V value) convert)`
##### `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)`
##### `Iterable values`
##### `Map toMap()`
Snapshot of **MapEntries**
---
## Page: TrackedSignalMixin
Url: https://dartsignals.dev/packages/signals_flutter/mixins/tracked
Description: A mixin that adds tracking for the initial and previous values to a Signal.
---
A mixin that adds tracking for the initial and previous values to a [Signal](/types/signal).
[TrackedSignalMixin](/types/trackedsignalmixin) stores the initialValue (the value the signal had when it was
created or initialized) and the previousValue (the value of the signal right before
the most recent update).
If you are looking for full undo/redo capabilities, use ChangeStackSignalMixin instead.
### Example Usage
```dart
import 'package:signals/signals.dart';
class MyTrackedSignal extends Signal with TrackedSignalMixin {
MyTrackedSignal(super.internalValue);
}
void main() {
final signal = MyTrackedSignal(0);
print('Initial: ${signal.initialValue}'); // Prints: "Initial: 0"
print('Previous: ${signal.previousValue}'); // Prints: "Previous: null"
signal.value = 1;
print('Initial: ${signal.initialValue}'); // Prints: "Initial: 0"
print('Previous: ${signal.previousValue}'); // Prints: "Previous: 0"
signal.value = 2;
print('Initial: ${signal.initialValue}'); // Prints: "Initial: 0"
print('Previous: ${signal.previousValue}'); // Prints: "Previous: 1"
}
```
This mixin only works with values that are immutable or are copied/cloned on mutation.
If the value is mutated directly in-place without re-assigning, initialValue and
previousValue will end up pointing to the same modified instance as the current value.
### Methods
View Methods
##### `T initialValue`
The initial value the signal was created with
##### `T? previousValue`
Get the previous value (if exists)
---
## trackedSignal
Create a signal that stores the initial and previous value
---
## TrackedSignal
A signal that stores the initial and previous value
### Constructors
View Constructors
##### `TrackedSignal(super.value, {TrackedSignalOptions? options, @Deprecated('Use options: TrackedSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: TrackedSignalOptions(name: ...) instead') String? debugLabel})`
A signal that stores the initial and previous value
---
## TrackedSignalOptions
Configuration options for a [TrackedSignal](/types/trackedsignal).
### Constructors
View Constructors
##### `TrackedSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched})`
Creates a new [TrackedSignalOptions](/types/trackedsignaloptions) instance.
### Methods
View Methods
##### `TrackedSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched})`
##### `bool ==(Object other)`
##### `int hashCode`
---
## Page: ValueListenableSignalMixin
Url: https://dartsignals.dev/packages/signals_flutter/mixins/value-listenable-signal-mixin
Description: ValueListenable implementation for ReadonlySignal.
---
**ValueListenable** implementation for [ReadonlySignal](/types/readonlysignal)
### Methods
View Methods
##### `bool runCallbackOnListen`
If true, the callback will be run when the listener is added
##### `void addListener(VoidCallback listener)`
##### `void removeListener(VoidCallback listener)`
---
## Page: SetSignalMixin
Url: https://dartsignals.dev/packages/signals_flutter/mixins/set
Description: A mixin that adds reactive Set methods and operations to a Signal.
---
A mixin that adds reactive Set methods and operations to a [Signal](/types/signal)
holding a **Set** value.
This mixin delegates all standard **Set** operations (such as mutations like add,
remove, addAll, removeAll, retainAll, and clear) to the underlying set,
while ensuring that any reads register a dependency and any mutations
automatically trigger reactive updates.
This mixin only works with signals that have a value type extending Set .
### Example Usage
```dart
import 'package:signals/signals.dart';
class MySetSignal extends Signal>
with IterableSignalMixin>, SetSignalMixin> {
MySetSignal(super.internalValue);
}
void main() {
final numbers = MySetSignal({1, 2, 3});
effect(() {
print('Elements: $numbers, Length: ${numbers.length}');
}); // Prints: "Elements: {1, 2, 3}, Length: 3"
// Adding an element (automatically calls set() and triggers updates)
numbers.add(4); // Prints: "Elements: {1, 2, 3, 4}, Length: 4"
// Removing an element (triggers updates)
numbers.remove(1); // Prints: "Elements: {2, 3, 4}, Length: 3"
}
```
Since mutations on SetSignalMixin notify listeners automatically, you do not
need to assign numbers.value = ... to force updates. Methods like add, addAll,
and remove take care of notification.
### Methods
View Methods
##### `bool add(E value)`
##### `void addAll(Iterable elements)`
##### `Set cast()`
##### `void clear()`
##### `bool containsAll(Iterable other)`
##### `Set difference(Set other)`
##### `Set intersection(Set other)`
##### `E? lookup(Object? object)`
##### `bool remove(Object? value)`
##### `void removeAll(Iterable elements)`
##### `void removeWhere(bool Function(E element) test)`
##### `void retainAll(Iterable elements)`
##### `void retainWhere(bool Function(E element) test)`
##### `Set union(Set other)`
---
## Page: ListSignalMixin
Url: https://dartsignals.dev/packages/signals_flutter/mixins/list
Description: A mixin that adds reactive List methods and operators to a Signal.
---
A mixin that adds reactive List methods and operators to a [Signal](/types/signal)
holding a **List** value.
This mixin delegates all standard **List** operations (such as mutations like add,
remove, insert, sort, and clear, and accessor operators like [] and []=)
to the underlying list, while ensuring that any reads register a dependency
and any mutations automatically trigger reactive updates.
This mixin only works with signals that have a value type extending List .
### Example Usage
```dart
import 'package:signals/signals.dart';
class MyListSignal extends Signal>
with IterableSignalMixin>, ListSignalMixin> {
MyListSignal(super.internalValue);
}
void main() {
final numbers = MyListSignal([1, 2, 3]);
effect(() {
print('Elements: $numbers, Length: ${numbers.length}');
}); // Prints: "Elements: [1, 2, 3], Length: 3"
// Adding an element (automatically calls set() and triggers updates)
numbers.add(4); // Prints: "Elements: [1, 2, 3, 4], Length: 4"
// Modifying an element by index (triggers updates)
numbers[0] = 10; // Prints: "Elements: [10, 2, 3, 4], Length: 4"
}
```
Since mutations on ListSignalMixin notify listeners automatically, you do not
need to assign numbers.value = ... to force updates. Methods like add, addAll,
and operator []= take care of notification.
### Methods
View Methods
##### `List cast()`
##### `first(E val)`
##### `E last`
##### `last(E val)`
##### `length(int value)`
##### `List +(List other)`
##### `E [](int index)`
##### `void []=(int index, E value)`
##### `void add(E value)`
##### `void addAll(Iterable iterable)`
##### `Map asMap()`
##### `void clear()`
##### `Iterable expand(Iterable Function(E element) toElements)`
##### `void fillRange(int start, int end, [E? fillValue])`
##### `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])`
##### `void insert(int index, E element)`
##### `void insertAll(int index, Iterable iterable)`
##### `int lastIndexOf(E element, [int? start])`
##### `int lastIndexWhere(bool Function(E element) test, [int? start])`
##### `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)`
##### `Iterable reversed`
##### `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])`
##### `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])`
---
## Page: ValueNotifierSignalMixin
Url: https://dartsignals.dev/packages/signals_flutter/mixins/value-notifier-signal-mixin
Description: ValueNotifier implementation for Signal.
---
**ValueNotifier** implementation for [Signal](/types/signal)
### Methods
View Methods
##### `bool runCallbackOnListen`
If true, the callback will be run when the listener is added
##### `void addListener(VoidCallback listener)`
##### `void removeListener(VoidCallback listener)`
##### `bool hasListeners`
##### `void notifyListeners()`
---
## Page: SinkSignalMixin
Url: https://dartsignals.dev/packages/signals_flutter/mixins/sink
Description: A mixin that implements the standard Sink interface for a Signal.
---
A mixin that implements the standard **Sink** interface for a [Signal](/types/signal).
This mixin allows you to treat a writable [Signal](/types/signal) as a sink of events, where
adding an element using **add** automatically updates the signal's value and
notifies all reactive listeners. Calling **close** automatically disposes
the signal, freeing up resources and removing all active subscriptions.
This provides excellent compatibility with streams, transformers, or any
APIs that expect a standard Dart **Sink**.
### Example Usage
```dart
import 'package:signals/signals.dart';
class MySinkSignal extends Signal with SinkSignalMixin {
MySinkSignal(super.internalValue);
}
void main() {
final signal = MySinkSignal(0);
effect(() {
print('Signal value changed to: ${signal.value}');
}); // Prints: "Signal value changed to: 0"
// Treat it as a Sink and push elements to it
signal.add(42); // Prints: "Signal value changed to: 42"
signal.add(100); // Prints: "Signal value changed to: 100"
// Dispose the signal when finished
signal.close();
print('Is disposed: ${signal.disposed}'); // Prints: "Is disposed: true"
}
```
Once close is called, the signal is permanently disposed and cannot be reused
or written to anymore. Any subsequent add calls will throw an exception.
### Methods
View Methods
##### `void add(T event)`
##### `void close()`
---
## Page: SignalsMixin
Url: https://dartsignals.dev/packages/signals_flutter/mixins/signals-mixin
Description: A State mixin that automatically handles subscription and cleanup of signals.
---
A State mixin that automatically handles subscription and cleanup of signals
and effects created locally within a **StatefulWidget**.
DEPRECATED : This mixin is deprecated. While fully supported for backward compatibility,
it adds extra stateful widget lifecycle overhead and manual binding.
For superior, self-contained reactivity without mixin overhead, migrate to modern, high-performance APIs:
- Use SignalWidget for stateless reactive widgets.
- Use SignalStatefulWidget for stateful reactive widgets.
- Use SignalBuilder for surgical, localized rebuilding.
### Legacy Usage Example
```dart
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State with SignalsMixin {
late final count = createSignal(0);
late final doubled = createComputed(() => count.value * 2);
@override
void initState() {
super.initState();
createEffect(() {
print('Count: ${count.value}, Doubled: ${doubled.value}');
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text('Count: ${count.value}'),
Text('Doubled: ${doubled.value}'),
ElevatedButton(
onPressed: () => count.value++,
child: const Text('Increment'),
),
],
);
}
}
```
### Modern Migration Example
```dart
// Modern alternative using SignalStatefulWidget:
class CounterWidget extends SignalStatefulWidget {
const CounterWidget({super.key});
@override
State createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State {
final count = signal(0);
late final doubled = computed(() => count.value * 2);
@override
void initState() {
super.initState();
// For non-widget effects, use the standard `effect` function:
effect(() {
print('Count: ${count.value}, Doubled: ${doubled.value}');
});
}
@override
Widget build(BuildContext context) {
// Implicitly tracks both signals and rebuilds on change:
return Column(
children: [
Text('Count: ${count.value}'),
Text('Doubled: ${doubled.value}'),
ElevatedButton(
onPressed: () => count.value++,
child: const Text('Increment'),
),
],
);
}
}
```
### Methods
View Methods
##### `void disposeSignal(int id)`
Dispose and remove signal
##### `FutureSignal createComputedFrom(List> signals, Future Function(List args) fn, {S? initialValue, String? debugLabel, bool lazy = true})`
Async Computed is syntax sugar around [FutureSignal](/types/futuresignal).
_Inspired by [computedFrom](https://ngxtension.netlify.app/utilities/signals/computed-from/) from Angular NgExtension._
computedFrom takes a list of **signals** and a **callback** function to
compute the value of the signal every time one of the **signals** changes.
```dart
final movieId = signal('id');
late final movie = computedFrom(args, ([movieId]) => fetchMovie(args.first));
```
Since all dependencies are passed in as arguments there is no need to worry about calling the signals before any async gaps with await.
##### `FutureSignal createComputedAsync(Future Function() fn, {S? initialValue, String? debugLabel, List> dependencies = const [], bool lazy = true})`
Async Computed is syntax sugar around [FutureSignal](/types/futuresignal).
_Inspired by [computedAsync](https://ngxtension.netlify.app/utilities/signals/computed-async/) from Angular NgExtension._
computedAsync takes a **callback** function to compute the value
of the signal. This callback is converted into a [Computed](/types/computed) signal.
```dart
final movieId = signal('id');
late final movie = computedAsync(() => fetchMovie(movieId()));
```
**It is important that signals are called before any async gaps with await.**
Any signal that is read inside the callback will be tracked as a dependency and the computed signal will be re-evaluated when any of the dependencies change.
##### `FutureSignal createFutureSignal(Future Function() fn, {S? initialValue, String? debugLabel, List> dependencies = const [], bool lazy = true})`
Create a signal from a future
##### `StreamSignal createStreamSignal(Stream Function() callback, {S? initialValue, String? debugLabel, List> dependencies = const [], void Function()? onDone, bool? cancelOnError, bool lazy = true})`
Create a signals from a stream
##### `AsyncSignal createAsyncSignal(AsyncState value, {String? debugLabel})`
Create a signal holding an async value
##### `FlutterSignal createSignal(V val, {String? debugLabel})`
Create a signal and watch for changes
##### `ListSignal createListSignal(List list, {String? debugLabel})`
Create a [ListSignal](/types/listsignal) and watch for changes
##### `SetSignal createSetSignal(Set set, {String? debugLabel})`
Create a [SetSignal](/types/setsignal) and watch for changes
##### `QueueSignal createQueueSignal(Queue queue, {String? debugLabel})`
Create a [QueueSignal](/types/queuesignal) and watch for changes
##### `MapSignal createMapSignal(Map value, {String? debugLabel})`
Create a [MapSignal](/types/mapsignal) and watch for changes
##### `FlutterComputed createComputed(V Function() cb, {String? debugLabel})`
Create a computed and watch for changes
##### `S bindSignal(S val)`
Bind an existing signal and watch for changes
##### `S unbindSignal(S val)`
Unbind an existing signal changes
##### `V watchSignal(S val)`
Watch signal value
##### `V unwatchSignal(S val)`
Unwatch an existing signal value changes
##### `void listenSignal(ReadonlySignal target, void Function() callback, {String? debugLabel})`
Watch signal value
##### `void unlistenSignal(ReadonlySignal target, void Function() callback)`
Stop listening to a signal value
##### `EffectCleanup createEffect(dynamic Function() cb, {String? debugLabel, dynamic Function()? onDispose})`
Create a effect.
Do not call inside the build method.
Calling this method in build() will create a new
effect every render.
##### `void clearSignalsAndEffects()`
Reset all stored signals and effects
##### `void dispose()`
---
## Page: QueueSignalMixin
Url: https://dartsignals.dev/packages/signals_flutter/mixins/queue
Description: A mixin that adds reactive Queue methods and operations to a Signal.
---
A mixin that adds reactive Queue methods and operations to a [Signal](/types/signal)
holding a **Queue** value.
This mixin delegates all standard **Queue** operations (such as mutations like add,
addAll, addFirst, addLast, removeFirst, removeLast, and clear) to the
underlying queue, while ensuring that any reads register a dependency and any
mutations automatically trigger reactive updates.
This mixin only works with signals that have a value type extending Queue .
### Example Usage
```dart
import 'dart:collection';
import 'package:signals/signals.dart';
class MyQueueSignal extends Signal>
with QueueSignalMixin> {
MyQueueSignal(super.internalValue);
}
void main() {
final q = Queue()..add(1);
final signal = MyQueueSignal(q);
effect(() {
print('Queue elements: $signal, Length: ${signal.length}');
}); // Prints: "Queue elements: {1}, Length: 1"
// Adding to the front of the queue (triggers updates)
signal.addFirst(0); // Prints: "Queue elements: {0, 1}, Length: 2"
// Adding to the back of the queue (triggers updates)
signal.addLast(2); // Prints: "Queue elements: {0, 1, 2}, Length: 3"
// Removing from the front of the queue (triggers updates)
final first = signal.removeFirst(); // Prints: "Queue elements: {1, 2}, Length: 2"
}
```
Since mutations on QueueSignalMixin notify listeners automatically, you do not
need to assign signal.value = ... to force updates. Methods like addFirst,
addLast, removeFirst, and removeLast take care of notification.
### Methods
View Methods
##### `void add(T value)`
##### `void addAll(Iterable iterable)`
##### `void addFirst(T value)`
##### `void addLast(T value)`
##### `bool any(bool Function(T element) test)`
##### `Queue cast()`
##### `void clear()`
##### `bool contains(Object? element)`
##### `T elementAt(int index)`
##### `bool every(bool Function(T element) test)`
##### `Iterable expand(Iterable Function(T element) toElements)`
##### `T first`
##### `T firstWhere(bool Function(T element) test, {T Function()? orElse})`
##### `U fold(U initialValue, U Function(U previousValue, T element) combine)`
##### `Iterable followedBy(Iterable other)`
##### `void forEach(void Function(T element) action)`
##### `bool isEmpty`
##### `bool isNotEmpty`
##### `Iterator iterator`
##### `String join([String separator = ""])`
##### `T last`
##### `T lastWhere(bool Function(T element) test, {T Function()? orElse})`
##### `int length`
##### `Iterable map(U Function(T e) toElement)`
##### `T reduce(T Function(T value, T element) combine)`
##### `bool remove(Object? value)`
##### `T removeFirst()`
##### `T removeLast()`
##### `void removeWhere(bool Function(T element) test)`
##### `void retainWhere(bool Function(T element) test)`
##### `T single`
##### `T singleWhere(bool Function(T element) test, {T Function()? orElse})`
##### `Iterable skip(int count)`
##### `Iterable skipWhile(bool Function(T value) test)`
##### `Iterable take(int count)`
##### `Iterable takeWhile(bool Function(T value) test)`
##### `List toList({bool growable = true})`
##### `Set toSet()`
##### `Iterable where(bool Function(T element) test)`
##### `Iterable whereType()`
---
## SignalQueueUtils
Utility extension methods on **Queue** to convert them to [QueueSignal](/types/queuesignal)s.
### Methods
View Methods
##### `QueueSignal toSignal({QueueSignalOptions? options, @Deprecated('Use options: QueueSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: QueueSignalOptions(name: ...) instead') String? debugLabel})`
Convert an existing list to [QueueSignal](/types/queuesignal)
---
## queueSignal
Creates a [QueueSignal](/types/queuesignal) with the given **list** (Queue).
---
## QueueSignalOptions
Configuration options for a [QueueSignal](/types/queuesignal).
### Constructors
View Constructors
##### `QueueSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched, super.equality = const SignalDeepEquality()})`
Creates a new [QueueSignalOptions](/types/queuesignaloptions) instance.
### Methods
View Methods
##### `QueueSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, SignalEquality>? equality})`
##### `bool ==(Object other)`
##### `int hashCode`
---
## QueueSignal
A [Signal](/types/signal) that holds a **Queue**.
### Constructors
View Constructors
##### `QueueSignal(super.value, {QueueSignalOptions? options, @Deprecated('Use options: QueueSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: QueueSignalOptions(name: ...) instead') String? debugLabel})`
Creates a [QueueSignal](/types/queuesignal) with the given **value**.
---
## Page: IterableSignalMixin
Url: https://dartsignals.dev/packages/signals_flutter/mixins/iterable
Description: A mixin that adds reactive Iterable methods and properties to a Signal.
---
A mixin that adds reactive Iterable methods and properties to a [Signal](/types/signal)
holding an **Iterable** value.
This mixin delegates all standard **Iterable** operations (such as length,
first, last, map, where, and any) directly to the underlying
collection, while ensuring that any read operations register a reactive
dependency on the signal.
This mixin only works with signals that have a value type extending Iterable .
### Example Usage
```dart
import 'package:signals/signals.dart';
class MyIterableSignal extends Signal>
with IterableSignalMixin> {
MyIterableSignal(super.internalValue);
}
void main() {
final numbers = MyIterableSignal([1, 2, 3]);
// Set up a reactive effect that prints the list size and first element
effect(() {
print('Size: ${numbers.length}, First: ${numbers.first}');
}); // Prints: "Size: 3, First: 1"
// Update the signal value (triggers the effect)
numbers.value = [10, 20, 30, 40]; // Prints: "Size: 4, First: 10"
}
```
Direct mutation of elements inside the iterable will NOT notify listeners
unless you reassign the value or use a specialized signal class like ListSignal,
SetSignal, or MapSignal which automatically trigger updates when modified.
### 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)`
##### `void forEach(void Function(E element) action)`
##### `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()`
---
## Page: EventSinkSignalMixin
Url: https://dartsignals.dev/packages/signals_flutter/mixins/event-sink
Description: A mixin that implements the standard EventSink interface for a Signal.
---
A mixin that implements the standard **EventSink** interface for a [Signal](/types/signal)
holding an [AsyncState](/types/asyncstate) value.
This mixin is designed specifically to interface with **Stream** consumers or
other asynchronous data producers. It maps **add** to AsyncState.data(...),
**addError** to AsyncState.error(...), and **close** to disposing the signal.
This mixin only works with signals whose value is of type AsyncState .
### Example Usage
```dart
import 'package:signals/signals.dart';
class MyEventSinkSignal extends Signal> with EventSinkSignalMixin {
MyEventSinkSignal(int initialValue) : super(AsyncState.data(initialValue));
}
void main() {
final signal = MyEventSinkSignal(0);
effect(() {
final state = signal.value;
state.map(
data: (val) => print('Data received: $val'),
error: (err, stack) => print('Error occurred: $err'),
loading: () => print('Loading...'),
);
}); // Prints: "Data received: 0"
// Treat it as an EventSink and add data
signal.add(10); // Prints: "Data received: 10"
// Push an error event
signal.addError(Exception('Failure')); // Prints: "Error occurred: Exception: Failure"
// Close/dispose the sink
signal.close();
print('Is disposed: ${signal.disposed}'); // Prints: "Is disposed: true"
}
```
Once close is called, the signal is disposed. Trying to call add or addError
after closing will result in an exception.
### Methods
View Methods
##### `void add(T event)`
##### `void addError(Object error, [StackTrace? stackTrace])`
##### `void close()`
---
## Page: StreamSignalMixin
Url: https://dartsignals.dev/packages/signals_flutter/mixins/stream
Description: A mixin that implements the standard Stream interface for a ReadonlySignal.
---
A mixin that implements the standard **Stream** interface for a [ReadonlySignal](/types/readonlysignal).
This mixin allows you to treat a read-only or writable signal as a standard asynchronous
**Stream**, where updates to the signal's value are pushed as stream events.
This provides out-of-the-box compatibility with the entire asynchronous Dart SDK,
such as stream transformers, await for loops, and Flutter's **StreamBuilder**.
### Example Usage
```dart
import 'package:signals/signals.dart';
class MyStreamSignal extends Signal with StreamSignalMixin {
MyStreamSignal(super.internalValue);
}
void main() async {
final counter = MyStreamSignal(0);
// Standard stream subscription
final subscription = counter.listen((val) {
print('Stream emitted: $val');
}); // Prints: "Stream emitted: 0" (if listened immediately)
counter.value = 1; // Prints: "Stream emitted: 1"
counter.value = 2; // Prints: "Stream emitted: 2"
await subscription.cancel();
}
```
### Flutter StreamBuilder Example
```dart
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
class CounterSignal extends Signal with StreamSignalMixin {
CounterSignal(int value) : super(value);
}
final counter = CounterSignal(0);
Widget build(BuildContext context) {
return StreamBuilder(
stream: counter,
builder: (context, snapshot) {
return Text('Count: ${snapshot.data}');
},
);
}
```
Since StreamSignalMixin creates an internal broadcast StreamController , multiple listeners
can subscribe simultaneously. All subscriptions are closed and resources are released
automatically when the signal is disposed.
### Methods
View Methods
##### `Future any(bool Function(T element) test)`
##### `Stream asBroadcastStream({void Function(StreamSubscription subscription)? onListen, void Function(StreamSubscription subscription)? onCancel})`
##### `Stream asyncExpand(Stream? Function(T event) convert)`
##### `Stream asyncMap(FutureOr Function(T event) convert)`
##### `Stream cast()`
##### `Future contains(Object? needle)`
##### `Stream distinct([bool Function(T previous, T next)? equals])`
##### `Future drain([E? futureValue])`
##### `Future elementAt(int index)`
##### `Future every(bool Function(T element) test)`
##### `Stream expand(Iterable Function(T element) convert)`
##### `Future first`
##### `Future firstWhere(bool Function(T element) test, {T Function()? orElse})`
##### `Future fold(S initialValue, S Function(S previous, T element) combine)`
##### `Future forEach(void Function(T element) action)`
##### `Stream handleError(Function onError, {bool Function(dynamic error)? test})`
##### `bool isBroadcast`
##### `Future isEmpty`
##### `Future join([String separator = ""])`
##### `Future last`
##### `Future lastWhere(bool Function(T element) test, {T Function()? orElse})`
##### `Future length`
##### `StreamSubscription listen(void Function(T event)? onData, {Function? onError, void Function()? onDone, bool? cancelOnError})`
##### `Stream map(S Function(T event) convert)`
##### `Future pipe(StreamConsumer streamConsumer)`
##### `Future reduce(T Function(T previous, T element) combine)`
##### `Future single`
##### `Future singleWhere(bool Function(T element) test, {T Function()? orElse})`
##### `Stream skip(int count)`
##### `Stream skipWhile(bool Function(T element) test)`
##### `Stream take(int count)`
##### `Stream takeWhile(bool Function(T element) test)`
##### `Stream timeout(Duration timeLimit, {void Function(EventSink sink)? onTimeout})`
##### `Future> toList()`
##### `Future> toSet()`
##### `Stream transform(StreamTransformer streamTransformer)`
##### `Stream where(bool Function(T event) test)`
---
## Page: ChangeStackSignalMixin
Url: https://dartsignals.dev/packages/signals_flutter/mixins/change-stack
Description: A mixin that adds undo, redo, and state history replay capabilities to a Signal.
---
A mixin that adds undo, redo, and state history replay capabilities to a [Signal](/types/signal).
[ChangeStackSignalMixin](/types/changestacksignalmixin) keeps track of past states of a signal's value in a double-ended
queue, allowing you to easily go back to previous values using **undo** and go forward to subsequent
values using **redo**. You can inspect if undo or redo are available via **canUndo** and **canRedo**.
You can also set a **limit** on the maximum size of the history stack, preventing memory leak
issues in long-running scenarios.
If you only need access to the initial and immediate previous values of a signal (without a full
history stack or undo/redo mechanisms), use the lightweight TrackedSignalMixin instead.
### Example Usage
```dart
import 'package:signals/signals.dart';
class HistorySignal extends Signal with ChangeStackSignalMixin {
HistorySignal(super.internalValue);
}
void main() {
final counter = HistorySignal(0);
counter.limit = 5; // Cap history stack to 5 items
counter.value = 1;
counter.value = 2;
counter.value = 3;
print('Current: ${counter.value}'); // Prints: "Current: 3"
print('Can Undo: ${counter.canUndo}'); // Prints: "Can Undo: true"
// Undo last change
counter.undo();
print('Undone: ${counter.value}'); // Prints: "Undone: 2"
// Undo once more
counter.undo();
print('Undone: ${counter.value}'); // Prints: "Undone: 1"
// Redo the previous undo
counter.redo();
print('Redone: ${counter.value}'); // Prints: "Redone: 2"
}
```
This mixin only works with values that are immutable or are copied/cloned when changed.
If you mutate an object in-place directly without replacing the value using set or the .value
setter, the history queue will store references to the same mutated object, and undo/redo
will appear to do nothing.
### Properties
View Properties
##### `int? limit`
Max values to keep in history
### Methods
View Methods
##### `Iterable> history`
List of changes in the history
##### `Iterable> redos`
List of changes in the redo stack
##### `bool canRedo`
Can redo the previous change
##### `bool canUndo`
Can undo the previous change
##### `bool set(T val, {bool force = false})`
##### `void redo()`
Redo Previous Undo
##### `void undo()`
Undo Last Change
##### `void clear()`
Clear the history for redo and undo
##### `void clearUndo()`
Clear undo stack
##### `void clearRedo()`
Clear redo stack
---
## Page: FlutterComputed
Url: https://dartsignals.dev/packages/signals_flutter/signals/flutter-computed
Description: A read-only reactive signal whose value is automatically computed and cached.
---
A read-only reactive signal whose value is automatically computed and cached
based on other signals it depends on.
FlutterComputed extends the core Computed signal and implements Flutter's
**ValueListenable**, making it seamlessly compatible with native Flutter widgets
(like ValueListenableBuilder) and other ecosystem packages.
### Example Usage
```dart
final count = signal(2);
final doubled = computed(() => count.value * 2);
print(doubled.value); // 4
count.value = 5;
print(doubled.value); // 10
```
### Custom Signals
To create a custom signal that behaves like a computed value and implements **ValueListenable**,
you can extend FlutterComputed directly or mix in ValueListenableSignalMixin.
### Constructors
View Constructors
##### `FlutterComputed(super.internalValue, {super.options, @Deprecated('Use options: ComputedOptions(autoDispose: ...) instead') super.autoDispose, @Deprecated('Use options: ComputedOptions(name: ...) instead') super.debugLabel, this.runCallbackOnListen = false})`
Create a new signal that is computed based on the values of other signals.
The returned computed signal is read-only, and its value is automatically
updated when any signals accessed from within the callback function change.
### Properties
View Properties
##### `bool runCallbackOnListen`
---
## Page: Ticker Signal
Url: https://dartsignals.dev/packages/signals_flutter/signals/ticker-signal
Description: Ticker signal used to drive animations and can create animation controllers.
---
## tickerSignal
Ticker signal used to drive animations and can create animation controllers
```dart
void main() {
final ticker = tickerSignal(); // could be a global
final controller = ticker.toAnimationController(); // can be local or global
final curve = CurvedAnimation(parent: controller, curve: Curves.easeOut); // can be used outside of widget tree
final alpha = IntTween(begin: 0, end: 255).animate(curve);
...
final alphaSignal = alpha.toSignal(); // can be converted to a signal
}
```
---
## TickerSignal
Ticker signal used to drive animations and can create animation controllers
```dart
void main() {
final ticker = TickerSignal(); // could be a global
final controller = ticker.toAnimationController(); // can be local or global
final curve = CurvedAnimation(parent: controller, curve: Curves.easeOut); // can be used outside of widget tree
final alpha = IntTween(begin: 0, end: 255).animate(curve);
...
final alphaSignal = alpha.toSignal(); // can be converted to a signal
}
```
### Constructors
View Constructors
##### `TickerSignal({Duration? initialDuration, super.options, @Deprecated('Use options: SignalOptions(name: ...) instead') super.debugLabel})`
Ticker signal used to drive animations and can create animation controllers
```dart
void main() {
final ticker = TickerSignal(); // could be a global
final controller = ticker.toAnimationController(); // can be local or global
final curve = CurvedAnimation(parent: controller, curve: Curves.easeOut); // can be used outside of widget tree
final alpha = IntTween(begin: 0, end: 255).animate(curve);
...
final alphaSignal = alpha.toSignal(); // can be converted to a signal
}
```
### Methods
View Methods
##### `Ticker createTicker(TickerCallback onTick)`
##### `void dispose()`
##### `AnimationController toAnimationController({double? value, Duration? duration, Duration? reverseDuration, String? debugLabel, double lowerBound = 0.0, double upperBound = 1.0, AnimationBehavior animationBehavior = AnimationBehavior.normal})`
Create a new **AnimationController**
---
## Page: FlutterSignal
Url: https://dartsignals.dev/packages/signals_flutter/signals/flutter-signal
Description: A mutable, reactive state cell that exposes both standard Signals and Flutter.
---
A mutable, reactive state cell that exposes both standard Signals and Flutter
**ValueNotifier** interfaces.
FlutterSignal extends the core Signal class and mixes in ValueNotifierSignalMixin,
making it completely interoperable with Flutter's build pipelines and widgets that expect
a **ValueNotifier** or **ValueListenable**.
### Example Usage
```dart
final count = signal(0);
// Interoperates with native ValueListenable listeners:
count.addListener(() {
print('ValueNotifier update: ${count.value}');
});
count.value++; // Mutates the signal and triggers both Signals and ValueNotifier listeners.
```
### Constructors
View Constructors
##### `FlutterSignal(super.internalValue, {super.options, @Deprecated('Use options: SignalOptions(autoDispose: ...) instead') super.autoDispose, @Deprecated('Use options: SignalOptions(name: ...) instead') super.debugLabel, this.runCallbackOnListen = false})`
Simple writeable signal.
```dart
final count = signal(0);
print(count.value); // 0
count.value++;
print(count.value); // 1
```
##### `FlutterSignal.lazy({super.options, @Deprecated('Use options: SignalOptions(autoDispose: ...) instead') super.autoDispose, @Deprecated('Use options: SignalOptions(name: ...) instead') super.debugLabel, this.runCallbackOnListen = false})`
Lazy signal that can be created with type T that
the value will be assigned later.
```dart
final db = FlutterSignal.lazy();
...
db.value = DatabaseConnect(...);
```
### Properties
View Properties
##### `bool runCallbackOnListen`
---
## Page: Flutter Readonly Signal
Url: https://dartsignals.dev/packages/signals_flutter/signals/flutter-readonly-signal
Description: Signal extensions for watching value changes on BuildContext.
---
## FlutterReadonlySignalUtils
Signal extensions for watching value changes on BuildContext.
### Methods
View Methods
##### `T watch(BuildContext context, {String? debugLabel})`
Rebuild the **Element** that the current signal is inside of.
##### `void unwatch(BuildContext context)`
Stop subscriptions to updates on a signal for watchers.
---
## FlutterReadonlySignal
Simple writeable single
---
## Page: AsyncState
Url: https://dartsignals.dev/packages/signals_flutter/async/state
Description: A sealed union representing the lifecycle states of an asynchronous operation.
---
A sealed union representing the lifecycle states of an asynchronous operation.
AsyncState is commonly wrapped by AsyncSignal or returned by asynchronous
computed signals (computedAsync, computedFrom) to model loading, success (data),
and error outcomes.
### State Hierarchy
The states are modeled as a robust hierarchy of immutable types:
- [AsyncLoading](/types/asyncloading): Pure loading state with no pre-existing data.
- [AsyncData](/types/asyncdata): Success state holding a resolved value of type T.
- [AsyncDataRefreshing](/types/asyncdatarefreshing): Refreshing in the background (holding historical data).
- [AsyncDataReloading](/types/asyncdatareloading): Reloading (holding historical data).
- [AsyncError](/types/asyncerror): Failure state holding an error and optional stack trace.
- [AsyncErrorRefreshing](/types/asyncerrorrefreshing): Refreshing in the background (holding historical error).
- [AsyncErrorReloading](/types/asyncerrorreloading): Reloading (holding historical error).
### Pattern Matching & Switch Expressions
Standard Dart switch expressions provide type-safe branching across all states:
> [!IMPORTANT]
> **Branch Matching Order & Existing Value Preservation:**
> Since reloading and refreshing states (e.g., AsyncDataRefreshing, AsyncDataReloading) implement both AsyncData and AsyncLoading, matching on AsyncLoading **first** will prematurely swallow existing data!
> Always place AsyncData and AsyncError branches **before** AsyncLoading to ensure pre-existing data or error states are successfully rendered during refreshes:
```dart
final value = switch (state) {
AsyncDataRefreshing r => 'Refreshing with value: ${r.value}',
AsyncDataReloading r => 'Reloading with value: ${r.value}',
AsyncData data => 'Stable value: ${data.value}',
AsyncErrorRefreshing r => 'Refreshing error: ${r.error}',
AsyncErrorReloading r => 'Reloading error: ${r.error}',
AsyncError error => 'Stable error: ${error.error}',
AsyncLoading() => 'Pure Loading State (no prior data)',
};
```
### Standard Branching Methods (map and maybeMap)
If you prefer standard callbacks over switch expressions, use map or maybeMap:
```dart
state.map(
data: (value) => 'Value: $value',
error: (error, stackTrace) => 'Error: $error',
loading: () => 'Loading...',
);
```
### Constructors
View Constructors
##### `AsyncState()`
##### `AsyncState.dataReloading(T data)`
Create a state with a value that is reloading
##### `AsyncState.dataRefreshing(T data)`
Create a state with a value that is refreshing
##### `AsyncState.data(T data)`
Create a state with a value
##### `AsyncState.errorReloading(Object error, [StackTrace? stackTrace])`
Create a state with an error that is reloading
##### `AsyncState.errorRefreshing(Object error, [StackTrace? stackTrace])`
Create a state with an error that is refreshing
##### `AsyncState.error(Object error, [StackTrace? stackTrace])`
Create a state with an error
##### `AsyncState.loading()`
Create a loading state
### Methods
View Methods
##### `bool hasValue`
Returns true if the state has a value
##### `bool hasError`
Returns true if the state has an error
##### `bool isLoading`
Check if the state is a loading state
##### `bool isRefreshing`
Returns true if the state is refreshing with a loading flag,
has a value or error and is not the loading state
##### `bool isReloading`
Returns true if the state is reloading with having a value or error,
and is the loading state
##### `T requireValue`
Force unwrap the value of the state.
This will throw an error if the state does not have a value.
##### `T? value`
Returns the value of the state.
##### `Object? error`
Returns the error of the state.
##### `StackTrace? stackTrace`
Returns the stack trace of the state.
##### `E map({required AsyncDataBuilder data, required AsyncErrorBuilder error, required AsyncStateBuilder loading, AsyncStateBuilder? reloading, AsyncStateBuilder? refreshing})`
Map the state to a value.
```dart
final signal = StreamSignal();
signal.value.map(
data: (value) => 'Value: $value',
error: (error, stackTrace) => 'Error: $error',
loading: () => 'Loading...',
);
```
The error Function below can be one of two types:
- (dynamic) -> FutureOr
- (dynamic, StackTrace) -> FutureOr
##### `E maybeMap({AsyncDataBuilder? data, AsyncErrorBuilder? error, AsyncStateBuilder? loading, AsyncStateBuilder? reloading, AsyncStateBuilder? refreshing, required AsyncStateBuilder orElse})`
Map the state to a value with optional or else.
```dart
final signal = StreamSignal();
signal.value.maybeMap(
data: (value) => 'Value: $value',
orElse: () => 'Loading...',
);
```
The error Function below can be one of two types:
- (dynamic) -> FutureOr
- (dynamic, StackTrace) -> FutureOr
##### `bool ==(covariant AsyncState other)`
##### `int hashCode`
---
## AsyncErrorRefreshing
A loading state with an error. Signal the query conditions that led to the error
has remained the same and is being refreshed.
### Constructors
View Constructors
##### `AsyncErrorRefreshing(super.error, super.stackTrace)`
Create a state with an error that is refreshing
### Methods
View Methods
##### `bool isLoading`
##### `bool isRefreshing`
##### `bool ==(covariant AsyncState other)`
---
## AsyncDataRefreshing
A loading state with a value. Signals the query conditions that led to the data
has remained the same and is being refreshed
### Constructors
View Constructors
##### `AsyncDataRefreshing(super.data)`
Create a state with a value that is refreshing
### Methods
View Methods
##### `bool isLoading`
##### `bool isRefreshing`
##### `bool ==(covariant AsyncState other)`
---
## LinkedSignalPreviousState
Previous state of a [LinkedSignal](/types/linkedsignal), containing both the **source** value
and the computed **value** from that source version.
### Constructors
View Constructors
##### `LinkedSignalPreviousState(this.source, this.value)`
Creates a [LinkedSignalPreviousState](/types/linkedsignalpreviousstate).
### Properties
View Properties
##### `S source`
The source value.
##### `T value`
The computed value.
---
## AsyncErrorReloading
A loading state with an error. Signal the query conditions that led to the error
has changed and is being reloaded.
### Constructors
View Constructors
##### `AsyncErrorReloading(super.error, super.stackTrace)`
Create a state with an error that is reloading
### Methods
View Methods
##### `bool isLoading`
##### `bool isReloading`
##### `bool ==(covariant AsyncState other)`
---
## AsyncDataReloading
A loading state with a value. Signals the query conditions that led to the data
has changed and is being reloaded.
### Constructors
View Constructors
##### `AsyncDataReloading(super.data)`
Create a state with a value that is reloading
### Methods
View Methods
##### `bool isLoading`
##### `bool isReloading`
##### `bool ==(covariant AsyncState other)`
---
## AsyncLoading
State for an [AsyncState](/types/asyncstate) with a loading state
### Constructors
View Constructors
##### `AsyncLoading()`
State for an [AsyncState](/types/asyncstate) with a loading state
### Methods
View Methods
##### `bool hasValue`
##### `bool hasError`
##### `T? value`
##### `bool isLoading`
##### `bool isRefreshing`
##### `bool isReloading`
##### `T requireValue`
##### `Object? error`
##### `StackTrace? stackTrace`
##### `bool ==(covariant AsyncState other)`
##### `int hashCode`
---
## AsyncError
State for an [AsyncState](/types/asyncstate) with an error
### Constructors
View Constructors
##### `AsyncError(this.error, this.stackTrace)`
State for an [AsyncState](/types/asyncstate) with an error
### Properties
View Properties
##### `Object error`
##### `StackTrace stackTrace`
### Methods
View Methods
##### `bool hasValue`
##### `bool hasError`
##### `T? value`
##### `bool isLoading`
##### `bool isRefreshing`
##### `bool isReloading`
##### `T requireValue`
##### `bool ==(covariant AsyncState other)`
##### `int hashCode`
---
## AsyncData
State for an [AsyncState](/types/asyncstate) with a value
### Constructors
View Constructors
##### `AsyncData(T data)`
State for an [AsyncState](/types/asyncstate) with a value
### Properties
View Properties
##### `T value`
### Methods
View Methods
##### `bool hasValue`
##### `bool hasError`
##### `bool isLoading`
##### `bool isRefreshing`
##### `bool isReloading`
##### `T requireValue`
##### `Object? error`
##### `StackTrace? stackTrace`
##### `bool ==(covariant AsyncState other)`
##### `int hashCode`
---
## AsyncSignalState
Extensions for [Signal>]
### Methods
View Methods
##### `Computed> selectData(R Function(T data) selector)`
Select from data when available, preserving async state
---
## Page: FutureSignal
Url: https://dartsignals.dev/packages/signals_flutter/async/future
Description: Future signals wrap a standard asynchronous Future and bridge it into the reactive state framework, exposing its lifecycle and value as a reactive ...
---
Future signals wrap a standard asynchronous **Future** and bridge it into the reactive state framework, exposing its lifecycle and value as a reactive [AsyncState](/types/asyncstate).
You can construct a future signal via the helper function [futureSignal](/types/futuresignal) or by calling the .toSignal() extension method on any standard **Future**.
### 1. Basic Async Fetching
```dart
final s = futureSignal(() async {
final data = await fetchUserData(123);
return data;
});
```
Or via the extension:
```dart
final s = fetchUserData(123).toSignal();
```
### 2. Consuming and Pattern Matching AsyncState
Reading .value on a [FutureSignal](/types/futuresignal) returns an [AsyncState](/types/asyncstate) object. You can safely pattern-match or map this state to reactively build your user interface or perform side-effects:
```dart
effect(() {
s.value.map(
data: (user) => print('User fetched successfully: ${user.name}'),
error: (err, stack) => print('Failed to fetch user: $err'),
loading: () => print('Loading user...'),
);
});
```
### 3. Reset, Refresh, and Reload
- **reset()**: Reverts the signal back to its initial/loading state.
- **refresh()**: Triggers a new evaluation of the future while maintaining the current data in the meantime (sets isLoading to true but does not discard existing data/error).
- **reload()**: Discards current state, sets the signal to AsyncLoading, and executes a fresh evaluation of the future.
```dart
final s = futureSignal(() => fetchConfig());
s.refresh(); // Triggers reload under the hood
```
### 4. Reactive Dependencies
Any reactive signals read *synchronously* inside the future callback are registered as dependencies. When they mutate, the future signal automatically invalidates and schedules a fresh fetch.
```dart
final userId = signal(123);
final userProfile = futureSignal(() async {
// Subscribes to userId! Mutating userId automatically re-runs this future.
final currentId = userId.value;
return fetchUserProfile(currentId);
});
```
If you need to track dependencies across an asynchronous gap (i.e. reading a signal's value after an await), pass them explicitly in the dependencies list inside AsyncSignalOptions or the constructor to guarantee they are properly subscribed.
### Constructors
View Constructors
##### `FutureSignal(Future Function() fn, {AsyncSignalOptions? options, @Deprecated('Use options: AsyncSignalOptions(initialValue: ...) instead') T? initialValue, @Deprecated('Use options: AsyncSignalOptions(dependencies: ...) instead') List>? dependencies, @Deprecated('Use options: AsyncSignalOptions(lazy: ...) instead') bool? lazy, @Deprecated('Use options: AsyncSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: AsyncSignalOptions(name: ...) instead') String? debugLabel})`
Future signals can be created by extension or method.
### futureSignal
```dart
final s = futureSignal(() async => 1);
```
### toSignal()
```dart
final s = Future(() => 1).toSignal();
```
## .value, .peek()
Returns [AsyncState](/dart/async/state) for the value and can handle the various states.
The value getter returns the value of the future if it completed successfully.
> .peek() can also be used to not subscribe in an effect
```dart
final s = futureSignal(() => Future(() => 1));
final value = s.value.value; // 1 or null
```
## .reset()
The reset method resets the future to its initial state to recall on the next evaluation.
```dart
final s = futureSignal(() => Future(() => 1));
s.reset();
```
## .refresh()
Refresh the future value by setting isLoading to true, but maintain the current state (AsyncData, AsyncLoading, AsyncError).
```dart
final s = futureSignal(() => Future(() => 1));
s.refresh();
print(s.value.isLoading); // true
```
## .reload()
Reload the future value by setting the state to AsyncLoading and pass in the value or error as data.
```dart
final s = futureSignal(() => Future(() => 1));
s.reload();
print(s.value is AsyncLoading); // true
```
## Dependencies
By default the callback will be called once and the future will be cached unless a signal is read in the callback.
```dart
final count = signal(0);
final s = futureSignal(() async => count.value);
await s.future; // 0
count.value = 1;
await s.future; // 1
```
If there are signals that need to be tracked across an async gap then use the dependencies when creating the futureSignal to [reset](#.reset()) every time any signal in the dependency array changes.
```dart
final count = signal(0);
final s = futureSignal(
() async => count.value,
dependencies: [count],
);
s.value; // state with count 0
count.value = 1; // resets the future
s.value; // state with count 1
```
### Properties
View Properties
##### `List> dependencies`
List of dependencies to recompute the future
### Methods
View Methods
##### `void dispose()`
##### `void reset([AsyncState? value])`
##### `void init()`
##### `AsyncState value`
##### `Future reload()`
##### `Future refresh()`
---
## futureSignal
Future signals can be created by extension or method.
### futureSignal
```dart
final s = futureSignal(() async => 1);
```
### toSignal()
```dart
final s = Future(() => 1).toSignal();
```
## .value, .peek()
Returns [AsyncState](/dart/async/state) for the value and can handle the various states.
The value getter returns the value of the future if it completed successfully.
> .peek() can also be used to not subscribe in an effect
```dart
final s = futureSignal(() => Future(() => 1));
final value = s.value.value; // 1 or null
```
## .reset()
The reset method resets the future to its initial state to recall on the next evaluation.
```dart
final s = futureSignal(() => Future(() => 1));
s.reset();
```
## .refresh()
Refresh the future value by setting isLoading to true, but maintain the current state (AsyncData, AsyncLoading, AsyncError).
```dart
final s = futureSignal(() => Future(() => 1));
s.refresh();
print(s.value.isLoading); // true
```
## .reload()
Reload the future value by setting the state to AsyncLoading and pass in the value or error as data.
```dart
final s = futureSignal(() => Future(() => 1));
s.reload();
print(s.value is AsyncLoading); // true
```
## Dependencies
By default the callback will be called once and the future will be cached unless a signal is read in the callback.
```dart
final count = signal(0);
final s = futureSignal(() async => count.value);
await s.future; // 0
count.value = 1;
await s.future; // 1
```
If there are signals that need to be tracked across an async gap then use the dependencies when creating the futureSignal to [reset](#.reset()) every time any signal in the dependency array changes.
```dart
final count = signal(0);
final s = futureSignal(
() async => count.value,
dependencies: [count],
);
s.value; // state with count 0
count.value = 1; // resets the future
s.value; // state with count 1
```
---
## Page: AsyncSignal
Url: https://dartsignals.dev/packages/signals_flutter/async/signal
Description: A highly powerful Signal specifically designed for manual, imperative asynchronous state management.
---
A highly powerful [Signal](/types/signal) specifically designed for manual, imperative asynchronous state management.
Unlike declarative reactive signals like [futureSignal](/types/futuresignal) or [streamSignal](/types/streamsignal) (which automatically wrap and listen
to an existing Future or Stream), AsyncSignal gives you full manual/imperative control over pushing
async states ([AsyncState.loading](/types/asyncstate#loading), [AsyncState.data](/types/asyncstate#data), and [AsyncState.error](/types/asyncstate#error)) into the reactive graph.
This is the perfect state primitive for building custom repositories, handling manual user action triggers
(e.g., submitting a registration form, calling an API on button click), or bridging low-level callback-based APIs
into reactive states.
### 1. Imperative State Mutations
You can update the state of the signal directly using specialized mutation helpers:
- setLoading() puts the signal into a clean AsyncLoading state.
- setValue(T data) pushes a new AsyncData state containing the data.
- setError(Object error, [StackTrace? stackTrace]) transitions the signal to an AsyncError state.
```dart
final authState = asyncSignal(AsyncState.loading());
Future login(String email, String password) async {
try {
authState.setLoading(); // Set UI to loading state
final user = await authApi.signIn(email, password);
authState.setValue(user); // Push success data
} catch (err, stack) {
authState.setError(err, stack); // Push error state
}
}
```
### 2. Awaiting Async Completion via .future
An outstanding capability of AsyncSignal is its built-in .future getter. Any part of your code can await
this future. It returns a standard Future that resolves when the signal next receives a data value,
or throws if the signal next receives an error state.
```dart
final loginSignal = asyncSignal(AsyncState.loading());
// Task A: Start background operation
Future.delayed(Duration(seconds: 2), () {
loginSignal.setValue(User(name: 'Charlie'));
});
// Task B: Wait for the signal to resolve!
final user = await loginSignal.future; // Suspends execution until Task A completes!
print(user.name); // 'Charlie'
```
### 3. Rendering in Flutter using Watch and AsyncState Pattern matching
In your Flutter widgets, you can seamlessly watch the signal and use Dart's native pattern matching
on [AsyncState](/types/asyncstate) to render different widgets corresponding to the current asynchronous lifecycle:
```dart
Widget build(BuildContext context) {
final state = authState.watch(context);
return state.map(
data: (user) => HomeScreen(user: user),
error: (error, stackTrace) => ErrorWidget(error),
loading: () => const CircularProgressIndicator(),
);
}
```
### 4. Bridging callback/event-driven systems via EventSink
AsyncSignal implements Dart's standard **EventSink** interface. This allows it to act directly as an event sink
for streams, websockets, or callback listeners:
```dart
final messageLog = asyncSignal(AsyncState.loading());
final chatStream = webSocket.stream.map((event) => event.toString());
// Automatically push all incoming messages and errors from the stream into the signal:
chatStream.listen(
(msg) => messageLog.add(msg),
onError: (err) => messageLog.addError(err),
onDone: () => messageLog.close(),
);
```
Favor AsyncSignal when you need manual, callback-driven, or button-press-triggered state mutations.
For auto-triggering, declarative, or read-only asynchronous data dependencies (like pulling data when an ID changes),
favor futureSignal or computedAsync instead.
### Constructors
View Constructors
##### `AsyncSignal(super.value, {super.options})`
A [Signal](/types/signal) that stores value in [AsyncState](/types/asyncstate)
### Methods
View Methods
##### `Future future`
The future of the signal completer
##### `bool isCompleted`
Returns true if the signal is completed an error or data
##### `void setError(Object error, [StackTrace? stackTrace])`
Set the error with optional stackTrace to [AsyncError](/types/asyncerror)
##### `void setValue(T value)`
Set the value to [AsyncData](/types/asyncdata)
##### `void setLoading([AsyncState? state])`
Set the loading state to [AsyncLoading](/types/asyncloading)
##### `void reset([AsyncState? value])`
Reset the signal to the initial value
##### `void init()`
Initialize the signal
##### `Future reload()`
Reload the future
##### `Future refresh()`
Refresh the future
##### `AsyncState value`
##### `T requireValue`
Returns the value of the signal
---
## asyncSignal
Helper function to create an [AsyncSignal](/types/asyncsignal) initialized with an [AsyncState](/types/asyncstate).
### Example
```dart
// Create an AsyncSignal initialized to a loading state
final counter = asyncSignal(AsyncState.loading());
// Create an AsyncSignal initialized with initial data
final status = asyncSignal(AsyncState.data('Active'));
```
---
## TimerSignal
Emit recurring **TimerSignalEvent** aka [AsyncSignal](/types/asyncsignal)
### Constructors
View Constructors
##### `TimerSignal({required this.every, super.cancelOnError, AsyncSignalOptions? options, @Deprecated('Use options: AsyncSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: AsyncSignalOptions(name: ...) instead') String? debugLabel})`
Emit recurring **TimerSignalEvent** aka [AsyncSignal](/types/asyncsignal)
### Properties
View Properties
##### `Duration every`
Trigger an **TimerSignalEvent** every duration
---
## AsyncSignalOptions
Configuration options for an [AsyncSignal](/types/asyncsignal).
### Constructors
View Constructors
##### `AsyncSignalOptions({this.initialValue, this.dependencies = const [], this.onDone, this.cancelOnError, this.lazy = true, super.name, super.autoDispose, super.watched, super.unwatched})`
Creates a new [AsyncSignalOptions](/types/asyncsignaloptions) instance.
### Properties
View Properties
##### `T? initialValue`
The initial value of the async signal.
##### `List> dependencies`
The list of dependencies to watch/listen to.
##### `void Function()? onDone`
Optional function called when a stream completes.
##### `bool? cancelOnError`
Whether to cancel the stream subscription on error.
##### `bool lazy`
Whether the execution is lazy.
### Methods
View Methods
##### `AsyncSignalOptions copyWith({T? initialValue, List>? dependencies, void Function()? onDone, bool? cancelOnError, bool? lazy, bool? autoDispose, String? name, void Function()? watched, void Function()? unwatched})`
Creates a copy of this options with custom overrides.
##### `bool ==(Object other)`
##### `int hashCode`
---
## TimerSignalDurationUtils
Expose Duration as a [TimerSignal](/types/timersignal)
### Methods
View Methods
##### `TimerSignal toSignal({bool? cancelOnError, AsyncSignalOptions? options, @Deprecated('Use options: AsyncSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: AsyncSignalOptions(name: ...) instead') String? debugLabel})`
Expose Duration as a [TimerSignal](/types/timersignal)
---
## timerSignal
Create a [TimerSignal](/types/timersignal)
---
## Page: Computed
Url: https://dartsignals.dev/packages/signals_flutter/async/computed
Description: Create an asynchronous computed signal with implicit dependency tracking.
---
## computedAsync
Create an asynchronous computed signal with implicit dependency tracking.
computedAsync takes an asynchronous **callback** function to compute the value
of the signal. Any signal read *synchronously* inside the callback is automatically
tracked as a dependency, and the computed signal is re-evaluated when any of those
dependencies change.
### ⚠️ The Async Gap Gotcha
Because Dart yields execution at every await expression, the reactive context that
automatically tracks reads is lost after an asynchronous gap.
**Rule:** All signal values MUST be read synchronously *before* the first await statement.
#### ❌ Incorrect Pattern (Signal read after await is NOT tracked):
```dart
final movie = computedAsync(() async {
await Future.delayed(Duration(milliseconds: 100));
// BUG: movieId is read AFTER an async gap.
// Changes to movieId will NOT re-evaluate this computedAsync!
return fetchMovie(movieId.value);
});
```
#### Correct Pattern (Read dependencies synchronously first):
```dart
final movie = computedAsync(() async {
// Capture all dependency values synchronously at the start!
final currentId = movieId.value;
await Future.delayed(Duration(milliseconds: 100));
// Use the captured local variable after the async gap
return fetchMovie(currentId);
});
```
### Advanced Example: Search Query with Debouncing
```dart
final searchQuery = signal('');
final searchResults = computedAsync(() async {
// Capture dependency synchronously
final query = searchQuery.value;
if (query.isEmpty) return [];
// Debounce: Wait 300ms before making the API request
await Future.delayed(Duration(milliseconds: 300));
return performSearchApiCall(query);
});
```
---
## computedFrom
Create an asynchronous computed signal by explicitly declaring its dependencies.
computedFrom takes a list of **signals** and a **callback** function to compute
the value of the signal every time one of the declared **signals** changes.
Unlike [computedAsync](/types/computedasync), which tracks dependencies implicitly, computedFrom is
immune to the **Async Gap Gotcha** because all tracking is declared upfront.
### Why use computedFrom?
When writing asynchronous code, Dart yields control at every await keyword.
Implicit tracking (in [computedAsync](/types/computedasync) or [computed](/types/computed)) cannot track reads that happen
*after* an asynchronous gap because the active reactive reader context is lost.
computedFrom solves this by:
1. Subscribing to the list of input **signals** synchronously.
2. Reading their latest values synchronously.
3. Passing those resolved values into your callback as an ordered list of arguments.
### Example: Fetching user details when an ID signal changes
```dart
final userId = signal(123);
// The callback receives the current values of the declared signals
final userProfile = computedFrom([userId], (args) async {
final currentId = args.first; // Type-safe list of dependencies
return fetchUserProfileFromServer(currentId);
});
// userProfile is a FutureSignal which can be pattern-matched
effect(() {
userProfile.value.map(
data: (profile) => print('Loaded profile: ${profile.name}'),
error: (err, stack) => print('Error: $err'),
loading: () => print('Fetching profile...'),
);
});
// Updating userId automatically triggers a new asynchronous fetch
userId.value = 456;
```
---
## Page: Connect
Url: https://dartsignals.dev/packages/signals_flutter/async/connect
Description: A highly powerful connector utility that allows you to dynamically stream and pipe multiple asynchronous streams directly into a single reactive Si...
---
A highly powerful connector utility that allows you to dynamically stream and pipe multiple asynchronous streams directly into a single reactive [Signal](/types/signal).
The concept is inspired by **Angular Signals** integration with RxJS streams.
Start with an existing mutable [Signal](/types/signal) and call connect(signal) to create a connector instance.
### 1. Chaining Streams
You can bind multiple streams to feed the same destination signal. The connector will handle the subscription management for all streams seamlessly.
```dart
final counter = signal(0);
final connector = connect(counter);
final fastStream = Stream.periodic(Duration(seconds: 1), (i) => i);
final slowStream = Stream.periodic(Duration(seconds: 5), (i) => i * 10);
// Values from both streams will be piped into the counter signal!
connector.from(fastStream).from(slowStream);
```
### 2. The Shift Operator (<<)
For a more concise and beautiful visual flow, you can use the shift operator (<<) to chain streams:
```dart
final s = signal(0);
final c = connect(s);
c << fastStream << slowStream;
```
### 3. Lifecycle and Disposal
To avoid memory leaks, make sure to dispose the connector when it is no longer needed. Disposing the connector will automatically cancel all underlying active stream subscriptions.
```dart
connector.dispose(); // Cancels all stream subscriptions safely
```
### Constructors
View Constructors
##### `Connect(this.signal)`
Connects a **Stream** to a [Signal](/types/signal).
### Properties
View Properties
##### `Signal signal`
Internal signal to connect to.
### Methods
View Methods
##### `Connect from(Stream source, {bool? cancelOnError, Function? onError, Function? onDone, void Function(T)? onValue})`
Connects a **Stream** to a [Signal](/types/signal).
```dart
final counter = signal(0);
final c = connect(counter);
final s1 = Stream.value(1);
final s2 = Stream.value(2);
c.from(s1).from(s2);
c.dispose();
```
##### `Connect <<(Stream source)`
Synonym for from(Stream source)
##### `void dispose()`
Cancels all subscriptions.
---
## connect
The idea for connect comes from Anguar Signals with RxJS:
VIDEO
Start with a signal and then use the connect method to create a connector.
Streams will feed Signal value.
```dart
final s = signal(0);
final c = connect(s);
```
### to
Add streams to the connector.
```dart
final s = signal(0);
final c = connect(s);
final s1 = Stream.value(1);
final s2 = Stream.value(2);
c.from(s1).from(s2); // These can be chained
```
### dispose
Cancel all subscriptions.
```dart
final s = signal(0);
final c = connect(s);
final s1 = Stream.value(1);
final s2 = Stream.value(2);
c.from(s1).from(s2);
// or
c << s1 << s2
c.dispose(); // This will cancel all subscriptions
```
---
## Page: Stream
Url: https://dartsignals.dev/packages/signals_flutter/async/stream
Description: Stream signals can be created by extension or method.
---
## streamSignal
Stream signals can be created by extension or method.
### streamSignal
```dart
final stream = () async* {
yield 1;
};
final s = streamSignal(() => stream);
```
### toSignal()
```dart
final stream = () async* {
yield 1;
};
final s = stream.toSignal();
```
## .value, .peek()
Returns [AsyncState](/dart/async/state) for the value and can handle the various states.
The value getter returns the value of the stream if it completed successfully.
> .peek() can also be used to not subscribe in an effect
```dart
final stream = (int value) async* {
yield value;
};
final s = streamSignal(() => stream);
final value = s.value.value; // 1 or null
```
## .reset()
The reset method resets the stream to its initial state to recall on the next evaluation.
```dart
final stream = (int value) async* {
yield value;
};
final s = streamSignal(() => stream);
s.reset();
```
## .refresh()
Refresh the stream value by setting isLoading to true, but maintain the current state (AsyncData, AsyncLoading, AsyncError).
```dart
final stream = (int value) async* {
yield value;
};
final s = streamSignal(() => stream);
s.refresh();
print(s.value.isLoading); // true
```
## .reload()
Reload the stream value by setting the state to AsyncLoading and pass in the value or error as data.
```dart
final stream = (int value) async* {
yield value;
};
final s = streamSignal(() => stream);
s.reload();
print(s.value is AsyncLoading); // true
```
## Dependencies
By default the callback will be called once and the stream will be cached unless a signal is read in the callback.
```dart
final count = signal(0);
final s = streamSignal(() async* {
final value = count();
yield value;
});
await s.future; // 0
count.value = 1;
await s.future; // 1
```
If there are signals that need to be tracked across an async gap then use the dependencies when creating the streamSignal to [reset](#.reset()) every time any signal in the dependency array changes.
```dart
final count = signal(0);
final s = streamSignal(
() async* {
final value = count();
yield value;
},
dependencies: [count],
);
s.value; // state with count 0
count.value = 1; // resets the future
s.value; // state with count 1
```
---
## StreamSignal
Stream signals wrap a standard asynchronous **Stream** and bridge it into the reactive state framework, exposing its emissions as a reactive [AsyncState](/types/asyncstate).
You can construct a stream signal via the helper function [streamSignal](/types/streamsignal) or by calling the .toSignal() extension method on any standard **Stream**.
### 1. Basic Stream Binding
```dart
final s = streamSignal(() => countStream());
```
Or via the extension:
```dart
final s = countStream().toSignal();
```
### 2. Consuming stream emissions reactively
Reading .value on a [StreamSignal](/types/streamsignal) returns an [AsyncState](/types/asyncstate) object:
```dart
effect(() {
s.value.map(
data: (val) => print('Stream emitted: $val'),
error: (err, stack) => print('Stream encountered error: $err'),
loading: () => print('Waiting for first stream emission...'),
);
});
```
### 3. Subscription Lifecycle and Manual Control
A stream signal automatically manages the underlying **StreamSubscription**. It listens when the signal has active subscribers and automatically cleans up/cancels when disposed to prevent memory leaks.
You can also manually control the subscription state:
- **pause()**: Pauses the underlying stream subscription.
- **resume()**: Resumes a paused subscription.
- **cancel()**: Cancels the subscription and marks the stream signal as done.
- **isDone**: Returns whether the stream has finished emitting or has been cancelled.
```dart
final s = streamSignal(() => countStream());
s.pause(); // Temporarily halt stream values
```
### 4. Reactive Dependencies
Any reactive signals read synchronously inside the stream callback act as dependencies. When they mutate, the stream signal automatically cancels the current stream subscription, recreates a new stream using the updated values, and starts listening.
```dart
final query = signal('flutter');
final s = streamSignal(() {
// Re-subscribes to a new database query stream every time the query changes!
return db.watchItems(query.value);
});
```
### Constructors
View Constructors
##### `StreamSignal(Stream Function() fn, {AsyncSignalOptions? options, @Deprecated('Use options: AsyncSignalOptions(cancelOnError: ...) instead') bool? cancelOnError, @Deprecated('Use options: AsyncSignalOptions(initialValue: ...) instead') T? initialValue, @Deprecated('Use options: AsyncSignalOptions(dependencies: ...) instead') List>? dependencies, @Deprecated('Use options: AsyncSignalOptions(onDone: ...) instead') void Function()? onDone, @Deprecated('Use options: AsyncSignalOptions(lazy: ...) instead') bool? lazy, @Deprecated('Use options: AsyncSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: AsyncSignalOptions(name: ...) instead') String? debugLabel})`
Stream signals can be created by extension or method.
### streamSignal
```dart
final stream = () async* {
yield 1;
};
final s = streamSignal(() => stream);
```
### toSignal()
```dart
final stream = () async* {
yield 1;
};
final s = stream.toSignal();
```
## .value, .peek()
Returns [AsyncState](/dart/async/state) for the value and can handle the various states.
The value getter returns the value of the stream if it completed successfully.
> .peek() can also be used to not subscribe in an effect
```dart
final stream = (int value) async* {
yield value;
};
final s = streamSignal(() => stream);
final value = s.value.value; // 1 or null
```
## .reset()
The reset method resets the stream to its initial state to recall on the next evaluation.
```dart
final stream = (int value) async* {
yield value;
};
final s = streamSignal(() => stream);
s.reset();
```
## .refresh()
Refresh the stream value by setting isLoading to true, but maintain the current state (AsyncData, AsyncLoading, AsyncError).
```dart
final stream = (int value) async* {
yield value;
};
final s = streamSignal(() => stream);
s.refresh();
print(s.value.isLoading); // true
```
## .reload()
Reload the stream value by setting the state to AsyncLoading and pass in the value or error as data.
```dart
final stream = (int value) async* {
yield value;
};
final s = streamSignal(() => stream);
s.reload();
print(s.value is AsyncLoading); // true
```
## Dependencies
By default the callback will be called once and the stream will be cached unless a signal is read in the callback.
```dart
final count = signal(0);
final s = streamSignal(() async* {
final value = count();
yield value;
});
await s.future; // 0
count.value = 1;
await s.future; // 1
```
If there are signals that need to be tracked across an async gap then use the dependencies when creating the streamSignal to [reset](#.reset()) every time any signal in the dependency array changes.
```dart
final count = signal(0);
final s = streamSignal(
() async* {
final value = count();
yield value;
},
dependencies: [count],
);
s.value; // state with count 0
count.value = 1; // resets the future
s.value; // state with count 1
```
### Properties
View Properties
##### `bool? cancelOnError`
Cancel the subscription on error
##### `List> dependencies`
List of dependencies to recompute the stream
### Methods
View Methods
##### `bool isDone`
Check if the signal is done
##### `Future last`
First value of the stream
##### `Future first`
Last value of the stream
##### `Future execute(Stream src)`
Execute the stream
##### `bool isPaused`
Check if the subscription is paused
##### `void pause([Future? resume])`
Pause the subscription
##### `void resume()`
Resume the subscription
##### `Future cancel()`
Cancel the subscription
##### `Future reload()`
##### `Future refresh()`
##### `void reset([AsyncState? value])`
##### `void dispose()`
##### `AsyncState value`
##### `void setError(Object error, [StackTrace? stackTrace])`
---
## Page: MapSignal
Url: https://dartsignals.dev/packages/signals_flutter/value/map
Description: A reactive Signal that holds a Map and implements the Map interface.
---
A reactive [Signal](/types/signal) that holds a **Map** and implements the **Map** interface.
[MapSignal](/types/mapsignal) lets you listen to changes on a map reactively and mutate it directly using
standard map operations (like adding/modifying keys with operator []=, addAll, remove,
clear, etc.). Any mutations automatically trigger reactive updates to all active listeners
(e.g., inside an [effect](/types/effect) or [computed](/types/computed)).
Additionally, [MapSignal](/types/mapsignal) defines convenient operators:
- << injects/adds all entries from another map into the current map.
- & forks/concatenates the map with another map into a new [MapSignal](/types/mapsignal).
- | pipes/concatenates the map with another signal holding a map into a new [MapSignal](/types/mapsignal).
### Example Usage
```dart
import 'package:signals/signals.dart';
void main() {
final settings = mapSignal({
'theme': 'light',
'notifications': true,
});
effect(() {
print('Theme is currently: ${settings['theme']}');
}); // Prints: "Theme is currently: light"
// Update key/value pair directly (triggers updates)
settings['theme'] = 'dark'; // Prints: "Theme is currently: dark"
// Expose standard Map methods
settings.remove('notifications');
}
```
Mutating the collection directly calls the reactive set() routine under the hood automatically. You
do not need to assign settings.value = ... manually!
### Constructors
View Constructors
##### `MapSignal(super.value, {MapSignalOptions? options, @Deprecated('Use options: MapSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: MapSignalOptions(name: ...) instead') String? debugLabel})`
Creates a [MapSignal](/types/mapsignal) with the given **value**.
### Methods
View Methods
##### `MapSignal <<(Map other)`
Inject: Update current signal value with iterable
##### `MapSignal &(Map other)`
Fork: create a new signal with value is the concatenation of source signal and iterable parameter
##### `MapSignal |(Signal> other)`
Pipe: create a new signal by sending value from source to other
##### `bool ==(Object other)`
##### `int hashCode`
---
## mapSignal
Creates a [MapSignal](/types/mapsignal) initialized with the provided **map**.
This is a convenience helper function for creating reactive map signals.
```dart
import 'package:signals/signals.dart';
final settings = mapSignal({'theme': 'dark'});
```
---
## Page: SetSignal
Url: https://dartsignals.dev/packages/signals_flutter/value/set
Description: A reactive Signal that holds a Set and implements the Set interface.
---
A reactive [Signal](/types/signal) that holds a **Set** and implements the **Set** interface.
[SetSignal](/types/setsignal) lets you listen to changes on a set reactively and mutate it directly using
standard set operations (like add, addAll, remove, clear, etc.). Any mutations
automatically trigger reactive updates to all active listeners (e.g., inside an [effect](/types/effect)
or [computed](/types/computed)).
Additionally, [SetSignal](/types/setsignal) defines convenient operators:
- << injects/adds all items from another set into the current set.
- & forks/unions the set with another set into a new [SetSignal](/types/setsignal).
- | pipes/unions the set with another signal holding an iterable into a new [SetSignal](/types/setsignal).
### Example Usage
```dart
import 'package:signals/signals.dart';
void main() {
final numbers = setSignal({1, 2, 3});
effect(() {
print('Set content: $numbers, Length: ${numbers.length}');
}); // Prints: "Set content: {1, 2, 3}, Length: 3"
// Standard mutation (triggers updates)
numbers.add(4); // Prints: "Set content: {1, 2, 3, 4}, Length: 4"
// Removing an element (triggers updates)
numbers.remove(1); // Prints: "Set content: {2, 3, 4}, Length: 3"
// Set intersection (reactive query)
final common = numbers.intersection({3, 4, 5});
print(common); // Prints: {3, 4}
}
```
Mutating the collection directly calls the reactive set() routine under the hood automatically. You
do not need to assign numbers.value = ... manually!
### Constructors
View Constructors
##### `SetSignal(super.value, {SetSignalOptions? options, @Deprecated('Use options: SetSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: SetSignalOptions(name: ...) instead') String? debugLabel})`
Creates a [SetSignal](/types/setsignal) with the given **value**.
### Methods
View Methods
##### `SetSignal <<(Set other)`
Inject: Update current signal value with iterable
##### `SetSignal &(Set other)`
Fork: create a new signal with value is the concatenation of source signal and iterable parameter
##### `SetSignal |(Signal> other)`
Pipe: create a new signal by sending value from source to other
##### `bool ==(Object other)`
##### `int hashCode`
---
## setSignal
Creates a [SetSignal](/types/setsignal) initialized with the provided **set**.
This is a convenience helper function for creating reactive set signals.
```dart
import 'package:signals/signals.dart';
final mySet = setSignal({1, 2, 3});
```
---
## Page: ListSignal
Url: https://dartsignals.dev/packages/signals_flutter/value/list
Description: A reactive Signal that holds a List and implements the List interface.
---
A reactive [Signal](/types/signal) that holds a **List** and implements the **List** interface.
[ListSignal](/types/listsignal) lets you listen to changes on a list reactively and mutate it directly using
standard list operations (like add, addAll, remove, operators [] and []=, etc.).
Any mutations automatically trigger reactive updates to all active listeners (e.g., inside an
[effect](/types/effect) or [computed](/types/computed)).
Additionally, [ListSignal](/types/listsignal) defines convenient operators:
- << injects/adds all items from an iterable into the list.
- & forks/concatenates the list with an iterable into a new [ListSignal](/types/listsignal).
- | pipes/concatenates the list with another signal holding an iterable into a new [ListSignal](/types/listsignal).
### Example Usage
```dart
import 'package:signals/signals.dart';
void main() {
final numbers = listSignal([1, 2, 3]);
effect(() {
print('List content: $numbers, Length: ${numbers.length}');
}); // Prints: "List content: [1, 2, 3], Length: 3"
// Standard mutation (triggers updates)
numbers.add(4); // Prints: "List content: [1, 2, 3, 4], Length: 4"
// Update via index operator (triggers updates)
numbers[0] = 10; // Prints: "List content: [10, 2, 3, 4], Length: 4"
// Injection operator (triggers updates)
numbers << [5, 6]; // Prints: "List content: [10, 2, 3, 4, 5, 6], Length: 6"
}
```
Mutating the collection directly calls the reactive set() routine under the hood automatically. You
do not need to assign numbers.value = ... manually!
### Constructors
View Constructors
##### `ListSignal(super.value, {ListSignalOptions? options, @Deprecated('Use options: ListSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: ListSignalOptions(name: ...) instead') String? debugLabel})`
Creates a [ListSignal](/types/listsignal) with the given **value**.
### Methods
View Methods
##### `ListSignal <<(Iterable other)`
Inject: Update current signal value with iterable
##### `ListSignal &(Iterable other)`
Fork: create a new signal which value is the concatenation of source signal and iterable parameter
##### `ListSignal |(Signal> other)`
Pipe: create a new signal by sending value from source to other
##### `bool ==(Object other)`
##### `int hashCode`
---
## listSignal
Creates a [ListSignal](/types/listsignal) initialized with the provided **list**.
This is a convenience helper function for creating reactive list signals.
```dart
import 'package:signals/signals.dart';
final list = listSignal([1, 2, 3]);
```
---
## Page: IterableSignal
Url: https://dartsignals.dev/packages/signals_flutter/value/iterable
Description: A reactive Signal that holds an Iterable and implements the Iterable interface.
---
A reactive [Signal](/types/signal) that holds an **Iterable** and implements the **Iterable** interface.
[IterableSignal](/types/iterablesignal) allows you to listen to changes on an iterable collection reactively. It
exposes all standard **Iterable** properties and methods (like length, first, map, where, etc.)
directly on the signal itself. Calling these methods inside a reactive context (like an effect
or computed block) will automatically track them as dependencies.
### Example Usage
```dart
import 'package:signals/signals.dart';
void main() {
final items = iterableSignal([1, 2, 3]);
effect(() {
print('Items length: ${items.length}, First: ${items.first}');
}); // Prints: "Items length: 3, First: 1"
// Update the signal by assigning a new iterable
items.value = [10, 20, 30, 40]; // Prints: "Items length: 4, First: 10"
}
```
Direct mutation of the items inside the iterable will NOT trigger updates. To reactively mutate collections,
use specialized signals like ListSignal , SetSignal , or MapSignal .
### Constructors
View Constructors
##### `IterableSignal(super.value, {IterableSignalOptions? options, @Deprecated('Use options: IterableSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: IterableSignalOptions(name: ...) instead') String? debugLabel})`
Creates a [IterableSignal](/types/iterablesignal) with the given **value**.
### Methods
View Methods
##### `bool ==(Object other)`
##### `int hashCode`
---
## iterableSignal
Creates an [IterableSignal](/types/iterablesignal) holding the provided **iterable**.
This is a convenience function that instantiates an [IterableSignal](/types/iterablesignal), which delegates
all standard **Iterable** operations reactively and tracks changes.
### Example Usage
```dart
import 'package:signals/signals.dart';
final s = iterableSignal([1, 2, 3]);
print(s.length); // 3
```
---
## Page: ChangeStackSignal
Url: https://dartsignals.dev/packages/signals_flutter/value/change-stack
Description: A reactive Signal that records its history of values, allowing undo and redo operations.
---
A reactive [Signal](/types/signal) that records its history of values, allowing undo and redo operations.
[ChangeStackSignal](/types/changestacksignal) stores successive values of the signal in a double-ended queue.
This allows you to revert back to previous values using **undo** and re-apply undone values
using **redo**. You can also specify an optional **limit** parameter to cap the history queue size.
If you only need access to the initial and immediate previous values of a signal (without a full
history stack or undo/redo mechanisms), use the lightweight TrackedSignalMixin instead.
### Example Usage
```dart
import 'package:signals/signals.dart';
void main() {
final counter = ChangeStackSignal(0, limit: 5);
effect(() {
print('Counter: ${counter.value}');
}); // Prints: "Counter: 0"
counter.value = 1; // Prints: "Counter: 1"
counter.value = 2; // Prints: "Counter: 2"
print('Can Undo: ${counter.canUndo}'); // Prints: "Can Undo: true"
// Perform undo operation (automatically triggers reactive updates)
counter.undo(); // Prints: "Counter: 1"
counter.undo(); // Prints: "Counter: 0"
// Perform redo operation
counter.redo(); // Prints: "Counter: 1"
}
```
This class works best with values that are immutable or copied when updated.
If you mutate an object in-place directly without assigning a new value using the .value
setter or set(...), the history queue will store references to the same mutated object, and
undo/redo operations will not reflect changes correctly.
### Constructors
View Constructors
##### `ChangeStackSignal(super.value, {int? limit, ChangeSignalOptions? options, @Deprecated('Use options: ChangeSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: ChangeSignalOptions(name: ...) instead') String? debugLabel})`
Creates a [ChangeStackSignal](/types/changestacksignal) initialized with the provided **value**.
---
## changeStack
Creates a [ChangeStackSignal](/types/changestacksignal) initialized with the provided **value**.
This is a convenience helper function for creating reactive undo/redo history signals.
You can pass a **limit** to restrict the maximum history stack size.
```dart
import 'package:signals/signals.dart';
final s = changeStack(0, limit: 10);
s.value = 1;
s.undo(); // Returns to 0
```
---
## Page: RenderSignalCustomPaint
Url: https://dartsignals.dev/packages/signals_flutter/render/render-signal-custom-paint
Description: The RenderObject for SignalCustomPaint that delegates drawing to SignalCustomPainter.
---
The **RenderObject** for [SignalCustomPaint](/types/signalcustompaint) that delegates drawing to [SignalCustomPainter](/types/signalcustompainter).
### Constructors
View Constructors
##### `RenderSignalCustomPaint({required SignalCustomPainter painter})`
Creates a new [RenderSignalCustomPaint](/types/rendersignalcustompaint).
### Methods
View Methods
##### `SignalCustomPainter painter`
Gets the custom painter delegate.
##### `painter(SignalCustomPainter val)`
Sets a new custom painter delegate and triggers a repaint if necessary.
##### `void paint(PaintingContext context, Offset offset)`
---
## Page: RenderSignalProxyBox
Url: https://dartsignals.dev/packages/signals_flutter/render/render-signal-proxy-box
Description: A low-level RenderProxyBox that subscribes to multiple reactive signals.
---
A low-level **RenderProxyBox** that subscribes to multiple reactive signals
and automatically schedules repaints without layout/rebuild when signals fire.
### Constructors
View Constructors
##### `RenderSignalProxyBox({required List> signals})`
Creates a new [RenderSignalProxyBox](/types/rendersignalproxybox) with the specified list of observed **signals**.
### Methods
View Methods
##### `List> signals`
Gets the current list of signals being observed.
##### `signals(List> val)`
Sets a new list of observed signals and resubscribes to them.
##### `void detach()`
---
## Page: RenderSignalBox
Url: https://dartsignals.dev/packages/signals_flutter/render/render-signal-box
Description: The low-level RenderBox used by SignalPainterWidget to directly subscribe to a progress signal and paint.
---
The low-level **RenderBox** used by [SignalPainterWidget](/types/signalpainterwidget) to directly subscribe to a progress signal and paint.
### Constructors
View Constructors
##### `RenderSignalBox({required core.ReadonlySignal progress, required void Function(Canvas canvas, Size size, double value) painter})`
Creates a new [RenderSignalBox](/types/rendersignalbox) drawing using **_painter** and driven by **_progress** signal.
### Methods
View Methods
##### `progress(core.ReadonlySignal val)`
Sets a new progress signal and resubscribes to updates.
##### `painter(void Function(Canvas canvas, Size size, double value) val)`
Sets a new custom painter delegate.
##### `void performLayout()`
##### `void paint(PaintingContext context, Offset offset)`
##### `void detach()`
---
## Page: Signal Value Listenable Utils
Url: https://dartsignals.dev/packages/signals_flutter/extensions/signal-value-listenable-utils
Description: Extension on ValueListenable to seamlessly bridge standard Flutter values to reactive signals.
---
## SignalValueListenableUtils
Extension on **ValueListenable** to seamlessly bridge standard Flutter values to reactive signals.
### Methods
View Methods
##### `ReadonlySignal toSignal({String? debugLabel, bool autoDispose = false})`
## ReadonlySignal from ValueListenable
Converted **ValueListenable** objects become readable signals that automatically listen
to the underlying source. When the source updates, the signal updates.
The signal automatically manages the subscription, disposing of the listener when the
signal itself is disposed.
### Example: Converting a ValueNotifier
```dart
final ValueListenable listenable = ValueNotifier(10);
final signal = listenable.toSignal();
print(signal.value); // 10
```
### Example: ValueListenable from ReadonlySignal
To convert back to a standard **ValueListenable** for native Flutter compatibility:
```dart
final signal = Signal(10);
final listenable = signal.toValueListenable();
```
---
## Page: Stateful Element Convert Extension
Url: https://dartsignals.dev/packages/signals_flutter/extensions/stateful-element-convert-extension
Description: Extension to convert standard Flutter elements to their reactive counterparts.
---
## StatefulElementConvertExtension
Extension to convert standard Flutter elements to their reactive counterparts.
### Methods
View Methods
##### `void watchSignal(dynamic signal)`
Intercepts build to watch a signal if the element is a [SignalStatefulElement](/types/signalstatefulelement).
---
## Page: ConvertedSignalStatefulElement
Url: https://dartsignals.dev/packages/signals_flutter/extensions/converted-signal-stateful-element
Description: Element for ConvertedSignalStatefulWidget that overrides the widget getter.
---
Element for [ConvertedSignalStatefulWidget](/types/convertedsignalstatefulwidget) that overrides the widget getter
to return the wrapped **StatefulWidget**, preventing dynamic cast errors in **State.widget**.
### Constructors
View Constructors
##### `ConvertedSignalStatefulElement(ConvertedSignalStatefulWidget widget)`
Constructor for [ConvertedSignalStatefulElement](/types/convertedsignalstatefulelement).
---
## Page: SignalStatefulElement
Url: https://dartsignals.dev/packages/signals_flutter/extensions/signal-stateful-element
Description: Element for SignalStatefulWidget that manages implicit signal subscription and handles dynamic branching.
---
Element for [SignalStatefulWidget](/types/signalstatefulwidget) that manages implicit signal subscription and handles dynamic branching.
### Constructors
View Constructors
##### `SignalStatefulElement(super.widget)`
Constructor for [SignalStatefulElement](/types/signalstatefulelement).
### Methods
View Methods
##### `void watchSignal(core.ReadonlySignal value)`
Subscribes to changes of the provided **value** and schedules a rebuild.
##### `Widget build()`
##### `void unmount()`
---
## Page: Stateful Widget Convert Widget Extension
Url: https://dartsignals.dev/packages/signals_flutter/extensions/stateful-widget-convert-widget-extension
Description: Extension to convert standard Flutter widgets to reactive Signal stateful widgets.
---
## StatefulWidgetConvertWidgetExtension
Extension to convert standard Flutter widgets to reactive Signal stateful widgets.
### Methods
View Methods
##### `SignalStatefulWidget toSignalStatefulWidget()`
Converts this **StatefulWidget** to a [SignalStatefulWidget](/types/signalstatefulwidget).
---
## Page: SignalCustomPainter
Url: https://dartsignals.dev/packages/signals_flutter/extensions/signal-custom-painter
Description: A premium custom painter that automatically repaints when any observed signal changes,.
---
A premium custom painter that automatically repaints when any observed signal changes,
bypassing Flutter's widget build and layout phases completely.
SignalCustomPainter registers subscriptions to the provided list of **signals**. When any
of these signals fire, a GPU repaint is scheduled directly via markNeedsPaint(), bypassing
the widget-tree build cycle and layout passes for unmatched graphics performance.
### Interactive Star Field Canvas Example
```dart
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
final cursorOffset = signal(const Offset(0, 0));
class StarField extends StatelessWidget {
const StarField({super.key});
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanUpdate: (details) {
cursorOffset.value = details.localPosition;
},
child: Container(
color: const Color(0xFF0B0D19),
child: SignalCustomPaint(
painter: StarPainter(cursorOffset),
child: const SizedBox.expand(),
),
),
);
}
}
class StarPainter extends SignalCustomPainter {
StarPainter(this.offsetSignal) : super(signals: [offsetSignal]);
final ReadonlySignal offsetSignal;
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.white
..style = PaintingStyle.fill;
// Draw 100 stars deterministically distributed across the canvas size
for (int i = 0; i < 100; i++) {
final double x = ((i * 197) % 1000) / 1000 * size.width;
final double y = ((i * 541) % 1000) / 1000 * size.height;
final double starSize = ((i * 7) % 4) + 1.5;
final starPos = Offset(x, y);
final distance = (starPos - offsetSignal.value).distance;
if (distance < 120.0) {
// Spotlight/Twinkle effect: make stars near the cursor brighter & larger!
final factor = 1.0 - (distance / 120.0);
paint.color = Colors.amber.withOpacity(0.3 + 0.7 * factor);
final glowSize = starSize + (factor * 4.0);
canvas.drawCircle(starPos, glowSize, paint);
// Draw premium constellation lines connecting nearby stars to the cursor
final linePaint = Paint()
..color = Colors.amber.withOpacity(0.25 * factor)
..strokeWidth = 1.0;
canvas.drawLine(offsetSignal.value, starPos, linePaint);
} else {
// Normal distant star
paint.color = Colors.white.withOpacity(0.4);
canvas.drawCircle(starPos, starSize, paint);
}
}
// Draw the interactive controller glow source
final cursorPaint = Paint()
..color = Colors.amber.withOpacity(0.8)
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 6.0);
canvas.drawCircle(offsetSignal.value, 6.0, cursorPaint);
}
@override
bool shouldRepaint(covariant StarPainter oldDelegate) => true;
}
```
### Constructors
View Constructors
##### `SignalCustomPainter({required this.signals})`
Creates a new [SignalCustomPainter](/types/signalcustompainter).
### Properties
View Properties
##### `List> signals`
The list of signals to observe for changes.
### Methods
View Methods
##### `void paint(Canvas canvas, Size size)`
Paint on the canvas.
##### `bool shouldRepaint(covariant SignalCustomPainter oldDelegate)`
Return true if the painter should be updated when the widget configuration changes.
---
## Page: ConvertedSignalWidget
Url: https://dartsignals.dev/packages/signals_flutter/extensions/converted-signal-widget
Description: A wrapper SignalWidget that wraps a StatelessWidget.
---
A wrapper [SignalWidget](/types/signalwidget) that wraps a **StatelessWidget**.
### Constructors
View Constructors
##### `ConvertedSignalWidget(this.widget, {super.key})`
Creates a [ConvertedSignalWidget](/types/convertedsignalwidget).
### Properties
View Properties
##### `StatelessWidget widget`
The wrapped **StatelessWidget**.
### Methods
View Methods
##### `Widget build(BuildContext context)`
---
## Page: Stateless Widget Convert Widget Extension
Url: https://dartsignals.dev/packages/signals_flutter/extensions/stateless-widget-convert-widget-extension
Description: Extension to convert standard Flutter widgets to reactive Signal widgets.
---
## StatelessWidgetConvertWidgetExtension
Extension to convert standard Flutter widgets to reactive Signal widgets.
### Methods
View Methods
##### `SignalWidget toSignalWidget()`
Converts this **StatelessWidget** to a [SignalWidget](/types/signalwidget).
---
## Page: Signal Value Notifier Utils
Url: https://dartsignals.dev/packages/signals_flutter/extensions/signal-value-notifier-utils
Description: Extension on ValueNotifier to seamlessly bridge standard Flutter mutable values to reactive signals.
---
## SignalValueNotifierUtils
Extension on **ValueNotifier** to seamlessly bridge standard Flutter mutable values to reactive signals.
### Methods
View Methods
##### `Signal toSignal({String? debugLabel, bool autoDispose = false})`
## Bi-directional Signal and ValueNotifier Interoperability
Converted **ValueNotifier** objects become mutable [Signal](/types/signal) instances. Setting the value
on the signal or the notifier automatically propagates the update to the other.
The subscription is fully memory-safe and automatically unsubscribed when the signal
is disposed.
### Example: Converting a ValueNotifier to a Signal
```dart
final notifier = ValueNotifier(10);
final signal = notifier.toSignal();
signal.value = 20;
print(notifier.value); // 20
notifier.value = 30;
print(signal.value); // 30
```
### Example: Converting a Signal to a ValueNotifier
To bridge back to a standard **ValueNotifier** for Flutter compatibility:
```dart
final signal = Signal(10);
final notifier = signal.toValueNotifier();
```
---
## Page: SignalElement
Url: https://dartsignals.dev/packages/signals_flutter/extensions/signal-element
Description: Element for SignalWidget that manages implicit signal subscription and handles dynamic branching.
---
Element for [SignalWidget](/types/signalwidget) that manages implicit signal subscription and handles dynamic branching.
### Constructors
View Constructors
##### `SignalElement(super.widget)`
Constructor for [SignalElement](/types/signalelement).
### Methods
View Methods
##### `void watchSignal(core.ReadonlySignal value)`
Subscribes to changes of the provided **value** and schedules a rebuild.
##### `Widget build()`
##### `void unmount()`
---
## Page: Value Listenable To Signal
Url: https://dartsignals.dev/packages/signals_flutter/extensions/value-listenable-to-signal
Description: A global helper function to convert a Flutter ValueListenable to a ReadonlySignal.
---
## valueListenableToSignal
A global helper function to convert a Flutter **ValueListenable** to a [ReadonlySignal](/types/readonlysignal).
This helper is a functional equivalent of the toSignal() extension method.
### Example
```dart
final scrollPosition = ValueNotifier(0.0);
final scrollSignal = valueListenableToSignal(scrollPosition);
```
---
## Page: Stateless Element Convert Extension
Url: https://dartsignals.dev/packages/signals_flutter/extensions/stateless-element-convert-extension
Description: Extension to convert standard Flutter elements to their reactive counterparts.
---
## StatelessElementConvertExtension
Extension to convert standard Flutter elements to their reactive counterparts.
### Methods
View Methods
##### `void watchSignal(dynamic signal)`
Intercepts build to watch a signal if the element is a [SignalElement](/types/signalelement).
---
## Page: ConvertedSignalStatefulWidget
Url: https://dartsignals.dev/packages/signals_flutter/extensions/converted-signal-stateful-widget
Description: A wrapper SignalStatefulWidget that wraps a StatefulWidget.
---
A wrapper [SignalStatefulWidget](/types/signalstatefulwidget) that wraps a **StatefulWidget**.
### Constructors
View Constructors
##### `ConvertedSignalStatefulWidget(this.widget, {super.key})`
Creates a [ConvertedSignalStatefulWidget](/types/convertedsignalstatefulwidget).
### Properties
View Properties
##### `StatefulWidget widget`
The wrapped **StatefulWidget**.
### Methods
View Methods
##### `StatefulElement createElement()`
##### `State createState()`
---
## Page: Value Notifier To Signal
Url: https://dartsignals.dev/packages/signals_flutter/extensions/value-notifier-to-signal
Description: A global helper function to convert a Flutter ValueNotifier to a mutable Signal.
---
## valueNotifierToSignal
A global helper function to convert a Flutter **ValueNotifier** to a mutable [Signal](/types/signal).
Updates to either the notifier or the returned signal will automatically update the other.
This helper is a functional equivalent of the toSignal() extension method.
### Example
```dart
final textControllerValue = ValueNotifier('');
final textSignal = valueNotifierToSignal(textControllerValue);
```
---
## Page: SignalStatefulWidget
Url: https://dartsignals.dev/packages/signals_flutter/extensions/signal-stateful-widget
Description: A reactive StatefulWidget that implicitly tracks and rebuilds on signal changes.
---
A reactive **StatefulWidget** that implicitly tracks and rebuilds on signal changes.
Subclasses of SignalStatefulWidget establish a dynamic reactive context directly at the
Flutter element layer. Any signal value accessed synchronously inside State's **build** method
is **implicitly tracked** and subscribed to. When a signal changes, only that element is rebuilt.
This offers a clean, Javascript-style reactivity experience while retaining all stateful lifecycles.
It is the modern, highly recommended replacement for the legacy, deprecated SignalsMixin.
### Implicit Reactivity Example (Stateful)
```dart
class CounterControl extends SignalStatefulWidget {
const CounterControl({super.key});
@override
State createState() => _CounterControlState();
}
class _CounterControlState extends State {
// Create state signals directly inside the State object:
final count = signal(0);
@override
Widget build(BuildContext context) {
// Automatically tracked on access and safely unsubscribed on unmount:
return ElevatedButton(
onPressed: () => count.value++,
child: Text('Count: ${count.value}'),
);
}
}
```
> [!IMPORTANT]
> Only signals accessed *synchronously* during the execution of the build method are tracked.
> Signals read inside async callbacks, listeners, or deferred tasks are not subscribed to.
### Constructors
View Constructors
##### `SignalStatefulWidget({super.key})`
Constructor for [SignalStatefulWidget](/types/signalstatefulwidget).
### Methods
View Methods
##### `StatefulElement createElement()`
---
## Page: SignalEquality
Url: https://dartsignals.dev/packages/signals_flutter/utilities/equality
Description: Defines the equality check algorithm used by signals to determine if a new value.
---
Defines the equality check algorithm used by signals to determine if a new value
actually differs from the current value.
By default, signals use standard Dart operator equality (==). However, you can configure
a signal to use different strategies, such as deep equality check for collections or custom comparator checks.
Strategies:
- **standard**: Default value equality (a == b).
- **identity**: Identity-based comparison (identical(a, b)).
- **deep**: Deep collection comparison for Lists, Maps, and Sets.
- **custom**: User-defined boolean comparison function.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
void main() {
// Create a list signal using deep equality check
final items = signal(
[1, 2, 3],
options: SignalOptions(
equality: SignalEquality.deep(),
),
);
effect(() {
print('Items changed: ${items.value}');
});
// Reassigning an identical value structure does NOT trigger a rebuild!
items.value = [1, 2, 3];
// Triggers rebuild
items.value = [1, 2, 3, 4];
}
```
### Constructors
View Constructors
##### `SignalEquality()`
@nodoc
##### `SignalEquality.custom(bool Function(T a, T b) fn)`
Custom equality check
Uses a user-provided boolean function **fn** to check for equality.
### Methods
View Methods
##### `bool equals(Object? a, Object? b)`
Check if two values are equal
##### `static SignalEquality standard()`
Standard equality check (a == b)
Matches two objects if their standard == operator returns true.
This is the default strategy used by all signals.
##### `static SignalEquality identity()`
Identity equality check (identical(a, b))
Matches two objects only if they are the exact same instance in memory.
##### `static SignalEquality deep()`
Deep equality check
Matches collections (Lists, Maps, Sets) recursively by comparing their items.
---
## SignalIdentityEquality
Identity equality check (identical(a, b))
Matches two objects only if they point to the exact same instance in memory.
### Example Usage
```dart
final listA = [1, 2];
final listB = [1, 2];
final equality = SignalEquality.identity>();
print(equality.equals(listA, listB)); // false
print(equality.equals(listA, listA)); // true
```
### Constructors
View Constructors
##### `SignalIdentityEquality()`
Creates a new [SignalIdentityEquality](/types/signalidentityequality) instance.
### Methods
View Methods
##### `bool equals(Object? a, Object? b)`
---
## SignalDeepEquality
Deep equality check for collections
Recursively compares Lists, Maps, and Sets to see if their nested elements are equal.
### Example Usage
```dart
final equality = SignalEquality.deep();
print(equality.equals([1, [2, 3]], [1, [2, 3]])); // true
```
### Constructors
View Constructors
##### `SignalDeepEquality()`
Creates a new [SignalDeepEquality](/types/signaldeepequality) instance.
### Methods
View Methods
##### `bool equals(Object? a, Object? b)`
---
## SignalCustomEquality
Custom equality check using a custom function
Uses a custom comparison function to determine if two values of type **T** are equal.
### Example Usage
```dart
final equality = SignalEquality.custom((User a, User b) => a.id == b.id);
```
### Constructors
View Constructors
##### `SignalCustomEquality(this._fn)`
Creates a new [SignalCustomEquality](/types/signalcustomequality) instance.
### Methods
View Methods
##### `bool equals(Object? a, Object? b)`
---
## SignalStandardEquality
Standard equality check (a == b)
Matches two objects using the standard Dart operator ==.
### Example Usage
```dart
final equality = SignalEquality.standard();
print(equality.equals(5, 5)); // true
```
### Constructors
View Constructors
##### `SignalStandardEquality()`
Creates a new [SignalStandardEquality](/types/signalstandardequality) instance.
### Methods
View Methods
##### `bool equals(Object? a, Object? b)`
---
## Page: Model
Url: https://dartsignals.dev/packages/signals_flutter/utilities/model
Description: Creates a new model constructor with an instanced factory.
---
## createModel
Creates a new model constructor with an instanced factory.
A [SignalModel](/types/signalmodel) is a highly powerful architectural primitive designed to manage cohesive packages
of related state, business logic, actions, and side effects.
Under the hood, [SignalModel](/types/signalmodel) automatically tracks, scopes, and manages the lifecycle of any [Effect](/types/effect)s
instantiated during its factory execution. When the model is disposed (by calling model.dispose()),
all nested/captured effects are clean up automatically, ensuring complete prevention of memory leaks.
Furthermore, if the factory returns a standard Dart **Map**, and wrapInAction is enabled (default),
all nested function properties are automatically wrapped in batched [action](/types/action) transactions to optimize updates.
### 1. Type-Safe Models using Dart 3+ Records (Recommended)
The simplest and most built-in way to define a compile-safe model is to return a Dart **record** from your factory.
Records provide immediate type safety, autocomplete, and compile-time verification without any wrapper boilerplates.
```dart
import 'package:signals/signals.dart';
// Define the reactive model constructor returning a Record
final counterModel = createModel(() {
final count = signal(0);
// Captured nested side-effect (e.g. logging or syncing to local storage)
effect(() {
print('Nested logger: count is ${count.value}');
});
return (
count: count,
increment: () => count.value++,
);
});
void main() {
// Instantiate the model
final model = counterModel();
// Access properties type-safely via .value
print(model.value.count.value); // Prints: 0 (and registers effect print: Nested logger: count is 0)
model.value.increment(); // Prints: Nested logger: count is 1
// Dispose when done to clean up all captured nested effects
model.dispose();
}
```
### 2. Object-Oriented Style: Type-Safe Wrappers using Dart 3+ Extension Types
While records are great for lightweight structures, you can wrap the returned Map-based SignalModel in a standard Dart 3 **extension type** when you prefer a class-like API (e.g. implementing getters/setters or hiding subscript lookups).
```dart
import 'package:signals/signals.dart';
// 1. Define the reactive model constructor returning a Map
final counterModel = createModel(() {
final count = signal(0);
// Captured nested side-effect (e.g. logging or syncing to local storage)
effect(() {
print('Nested logger: count is ${count.value}');
});
return {
'count': count,
'increment': () => count.value++,
};
});
// 2. Create a premium, compile-safe extension type wrapper
extension type TypeSafeCounter(SignalModel> _model) {
int get count => (_model['count'] as Signal).value;
set count(int val) => (_model['count'] as Signal).value = val;
void increment() => (_model['increment'] as Function)();
void dispose() => _model.dispose();
}
void main() {
// 3. Instantiate and wrap the model
final counter = TypeSafeCounter(counterModel());
// Now you have a beautifully autocomplete-friendly, compile-safe API!
print(counter.count); // Prints: 0 (and registers effect print: Nested logger: count is 0)
counter.increment(); // Prints: Nested logger: count is 1
// Dispose when done to clean up all captured nested effects
counter.dispose();
}
```
Favor using Dart 3 records or extension types when defining models. They cost zero runtime overhead
while granting complete compile-safe parameters and autocomplete functionality.
---
## SignalModel
A premium wrapper for cohesive state packages constructed with [createModel](/types/createmodel).
It holds the instanced model **value** and all the [Effect](/types/effect)s that were captured
during its construction. Disposing the [SignalModel](/types/signalmodel) automatically disposes of all
nested/captured effects, completely avoiding memory leaks.
### Premium Pattern: Dart 3+ Extension Type Wrappers
To avoid unchecked subscript access like model['count'].value, wrap your model in an extension type:
```dart
extension type TypeSafeCounter(SignalModel> _model) {
int get count => (_model['count'] as Signal).value;
set count(int val) => (_model['count'] as Signal).value = val;
void increment() => (_model['increment'] as Function)();
void dispose() => _model.dispose();
}
```
### Constructors
View Constructors
##### `SignalModel(this.value, this._effects, {this.options = const SignalModelOptions()})`
Creates a new model instance.
### Properties
View Properties
##### `T value`
The instanced model value.
##### `SignalModelOptions options`
Options used to configure this model.
### Methods
View Methods
##### `dynamic [](Object? key)`
Access properties dynamically if the underlying **value** is a **Map**.
##### `void []=(dynamic key, dynamic val)`
Set properties dynamically if the underlying **value** is a **Map**.
##### `T call()`
Returns the **value** of this model. Alias for [.value]
##### `void dispose()`
Disposes of all captured effects.
---
## SignalModelOptions
Options for configuring a [SignalModel](/types/signalmodel).
Provides configuration for debug labeling (**name**) and whether to automatically wrap Map functions
in transaction-safe, batched actions (**wrapInAction**).
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
final options = const SignalModelOptions(
name: 'user-profile-model',
wrapInAction: true,
);
```
### Constructors
View Constructors
##### `SignalModelOptions({this.name, this.wrapInAction = true})`
Creates a new instance of [SignalModelOptions](/types/signalmodeloptions).
### Properties
View Properties
##### `String? name`
The name or debug label of the model.
##### `bool wrapInAction`
Whether to automatically wrap returned Map functions in actions.
Defaults to true.
### Methods
View Methods
##### `SignalModelOptions copyWith({String? name, bool? wrapInAction})`
Copy options with new values.
##### `bool ==(Object other)`
##### `int hashCode`
---
## SignalModelConstructor
A constructor for models that manages nested effects.
The model constructor starts capturing effects when called, storing them inside the returned [SignalModel](/types/signalmodel).
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
final myModel = SignalModelConstructor(() => 'data');
final model = myModel();
print(model.value); // Prints: data
```
### Constructors
View Constructors
##### `SignalModelConstructor(this._factory, {this.options = const SignalModelOptions()})`
Creates a new instance of [SignalModelConstructor](/types/signalmodelconstructor).
### Properties
View Properties
##### `SignalModelOptions options`
Options used to configure this constructor.
### Methods
View Methods
##### `SignalModel call()`
Instantiates a new [SignalModel](/types/signalmodel) instance.
---
## Page: SignalContainer
Url: https://dartsignals.dev/packages/signals_flutter/utilities/container
Description: Signal container used to create signals based on args.
---
Signal container used to create signals based on args
```dart
final container = readonlySignalContainer((e) {
return signal(Cache(e));
});
final cacheA = container('cache-a');
final cacheB = container('cache-b');
final cacheC = container('cache-c');
```
Example of settings and SharedPreferences:
```dart
class Settings {
final SharedPreferences prefs;
EffectCleanup? _cleanup;
Settings(this.prefs) {
_cleanup = effect(() {
for (final entry in setting.store.entries) {
final value = entry.value.peek();
if (prefs.getString(entry.key.$1) != value) {
prefs.setString(entry.key.$1, value).ignore();
}
}
});
}
late final setting = signalContainer(
(val) => signal(prefs.getString(val.$1) ?? val.$2),
cache: true,
);
Signal get darkMode => setting(('dark-mode', 'false'));
void dispose() {
_cleanup?.call();
setting.dispose();
}
}
void main() {
// Load or find instance
late final SharedPreferences prefs = ...;
// Create settings
final settings = Settings(prefs);
// Get value
print('dark mode: ${settings.darkMode}');
// Update value
settings.darkMode.value = 'true';
}
```
### Constructors
View Constructors
##### `SignalContainer(this._create, {this.cache = false, this.onEvict})`
Signal container used to create multiple signals via args
### Properties
View Properties
##### `bool cache`
If true then signals will be cached when created
##### `void Function(Arg key, S signal)? onEvict`
Optional callback when a signal is removed/evicted from the cache
##### `store`
Store of created signals (if cache is true)
### Methods
View Methods
##### `S call(Arg arg)`
Create the signal with the given args
##### `S? remove(Arg arg)`
Remove a signal from the cache
##### `bool containsKey(Arg arg)`
Check if signal is currently stored in the cache
##### `void clear()`
Clear the cache
##### `void dispose()`
Dispose of all created signals
##### `int length`
Returns the number of cached signals.
##### `bool isEmpty`
Returns true if the cache is empty.
##### `bool isNotEmpty`
Returns true if the cache is not empty.
##### `Iterable keys`
Returns all currently cached keys.
##### `Iterable values`
Returns all currently cached signals.
##### `Iterable> entries`
Returns all currently cached entries.
##### `S? lookup(Arg arg)`
Retrieve the cached signal for **arg** if it exists, without creating a new one if it is missing.
##### `void removeWhere(bool Function(Arg key, S signal) test)`
Filter and remove matching cached signals.
---
## readonlySignalContainer
Create a signal container used to instance signals based on args
```dart
final container = readonlySignalContainer((e) {
return signal(Cache(e));
});
final cacheA = container('cache-a');
final cacheB = container('cache-b');
final cacheC = container('cache-c');
```
The signals cannot be updated but allows for
using computed where the value is only derived from other signals.
---
## signalContainer
Create a signal container used to instance signals based on args
```dart
final container = signalContainer((e) {
return signal(Cache(e));
});
final cacheA = container('cache-a');
final cacheB = container('cache-b');
final cacheC = container('cache-c');
cacheA.value = 'a';
cacheB.value = 'b';
cacheC.value = 'c';
```
---
## streamSignalContainer
Create a signal container for StreamSignals based on args.
```dart
final container = streamSignalContainer((roomId) {
return streamSignal(() => listenToRoom(roomId));
});
```
---
## computedContainer
Create a signal container for computed signals based on args.
```dart
final container = computedContainer((arg) {
return computed(() => sourceSignal.value * arg);
});
```
---
## futureSignalContainer
Create a signal container for FutureSignals based on args.
```dart
final container = futureSignalContainer((id) {
return futureSignal(() => fetchPost(id));
});
```
---
## Page: SignalsObserver
Url: https://dartsignals.dev/packages/signals_flutter/utilities/observer
Description: You can observe all signal values in the dart application by providing an implementation of SignalsObserver:.
---
You can observe all signal values in the dart application by providing an implementation of SignalsObserver:
```dart
abstract class SignalsObserver {
void onSignalCreated(Signal instance);
void onSignalUpdated(Signal instance, dynamic value);
void onComputedCreated(Computed instance);
void onComputedUpdated(Computed instance, dynamic value);
static SignalsObserver? instance;
}
```
> There is a prebuilt LoggingSignalsObserver for printing updates to the console.
To add the observer override the instance at the start of the application:
```dart
void main() {
SignalsObserver.instance = LoggingSignalsObserver(); // or custom observer
...
}
```
This will have a slight performance hit since every update will be tracked via the observer. It is recommended to only set the SignalsObserver.instance in debug or profile mode.
### Properties
View Properties
##### `static SignalsObserver? instance`
The current observer instance.
### Methods
View Methods
##### `void onSignalCreated(Signal instance, T value)`
Called when a signal is created.
##### `void onSignalUpdated(Signal instance, T value)`
Called when a signal is updated.
##### `void onComputedCreated(Computed instance)`
Called when a computed is created.
##### `void onComputedUpdated(Computed instance, T value)`
Called when a computed is updated.
##### `void onEffectCreated(Effect instance)`
Called when a effect is created.
##### `void onEffectCalled(Effect instance)`
Called when a effect is called.
##### `void onEffectRemoved(Effect instance)`
Called when a effect is disposed.
---
## LoggingSignalsObserver
Logs all signals and computed changes to the console.
### Methods
View Methods
##### `void onComputedCreated(Computed instance)`
##### `void onComputedUpdated(Computed instance, T value)`
##### `void onSignalCreated(Signal instance, T value)`
##### `void onSignalUpdated(Signal instance, T value)`
##### `void onEffectCreated(Effect instance)`
##### `void onEffectCalled(Effect instance)`
##### `void onEffectRemoved(Effect instance)`
##### `void log(String message)`
Logs a message to the console.
---
## onSignalRead
Global callback when any signal is read.
---
## signalsDevToolsEnabled
Manually enable/disable signals devtools
---
## DevToolsSignalsObserver
Signals DevTools observer
### Constructors
View Constructors
##### `DevToolsSignalsObserver()`
Create a DevToolsSignalsObserver and register the VM service extensions.
### Methods
View Methods
##### `bool enabled`
Check if devTools is enabled
##### `enabled(bool value)`
Enable/Disable devTools
##### `void reassemble()`
Reload the signals devTools
##### `void onComputedCreated(Computed instance)`
##### `void onComputedUpdated(Computed instance, T value)`
##### `void onSignalCreated(Signal instance, T value)`
##### `void onSignalUpdated(Signal instance, T value)`
##### `void log(String message)`
Logs a message to the console.
##### `void onEffectCreated(Effect instance)`
##### `void onEffectCalled(Effect instance)`
##### `void onEffectRemoved(Effect instance)`
##### `Map getNodes()`
Returns a map representation of all active signals, computeds, and effects
in the reactive graph.
---
## disableSignalsDevTools
Disable the devtools
---
## reloadSignalsDevTools
Reload the devtools
---
## Page: PersistedSignal
Url: https://dartsignals.dev/packages/signals_flutter/utilities/persisted
Description: A Signal whose value is persistently stored in a key-value database.
---
A Signal whose value is persistently stored in a key-value database.
PersistedSignal allows application state (such as user preferences, theme options,
authentication tokens, and drafts) to automatically survive application restarts
without writing tedious boilerplate for manual loading and saving.
### Concrete Subclasses
For common primitive types, use the provided concrete classes:
- [PersistedBoolSignal](/types/persistedboolsignal) / [PersistedNullableBoolSignal](/types/persistednullableboolsignal)
- [PersistedIntSignal](/types/persistedintsignal) / [PersistedNullableIntSignal](/types/persistednullableintsignal)
- [PersistedDoubleSignal](/types/persisteddoublesignal) / [PersistedNullableDoubleSignal](/types/persistednullabledoublesignal)
- [PersistedNumSignal](/types/persistednumsignal) / [PersistedNullableNumSignal](/types/persistednullablenumsignal)
- [PersistedStringSignal](/types/persistedstringsignal) / [PersistedNullableStringSignal](/types/persistednullablestringsignal)
- [PersistedEnumSignal](/types/persistedenumsignal) / [PersistedNullableEnumSignal](/types/persistednullableenumsignal)
### Simple Usage Example
```dart
// 1. Create or obtain a key-value store adapter (like standard in-memory)
final localStore = SignalsInMemoryKeyValueStore();
// 2. Create the persisted signal with a unique key
final darkModeSignal = PersistedBoolSignal(
false, // Fallback initial value
key: 'settings.dark_mode',
store: localStore,
);
// 3. The value is automatically loaded asynchronously on instantiation.
// Mutating the value synchronously schedules an async save under the hood:
darkModeSignal.value = true; // Automatically persisted to store
```
### Custom Serialization / Complex Objects
To persist complex objects (e.g. custom classes), subclass [PersistedSignal](/types/persistedsignal)
and override the **decode** and **encode** methods, or mixin [PersistedSignalMixin](/types/persistedsignalmixin)
on a custom [Signal](/types/signal) class.
```dart
class User {
final String name;
final int age;
User(this.name, this.age);
Map toJson() => {'name': name, 'age': age};
factory User.fromJson(Map json) => User(json['name'], json['age']);
}
class PersistedUserSignal extends PersistedSignal {
PersistedUserSignal(
super.internalValue, {
required super.key,
required super.store,
});
@override
User decode(String value) => User.fromJson(jsonDecode(value));
@override
String encode(User value) => jsonEncode(value.toJson());
}
```
### Constructors
View Constructors
##### `PersistedSignal(super.internalValue, {required this.key, required this.store, PersistedSignalOptions? options, @Deprecated('Use options: PersistedSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: PersistedSignalOptions(name: ...) instead') String? debugLabel, bool autoInit = true})`
Creates a new PersistedSignal.
### Properties
View Properties
##### `String key`
##### `SignalsKeyValueStore store`
---
## SignalsKeyValueStore
An abstract class defining the persistence adapter contract for [PersistedSignal](/types/persistedsignal).
Implement this interface to bind PersistedSignal to your storage engine of
choice, such as local files, SQLite, SharedPreferences, Hive, or indexedDB.
### Example: Custom Shared Preferences Store (Flutter)
```dart
import 'package:shared_preferences/shared_preferences.dart';
import 'package:signals/signals.dart';
class SharedPreferencesStore implements SignalsKeyValueStore {
final SharedPreferences prefs;
SharedPreferencesStore(this.prefs);
@override
Future getItem(String key) async {
return prefs.getString(key);
}
@override
Future setItem(String key, String value) async {
await prefs.setString(key, value);
}
@override
Future removeItem(String key) async {
await prefs.remove(key);
}
}
```
### Properties
View Properties
##### `static SignalsKeyValueStore defaultStore`
The default store to be used if no store is provided.
### Methods
View Methods
##### `Future setItem(String key, String value)`
Sets an item in the store.
##### `Future getItem(String key)`
Gets an item from the store.
##### `Future removeItem(String key)`
Removes an item from the store.
---
## PersistedSignalMixin
A mixin that adds local persistence capabilities to a standard [Signal](/types/signal).
By mixing in PersistedSignalMixin on a Signal subclass, the signal
will automatically retrieve its stored state on boot and save its state whenever
.value is mutated.
Classes mixing in PersistedSignalMixin must implement:
- **key**: A unique identifier string for the key-value database.
- **store**: An implementation of [SignalsKeyValueStore](/types/signalskeyvaluestore).
### Serialization Customization
By default, the mixin uses standard JSON parsing (jsonDecode / jsonEncode).
If your data type T is not natively supported by JSON, override:
- **decode** to convert the raw string value back into type T.
- **encode** to serialize type T into a string.
### Properties
View Properties
##### `bool loaded`
Whether the signal has been loaded from the store.
### Methods
View Methods
##### `String key`
The key to use for storing the value.
##### `SignalsKeyValueStore store`
The store to use for storing the value.
##### `Future init()`
Initializes the signal by loading the value from the store.
##### `T value`
##### `value(T value)`
##### `Future load()`
Loads the value from the store.
##### `Future save(T value)`
Saves the value to the store.
##### `T decode(String value)`
Decodes the value from a string.
##### `String encode(T value)`
Encodes the value to a string.
---
## SignalsInMemoryKeyValueStore
An in-memory, volatile implementation of [SignalsKeyValueStore](/types/signalskeyvaluestore).
This serves as a fallback engine and does not persist across restarts/reload.
### Properties
View Properties
##### `store`
The in-memory store.
### Methods
View Methods
##### `Future getItem(String key)`
##### `Future removeItem(String key)`
##### `Future setItem(String key, String value)`
---
## PersistedNullableStringSignal
A PersistedSignal that stores a nullable string value.
> [!warning] An empty value is considered null
### Constructors
View Constructors
##### `PersistedNullableStringSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new NullableStringSignal.
### Methods
View Methods
##### `String? decode(String value)`
##### `String encode(String? value)`
---
## PersistedNullableIntSignal
A PersistedSignal that stores a nullable integer value.
### Constructors
View Constructors
##### `PersistedNullableIntSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new NullableIntSignal.
### Methods
View Methods
##### `int? decode(String value)`
##### `String encode(int? value)`
---
## PersistedNullableNumSignal
A PersistedSignal that stores a nullable numeric value.
### Constructors
View Constructors
##### `PersistedNullableNumSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new NullableNumSignal.
### Methods
View Methods
##### `num? decode(String value)`
##### `String encode(num? value)`
---
## PersistedNullableBoolSignal
A PersistedSignal that stores a nullable string value.
### Constructors
View Constructors
##### `PersistedNullableBoolSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new NullableBoolSignal.
### Methods
View Methods
##### `bool? decode(String value)`
##### `String encode(bool? value)`
---
## PersistedNullableDoubleSignal
A PersistedSignal that stores a nullable double value.
### Constructors
View Constructors
##### `PersistedNullableDoubleSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new NullableDoubleSignal.
### Methods
View Methods
##### `double? decode(String value)`
##### `String encode(double? value)`
---
## PersistedNullableEnumSignal
A PersistedSignal that stores a nullable enum value.
### Constructors
View Constructors
##### `PersistedNullableEnumSignal(super.val, String key, this.values, {SignalsKeyValueStore? store})`
Creates a new NullableEnumSignal.
### Properties
View Properties
##### `List values`
### Methods
View Methods
##### `T? decode(String value)`
##### `String encode(T? value)`
---
## PersistedIntSignal
A PersistedSignal that stores an integer value.
### Constructors
View Constructors
##### `PersistedIntSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new IntSignal.
### Methods
View Methods
##### `int decode(String value)`
##### `String encode(int value)`
---
## PersistedBoolSignal
A PersistedSignal that stores a boolean value.
### Constructors
View Constructors
##### `PersistedBoolSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new BoolSignal.
### Methods
View Methods
##### `bool decode(String value)`
##### `String encode(bool value)`
---
## PersistedDoubleSignal
A PersistedSignal that stores an double value.
### Constructors
View Constructors
##### `PersistedDoubleSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new DoubleSignal.
### Methods
View Methods
##### `double decode(String value)`
##### `String encode(double value)`
---
## PersistedNumSignal
A PersistedSignal that stores a numeric value.
### Constructors
View Constructors
##### `PersistedNumSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new NumSignal.
### Methods
View Methods
##### `num decode(String value)`
##### `String encode(num value)`
---
## PersistedStringSignal
A PersistedSignal that stores a string value.
### Constructors
View Constructors
##### `PersistedStringSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new StringSignal.
### Methods
View Methods
##### `String decode(String value)`
##### `String encode(String value)`
---
## PersistedEnumSignal
A PersistedSignal that stores an enum value.
### Constructors
View Constructors
##### `PersistedEnumSignal(super.val, String key, this.values, {SignalsKeyValueStore? store})`
Creates a new EnumSignal.
### Properties
View Properties
##### `List values`
### Methods
View Methods
##### `T decode(String value)`
##### `String encode(T value)`
---
## PersistedSignalOptions
Configuration options for a [PersistedSignal](/types/persistedsignal).
### Constructors
View Constructors
##### `PersistedSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched})`
Creates a new [PersistedSignalOptions](/types/persistedsignaloptions) instance.
### Methods
View Methods
##### `PersistedSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched})`
##### `bool ==(Object other)`
##### `int hashCode`
---
## Page: signals_flutter
Url: https://dartsignals.dev/packages/signals_flutter/index
Description: The signals library exposes four core functions which are the building blocks to model any business logic you can think of.
---
> Version: `7.1.0`
## Installation
```bash
flutter pub add signals_flutter
```
The `signals_flutter` package delivers high-performance, premium reactive UI updates for Flutter applications. By binding signals directly to the widget tree, it enables surgical, localized widget rebuilds without redrawing parent elements or complex state management boilerplate.
## Key Features
- **🚀 Implicit Tracking**: Inherit from [SignalWidget] or [SignalStatefulWidget] to establish automatic, mixin-free reactivity inside widget build methods.
- **⚡ Surgical Rebuilds**: Use [SignalBuilder] to surgically rebuild specific, localized nodes of the widget tree without redrawing parent elements.
- **🔄 Interoperability**: Seamlessly convert back and forth between Dart `Signals`, standard Flutter `ValueNotifiers`, and asynchronous `Streams`.
## Quick Start
```dart
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
final counter = signal(0);
class CounterWidget extends SignalWidget {
const CounterWidget({super.key});
@override
Widget build(BuildContext context) {
// Accessing .value implicitly tracks and rebuilds this widget on change:
return ElevatedButton(
onPressed: () => counter.value++,
child: Text('Count: ${counter.value}'),
);
}
}
```
## Package Contents
---
## Page: SignalEffect
Url: https://dartsignals.dev/packages/signals_flutter/effects/signal-effect
Description: A widget that enables executing scoped reactive side-effects inline within the widget tree.
---
A widget that enables executing scoped reactive side-effects inline within the widget tree.
SignalEffect (and its direct type alias **SignalListener**) allows you to run side-effects
(such as showing snackbars, opening dialogs, navigating, or logging metrics) in response
to signal updates, without triggering rebuilds of the child widget tree.
The [effect](/types/effect) callback runs immediately on mount and dynamically tracks any signals accessed
within its scope. The underlying subscription is automatically disposed when this widget
is removed from the tree. You can optionally return a cleanup function (e.g. void Function())
to run before the next execution or when the widget is disposed.
### Dialog and Snackbar Trigger Example
```dart
final count = signal(0);
class SnackBarTrigger extends StatelessWidget {
const SnackBarTrigger({super.key});
@override
Widget build(BuildContext context) {
return SignalEffect(
effect: (context) {
// Triggers whenever 'count' updates:
if (count.value >= 10) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Limit reached: ${count.value}!')),
);
}
// Optional: return cleanup callback
return () => print('Cleaning up effect');
},
child: ElevatedButton(
onPressed: () => count.value++,
child: const Text('Increment and Watch'),
),
);
}
}
```
> [!IMPORTANT]
> Do not perform synchronous state changes or trigger widget rebuilds directly inside
> the effect callback to prevent infinite reactive loops.
### Constructors
View Constructors
##### `SignalEffect({dynamic Function(BuildContext context)? effect, @Deprecated('Use effect instead') void Function(BuildContext context)? callback, required this.child, this.debugLabel, super.key})`
Creates a [SignalEffect](/types/signaleffect) widget.
The [effect](/types/effect) is executed inside a reactive effect.
The **child** is rendered normally.
### Properties
View Properties
##### `Widget child`
The child widget to render.
##### `String? debugLabel`
Optional debug label for the effect.
### Methods
View Methods
##### `dynamic Function(BuildContext context) effect`
Gets the effect callback to run inside the reactive effect.
##### `State createState()`
---
## Page: SignalPainterWidget
Url: https://dartsignals.dev/packages/signals_flutter/widgets/signal-painter-widget
Description: A high-performance, leaf render-object widget driven by a double progress signal.
---
A high-performance, leaf render-object widget driven by a double progress signal.
SignalPainterWidget bypasses the entire widget build and layout phases, subscribing
directly to a **progress** signal and rendering on the canvas. When **progress** updates,
only the GPU paint phase is run.
### Example
```dart
final progress = signal(0.0);
@override
Widget build(BuildContext context) {
return SignalPainterWidget(
progress: progress,
painter: (canvas, size, value) {
final paint = Paint()
..color = Colors.blue
..style = PaintingStyle.stroke
..strokeWidth = 4.0;
canvas.drawCircle(
Offset(size.width / 2, size.height / 2),
value * 50.0,
paint,
);
},
);
}
```
### Constructors
View Constructors
##### `SignalPainterWidget({super.key, required this.progress, required this.painter})`
Creates a new [SignalPainterWidget](/types/signalpainterwidget).
### Properties
View Properties
##### `core.ReadonlySignal progress`
The progress signal whose value will be passed to **painter**.
##### `void Function(Canvas canvas, Size size, double value) painter`
The custom painting callback function.
### Methods
View Methods
##### `RenderSignalBox createRenderObject(BuildContext context)`
##### `void updateRenderObject(BuildContext context, RenderSignalBox renderObject)`
---
## Page: WatchBuilder
Url: https://dartsignals.dev/packages/signals_flutter/widgets/watch-builder
Description: WatchBuilder.
---
## WatchBuilder
To watch a signal for changes in Flutter, use the WatchBuilder widget. This will only rebuild this widget method and not the entire widget tree.
```dart
final signal = signal(10);
...
@override
Widget build(BuildContext context) {
return WatchBuilder(
child: const Icon(Icons.add),
builder: (context, child) => Row(children: [Text('$signal'), child!]),
);
}
```
### Constructors
View Constructors
##### `WatchBuilder({super.key, required this.builder, this.debugLabel, this.dependencies = const [], this.child})`
Minimal builder for signal changes that rerender a widget tree.
```dart
final counter = signal(0);
...
WatchBuilder(
builder: (context, child) => Text('$counter')
)
```
### Properties
View Properties
##### `T Function(BuildContext context, Widget? child) builder`
The widget to rebuild when any signals change
##### `String? debugLabel`
Optional debug label to use for devtools
##### `Widget? child`
Cached widget to pass in
##### `List> dependencies`
List of optional dependencies to watch
### Methods
View Methods
##### `Widget build(BuildContext context)`
---
## Page: Watch
Url: https://dartsignals.dev/packages/signals_flutter/widgets/watch
Description: A deprecated widget for watching signal changes in the widget tree.
---
A deprecated widget for watching signal changes in the widget tree.
DEPRECATED : Use SignalBuilder instead for superior, self-contained reactivity
and consistent API design.
### Migration to [SignalBuilder](/types/signalbuilder)
```dart
// Deprecated legacy pattern:
Watch((context) => Text('$counter'))
// Modern, streamlined pattern:
SignalBuilder(builder: (context) => Text('${counter.value}'))
```
### Constructors
View Constructors
##### `Watch(this.builder, {super.key, this.debugLabel, this.dependencies = const []})`
Minimal builder for signal changes that rerender a widget tree.
```dart
final counter = signal(0);
...
Watch((context) => Text('$counter'))
```
##### `Watch.builder({super.key, required this.builder, this.debugLabel, this.dependencies = const []})`
Drop in replacement for the Flutter builder widget.
```dart
final counter = signal(0);
...
- Builder(
+ Watch.builder(
builder: (context) {
return Text('$counter');
}
)
```
### Properties
View Properties
##### `T Function(BuildContext context) builder`
The widget to rebuild when any signals change
##### `String? debugLabel`
Optional debug label to use for devtools
##### `List> dependencies`
List of optional dependencies to watch
### Methods
View Methods
##### `Widget build(BuildContext context)`
---
## Page: SignalWidget
Url: https://dartsignals.dev/packages/signals_flutter/widgets/signal-widget
Description: A reactive StatelessWidget that implicitly tracks and rebuilds on signal changes.
---
A reactive **StatelessWidget** that implicitly tracks and rebuilds on signal changes.
SignalWidget establishes a dynamic reactive context directly at the Flutter element layer.
Any signal accessed via .value inside the **build** method is **implicitly tracked** and
subscribed to. When any of these signals mutate, only this widget is rebuilt.
This offers a clean, Javascript-style reactivity experience without needing manual
builder widgets (like SignalBuilder) or deprecated context watch extensions.
### Implicit Reactivity Example (Stateless)
```dart
final username = signal('Rody');
final status = signal('Online');
class UserProfileView extends SignalWidget {
const UserProfileView({super.key});
@override
Widget build(BuildContext context) {
// 'username' and 'status' are implicitly tracked on access:
return Column(
children: [
Text('Name: ${username.value}'),
Text('Status: ${status.value}'),
],
);
}
}
```
> [!IMPORTANT]
> Only signals accessed *synchronously* during the execution of the build method are tracked.
> Signals read inside async callbacks, listeners, or deferred tasks are not subscribed to.
### Constructors
View Constructors
##### `SignalWidget({super.key})`
Constructor for [SignalWidget](/types/signalwidget).
### Methods
View Methods
##### `StatelessElement createElement()`
---
## Page: MultiSignalProvider
Url: https://dartsignals.dev/packages/signals_flutter/widgets/multi-signal-provider
Description: A dependency-injection / state propagation widget that allows passing.
---
A dependency-injection / state propagation widget that allows passing
multiple reactive signals down the Flutter widget tree.
This avoids the deeply nested trees that result from nesting multiple
single [SignalProvider](/types/signalprovider) widgets.
### Constructors
View Constructors
##### `MultiSignalProvider({super.key, required this.providers, required this.child})`
Exposes multiple [SignalProvider](/types/signalprovider) widgets inside a flat list.
### Properties
View Properties
##### `List providers`
The list of single [SignalProvider](/types/signalprovider) widgets to nest.
##### `Widget child`
The widget subtree that will have access to the provided signals.
### Methods
View Methods
##### `Widget build(BuildContext context)`
---
## Page: SignalBuilder
Url: https://dartsignals.dev/packages/signals_flutter/widgets/signal-builder
Description: A premium, surgical builder widget that rebuilds locally on signal changes.
---
A premium, surgical builder widget that rebuilds locally on signal changes.
SignalBuilder tracks any signals read dynamically within its builder callback and
automatically subscribes to them. When any of these signals update, only the SignalBuilder
is rebuilt, preventing costly rebuilds of the surrounding widget subtree.
This widget provides predictable, immediate, and memory-safe subscription management,
automatically tearing down all active signal subscriptions when the builder is unmounted.
### Surgical Counter Example
```dart
final count = signal(0);
class CounterPage extends StatelessWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text('This static text never rebuilds!'),
const SizedBox(height: 20),
// Only this specific Text widget rebuilds when 'count' changes:
SignalBuilder(
builder: (context) => Text(
'Count: ${count.value}',
style: Theme.of(context).textTheme.headlineMedium,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => count.value++,
child: const Icon(Icons.add),
),
);
}
}
```
> [!TIP]
> Keep SignalBuilder widgets as small and leaf-level as possible in your tree to maximize
> rendering performance.
### Constructors
View Constructors
##### `SignalBuilder({super.key, required this.builder, this.dependencies = const []})`
Creates a [SignalBuilder](/types/signalbuilder) widget.
The **builder** is called to construct the widget tree and tracks read signals.
The optional **dependencies** allows explicitly specifying a list of signals to watch
regardless of whether they are read during the build phase.
### Properties
View Properties
##### `Widget Function(BuildContext context) builder`
The widget to rebuild when any signals change.
##### `List> dependencies`
List of optional dependencies to watch.
### Methods
View Methods
##### `Widget build(BuildContext context)`
---
## Page: SignalProxyWidget
Url: https://dartsignals.dev/packages/signals_flutter/widgets/signal-proxy-widget
Description: A low-level widget wrapper around RenderSignalProxyBox for custom painting / sizing needs.
---
A low-level widget wrapper around [RenderSignalProxyBox](/types/rendersignalproxybox) for custom painting / sizing needs.
### Constructors
View Constructors
##### `SignalProxyWidget({super.key, required this.signals, super.child})`
Creates a new [SignalProxyWidget](/types/signalproxywidget).
### Properties
View Properties
##### `List> signals`
The list of signals to observe.
### Methods
View Methods
##### `RenderSignalProxyBox createRenderObject(BuildContext context)`
##### `void updateRenderObject(BuildContext context, RenderSignalProxyBox renderObject)`
---
## Page: SignalCustomPaint
Url: https://dartsignals.dev/packages/signals_flutter/widgets/signal-custom-paint
Description: A high-performance canvas painting widget that subscribes to signals and renders.
---
A high-performance canvas painting widget that subscribes to signals and renders
directly on the GPU, completely bypassing the widget build and layout phases.
Use SignalCustomPaint in performance-critical rendering scenarios like real-time charts,
complex visual animations, particle systems, or game loops.
### Constructors
View Constructors
##### `SignalCustomPaint({super.key, required this.painter, super.child})`
Creates a new [SignalCustomPaint](/types/signalcustompaint).
### Properties
View Properties
##### `SignalCustomPainter painter`
The painter to draw on the canvas.
### Methods
View Methods
##### `RenderSignalCustomPaint createRenderObject(BuildContext context)`
##### `void updateRenderObject(BuildContext context, RenderSignalCustomPaint renderObject)`
---
## Page: SignalAnimatedBuilder
Url: https://dartsignals.dev/packages/signals_flutter/widgets/signal-animated-builder
Description: A reactive builder widget designed for performance optimizations using child caching.
---
A reactive builder widget designed for performance optimizations using child caching.
SignalAnimatedBuilder is the modern, drop-in replacement for Flutter's native **AnimatedBuilder**
or the deprecated WatchBuilder.
When you have a complex or computationally heavy widget subtree that does *not* depend on
any signal values, you should pass it as the **child** parameter. This subtree is cached
and is *never* rebuilt when the signals mutate, delivering a massive rendering boost.
### Performance Optimization Example
```dart
final count = signal(0);
class OptimizedCounter extends StatelessWidget {
const OptimizedCounter({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
body: SignalAnimatedBuilder(
// 1. Define the heavy subtree once. It will be cached:
child: const HeavyComplexSubtreeWidget(),
// 2. The builder receives the cached child:
builder: (context, cachedChild) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Dynamic Count: ${count.value}'),
const SizedBox(height: 20),
// 3. Render the cached child directly:
cachedChild!,
],
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () => count.value++,
child: const Icon(Icons.add),
),
);
}
}
```
> [!TIP]
> Always use SignalAnimatedBuilder when rendering dynamic signal values alongside static,
> heavy subtrees. This minimizes CPU cycles and avoids rebuilding static layouts on frame updates.
### Constructors
View Constructors
##### `SignalAnimatedBuilder({super.key, required this.builder, this.child, this.debugLabel, this.dependencies = const []})`
Creates a [SignalAnimatedBuilder](/types/signalanimatedbuilder) widget.
The **builder** constructs the widget tree around the cached **child**.
The **dependencies** is an optional list of signals to watch explicitly.
### Properties
View Properties
##### `Widget Function(BuildContext context, Widget? child) builder`
The widget to rebuild when any signals change.
##### `Widget? child`
Optional pre-built child subtree that does not rebuild.
##### `String? debugLabel`
Optional debug label to use for devtools.
##### `List> dependencies`
List of optional dependencies to watch.
### Methods
View Methods
##### `Widget build(BuildContext context)`
---
## Page: SignalProvider
Url: https://dartsignals.dev/packages/signals_flutter/widgets/signal-provider
Description: A premium dependency-injection / state propagation widget that allows passing.
---
A premium dependency-injection / state propagation widget that allows passing
reactive signals down the Flutter widget tree using **InheritedNotifier**.
SignalProvider makes a signal accessible to all child widgets in the subtree.
Any child widget that reads the signal using SignalProvider.of(context) will
automatically rebuild when the signal's value changes, while parent widgets remain unaffected.
For version 7, SignalProvider is a stateful widget that manages the lifecycle of the created
signal, ensuring it is persisted across parent rebuilds and automatically calling dispose()
when the provider is unmounted to prevent memory leaks.
### Example Usage
#### 1. Standard Constructor (Manages Lifecycle)
```dart
SignalProvider(
create: () => CounterSignal(0),
child: const CounterDisplay(),
)
```
#### 2. Value Constructor (Exposes Existing Instance)
If the signal is created elsewhere (e.g. in a StatefulWidget's State or globally) and you want
to expose it without managing its lifecycle or calling dispose, use [SignalProvider.value](/types/signalprovider#value):
```dart
SignalProvider.value(
value: myCounterSignal,
child: const CounterDisplay(),
)
```
#### 3. Multi-Providing Multiple Signals
Wrap multiple providers in a flat list to avoid deeply nested trees using [SignalProvider.multi](/types/signalprovider#multi):
```dart
SignalProvider.multi(
providers: [
SignalProvider(create: () => Counter()),
SignalProvider(create: () => User()),
],
child: const MyApp(),
)
```
### Constructors
View Constructors
##### `SignalProvider({super.key, required T Function() create, this.child, this.dispose})`
Creates a [SignalProvider](/types/signalprovider) that manages the lifecycle of a created signal.
The **create** callback is invoked once to instantiate the signal.
When this provider is unmounted, it automatically calls dispose() on the signal.
##### `SignalProvider.value({super.key, required T value, this.child})`
Exposes an existing signal **value** to the widget tree.
Unlike the default constructor, the signal is NOT created by this provider,
and its lifecycle (including disposal) must be managed elsewhere.
##### `SignalProvider._({super.key, required T Function()? create, required T? value, required this.child, required this.dispose})`
Private constructor for internal cloning and subclass usage.
##### `SignalProvider.multi({Key? key, required List providers, required Widget child})`
Creates a [SignalProvider](/types/signalprovider) that wraps multiple other [SignalProvider](/types/signalprovider)s.
This is a convenience constructor to avoid deeply nested trees when
providing multiple signals.
### Properties
View Properties
##### `Widget? child`
The widget subtree that will have access to the signal.
##### `void Function(T)? dispose`
An optional custom dispose callback.
### Methods
View Methods
##### `SignalProvider copyWith(Widget child)`
Returns a clone of this [SignalProvider](/types/signalprovider) with a new **child** widget.
Used internally by [MultiSignalProvider](/types/multisignalprovider).
##### `State> createState()`
##### `static SignalProvider? providerOf(BuildContext context, {bool listen = true})`
Retrieves the [SignalProvider](/types/signalprovider) widget itself from the ancestor path.
Note: Prefer using SignalProvider.of(context) to retrieve the reactive
signal directly.
##### `static T? of(BuildContext context, {bool listen = true})`
Retrieves the reactive signal instance of type **T** directly from the nearest [SignalProvider](/types/signalprovider).
- If **listen** is true (default), the calling widget will automatically subscribe
to the signal and rebuild whenever the signal's value changes.
- If **listen** is false, the signal is returned without establishing a subscription.
Use listen: false when mutating the signal inside action callbacks.
---
## Page: Untracked
Url: https://dartsignals.dev/packages/signals_core/core/untracked
Description: Runs a callback function fn that can read signal values without establishing a reactive subscription.
---
Runs a callback function **fn** that can read signal values without establishing a reactive subscription.
Normally, reading a signal's value (via .value or ()) inside an [effect](/types/effect) or a [computed](/types/computed) callback
automatically subscribes the surrounding context to that signal. If the signal changes, the context is
re-executed.
In some scenarios, you want to read a signal's current value inside a reactive context but *avoid* creating
a subscription. This is where [untracked](/types/untracked) is useful. It temporarily suspends active tracking, executes **fn**,
and then restores tracking.
Parameters:
- **fn**: The callback function to execute. Any signal read inside this callback will not register a dependency.
Returns:
- The value returned by the callback function **fn**.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
void main() {
final counter = signal(0);
final loggingThreshold = signal(5);
effect(() {
final currentCount = counter.value; // Establishing a subscription to `counter`
// We want to read `loggingThreshold` but we do NOT want this effect to
// trigger whenever `loggingThreshold` changes.
final threshold = untracked(() => loggingThreshold.value);
if (currentCount > threshold) {
print("Counter ($currentCount) has exceeded the threshold ($threshold)!");
}
});
counter.value = 6; // Prints: "Counter (6) has exceeded the threshold (5)!"
// Updating the threshold will NOT trigger the effect, because it was read inside `untracked`
loggingThreshold.value = 10;
}
```
untracked is particularly useful inside event handlers, conditional logging, or when you are performing
a write to a signal based on another signal's value inside an effect to prevent infinite dependency cycles.
Be cautious when using untracked , as it bypasses the dependency tracking system. If the values read inside
untracked change, your reactive side effects or computed derivations will not automatically re-run.
---
## Page: LinkedSignal
Url: https://dartsignals.dev/packages/signals_core/core/linked-signal
Description: A highly powerful, mutable computed signal that derives its default value from an underlying source,.
---
A highly powerful, mutable computed signal that derives its default value from an underlying source,
but allows manual write overrides. Crucially, **whenever the underlying source value changes, the signal**
**automatically discards any local manual overrides and resets back to the newly computed default.**
This hybrid behavior is the perfect solution for synchronizing local edit state with external remote state.
### 1. Real-World Use Case: Profile Form Editor
Imagine you are building a profile editor where the user can modify their username:
- The initial/remote username is fetched from a database and held in a source signal.
- The text input field is bound to a local signal.
- The user should be able to edit the field locally (overriding the remote default).
- If the selected user changes (e.g., they switch to a different profile in a list), the text field must automatically discard any local changes and reset to the new user's username.
```dart
// The remote/source state
final selectedUser = signal(User(id: 1, name: 'Alice'));
// The local editable state linked to the remote source
final username = linkedSignal(() => selectedUser.value.name);
print(username.value); // 'Alice'
// User edits the text field:
username.value = 'Bob';
print(username.value); // 'Bob' (local override active)
// Switch remote profile:
selectedUser.value = User(id: 2, name: 'Charlie');
// Local overrides are discarded and reset to the new source:
print(username.value); // 'Charlie'
```
### 2. Custom Computations using LinkedSignalOptions
By default, a linked signal directly passes the source value through. You can customize this mapping using a custom computation function that has access to both the current source value and the previous state:
```dart
final counter = signal(10);
final doubled = linkedSignal(
() => counter.value,
options: LinkedSignalOptions(
computation: (sourceVal, prev) {
print('Source changed to $sourceVal. Previous value was: ${prev?.value}');
return sourceVal * 2;
},
),
);
```
### 3. Custom Source Equality
To prevent unnecessary resets, you can supply a custom sourceEquality callback. The signal will only reset when the equality check returns false:
```dart
final selectedUser = signal(User(id: 1, name: 'Alice'));
final username = linkedSignal(
() => selectedUser.value,
options: LinkedSignalOptions(
// Only reset when the user ID actually changes:
sourceEquality: (a, b) => a.id == b.id,
),
);
```
Always use LinkedSignal rather than manual effect listeners to synchronize local editable values with remote defaults. It is simpler, avoids race conditions, and consumes significantly less memory.
### Constructors
View Constructors
##### `LinkedSignal({required S Function() source, LinkedSignalOptions? options})`
Creates a new [LinkedSignal](/types/linkedsignal).
### Methods
View Methods
##### `bool set(T val, {bool force = false})`
##### `T value`
##### `value(T val)`
##### `void dispose()`
---
## linkedSignal
{@macro linked_signal}
---
## Page: Effect
Url: https://dartsignals.dev/packages/signals_core/core/effect
Description: Represents a passive observer that runs arbitrary side-effect code in response to signal changes.
---
Represents a passive observer that runs arbitrary side-effect code in response to signal changes.
An [Effect](/types/effect) tracks which signals are accessed within its callback function,
and automatically schedules itself to re-run whenever those dependencies change.
Under the hood, the reactivity engine tracks reads on .value inside the active effect block.
Once the block completes, a subscription is registered for each accessed signal. When any of those signals
mutate, the effect is added to the microtask queue and executed synchronously during the next tick.
Do not modify a tracked signal directly inside an effect callback, as this will trigger another execution
of the same effect, causing an infinite loop (cycle) and throwing a cycle detection error.
To read a signal non-reactively, use .peek().
### Example Usage
#### 1. Standard Side-Effect
```dart
import 'package:preact_signals/preact_signals.dart';
final count = signal(0);
void main() {
// Creates and immediately starts the effect
final logger = Effect(() {
print('Active count is: ${count.value}');
});
count.value = 1; // Prints: "Active count is: 1"
logger.dispose();
}
```
#### 2. Effect Cleanup Callback
If your effect returns a function, that function is registered as a **cleanup callback**.
The cleanup callback is executed right before the next effect run, or when the effect is disposed.
This is highly useful for cleaning up timers, controllers, or other subscriptions:
```dart
final query = signal('search_term');
final searchEffect = Effect(() {
final currentQuery = query.value;
print('Initiating search for: $currentQuery');
final timer = Timer(Duration(milliseconds: 500), () {
print('Search completed for: $currentQuery');
});
// Return cleanup callback
return () {
print('Cancelling previous search timer');
timer.cancel();
};
});
```
### Constructors
View Constructors
##### `Effect(this.fn, {String? name, EffectOptions? options})`
Creates a new [Effect](/types/effect) instance with the passive side-effect callback **fn**.
You can optionally provide:
- A **name** for debugging/observer tracing.
```dart
final effectObj = Effect(() => print(count.value), name: 'count_logger');
```
### Properties
View Properties
##### `int globalId`
##### `int flags`
##### `String? name`
The name of the effect for debugging.
### Methods
View Methods
##### `void notify()`
##### `void dispose()`
Disposes of the effect, stopping future callback executions,
executing any registered cleanup routines, and unsubscribing from all dependency signals.
##### `void Function() call()`
Activates/Runs the effect immediately.
Returns a bound disposer function that can be called to stop the effect.
---
## effect
Creates and immediately executes a new reactive [Effect](/types/effect).
Returns a bound disposer function that can be called to stop the effect and unsubscribe
it from all tracked signals.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
final count = signal(0);
final dispose = effect(() {
print('Count is: ${count.value}');
return () => print('Cleaning up!');
});
void main() {
count.value = 10; // Prints: "Cleaning up!" then "Count is: 10"
dispose(); // Stops the effect and unsubscribes
}
```
---
## Page: ReadonlySignal
Url: https://dartsignals.dev/packages/signals_core/core/readonly
Description: An interface for read-only signals.
---
An interface for read-only signals.
A [ReadonlySignal](/types/readonlysignal) is a reactive container whose value can be read but not directly mutated.
Under the hood, any [Signal](/types/signal) implements or can be cast/exposed as a [ReadonlySignal](/types/readonlysignal). This
is a core architectural pattern for encapsulating state: classes can modify state internally
using a private mutable Signal, while exposing a public ReadonlySignal to consumers to
enforce unidirectional data flow.
Whenever the underlying value changes, any active [effect](/types/effect) or [computed](/types/computed) signal that reads this
signal's value will automatically be re-evaluated.
### Example Usage
````dart
import 'package:preact_signals/preact_signals.dart';
class CounterController {
// Keep the mutable state private to the controller
final _counter = signal(0);
// Expose a public read-only signal to external consumers
ReadonlySignal get counter => _counter;
void increment() {
_counter.value++;
}
void decrement() {
_counter.value--;
}
}
void main() {
final controller = CounterController();
// React to updates from the read-only signal
final dispose = effect(() {
print("The current count is: ${controller.counter.value}");
});
// controller.counter.value = 10; // Error: Cannot mutate a ReadonlySignal!
controller.increment(); // Prints: "The current count is: 1"
controller.increment(); // Prints: "The current count is: 2"
dispose();
}
````
Use [ReadonlySignal] to prevent consumers of your stores or controllers from modifying state
bypassing the controller's methods. This ensures consistent, predictable, and traceable mutations
throughout your application.
### Methods
View Methods
##### `int globalId`
Global ID of the signal
##### `T value`
Compute the current value
##### `String? name`
The name of the signal for debugging purposes.
##### `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.
##### `String toString()`
##### `dynamic toJson()`
Convert value to JSON
##### `T call()`
Return the value when invoked
##### `T get()`
Helper method to get the current value
##### `T 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().
```dart
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.
##### `void Function() subscribe(void Function(T value) fn)`
Subscribe to value changes with a dispose function
---
## readonly
Creates a new read-only signal initialized with **value**.
This function returns a [ReadonlySignal](/types/readonlysignal) containing **value**. Under the hood, a mutable [Signal](/types/signal)
is created, but it is returned under the [ReadonlySignal](/types/readonlysignal) interface to prevent modification by clients.
This is particularly useful when you need to expose a constant reactive value, or bridge some external,
immutable value source into the signals reactivity system.
Parameters:
- **value**: The initial value held by the read-only signal.
- **options**: Optional configuration options (e.g., custom debug name or lifecycle callbacks like watched/unwatched).
Returns:
- A [ReadonlySignal](/types/readonlysignal) containing the initial value.
### Example Usage
````dart
import 'package:preact_signals/preact_signals.dart';
final configUrl = readonly('https://api.example.com');
void main() {
effect(() {
print("Connecting to: ${configUrl.value}");
});
}
````
If you are trying to derive a value from other signals, do not use [readonly]. Use [computed] instead
to ensure the derived signal automatically re-evaluates when its source signals change.
---
## Page: Signal
Url: https://dartsignals.dev/packages/signals_core/core/signal
Description: 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](/types/computed)) or effects (like [effect](/types/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
```dart
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:
```dart
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](/types/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.
```dart
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](/types/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.
```dart
final lazyUser = Signal.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](/types/effect) or [computed](/types/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.
```dart
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](/types/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
```dart
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](/types/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>] 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.
```dart
import 'package:signals_core/signals_core.dart';
final tags = {}.$;
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 elements)`
##### `void removeWhere(bool Function(E element) test)`
##### `void retainAll(Iterable elements)`
##### `void retainWhere(bool Function(E element) test)`
---
## ComputedOptions
Configuration options for a [Computed](/types/computed) signal.
Enables configuring debugging names and subscription state event listeners
for computed derivations.
### Example Usage
```dart
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](/types/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](/types/signal).
Extends [ReadonlySignalOptions](/types/readonlysignaloptions) to also support custom **equality** checkers,
which control whether incoming values trigger update events.
### Example Usage
```dart
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](/types/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 [Effect](/types/effect)s.
Permits naming the effect for debugging, performance profiling,
and tracing within the signals developer tools.
### Example Usage
```dart
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](/types/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](/types/readonlysignal), enabling direct reactive logical conjunction (&), disjunction (|), and exclusive or (^) operations.
```dart
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](/types/readonlysignal), providing convenient reactive math and comparison operations without needing to manually unwrap .value.
```dart
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:
```dart
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 **int**s, 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:
```dart
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.
```dart
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:
```dart
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:
```dart
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:
```dart
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:
```dart
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:
```dart
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](/types/readonlysignal), enabling direct reactive arithmetic and rounding operations on double signals.
```dart
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).
```dart
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).
```dart
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).
```dart
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).
```dart
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.
```dart
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.
```dart
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.
```dart
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.
```dart
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>], providing delegators to compute set operations reactively.
```dart
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 other)`
##### `Set difference(Set other)`
##### `Set intersection(Set other)`
##### `E? lookup(Object? object)`
##### `Set union(Set other)`
---
## SignalStreamUtils
Extension on **Stream** to provide convenient utilities to convert streams into reactive signals.
```dart
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> dependencies = const [], void Function()? onDone, AsyncSignalOptions? options})`
Convert a stream to a signal
```dart
import 'package:signals/signals.dart';
Stream 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](/types/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](/types/asyncstate).
```dart
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
```dart
import 'package:preact_signals/preact_signals.dart';
final count = signal(0);
final name = signal('Jane');
```
---
## lazySignal
Lazy signal that can be created with type T that
the value will be assigned later.
```dart
final db = lazySignal();
...
db.value = DatabaseConnect(...);
```
---
## 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](/types/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](/types/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.
```dart
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](/types/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
```dart
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](/types/computed) signal.
### Methods
View Methods
##### `Computed $`
Return a cached, derived [Computed](/types/computed) signal from this getter function.
```dart
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](/types/signal).
### Methods
View Methods
##### `Signal $`
Lift a primitive **double** into a reactive [Signal](/types/signal).
```dart
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](/types/signal).
### Methods
View Methods
##### `Signal $`
Lift a primitive **bool** into a reactive [Signal](/types/signal).
```dart
import 'package:signals_core/signals_core.dart';
final isEnabled = true.$;
print(isEnabled.value); // true
```
---
## SignalIterableUtils
Utility extension methods on **Iterable** to convert them to [IterableSignal](/types/iterablesignal)s.
### 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](/types/iterablesignal).
This returns an [IterableSignal](/types/iterablesignal) initialized with the current collection.
```dart
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](/types/signal).
### Methods
View Methods
##### `Signal $`
Lift a primitive **num** into a reactive [Signal](/types/signal).
```dart
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](/types/signal).
### Methods
View Methods
##### `Signal> $`
Lift a primitive **Set** into a reactive [Signal>].
```dart
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 [ListSignal](/types/listsignal)s.
### 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](/types/listsignal).
```dart
import 'package:signals/signals.dart';
final myList = [1, 2, 3];
final signal = myList.toSignal();
```
---
## SignalMapUtils
Utility extension methods on **Map** to convert them to [MapSignal](/types/mapsignal)s.
### Methods
View Methods
##### `MapSignal toSignal({MapSignalOptions? 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](/types/mapsignal).
```dart
import 'package:signals/signals.dart';
final myMap = {'key': 'value'};
final signal = myMap.toSignal();
```
---
## SignalSetUtils
Utility extension methods on **Set** to convert them to [SetSignal](/types/setsignal)s.
### 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](/types/setsignal).
```dart
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> dependencies = const [], AsyncSignalOptions? options})`
Convert an existing future to [FutureSignal](/types/futuresignal)
```dart
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>]
### 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](/types/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](/types/changestacksignal).
### Constructors
View Constructors
##### `ChangeSignalOptions({this.limit, super.name, super.autoDispose, super.watched, super.unwatched})`
Creates a new [ChangeSignalOptions](/types/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](/types/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).
```dart
RegExp exp = RegExp(r'(\w+)');
var str = 'Dash is a bird';
Iterable 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.
```dart
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](/types/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:
```dart
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
'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:
```dart
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**.
```dart
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**:
```dart
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:
```dart
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:
```dart
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:
```dart
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**:
```dart
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.
```dart
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:
```dart
const string = 'dart' + 'lang'; // 'dartlang'
```
##### `String substring(int start, [int? end])`
The substring of this string from **start**, inclusive, to **end**, exclusive.
Example:
```dart
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:
```dart
final trimmed = '\tDart is fun\n'.trim();
print(trimmed); // 'Dart is fun'
```
Otherwise, the original string itself is returned:
```dart
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:
```plaintext
0009..000D ; White_Space # Cc ..
0020 ; White_Space # Zs SPACE
0085 ; White_Space # Cc
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.
```dart
final string = ' Dart '.trimLeft();
print(string); // 'Dart '
```
##### `String trimRight()`
The string without any trailing whitespace.
As **trim**, but only removes trailing whitespace.
```dart
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.
```dart
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**.
```dart
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**.
```dart
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:
```dart
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:
```dart
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:
```dart
'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.
```dart
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]}>')); // 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**.
```dart
'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**:
```dart
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:
```dart
this.substring(0, start) + replacement + this.substring(end)
```
Example:
```dart
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.
```dart
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:
```dart
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.
```dart
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:
```dart
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).
```dart
const string = '';
print(string.split('')); // []
print(string.split('a')); // []
```
Splitting with an empty pattern splits the string into single-code unit
strings.
```dart
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:
```dart
// 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:
```dart
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.
```dart
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.
```dart
'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.
```dart
'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](/types/iterablesignal).
### Constructors
View Constructors
##### `IterableSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched, super.equality = const SignalDeepEquality()})`
Creates a new [IterableSignalOptions](/types/iterablesignaloptions) instance.
### Methods
View Methods
##### `IterableSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, SignalEquality>? equality})`
##### `bool ==(Object other)`
##### `int hashCode`
---
## EnumSignalExtension
Helper extensions for [ReadonlySignal](/types/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:
```dart
enum MyEnum {
value1,
value2
}
```
the result of MyEnum.value1.name is the string "value1".
---
## ReadonlyListSignalExtension
Helper extensions for [ReadonlySignal](/types/readonlysignal)
### Methods
View Methods
##### `List cast()`
##### `E last`
##### `List +(List other)`
##### `E [](int index)`
##### `Map 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])`
---
## IntSignalExtension
Helper extensions for [ReadonlySignal](/types/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.
```dart
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:
```dart
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:
```dart
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:
```dart
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:
```dart
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:
```dart
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:
```dart
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.
```dart
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**.
```dart
(-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:
```dart
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.
```dart
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**.
```dart
// 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:
```dart
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.
```dart
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:
```dart
// 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
```
---
## ReadonlyMapSignalExtension
Helper extensions for [ReadonlySignal](/types/readonlysignal)
### Methods
View Methods
##### `V? [](Object? key)`
##### `Map cast()`
##### `bool containsKey(Object? key)`
##### `bool containsValue(Object? value)`
##### `Iterable> entries`
##### `bool isEmpty`
##### `bool isNotEmpty`
##### `Iterable keys`
##### `int length`
##### `Map map(MapEntry Function(K key, V value) convert)`
##### `Iterable values`
---
## ListSignalOptions
Configuration options for a [ListSignal](/types/listsignal).
### Constructors
View Constructors
##### `ListSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched, super.equality = const SignalDeepEquality()})`
Creates a new [ListSignalOptions](/types/listsignaloptions) instance.
### Methods
View Methods
##### `ListSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, SignalEquality>? equality})`
##### `bool ==(Object other)`
##### `int hashCode`
---
## MapSignalOptions
Configuration options for a [MapSignal](/types/mapsignal).
### Constructors
View Constructors
##### `MapSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched, super.equality = const SignalDeepEquality()})`
Creates a new [MapSignalOptions](/types/mapsignaloptions) instance.
### Methods
View Methods
##### `MapSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, SignalEquality>? equality})`
##### `bool ==(Object other)`
##### `int hashCode`
---
## SetSignalOptions
Configuration options for a [SetSignal](/types/setsignal).
### Constructors
View Constructors
##### `SetSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched, super.equality = const SignalDeepEquality()})`
Creates a new [SetSignalOptions](/types/setsignaloptions) instance.
### Methods
View Methods
##### `SetSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, SignalEquality>? 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
```dart
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](/types/linkedsignal).
### Constructors
View Constructors
##### `LinkedSignalOptions({this.computation, this.sourceEquality, super.name, super.autoDispose})`
Creates [LinkedSignalOptions](/types/linkedsignaloptions).
### Properties
View Properties
##### `T Function(S source, LinkedSignalPreviousState? 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 copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, T Function(S source, LinkedSignalPreviousState? previous)? computation, bool Function(S a, S b)? sourceEquality})`
##### `bool ==(Object other)`
##### `int hashCode`
---
## ListSignalExtension
Helper extensions for [Signal](/types/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](/types/signal)
### Methods
View Methods
##### `void []=(K key, V value)`
##### `void addAll(Map other)`
##### `void addEntries(Iterable> 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](/types/signal)
---
## SignalComparableExtensions
Extensions for **Comparable**
### Methods
View Methods
##### `Signal> $`
Return a signal from a Comparable value
---
## SignalIterableExtensions
Extensions for **Iterable**
### Methods
View Methods
##### `Signal> $`
Return a signal from a Iterable value
---
## SignalListExtensions
Extensions for **List**
### Methods
View Methods
##### `Signal> $`
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> $`
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()`
---
## Page: Computed
Url: https://dartsignals.dev/packages/signals_core/core/computed
Description: Represents a derived, read-only reactive state value computed from one or more other signals.
---
Represents a derived, read-only reactive state value computed from one or more other signals.
Computed signals are **lazily evaluated** and **memoized (cached)**. Their callback function **fn**
is only executed when its value is read *and* one of its upstream dependencies has mutated since the
last calculation. If none of the dependencies have changed, the cached value is returned directly.
Under the hood, a Computed signal tracks its sources dynamically. If a conditional branch inside
the computation changes such that certain signals are no longer read, those signals are automatically pruned
from the dependency list, preventing redundant triggers.
The computation callback fn should be pure and side-effect free. Writing to other signals or
performing network/database operations inside a computed callback is a critical anti-pattern that can lead to
infinite loops (cycles) or unpredictable state transitions.
### Example Usage
#### 1. Basic Derived State
```dart
import 'package:preact_signals/preact_signals.dart';
void main() {
final firstName = Signal('Jane');
final lastName = Signal('Doe');
// Computed automatically tracks both firstName and lastName
final fullName = Computed(() => '${firstName.value} ${lastName.value}');
print(fullName.value); // Jane Doe
lastName.value = 'Smith';
print(fullName.value); // Jane Smith
}
```
#### 2. Dynamic Dependency Tracking (Branching)
```dart
final showFull = Signal(false);
final detailedInfo = Signal('High Latency Alert');
final briefInfo = Signal('Alert');
final message = Computed(() {
if (showFull.value) {
return detailedInfo.value; // Subscribes to detailedInfo
} else {
return briefInfo.value; // Subscribes to briefInfo
}
});
```
### Constructors
View Constructors
##### `Computed(this.fn, {String? name, void Function()? watched, void Function()? unwatched, ComputedOptions? options})`
Creates a new [Computed](/types/computed) signal instance with the derivation callback **fn**.
You can optionally provide:
- A **name** for debugging/observer tracing.
- **watched**/**unwatched** hooks triggered when the computed gains its first subscriber or loses its last subscriber.
```dart
final doubleCount = Computed(() => count.value * 2, name: 'double_counter');
```
### Properties
View Properties
##### `int globalId`
##### `String? name`
##### `void Function()? watched`
##### `void Function()? unwatched`
##### `int flags`
##### `int version`
### Methods
View Methods
##### `bool isInitialized`
Check if the value has been computed
##### `T internalValue`
##### `bool internalRefresh()`
##### `void subscribeToNode(Node node)`
##### `void unsubscribeFromNode(Node node)`
##### `void notify()`
##### `T value`
##### `void Function() subscribe(void Function(T value) fn)`
---
## computed
Convenient global constructor for creating a derived computed signal.
Computed signals are lazily evaluated and cached (memoized). Their values
automatically update when any dependency signals accessed inside the callback function change.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
final firstName = signal('Jane');
final lastName = signal('Doe');
final fullName = computed(() => '${firstName.value} ${lastName.value}');
void main() {
print(fullName.value); // Prints: Jane Doe
}
```
---
## Page: Action
Url: https://dartsignals.dev/packages/signals_core/core/action
Description: Wraps a callback function into a reusable, batched, and untracked action.
---
Wraps a callback function into a reusable, batched, and untracked action.
An **action** is a higher-order function that takes a callback and returns a new function
with the exact same signature. When the returned function is executed, it runs the original
callback inside both a [batch](/types/batch) and an [untracked](/types/untracked) block.
### Why use action instead of batch?
1. **Reusability**: batch(fn) executes the callback immediately. In contrast, action(fn)
returns a *reusable function* that you can store, pass around, and invoke multiple times
to perform batch transactions on demand.
2. **Untracked Execution**: The callback runs inside untracked. If you invoke the action
from within an effect or a computed signal, the outer reactive context **will not**
establish subscriptions to any signals read inside the action.
---
### Example: Comparing Normal Updates vs. Action Batching
#### Without Actions (Standard Sequential Updates)
Every signal write immediately notifies active subscribers. This causes transient states
and redundant, intermediate executions:
```dart
import 'package:preact_signals/preact_signals.dart';
final a = signal('a');
final b = signal('b');
void main() {
// Set up a subscriber effect
effect(() => print('${a.value} ${b.value}'));
// Prints immediately: "a b"
a.value = 'aa'; // Prints: "aa b"
b.value = 'bb'; // Prints: "aa bb"
}
```
Total prints: **3** (initial execution + 2 updates).
#### With Actions (Coalesced Transaction)
By wrapping the state-mutating function in [action](/types/action), all updates are postponed and flushed
in a single notification block once the function completes:
```dart
import 'package:preact_signals/preact_signals.dart';
final a = signal('a');
final b = signal('b');
// Create a reusable action
final updateFields = action((String nextA, String nextB) {
a.value = nextA;
b.value = nextB;
});
void main() {
effect(() => print('${a.value} ${b.value}'));
// Prints immediately: "a b"
updateFields('aa', 'bb');
// The effect is deferred during execution and triggers exactly once at the end.
// Prints: "aa bb"
}
```
Total prints: **2** (initial execution + 1 coalesced update).
---
### Type-Safety & Extensions
While action accepts any generic Function, Dart's static analysis benefits greatly from
type-safe variants or extensions.
- **Type-safe functions**: Use action0 through action10 (e.g. action2(...) for 2 arguments) to preserve type arguments.
- **Extensions**: Call .action directly on any Dart function (e.g., myFunction.action).
---
## Type-Safe Variants & Extensions
To ensure complete type safety and optimize static analysis in Dart, the package exposes distinct variants and extension methods corresponding to the number of arguments (from 0 up to 10):
| Variant / Extension | Description |
| --- | --- |
| `action0` | Wraps a 0-argument callback function in a type-safe action. `signature` |
| `action1` | Wraps a 1-argument callback function in a type-safe action. `signature` |
| `action2` | Wraps a 2-argument callback function in a type-safe action. `signature` |
| `action3` | Wraps a 3-argument callback function in a type-safe action. `signature` |
| `action4` | Wraps a 4-argument callback function in a type-safe action. `signature` |
| `action5` | Wraps a 5-argument callback function in a type-safe action. `signature` |
| `action6` | Wraps a 6-argument callback function in a type-safe action. `signature` |
| `action7` | Wraps a 7-argument callback function in a type-safe action. `signature` |
| `action8` | Wraps an 8-argument callback function in a type-safe action. `signature` |
| `action9` | Wraps a 9-argument callback function in a type-safe action. `signature` |
| `action10` | Wraps a 10-argument callback function in a type-safe action. `signature` |
| `ActionExt0` | Extension on a 0-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt1` | Extension on a 1-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt2` | Extension on a 2-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt3` | Extension on a 3-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt4` | Extension on a 4-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt5` | Extension on a 5-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt6` | Extension on a 6-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt7` | Extension on a 7-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt8` | Extension on an 8-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt9` | Extension on a 9-argument function to wrap it in a type-safe action. `signature` |
| `ActionExt10` | Extension on a 10-argument function to wrap it in a type-safe action. `signature` |
Show Full API Signatures & Examples
### action0
Wraps a 0-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
### Example Usage
```dart
final increment = action0(() {
count.value++;
clicks.value++;
});
```
---
### action1
Wraps a 1-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
### Example Usage
```dart
final setName = action1((String newName) {
name.value = newName;
updatedAt.value = DateTime.now();
});
```
---
### action2
Wraps a 2-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
### Example Usage
```dart
final updateProfile = action2((String newName, int newAge) {
name.value = newName;
age.value = newAge;
});
```
---
### action3
Wraps a 3-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
### Example Usage
```dart
final setCoordinates = action3((double lat, double lng, String label) {
latitude.value = lat;
longitude.value = lng;
locationName.value = label;
});
```
---
### action4
Wraps a 4-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
### Example Usage
```dart
final updateUserData = action4((String name, int age, double score, bool active) {
userName.value = name;
userAge.value = age;
userScore.value = score;
userActive.value = active;
});
```
---
### action5
Wraps a 5-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
### Example Usage
```dart
final setConfig = action5((int w, int h, String title, bool dark, double opacity) {
width.value = w;
height.value = h;
appTitle.value = title;
themeDark.value = dark;
bgOpacity.value = opacity;
});
```
---
### action6
Wraps a 6-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
---
### action7
Wraps a 7-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
---
### action8
Wraps an 8-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
---
### action9
Wraps a 9-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
---
### action10
Wraps a 10-argument callback function in a type-safe action.
Executes **fn** inside a transaction-safe [batch](/types/batch) and [untracked](/types/untracked) block.
---
### ActionExt0
Extension on a 0-argument function to wrap it in a type-safe action.
Enables calling .action directly on any 0-argument function to wrap it.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
final count = signal(0);
final clicks = signal(0);
void incrementCount() {
count.value++;
clicks.value++;
}
// Create a batched, untracked action from the function
final increment = incrementCount.action;
void main() {
effect(() => print('Count: ${count.value}, Clicks: ${clicks.value}'));
// Prints: "Count: 0, Clicks: 0"
increment();
// Updates both count and clicks inside a batch.
// Triggers the effect exactly once.
// Prints: "Count: 1, Clicks: 1"
}
```
### Methods
View Methods
##### `R Function() action`
Wraps the 0-argument function in a type-safe action.
---
### ActionExt1
Extension on a 1-argument function to wrap it in a type-safe action.
Enables calling .action directly on any 1-argument function to wrap it.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
final name = signal('Jane');
final clicks = signal(0);
void updateName(String newName) {
name.value = newName;
clicks.value++;
}
// Create a batched, untracked action from the function
final setName = updateName.action;
void main() {
effect(() => print('Name: ${name.value}, Clicks: ${clicks.value}'));
// Prints: "Name: Jane, Clicks: 0"
setName('John');
// Updates both name and clicks inside a batch.
// Triggers the effect exactly once.
// Prints: "Name: John, Clicks: 1"
}
```
### Methods
View Methods
##### `R Function(A) action`
Wraps the 1-argument function in a type-safe action.
---
### ActionExt2
Extension on a 2-argument function to wrap it in a type-safe action.
Enables calling .action directly on any 2-argument function to wrap it.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
final name = signal('Jane');
final age = signal(25);
void updateProfile(String newName, int newAge) {
name.value = newName;
age.value = newAge;
}
// Create a batched, untracked action from the function
final setProfile = updateProfile.action;
void main() {
effect(() => print('Name: ${name.value}, Age: ${age.value}'));
// Prints: "Name: Jane, Age: 25"
setProfile('John', 30);
// Updates both name and age inside a batch.
// Triggers the effect exactly once.
// Prints: "Name: John, Age: 30"
}
```
### Methods
View Methods
##### `R Function(A, B) action`
Wraps the 2-argument function in a type-safe action.
---
### ActionExt3
Extension on a 3-argument function to wrap it in a type-safe action.
Enables calling .action directly on any 3-argument function to wrap it.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
final latitude = signal(0.0);
final longitude = signal(0.0);
final locationName = signal('Unknown');
void setCoordinates(double lat, double lng, String label) {
latitude.value = lat;
longitude.value = lng;
locationName.value = label;
}
// Create a batched, untracked action from the function
final setCoords = setCoordinates.action;
void main() {
effect(() => print('${locationName.value}: (${latitude.value}, ${longitude.value})'));
// Prints: "Unknown: (0.0, 0.0)"
setCoords(37.7749, -122.4194, 'San Francisco');
// Updates latitude, longitude, and locationName inside a batch.
// Triggers the effect exactly once.
// Prints: "San Francisco: (37.7749, -122.4194)"
}
```
### Methods
View Methods
##### `R Function(A, B, C) action`
Wraps the 3-argument function in a type-safe action.
---
### ActionExt4
Extension on a 4-argument function to wrap it in a type-safe action.
### Methods
View Methods
##### `R Function(A, B, C, D) action`
Wraps the 4-argument function in a type-safe action.
---
### ActionExt5
Extension on a 5-argument function to wrap it in a type-safe action.
### Methods
View Methods
##### `R Function(A, B, C, D, E) action`
Wraps the 5-argument function in a type-safe action.
---
### ActionExt6
Extension on a 6-argument function to wrap it in a type-safe action.
### Methods
View Methods
##### `R Function(A, B, C, D, E, F) action`
Wraps the 6-argument function in a type-safe action.
---
### ActionExt7
Extension on a 7-argument function to wrap it in a type-safe action.
### Methods
View Methods
##### `R Function(A, B, C, D, E, F, G) action`
Wraps the 7-argument function in a type-safe action.
---
### ActionExt8
Extension on an 8-argument function to wrap it in a type-safe action.
### Methods
View Methods
##### `R Function(A, B, C, D, E, F, G, H) action`
Wraps the 8-argument function in a type-safe action.
---
### ActionExt9
Extension on a 9-argument function to wrap it in a type-safe action.
### Methods
View Methods
##### `R Function(A, B, C, D, E, F, G, H, I) action`
Wraps the 9-argument function in a type-safe action.
---
### ActionExt10
Extension on a 10-argument function to wrap it in a type-safe action.
### Methods
View Methods
##### `R Function(A, B, C, D, E, F, G, H, I, J) action`
Wraps the 10-argument function in a type-safe action.
---
---
## Page: Batch
Url: https://dartsignals.dev/packages/signals_core/core/batch
Description: Combines multiple signal writes into a single update transaction that is flushed only after the callback completes.
---
Combines multiple signal writes into a single update transaction that is flushed only after the callback completes.
Under normal circumstances, writing to a signal immediately notifies all of its active subscribers
(effects and computed signals), which can cause multiple redundant updates or temporary inconsistent/glitchy states
if you are updating several related signals sequentially.
By wrapping your mutations in [batch](/types/batch), notification events are deferred. Subscribed [effect](/types/effect)s and [computed](/types/computed)
signals will only run once at the very end of the batch callback block.
Always use batch when performing multiple state transitions together. This avoids flickering UI, unnecessary
rebuilds, and transient states where some dependencies are updated but others are not.
### Nested Batches
Batches can be nested. Updates are only flushed when the *outermost* batch callback completes.
### Mid-Batch Reads
If you read a mutated signal *inside* the batch callback, or access a computed signal that depends on a
mutated signal, that signal is immediately computed and updated inline to ensure your code always operates
on consistent, up-to-date data. However, other independent signals and effects are still deferred until
the batch finishes.
Parameters:
- **fn**: The callback function containing the signal write operations to be batched.
Returns:
- The value returned by the callback function **fn**.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
void main() {
final name = signal("Jane");
final surname = signal("Doe");
final fullName = computed(() => "${name.value} ${surname.value}");
// Set up an effect that reacts to changes
effect(() => print("Name changed to: ${fullName.value}"));
// Batching mutations ensures the effect runs only once
batch(() {
name.value = "John";
surname.value = "Smith";
});
// Prints: "Name changed to: John Smith" (Only once, not twice!)
}
```
---
## Page: MapSignalMixin
Url: https://dartsignals.dev/packages/signals_core/mixins/map
Description: A mixin that adds reactive Map methods and operators directly to a Signal.
---
A mixin that adds reactive Map methods and operators directly to a [Signal](/types/signal).
This mixin delegates all standard **Map** operations (such as mutations like []=, clear,
remove, and lookups like containsKey, isEmpty, keys, values) to the underlying
map value.
Every mutating operation automatically updates the signal and notifies its observers
(by forcing a change notification using force: true).
### Simple Example
```dart
class MyMapSignal extends Signal>
with MapSignalMixin> {
MyMapSignal(super.value);
}
final cart = MyMapSignal({'apple': 1});
// Register an effect reacting to cart changes
effect(() {
print('Cart length: ${cart.length}');
});
// Treating it as a standard Map triggers updates automatically!
cart['banana'] = 3; // Prints: Cart length: 2
cart.remove('apple'); // Prints: Cart length: 1
```
### Methods
View Methods
##### `V? [](Object? key)`
##### `void []=(K key, V value)`
##### `void addAll(Map other)`
##### `void addEntries(Iterable> newEntries)`
##### `Map cast()`
##### `void clear()`
##### `bool containsKey(Object? key)`
##### `bool containsValue(Object? value)`
##### `Iterable> entries`
##### `void forEach(void Function(K key, V value) action)`
##### `bool isEmpty`
##### `bool isNotEmpty`
##### `Iterable keys`
##### `int length`
##### `Map map(MapEntry Function(K key, V value) convert)`
##### `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)`
##### `Iterable values`
##### `Map toMap()`
Snapshot of **MapEntries**
---
## Page: TrackedSignalMixin
Url: https://dartsignals.dev/packages/signals_core/mixins/tracked
Description: A mixin that adds tracking for the initial and previous values to a Signal.
---
A mixin that adds tracking for the initial and previous values to a [Signal](/types/signal).
[TrackedSignalMixin](/types/trackedsignalmixin) stores the initialValue (the value the signal had when it was
created or initialized) and the previousValue (the value of the signal right before
the most recent update).
If you are looking for full undo/redo capabilities, use ChangeStackSignalMixin instead.
### Example Usage
```dart
import 'package:signals/signals.dart';
class MyTrackedSignal extends Signal with TrackedSignalMixin {
MyTrackedSignal(super.internalValue);
}
void main() {
final signal = MyTrackedSignal(0);
print('Initial: ${signal.initialValue}'); // Prints: "Initial: 0"
print('Previous: ${signal.previousValue}'); // Prints: "Previous: null"
signal.value = 1;
print('Initial: ${signal.initialValue}'); // Prints: "Initial: 0"
print('Previous: ${signal.previousValue}'); // Prints: "Previous: 0"
signal.value = 2;
print('Initial: ${signal.initialValue}'); // Prints: "Initial: 0"
print('Previous: ${signal.previousValue}'); // Prints: "Previous: 1"
}
```
This mixin only works with values that are immutable or are copied/cloned on mutation.
If the value is mutated directly in-place without re-assigning, initialValue and
previousValue will end up pointing to the same modified instance as the current value.
### Methods
View Methods
##### `T initialValue`
The initial value the signal was created with
##### `T? previousValue`
Get the previous value (if exists)
---
## trackedSignal
Create a signal that stores the initial and previous value
---
## TrackedSignal
A signal that stores the initial and previous value
### Constructors
View Constructors
##### `TrackedSignal(super.value, {TrackedSignalOptions? options, @Deprecated('Use options: TrackedSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: TrackedSignalOptions(name: ...) instead') String? debugLabel})`
A signal that stores the initial and previous value
---
## TrackedSignalOptions
Configuration options for a [TrackedSignal](/types/trackedsignal).
### Constructors
View Constructors
##### `TrackedSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched})`
Creates a new [TrackedSignalOptions](/types/trackedsignaloptions) instance.
### Methods
View Methods
##### `TrackedSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched})`
##### `bool ==(Object other)`
##### `int hashCode`
---
## Page: SetSignalMixin
Url: https://dartsignals.dev/packages/signals_core/mixins/set
Description: A mixin that adds reactive Set methods and operations to a Signal.
---
A mixin that adds reactive Set methods and operations to a [Signal](/types/signal)
holding a **Set** value.
This mixin delegates all standard **Set** operations (such as mutations like add,
remove, addAll, removeAll, retainAll, and clear) to the underlying set,
while ensuring that any reads register a dependency and any mutations
automatically trigger reactive updates.
This mixin only works with signals that have a value type extending Set .
### Example Usage
```dart
import 'package:signals/signals.dart';
class MySetSignal extends Signal>
with IterableSignalMixin>, SetSignalMixin> {
MySetSignal(super.internalValue);
}
void main() {
final numbers = MySetSignal({1, 2, 3});
effect(() {
print('Elements: $numbers, Length: ${numbers.length}');
}); // Prints: "Elements: {1, 2, 3}, Length: 3"
// Adding an element (automatically calls set() and triggers updates)
numbers.add(4); // Prints: "Elements: {1, 2, 3, 4}, Length: 4"
// Removing an element (triggers updates)
numbers.remove(1); // Prints: "Elements: {2, 3, 4}, Length: 3"
}
```
Since mutations on SetSignalMixin notify listeners automatically, you do not
need to assign numbers.value = ... to force updates. Methods like add, addAll,
and remove take care of notification.
### Methods
View Methods
##### `bool add(E value)`
##### `void addAll(Iterable elements)`
##### `Set cast()`
##### `void clear()`
##### `bool containsAll(Iterable other)`
##### `Set difference(Set other)`
##### `Set intersection(Set other)`
##### `E? lookup(Object? object)`
##### `bool remove(Object? value)`
##### `void removeAll(Iterable elements)`
##### `void removeWhere(bool Function(E element) test)`
##### `void retainAll(Iterable elements)`
##### `void retainWhere(bool Function(E element) test)`
##### `Set union(Set other)`
---
## Page: ListSignalMixin
Url: https://dartsignals.dev/packages/signals_core/mixins/list
Description: A mixin that adds reactive List methods and operators to a Signal.
---
A mixin that adds reactive List methods and operators to a [Signal](/types/signal)
holding a **List** value.
This mixin delegates all standard **List** operations (such as mutations like add,
remove, insert, sort, and clear, and accessor operators like [] and []=)
to the underlying list, while ensuring that any reads register a dependency
and any mutations automatically trigger reactive updates.
This mixin only works with signals that have a value type extending List .
### Example Usage
```dart
import 'package:signals/signals.dart';
class MyListSignal extends Signal>
with IterableSignalMixin>, ListSignalMixin> {
MyListSignal(super.internalValue);
}
void main() {
final numbers = MyListSignal([1, 2, 3]);
effect(() {
print('Elements: $numbers, Length: ${numbers.length}');
}); // Prints: "Elements: [1, 2, 3], Length: 3"
// Adding an element (automatically calls set() and triggers updates)
numbers.add(4); // Prints: "Elements: [1, 2, 3, 4], Length: 4"
// Modifying an element by index (triggers updates)
numbers[0] = 10; // Prints: "Elements: [10, 2, 3, 4], Length: 4"
}
```
Since mutations on ListSignalMixin notify listeners automatically, you do not
need to assign numbers.value = ... to force updates. Methods like add, addAll,
and operator []= take care of notification.
### Methods
View Methods
##### `List cast()`
##### `first(E val)`
##### `E last`
##### `last(E val)`
##### `length(int value)`
##### `List +(List other)`
##### `E [](int index)`
##### `void []=(int index, E value)`
##### `void add(E value)`
##### `void addAll(Iterable iterable)`
##### `Map asMap()`
##### `void clear()`
##### `Iterable expand(Iterable Function(E element) toElements)`
##### `void fillRange(int start, int end, [E? fillValue])`
##### `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])`
##### `void insert(int index, E element)`
##### `void insertAll(int index, Iterable iterable)`
##### `int lastIndexOf(E element, [int? start])`
##### `int lastIndexWhere(bool Function(E element) test, [int? start])`
##### `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)`
##### `Iterable reversed`
##### `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])`
##### `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])`
---
## Page: SinkSignalMixin
Url: https://dartsignals.dev/packages/signals_core/mixins/sink
Description: A mixin that implements the standard Sink interface for a Signal.
---
A mixin that implements the standard **Sink** interface for a [Signal](/types/signal).
This mixin allows you to treat a writable [Signal](/types/signal) as a sink of events, where
adding an element using **add** automatically updates the signal's value and
notifies all reactive listeners. Calling **close** automatically disposes
the signal, freeing up resources and removing all active subscriptions.
This provides excellent compatibility with streams, transformers, or any
APIs that expect a standard Dart **Sink**.
### Example Usage
```dart
import 'package:signals/signals.dart';
class MySinkSignal extends Signal with SinkSignalMixin {
MySinkSignal(super.internalValue);
}
void main() {
final signal = MySinkSignal(0);
effect(() {
print('Signal value changed to: ${signal.value}');
}); // Prints: "Signal value changed to: 0"
// Treat it as a Sink and push elements to it
signal.add(42); // Prints: "Signal value changed to: 42"
signal.add(100); // Prints: "Signal value changed to: 100"
// Dispose the signal when finished
signal.close();
print('Is disposed: ${signal.disposed}'); // Prints: "Is disposed: true"
}
```
Once close is called, the signal is permanently disposed and cannot be reused
or written to anymore. Any subsequent add calls will throw an exception.
### Methods
View Methods
##### `void add(T event)`
##### `void close()`
---
## Page: QueueSignalMixin
Url: https://dartsignals.dev/packages/signals_core/mixins/queue
Description: A mixin that adds reactive Queue methods and operations to a Signal.
---
A mixin that adds reactive Queue methods and operations to a [Signal](/types/signal)
holding a **Queue** value.
This mixin delegates all standard **Queue** operations (such as mutations like add,
addAll, addFirst, addLast, removeFirst, removeLast, and clear) to the
underlying queue, while ensuring that any reads register a dependency and any
mutations automatically trigger reactive updates.
This mixin only works with signals that have a value type extending Queue .
### Example Usage
```dart
import 'dart:collection';
import 'package:signals/signals.dart';
class MyQueueSignal extends Signal>
with QueueSignalMixin> {
MyQueueSignal(super.internalValue);
}
void main() {
final q = Queue()..add(1);
final signal = MyQueueSignal(q);
effect(() {
print('Queue elements: $signal, Length: ${signal.length}');
}); // Prints: "Queue elements: {1}, Length: 1"
// Adding to the front of the queue (triggers updates)
signal.addFirst(0); // Prints: "Queue elements: {0, 1}, Length: 2"
// Adding to the back of the queue (triggers updates)
signal.addLast(2); // Prints: "Queue elements: {0, 1, 2}, Length: 3"
// Removing from the front of the queue (triggers updates)
final first = signal.removeFirst(); // Prints: "Queue elements: {1, 2}, Length: 2"
}
```
Since mutations on QueueSignalMixin notify listeners automatically, you do not
need to assign signal.value = ... to force updates. Methods like addFirst,
addLast, removeFirst, and removeLast take care of notification.
### Methods
View Methods
##### `void add(T value)`
##### `void addAll(Iterable iterable)`
##### `void addFirst(T value)`
##### `void addLast(T value)`
##### `bool any(bool Function(T element) test)`
##### `Queue cast()`
##### `void clear()`
##### `bool contains(Object? element)`
##### `T elementAt(int index)`
##### `bool every(bool Function(T element) test)`
##### `Iterable expand(Iterable Function(T element) toElements)`
##### `T first`
##### `T firstWhere(bool Function(T element) test, {T Function()? orElse})`
##### `U fold(U initialValue, U Function(U previousValue, T element) combine)`
##### `Iterable followedBy(Iterable other)`
##### `void forEach(void Function(T element) action)`
##### `bool isEmpty`
##### `bool isNotEmpty`
##### `Iterator iterator`
##### `String join([String separator = ""])`
##### `T last`
##### `T lastWhere(bool Function(T element) test, {T Function()? orElse})`
##### `int length`
##### `Iterable map(U Function(T e) toElement)`
##### `T reduce(T Function(T value, T element) combine)`
##### `bool remove(Object? value)`
##### `T removeFirst()`
##### `T removeLast()`
##### `void removeWhere(bool Function(T element) test)`
##### `void retainWhere(bool Function(T element) test)`
##### `T single`
##### `T singleWhere(bool Function(T element) test, {T Function()? orElse})`
##### `Iterable skip(int count)`
##### `Iterable skipWhile(bool Function(T value) test)`
##### `Iterable take(int count)`
##### `Iterable takeWhile(bool Function(T value) test)`
##### `List toList({bool growable = true})`
##### `Set toSet()`
##### `Iterable where(bool Function(T element) test)`
##### `Iterable whereType()`
---
## SignalQueueUtils
Utility extension methods on **Queue** to convert them to [QueueSignal](/types/queuesignal)s.
### Methods
View Methods
##### `QueueSignal toSignal({QueueSignalOptions? options, @Deprecated('Use options: QueueSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: QueueSignalOptions(name: ...) instead') String? debugLabel})`
Convert an existing list to [QueueSignal](/types/queuesignal)
---
## queueSignal
Creates a [QueueSignal](/types/queuesignal) with the given **list** (Queue).
---
## QueueSignalOptions
Configuration options for a [QueueSignal](/types/queuesignal).
### Constructors
View Constructors
##### `QueueSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched, super.equality = const SignalDeepEquality()})`
Creates a new [QueueSignalOptions](/types/queuesignaloptions) instance.
### Methods
View Methods
##### `QueueSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched, SignalEquality>? equality})`
##### `bool ==(Object other)`
##### `int hashCode`
---
## QueueSignal
A [Signal](/types/signal) that holds a **Queue**.
### Constructors
View Constructors
##### `QueueSignal(super.value, {QueueSignalOptions? options, @Deprecated('Use options: QueueSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: QueueSignalOptions(name: ...) instead') String? debugLabel})`
Creates a [QueueSignal](/types/queuesignal) with the given **value**.
---
## Page: IterableSignalMixin
Url: https://dartsignals.dev/packages/signals_core/mixins/iterable
Description: A mixin that adds reactive Iterable methods and properties to a Signal.
---
A mixin that adds reactive Iterable methods and properties to a [Signal](/types/signal)
holding an **Iterable** value.
This mixin delegates all standard **Iterable** operations (such as length,
first, last, map, where, and any) directly to the underlying
collection, while ensuring that any read operations register a reactive
dependency on the signal.
This mixin only works with signals that have a value type extending Iterable .
### Example Usage
```dart
import 'package:signals/signals.dart';
class MyIterableSignal extends Signal>
with IterableSignalMixin> {
MyIterableSignal(super.internalValue);
}
void main() {
final numbers = MyIterableSignal([1, 2, 3]);
// Set up a reactive effect that prints the list size and first element
effect(() {
print('Size: ${numbers.length}, First: ${numbers.first}');
}); // Prints: "Size: 3, First: 1"
// Update the signal value (triggers the effect)
numbers.value = [10, 20, 30, 40]; // Prints: "Size: 4, First: 10"
}
```
Direct mutation of elements inside the iterable will NOT notify listeners
unless you reassign the value or use a specialized signal class like ListSignal,
SetSignal, or MapSignal which automatically trigger updates when modified.
### 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)`
##### `void forEach(void Function(E element) action)`
##### `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()`
---
## Page: EventSinkSignalMixin
Url: https://dartsignals.dev/packages/signals_core/mixins/event-sink
Description: A mixin that implements the standard EventSink interface for a Signal.
---
A mixin that implements the standard **EventSink** interface for a [Signal](/types/signal)
holding an [AsyncState](/types/asyncstate) value.
This mixin is designed specifically to interface with **Stream** consumers or
other asynchronous data producers. It maps **add** to AsyncState.data(...),
**addError** to AsyncState.error(...), and **close** to disposing the signal.
This mixin only works with signals whose value is of type AsyncState .
### Example Usage
```dart
import 'package:signals/signals.dart';
class MyEventSinkSignal extends Signal> with EventSinkSignalMixin {
MyEventSinkSignal(int initialValue) : super(AsyncState.data(initialValue));
}
void main() {
final signal = MyEventSinkSignal(0);
effect(() {
final state = signal.value;
state.map(
data: (val) => print('Data received: $val'),
error: (err, stack) => print('Error occurred: $err'),
loading: () => print('Loading...'),
);
}); // Prints: "Data received: 0"
// Treat it as an EventSink and add data
signal.add(10); // Prints: "Data received: 10"
// Push an error event
signal.addError(Exception('Failure')); // Prints: "Error occurred: Exception: Failure"
// Close/dispose the sink
signal.close();
print('Is disposed: ${signal.disposed}'); // Prints: "Is disposed: true"
}
```
Once close is called, the signal is disposed. Trying to call add or addError
after closing will result in an exception.
### Methods
View Methods
##### `void add(T event)`
##### `void addError(Object error, [StackTrace? stackTrace])`
##### `void close()`
---
## Page: StreamSignalMixin
Url: https://dartsignals.dev/packages/signals_core/mixins/stream
Description: A mixin that implements the standard Stream interface for a ReadonlySignal.
---
A mixin that implements the standard **Stream** interface for a [ReadonlySignal](/types/readonlysignal).
This mixin allows you to treat a read-only or writable signal as a standard asynchronous
**Stream**, where updates to the signal's value are pushed as stream events.
This provides out-of-the-box compatibility with the entire asynchronous Dart SDK,
such as stream transformers, await for loops, and Flutter's **StreamBuilder**.
### Example Usage
```dart
import 'package:signals/signals.dart';
class MyStreamSignal extends Signal with StreamSignalMixin {
MyStreamSignal(super.internalValue);
}
void main() async {
final counter = MyStreamSignal(0);
// Standard stream subscription
final subscription = counter.listen((val) {
print('Stream emitted: $val');
}); // Prints: "Stream emitted: 0" (if listened immediately)
counter.value = 1; // Prints: "Stream emitted: 1"
counter.value = 2; // Prints: "Stream emitted: 2"
await subscription.cancel();
}
```
### Flutter StreamBuilder Example
```dart
import 'package:flutter/material.dart';
import 'package:signals/signals_flutter.dart';
class CounterSignal extends Signal with StreamSignalMixin {
CounterSignal(int value) : super(value);
}
final counter = CounterSignal(0);
Widget build(BuildContext context) {
return StreamBuilder(
stream: counter,
builder: (context, snapshot) {
return Text('Count: ${snapshot.data}');
},
);
}
```
Since StreamSignalMixin creates an internal broadcast StreamController , multiple listeners
can subscribe simultaneously. All subscriptions are closed and resources are released
automatically when the signal is disposed.
### Methods
View Methods
##### `Future any(bool Function(T element) test)`
##### `Stream asBroadcastStream({void Function(StreamSubscription subscription)? onListen, void Function(StreamSubscription subscription)? onCancel})`
##### `Stream asyncExpand(Stream? Function(T event) convert)`
##### `Stream asyncMap(FutureOr Function(T event) convert)`
##### `Stream cast()`
##### `Future contains(Object? needle)`
##### `Stream distinct([bool Function(T previous, T next)? equals])`
##### `Future drain([E? futureValue])`
##### `Future elementAt(int index)`
##### `Future every(bool Function(T element) test)`
##### `Stream expand(Iterable Function(T element) convert)`
##### `Future first`
##### `Future firstWhere(bool Function(T element) test, {T Function()? orElse})`
##### `Future fold(S initialValue, S Function(S previous, T element) combine)`
##### `Future forEach(void Function(T element) action)`
##### `Stream handleError(Function onError, {bool Function(dynamic error)? test})`
##### `bool isBroadcast`
##### `Future isEmpty`
##### `Future join([String separator = ""])`
##### `Future last`
##### `Future lastWhere(bool Function(T element) test, {T Function()? orElse})`
##### `Future length`
##### `StreamSubscription listen(void Function(T event)? onData, {Function? onError, void Function()? onDone, bool? cancelOnError})`
##### `Stream map(S Function(T event) convert)`
##### `Future pipe(StreamConsumer streamConsumer)`
##### `Future reduce(T Function(T previous, T element) combine)`
##### `Future single`
##### `Future singleWhere(bool Function(T element) test, {T Function()? orElse})`
##### `Stream skip(int count)`
##### `Stream skipWhile(bool Function(T element) test)`
##### `Stream take(int count)`
##### `Stream takeWhile(bool Function(T element) test)`
##### `Stream timeout(Duration timeLimit, {void Function(EventSink sink)? onTimeout})`
##### `Future> toList()`
##### `Future> toSet()`
##### `Stream transform(StreamTransformer streamTransformer)`
##### `Stream where(bool Function(T event) test)`
---
## Page: ChangeStackSignalMixin
Url: https://dartsignals.dev/packages/signals_core/mixins/change-stack
Description: A mixin that adds undo, redo, and state history replay capabilities to a Signal.
---
A mixin that adds undo, redo, and state history replay capabilities to a [Signal](/types/signal).
[ChangeStackSignalMixin](/types/changestacksignalmixin) keeps track of past states of a signal's value in a double-ended
queue, allowing you to easily go back to previous values using **undo** and go forward to subsequent
values using **redo**. You can inspect if undo or redo are available via **canUndo** and **canRedo**.
You can also set a **limit** on the maximum size of the history stack, preventing memory leak
issues in long-running scenarios.
If you only need access to the initial and immediate previous values of a signal (without a full
history stack or undo/redo mechanisms), use the lightweight TrackedSignalMixin instead.
### Example Usage
```dart
import 'package:signals/signals.dart';
class HistorySignal extends Signal with ChangeStackSignalMixin {
HistorySignal(super.internalValue);
}
void main() {
final counter = HistorySignal(0);
counter.limit = 5; // Cap history stack to 5 items
counter.value = 1;
counter.value = 2;
counter.value = 3;
print('Current: ${counter.value}'); // Prints: "Current: 3"
print('Can Undo: ${counter.canUndo}'); // Prints: "Can Undo: true"
// Undo last change
counter.undo();
print('Undone: ${counter.value}'); // Prints: "Undone: 2"
// Undo once more
counter.undo();
print('Undone: ${counter.value}'); // Prints: "Undone: 1"
// Redo the previous undo
counter.redo();
print('Redone: ${counter.value}'); // Prints: "Redone: 2"
}
```
This mixin only works with values that are immutable or are copied/cloned when changed.
If you mutate an object in-place directly without replacing the value using set or the .value
setter, the history queue will store references to the same mutated object, and undo/redo
will appear to do nothing.
### Properties
View Properties
##### `int? limit`
Max values to keep in history
### Methods
View Methods
##### `Iterable> history`
List of changes in the history
##### `Iterable> redos`
List of changes in the redo stack
##### `bool canRedo`
Can redo the previous change
##### `bool canUndo`
Can undo the previous change
##### `bool set(T val, {bool force = false})`
##### `void redo()`
Redo Previous Undo
##### `void undo()`
Undo Last Change
##### `void clear()`
Clear the history for redo and undo
##### `void clearUndo()`
Clear undo stack
##### `void clearRedo()`
Clear redo stack
---
## Page: AsyncState
Url: https://dartsignals.dev/packages/signals_core/async/state
Description: A sealed union representing the lifecycle states of an asynchronous operation.
---
A sealed union representing the lifecycle states of an asynchronous operation.
AsyncState is commonly wrapped by AsyncSignal or returned by asynchronous
computed signals (computedAsync, computedFrom) to model loading, success (data),
and error outcomes.
### State Hierarchy
The states are modeled as a robust hierarchy of immutable types:
- [AsyncLoading](/types/asyncloading): Pure loading state with no pre-existing data.
- [AsyncData](/types/asyncdata): Success state holding a resolved value of type T.
- [AsyncDataRefreshing](/types/asyncdatarefreshing): Refreshing in the background (holding historical data).
- [AsyncDataReloading](/types/asyncdatareloading): Reloading (holding historical data).
- [AsyncError](/types/asyncerror): Failure state holding an error and optional stack trace.
- [AsyncErrorRefreshing](/types/asyncerrorrefreshing): Refreshing in the background (holding historical error).
- [AsyncErrorReloading](/types/asyncerrorreloading): Reloading (holding historical error).
### Pattern Matching & Switch Expressions
Standard Dart switch expressions provide type-safe branching across all states:
> [!IMPORTANT]
> **Branch Matching Order & Existing Value Preservation:**
> Since reloading and refreshing states (e.g., AsyncDataRefreshing, AsyncDataReloading) implement both AsyncData and AsyncLoading, matching on AsyncLoading **first** will prematurely swallow existing data!
> Always place AsyncData and AsyncError branches **before** AsyncLoading to ensure pre-existing data or error states are successfully rendered during refreshes:
```dart
final value = switch (state) {
AsyncDataRefreshing r => 'Refreshing with value: ${r.value}',
AsyncDataReloading r => 'Reloading with value: ${r.value}',
AsyncData data => 'Stable value: ${data.value}',
AsyncErrorRefreshing r => 'Refreshing error: ${r.error}',
AsyncErrorReloading r => 'Reloading error: ${r.error}',
AsyncError error => 'Stable error: ${error.error}',
AsyncLoading() => 'Pure Loading State (no prior data)',
};
```
### Standard Branching Methods (map and maybeMap)
If you prefer standard callbacks over switch expressions, use map or maybeMap:
```dart
state.map(
data: (value) => 'Value: $value',
error: (error, stackTrace) => 'Error: $error',
loading: () => 'Loading...',
);
```
### Constructors
View Constructors
##### `AsyncState()`
##### `AsyncState.dataReloading(T data)`
Create a state with a value that is reloading
##### `AsyncState.dataRefreshing(T data)`
Create a state with a value that is refreshing
##### `AsyncState.data(T data)`
Create a state with a value
##### `AsyncState.errorReloading(Object error, [StackTrace? stackTrace])`
Create a state with an error that is reloading
##### `AsyncState.errorRefreshing(Object error, [StackTrace? stackTrace])`
Create a state with an error that is refreshing
##### `AsyncState.error(Object error, [StackTrace? stackTrace])`
Create a state with an error
##### `AsyncState.loading()`
Create a loading state
### Methods
View Methods
##### `bool hasValue`
Returns true if the state has a value
##### `bool hasError`
Returns true if the state has an error
##### `bool isLoading`
Check if the state is a loading state
##### `bool isRefreshing`
Returns true if the state is refreshing with a loading flag,
has a value or error and is not the loading state
##### `bool isReloading`
Returns true if the state is reloading with having a value or error,
and is the loading state
##### `T requireValue`
Force unwrap the value of the state.
This will throw an error if the state does not have a value.
##### `T? value`
Returns the value of the state.
##### `Object? error`
Returns the error of the state.
##### `StackTrace? stackTrace`
Returns the stack trace of the state.
##### `E map({required AsyncDataBuilder data, required AsyncErrorBuilder error, required AsyncStateBuilder loading, AsyncStateBuilder? reloading, AsyncStateBuilder? refreshing})`
Map the state to a value.
```dart
final signal = StreamSignal();
signal.value.map(
data: (value) => 'Value: $value',
error: (error, stackTrace) => 'Error: $error',
loading: () => 'Loading...',
);
```
The error Function below can be one of two types:
- (dynamic) -> FutureOr
- (dynamic, StackTrace) -> FutureOr
##### `E maybeMap({AsyncDataBuilder? data, AsyncErrorBuilder? error, AsyncStateBuilder? loading, AsyncStateBuilder? reloading, AsyncStateBuilder? refreshing, required AsyncStateBuilder orElse})`
Map the state to a value with optional or else.
```dart
final signal = StreamSignal();
signal.value.maybeMap(
data: (value) => 'Value: $value',
orElse: () => 'Loading...',
);
```
The error Function below can be one of two types:
- (dynamic) -> FutureOr
- (dynamic, StackTrace) -> FutureOr
##### `bool ==(covariant AsyncState other)`
##### `int hashCode`
---
## AsyncErrorRefreshing
A loading state with an error. Signal the query conditions that led to the error
has remained the same and is being refreshed.
### Constructors
View Constructors
##### `AsyncErrorRefreshing(super.error, super.stackTrace)`
Create a state with an error that is refreshing
### Methods
View Methods
##### `bool isLoading`
##### `bool isRefreshing`
##### `bool ==(covariant AsyncState other)`
---
## AsyncDataRefreshing
A loading state with a value. Signals the query conditions that led to the data
has remained the same and is being refreshed
### Constructors
View Constructors
##### `AsyncDataRefreshing(super.data)`
Create a state with a value that is refreshing
### Methods
View Methods
##### `bool isLoading`
##### `bool isRefreshing`
##### `bool ==(covariant AsyncState other)`
---
## LinkedSignalPreviousState
Previous state of a [LinkedSignal](/types/linkedsignal), containing both the **source** value
and the computed **value** from that source version.
### Constructors
View Constructors
##### `LinkedSignalPreviousState(this.source, this.value)`
Creates a [LinkedSignalPreviousState](/types/linkedsignalpreviousstate).
### Properties
View Properties
##### `S source`
The source value.
##### `T value`
The computed value.
---
## AsyncErrorReloading
A loading state with an error. Signal the query conditions that led to the error
has changed and is being reloaded.
### Constructors
View Constructors
##### `AsyncErrorReloading(super.error, super.stackTrace)`
Create a state with an error that is reloading
### Methods
View Methods
##### `bool isLoading`
##### `bool isReloading`
##### `bool ==(covariant AsyncState other)`
---
## AsyncDataReloading
A loading state with a value. Signals the query conditions that led to the data
has changed and is being reloaded.
### Constructors
View Constructors
##### `AsyncDataReloading(super.data)`
Create a state with a value that is reloading
### Methods
View Methods
##### `bool isLoading`
##### `bool isReloading`
##### `bool ==(covariant AsyncState other)`
---
## AsyncLoading
State for an [AsyncState](/types/asyncstate) with a loading state
### Constructors
View Constructors
##### `AsyncLoading()`
State for an [AsyncState](/types/asyncstate) with a loading state
### Methods
View Methods
##### `bool hasValue`
##### `bool hasError`
##### `T? value`
##### `bool isLoading`
##### `bool isRefreshing`
##### `bool isReloading`
##### `T requireValue`
##### `Object? error`
##### `StackTrace? stackTrace`
##### `bool ==(covariant AsyncState other)`
##### `int hashCode`
---
## AsyncError
State for an [AsyncState](/types/asyncstate) with an error
### Constructors
View Constructors
##### `AsyncError(this.error, this.stackTrace)`
State for an [AsyncState](/types/asyncstate) with an error
### Properties
View Properties
##### `Object error`
##### `StackTrace stackTrace`
### Methods
View Methods
##### `bool hasValue`
##### `bool hasError`
##### `T? value`
##### `bool isLoading`
##### `bool isRefreshing`
##### `bool isReloading`
##### `T requireValue`
##### `bool ==(covariant AsyncState other)`
##### `int hashCode`
---
## AsyncData
State for an [AsyncState](/types/asyncstate) with a value
### Constructors
View Constructors
##### `AsyncData(T data)`
State for an [AsyncState](/types/asyncstate) with a value
### Properties
View Properties
##### `T value`
### Methods
View Methods
##### `bool hasValue`
##### `bool hasError`
##### `bool isLoading`
##### `bool isRefreshing`
##### `bool isReloading`
##### `T requireValue`
##### `Object? error`
##### `StackTrace? stackTrace`
##### `bool ==(covariant AsyncState other)`
##### `int hashCode`
---
## AsyncSignalState
Extensions for [Signal>]
### Methods
View Methods
##### `Computed> selectData(R Function(T data) selector)`
Select from data when available, preserving async state
---
## Page: FutureSignal
Url: https://dartsignals.dev/packages/signals_core/async/future
Description: Future signals wrap a standard asynchronous Future and bridge it into the reactive state framework, exposing its lifecycle and value as a reactive ...
---
Future signals wrap a standard asynchronous **Future** and bridge it into the reactive state framework, exposing its lifecycle and value as a reactive [AsyncState](/types/asyncstate).
You can construct a future signal via the helper function [futureSignal](/types/futuresignal) or by calling the .toSignal() extension method on any standard **Future**.
### 1. Basic Async Fetching
```dart
final s = futureSignal(() async {
final data = await fetchUserData(123);
return data;
});
```
Or via the extension:
```dart
final s = fetchUserData(123).toSignal();
```
### 2. Consuming and Pattern Matching AsyncState
Reading .value on a [FutureSignal](/types/futuresignal) returns an [AsyncState](/types/asyncstate) object. You can safely pattern-match or map this state to reactively build your user interface or perform side-effects:
```dart
effect(() {
s.value.map(
data: (user) => print('User fetched successfully: ${user.name}'),
error: (err, stack) => print('Failed to fetch user: $err'),
loading: () => print('Loading user...'),
);
});
```
### 3. Reset, Refresh, and Reload
- **reset()**: Reverts the signal back to its initial/loading state.
- **refresh()**: Triggers a new evaluation of the future while maintaining the current data in the meantime (sets isLoading to true but does not discard existing data/error).
- **reload()**: Discards current state, sets the signal to AsyncLoading, and executes a fresh evaluation of the future.
```dart
final s = futureSignal(() => fetchConfig());
s.refresh(); // Triggers reload under the hood
```
### 4. Reactive Dependencies
Any reactive signals read *synchronously* inside the future callback are registered as dependencies. When they mutate, the future signal automatically invalidates and schedules a fresh fetch.
```dart
final userId = signal(123);
final userProfile = futureSignal(() async {
// Subscribes to userId! Mutating userId automatically re-runs this future.
final currentId = userId.value;
return fetchUserProfile(currentId);
});
```
If you need to track dependencies across an asynchronous gap (i.e. reading a signal's value after an await), pass them explicitly in the dependencies list inside AsyncSignalOptions or the constructor to guarantee they are properly subscribed.
### Constructors
View Constructors
##### `FutureSignal(Future Function() fn, {AsyncSignalOptions? options, @Deprecated('Use options: AsyncSignalOptions(initialValue: ...) instead') T? initialValue, @Deprecated('Use options: AsyncSignalOptions(dependencies: ...) instead') List>? dependencies, @Deprecated('Use options: AsyncSignalOptions(lazy: ...) instead') bool? lazy, @Deprecated('Use options: AsyncSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: AsyncSignalOptions(name: ...) instead') String? debugLabel})`
Future signals can be created by extension or method.
### futureSignal
```dart
final s = futureSignal(() async => 1);
```
### toSignal()
```dart
final s = Future(() => 1).toSignal();
```
## .value, .peek()
Returns [AsyncState](/dart/async/state) for the value and can handle the various states.
The value getter returns the value of the future if it completed successfully.
> .peek() can also be used to not subscribe in an effect
```dart
final s = futureSignal(() => Future(() => 1));
final value = s.value.value; // 1 or null
```
## .reset()
The reset method resets the future to its initial state to recall on the next evaluation.
```dart
final s = futureSignal(() => Future(() => 1));
s.reset();
```
## .refresh()
Refresh the future value by setting isLoading to true, but maintain the current state (AsyncData, AsyncLoading, AsyncError).
```dart
final s = futureSignal(() => Future(() => 1));
s.refresh();
print(s.value.isLoading); // true
```
## .reload()
Reload the future value by setting the state to AsyncLoading and pass in the value or error as data.
```dart
final s = futureSignal(() => Future(() => 1));
s.reload();
print(s.value is AsyncLoading); // true
```
## Dependencies
By default the callback will be called once and the future will be cached unless a signal is read in the callback.
```dart
final count = signal(0);
final s = futureSignal(() async => count.value);
await s.future; // 0
count.value = 1;
await s.future; // 1
```
If there are signals that need to be tracked across an async gap then use the dependencies when creating the futureSignal to [reset](#.reset()) every time any signal in the dependency array changes.
```dart
final count = signal(0);
final s = futureSignal(
() async => count.value,
dependencies: [count],
);
s.value; // state with count 0
count.value = 1; // resets the future
s.value; // state with count 1
```
### Properties
View Properties
##### `List> dependencies`
List of dependencies to recompute the future
### Methods
View Methods
##### `void dispose()`
##### `void reset([AsyncState? value])`
##### `void init()`
##### `AsyncState value`
##### `Future reload()`
##### `Future refresh()`
---
## futureSignal
Future signals can be created by extension or method.
### futureSignal
```dart
final s = futureSignal(() async => 1);
```
### toSignal()
```dart
final s = Future(() => 1).toSignal();
```
## .value, .peek()
Returns [AsyncState](/dart/async/state) for the value and can handle the various states.
The value getter returns the value of the future if it completed successfully.
> .peek() can also be used to not subscribe in an effect
```dart
final s = futureSignal(() => Future(() => 1));
final value = s.value.value; // 1 or null
```
## .reset()
The reset method resets the future to its initial state to recall on the next evaluation.
```dart
final s = futureSignal(() => Future(() => 1));
s.reset();
```
## .refresh()
Refresh the future value by setting isLoading to true, but maintain the current state (AsyncData, AsyncLoading, AsyncError).
```dart
final s = futureSignal(() => Future(() => 1));
s.refresh();
print(s.value.isLoading); // true
```
## .reload()
Reload the future value by setting the state to AsyncLoading and pass in the value or error as data.
```dart
final s = futureSignal(() => Future(() => 1));
s.reload();
print(s.value is AsyncLoading); // true
```
## Dependencies
By default the callback will be called once and the future will be cached unless a signal is read in the callback.
```dart
final count = signal(0);
final s = futureSignal(() async => count.value);
await s.future; // 0
count.value = 1;
await s.future; // 1
```
If there are signals that need to be tracked across an async gap then use the dependencies when creating the futureSignal to [reset](#.reset()) every time any signal in the dependency array changes.
```dart
final count = signal(0);
final s = futureSignal(
() async => count.value,
dependencies: [count],
);
s.value; // state with count 0
count.value = 1; // resets the future
s.value; // state with count 1
```
---
## Page: AsyncSignal
Url: https://dartsignals.dev/packages/signals_core/async/signal
Description: A highly powerful Signal specifically designed for manual, imperative asynchronous state management.
---
A highly powerful [Signal](/types/signal) specifically designed for manual, imperative asynchronous state management.
Unlike declarative reactive signals like [futureSignal](/types/futuresignal) or [streamSignal](/types/streamsignal) (which automatically wrap and listen
to an existing Future or Stream), AsyncSignal gives you full manual/imperative control over pushing
async states ([AsyncState.loading](/types/asyncstate#loading), [AsyncState.data](/types/asyncstate#data), and [AsyncState.error](/types/asyncstate#error)) into the reactive graph.
This is the perfect state primitive for building custom repositories, handling manual user action triggers
(e.g., submitting a registration form, calling an API on button click), or bridging low-level callback-based APIs
into reactive states.
### 1. Imperative State Mutations
You can update the state of the signal directly using specialized mutation helpers:
- setLoading() puts the signal into a clean AsyncLoading state.
- setValue(T data) pushes a new AsyncData state containing the data.
- setError(Object error, [StackTrace? stackTrace]) transitions the signal to an AsyncError state.
```dart
final authState = asyncSignal(AsyncState.loading());
Future login(String email, String password) async {
try {
authState.setLoading(); // Set UI to loading state
final user = await authApi.signIn(email, password);
authState.setValue(user); // Push success data
} catch (err, stack) {
authState.setError(err, stack); // Push error state
}
}
```
### 2. Awaiting Async Completion via .future
An outstanding capability of AsyncSignal is its built-in .future getter. Any part of your code can await
this future. It returns a standard Future that resolves when the signal next receives a data value,
or throws if the signal next receives an error state.
```dart
final loginSignal = asyncSignal(AsyncState.loading());
// Task A: Start background operation
Future.delayed(Duration(seconds: 2), () {
loginSignal.setValue(User(name: 'Charlie'));
});
// Task B: Wait for the signal to resolve!
final user = await loginSignal.future; // Suspends execution until Task A completes!
print(user.name); // 'Charlie'
```
### 3. Rendering in Flutter using Watch and AsyncState Pattern matching
In your Flutter widgets, you can seamlessly watch the signal and use Dart's native pattern matching
on [AsyncState](/types/asyncstate) to render different widgets corresponding to the current asynchronous lifecycle:
```dart
Widget build(BuildContext context) {
final state = authState.watch(context);
return state.map(
data: (user) => HomeScreen(user: user),
error: (error, stackTrace) => ErrorWidget(error),
loading: () => const CircularProgressIndicator(),
);
}
```
### 4. Bridging callback/event-driven systems via EventSink
AsyncSignal implements Dart's standard **EventSink** interface. This allows it to act directly as an event sink
for streams, websockets, or callback listeners:
```dart
final messageLog = asyncSignal(AsyncState.loading());
final chatStream = webSocket.stream.map((event) => event.toString());
// Automatically push all incoming messages and errors from the stream into the signal:
chatStream.listen(
(msg) => messageLog.add(msg),
onError: (err) => messageLog.addError(err),
onDone: () => messageLog.close(),
);
```
Favor AsyncSignal when you need manual, callback-driven, or button-press-triggered state mutations.
For auto-triggering, declarative, or read-only asynchronous data dependencies (like pulling data when an ID changes),
favor futureSignal or computedAsync instead.
### Constructors
View Constructors
##### `AsyncSignal(super.value, {super.options})`
A [Signal](/types/signal) that stores value in [AsyncState](/types/asyncstate)
### Methods
View Methods
##### `Future future`
The future of the signal completer
##### `bool isCompleted`
Returns true if the signal is completed an error or data
##### `void setError(Object error, [StackTrace? stackTrace])`
Set the error with optional stackTrace to [AsyncError](/types/asyncerror)
##### `void setValue(T value)`
Set the value to [AsyncData](/types/asyncdata)
##### `void setLoading([AsyncState? state])`
Set the loading state to [AsyncLoading](/types/asyncloading)
##### `void reset([AsyncState? value])`
Reset the signal to the initial value
##### `void init()`
Initialize the signal
##### `Future reload()`
Reload the future
##### `Future refresh()`
Refresh the future
##### `AsyncState value`
##### `T requireValue`
Returns the value of the signal
---
## asyncSignal
Helper function to create an [AsyncSignal](/types/asyncsignal) initialized with an [AsyncState](/types/asyncstate).
### Example
```dart
// Create an AsyncSignal initialized to a loading state
final counter = asyncSignal(AsyncState.loading());
// Create an AsyncSignal initialized with initial data
final status = asyncSignal(AsyncState.data('Active'));
```
---
## TimerSignal
Emit recurring **TimerSignalEvent** aka [AsyncSignal](/types/asyncsignal)
### Constructors
View Constructors
##### `TimerSignal({required this.every, super.cancelOnError, AsyncSignalOptions? options, @Deprecated('Use options: AsyncSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: AsyncSignalOptions(name: ...) instead') String? debugLabel})`
Emit recurring **TimerSignalEvent** aka [AsyncSignal](/types/asyncsignal)
### Properties
View Properties
##### `Duration every`
Trigger an **TimerSignalEvent** every duration
---
## AsyncSignalOptions
Configuration options for an [AsyncSignal](/types/asyncsignal).
### Constructors
View Constructors
##### `AsyncSignalOptions({this.initialValue, this.dependencies = const [], this.onDone, this.cancelOnError, this.lazy = true, super.name, super.autoDispose, super.watched, super.unwatched})`
Creates a new [AsyncSignalOptions](/types/asyncsignaloptions) instance.
### Properties
View Properties
##### `T? initialValue`
The initial value of the async signal.
##### `List> dependencies`
The list of dependencies to watch/listen to.
##### `void Function()? onDone`
Optional function called when a stream completes.
##### `bool? cancelOnError`
Whether to cancel the stream subscription on error.
##### `bool lazy`
Whether the execution is lazy.
### Methods
View Methods
##### `AsyncSignalOptions copyWith({T? initialValue, List>? dependencies, void Function()? onDone, bool? cancelOnError, bool? lazy, bool? autoDispose, String? name, void Function()? watched, void Function()? unwatched})`
Creates a copy of this options with custom overrides.
##### `bool ==(Object other)`
##### `int hashCode`
---
## TimerSignalDurationUtils
Expose Duration as a [TimerSignal](/types/timersignal)
### Methods
View Methods
##### `TimerSignal toSignal({bool? cancelOnError, AsyncSignalOptions? options, @Deprecated('Use options: AsyncSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: AsyncSignalOptions(name: ...) instead') String? debugLabel})`
Expose Duration as a [TimerSignal](/types/timersignal)
---
## timerSignal
Create a [TimerSignal](/types/timersignal)
---
## Page: Computed
Url: https://dartsignals.dev/packages/signals_core/async/computed
Description: Create an asynchronous computed signal with implicit dependency tracking.
---
## computedAsync
Create an asynchronous computed signal with implicit dependency tracking.
computedAsync takes an asynchronous **callback** function to compute the value
of the signal. Any signal read *synchronously* inside the callback is automatically
tracked as a dependency, and the computed signal is re-evaluated when any of those
dependencies change.
### ⚠️ The Async Gap Gotcha
Because Dart yields execution at every await expression, the reactive context that
automatically tracks reads is lost after an asynchronous gap.
**Rule:** All signal values MUST be read synchronously *before* the first await statement.
#### ❌ Incorrect Pattern (Signal read after await is NOT tracked):
```dart
final movie = computedAsync(() async {
await Future.delayed(Duration(milliseconds: 100));
// BUG: movieId is read AFTER an async gap.
// Changes to movieId will NOT re-evaluate this computedAsync!
return fetchMovie(movieId.value);
});
```
#### Correct Pattern (Read dependencies synchronously first):
```dart
final movie = computedAsync(() async {
// Capture all dependency values synchronously at the start!
final currentId = movieId.value;
await Future.delayed(Duration(milliseconds: 100));
// Use the captured local variable after the async gap
return fetchMovie(currentId);
});
```
### Advanced Example: Search Query with Debouncing
```dart
final searchQuery = signal('');
final searchResults = computedAsync(() async {
// Capture dependency synchronously
final query = searchQuery.value;
if (query.isEmpty) return [];
// Debounce: Wait 300ms before making the API request
await Future.delayed(Duration(milliseconds: 300));
return performSearchApiCall(query);
});
```
---
## computedFrom
Create an asynchronous computed signal by explicitly declaring its dependencies.
computedFrom takes a list of **signals** and a **callback** function to compute
the value of the signal every time one of the declared **signals** changes.
Unlike [computedAsync](/types/computedasync), which tracks dependencies implicitly, computedFrom is
immune to the **Async Gap Gotcha** because all tracking is declared upfront.
### Why use computedFrom?
When writing asynchronous code, Dart yields control at every await keyword.
Implicit tracking (in [computedAsync](/types/computedasync) or [computed](/types/computed)) cannot track reads that happen
*after* an asynchronous gap because the active reactive reader context is lost.
computedFrom solves this by:
1. Subscribing to the list of input **signals** synchronously.
2. Reading their latest values synchronously.
3. Passing those resolved values into your callback as an ordered list of arguments.
### Example: Fetching user details when an ID signal changes
```dart
final userId = signal(123);
// The callback receives the current values of the declared signals
final userProfile = computedFrom([userId], (args) async {
final currentId = args.first; // Type-safe list of dependencies
return fetchUserProfileFromServer(currentId);
});
// userProfile is a FutureSignal which can be pattern-matched
effect(() {
userProfile.value.map(
data: (profile) => print('Loaded profile: ${profile.name}'),
error: (err, stack) => print('Error: $err'),
loading: () => print('Fetching profile...'),
);
});
// Updating userId automatically triggers a new asynchronous fetch
userId.value = 456;
```
---
## Page: Connect
Url: https://dartsignals.dev/packages/signals_core/async/connect
Description: A highly powerful connector utility that allows you to dynamically stream and pipe multiple asynchronous streams directly into a single reactive Si...
---
A highly powerful connector utility that allows you to dynamically stream and pipe multiple asynchronous streams directly into a single reactive [Signal](/types/signal).
The concept is inspired by **Angular Signals** integration with RxJS streams.
Start with an existing mutable [Signal](/types/signal) and call connect(signal) to create a connector instance.
### 1. Chaining Streams
You can bind multiple streams to feed the same destination signal. The connector will handle the subscription management for all streams seamlessly.
```dart
final counter = signal(0);
final connector = connect(counter);
final fastStream = Stream.periodic(Duration(seconds: 1), (i) => i);
final slowStream = Stream.periodic(Duration(seconds: 5), (i) => i * 10);
// Values from both streams will be piped into the counter signal!
connector.from(fastStream).from(slowStream);
```
### 2. The Shift Operator (<<)
For a more concise and beautiful visual flow, you can use the shift operator (<<) to chain streams:
```dart
final s = signal(0);
final c = connect(s);
c << fastStream << slowStream;
```
### 3. Lifecycle and Disposal
To avoid memory leaks, make sure to dispose the connector when it is no longer needed. Disposing the connector will automatically cancel all underlying active stream subscriptions.
```dart
connector.dispose(); // Cancels all stream subscriptions safely
```
### Constructors
View Constructors
##### `Connect(this.signal)`
Connects a **Stream** to a [Signal](/types/signal).
### Properties
View Properties
##### `Signal signal`
Internal signal to connect to.
### Methods
View Methods
##### `Connect from(Stream source, {bool? cancelOnError, Function? onError, Function? onDone, void Function(T)? onValue})`
Connects a **Stream** to a [Signal](/types/signal).
```dart
final counter = signal(0);
final c = connect(counter);
final s1 = Stream.value(1);
final s2 = Stream.value(2);
c.from(s1).from(s2);
c.dispose();
```
##### `Connect <<(Stream source)`
Synonym for from(Stream source)
##### `void dispose()`
Cancels all subscriptions.
---
## connect
The idea for connect comes from Anguar Signals with RxJS:
VIDEO
Start with a signal and then use the connect method to create a connector.
Streams will feed Signal value.
```dart
final s = signal(0);
final c = connect(s);
```
### to
Add streams to the connector.
```dart
final s = signal(0);
final c = connect(s);
final s1 = Stream.value(1);
final s2 = Stream.value(2);
c.from(s1).from(s2); // These can be chained
```
### dispose
Cancel all subscriptions.
```dart
final s = signal(0);
final c = connect(s);
final s1 = Stream.value(1);
final s2 = Stream.value(2);
c.from(s1).from(s2);
// or
c << s1 << s2
c.dispose(); // This will cancel all subscriptions
```
---
## Page: Stream
Url: https://dartsignals.dev/packages/signals_core/async/stream
Description: Stream signals can be created by extension or method.
---
## streamSignal
Stream signals can be created by extension or method.
### streamSignal
```dart
final stream = () async* {
yield 1;
};
final s = streamSignal(() => stream);
```
### toSignal()
```dart
final stream = () async* {
yield 1;
};
final s = stream.toSignal();
```
## .value, .peek()
Returns [AsyncState](/dart/async/state) for the value and can handle the various states.
The value getter returns the value of the stream if it completed successfully.
> .peek() can also be used to not subscribe in an effect
```dart
final stream = (int value) async* {
yield value;
};
final s = streamSignal(() => stream);
final value = s.value.value; // 1 or null
```
## .reset()
The reset method resets the stream to its initial state to recall on the next evaluation.
```dart
final stream = (int value) async* {
yield value;
};
final s = streamSignal(() => stream);
s.reset();
```
## .refresh()
Refresh the stream value by setting isLoading to true, but maintain the current state (AsyncData, AsyncLoading, AsyncError).
```dart
final stream = (int value) async* {
yield value;
};
final s = streamSignal(() => stream);
s.refresh();
print(s.value.isLoading); // true
```
## .reload()
Reload the stream value by setting the state to AsyncLoading and pass in the value or error as data.
```dart
final stream = (int value) async* {
yield value;
};
final s = streamSignal(() => stream);
s.reload();
print(s.value is AsyncLoading); // true
```
## Dependencies
By default the callback will be called once and the stream will be cached unless a signal is read in the callback.
```dart
final count = signal(0);
final s = streamSignal(() async* {
final value = count();
yield value;
});
await s.future; // 0
count.value = 1;
await s.future; // 1
```
If there are signals that need to be tracked across an async gap then use the dependencies when creating the streamSignal to [reset](#.reset()) every time any signal in the dependency array changes.
```dart
final count = signal(0);
final s = streamSignal(
() async* {
final value = count();
yield value;
},
dependencies: [count],
);
s.value; // state with count 0
count.value = 1; // resets the future
s.value; // state with count 1
```
---
## StreamSignal
Stream signals wrap a standard asynchronous **Stream** and bridge it into the reactive state framework, exposing its emissions as a reactive [AsyncState](/types/asyncstate).
You can construct a stream signal via the helper function [streamSignal](/types/streamsignal) or by calling the .toSignal() extension method on any standard **Stream**.
### 1. Basic Stream Binding
```dart
final s = streamSignal(() => countStream());
```
Or via the extension:
```dart
final s = countStream().toSignal();
```
### 2. Consuming stream emissions reactively
Reading .value on a [StreamSignal](/types/streamsignal) returns an [AsyncState](/types/asyncstate) object:
```dart
effect(() {
s.value.map(
data: (val) => print('Stream emitted: $val'),
error: (err, stack) => print('Stream encountered error: $err'),
loading: () => print('Waiting for first stream emission...'),
);
});
```
### 3. Subscription Lifecycle and Manual Control
A stream signal automatically manages the underlying **StreamSubscription**. It listens when the signal has active subscribers and automatically cleans up/cancels when disposed to prevent memory leaks.
You can also manually control the subscription state:
- **pause()**: Pauses the underlying stream subscription.
- **resume()**: Resumes a paused subscription.
- **cancel()**: Cancels the subscription and marks the stream signal as done.
- **isDone**: Returns whether the stream has finished emitting or has been cancelled.
```dart
final s = streamSignal(() => countStream());
s.pause(); // Temporarily halt stream values
```
### 4. Reactive Dependencies
Any reactive signals read synchronously inside the stream callback act as dependencies. When they mutate, the stream signal automatically cancels the current stream subscription, recreates a new stream using the updated values, and starts listening.
```dart
final query = signal('flutter');
final s = streamSignal(() {
// Re-subscribes to a new database query stream every time the query changes!
return db.watchItems(query.value);
});
```
### Constructors
View Constructors
##### `StreamSignal(Stream Function() fn, {AsyncSignalOptions? options, @Deprecated('Use options: AsyncSignalOptions(cancelOnError: ...) instead') bool? cancelOnError, @Deprecated('Use options: AsyncSignalOptions(initialValue: ...) instead') T? initialValue, @Deprecated('Use options: AsyncSignalOptions(dependencies: ...) instead') List>? dependencies, @Deprecated('Use options: AsyncSignalOptions(onDone: ...) instead') void Function()? onDone, @Deprecated('Use options: AsyncSignalOptions(lazy: ...) instead') bool? lazy, @Deprecated('Use options: AsyncSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: AsyncSignalOptions(name: ...) instead') String? debugLabel})`
Stream signals can be created by extension or method.
### streamSignal
```dart
final stream = () async* {
yield 1;
};
final s = streamSignal(() => stream);
```
### toSignal()
```dart
final stream = () async* {
yield 1;
};
final s = stream.toSignal();
```
## .value, .peek()
Returns [AsyncState](/dart/async/state) for the value and can handle the various states.
The value getter returns the value of the stream if it completed successfully.
> .peek() can also be used to not subscribe in an effect
```dart
final stream = (int value) async* {
yield value;
};
final s = streamSignal(() => stream);
final value = s.value.value; // 1 or null
```
## .reset()
The reset method resets the stream to its initial state to recall on the next evaluation.
```dart
final stream = (int value) async* {
yield value;
};
final s = streamSignal(() => stream);
s.reset();
```
## .refresh()
Refresh the stream value by setting isLoading to true, but maintain the current state (AsyncData, AsyncLoading, AsyncError).
```dart
final stream = (int value) async* {
yield value;
};
final s = streamSignal(() => stream);
s.refresh();
print(s.value.isLoading); // true
```
## .reload()
Reload the stream value by setting the state to AsyncLoading and pass in the value or error as data.
```dart
final stream = (int value) async* {
yield value;
};
final s = streamSignal(() => stream);
s.reload();
print(s.value is AsyncLoading); // true
```
## Dependencies
By default the callback will be called once and the stream will be cached unless a signal is read in the callback.
```dart
final count = signal(0);
final s = streamSignal(() async* {
final value = count();
yield value;
});
await s.future; // 0
count.value = 1;
await s.future; // 1
```
If there are signals that need to be tracked across an async gap then use the dependencies when creating the streamSignal to [reset](#.reset()) every time any signal in the dependency array changes.
```dart
final count = signal(0);
final s = streamSignal(
() async* {
final value = count();
yield value;
},
dependencies: [count],
);
s.value; // state with count 0
count.value = 1; // resets the future
s.value; // state with count 1
```
### Properties
View Properties
##### `bool? cancelOnError`
Cancel the subscription on error
##### `List> dependencies`
List of dependencies to recompute the stream
### Methods
View Methods
##### `bool isDone`
Check if the signal is done
##### `Future last`
First value of the stream
##### `Future first`
Last value of the stream
##### `Future execute(Stream src)`
Execute the stream
##### `bool isPaused`
Check if the subscription is paused
##### `void pause([Future? resume])`
Pause the subscription
##### `void resume()`
Resume the subscription
##### `Future cancel()`
Cancel the subscription
##### `Future reload()`
##### `Future refresh()`
##### `void reset([AsyncState? value])`
##### `void dispose()`
##### `AsyncState value`
##### `void setError(Object error, [StackTrace? stackTrace])`
---
## Page: MapSignal
Url: https://dartsignals.dev/packages/signals_core/value/map
Description: A reactive Signal that holds a Map and implements the Map interface.
---
A reactive [Signal](/types/signal) that holds a **Map** and implements the **Map** interface.
[MapSignal](/types/mapsignal) lets you listen to changes on a map reactively and mutate it directly using
standard map operations (like adding/modifying keys with operator []=, addAll, remove,
clear, etc.). Any mutations automatically trigger reactive updates to all active listeners
(e.g., inside an [effect](/types/effect) or [computed](/types/computed)).
Additionally, [MapSignal](/types/mapsignal) defines convenient operators:
- << injects/adds all entries from another map into the current map.
- & forks/concatenates the map with another map into a new [MapSignal](/types/mapsignal).
- | pipes/concatenates the map with another signal holding a map into a new [MapSignal](/types/mapsignal).
### Example Usage
```dart
import 'package:signals/signals.dart';
void main() {
final settings = mapSignal({
'theme': 'light',
'notifications': true,
});
effect(() {
print('Theme is currently: ${settings['theme']}');
}); // Prints: "Theme is currently: light"
// Update key/value pair directly (triggers updates)
settings['theme'] = 'dark'; // Prints: "Theme is currently: dark"
// Expose standard Map methods
settings.remove('notifications');
}
```
Mutating the collection directly calls the reactive set() routine under the hood automatically. You
do not need to assign settings.value = ... manually!
### Constructors
View Constructors
##### `MapSignal(super.value, {MapSignalOptions? options, @Deprecated('Use options: MapSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: MapSignalOptions(name: ...) instead') String? debugLabel})`
Creates a [MapSignal](/types/mapsignal) with the given **value**.
### Methods
View Methods
##### `MapSignal <<(Map other)`
Inject: Update current signal value with iterable
##### `MapSignal &(Map other)`
Fork: create a new signal with value is the concatenation of source signal and iterable parameter
##### `MapSignal |(Signal> other)`
Pipe: create a new signal by sending value from source to other
##### `bool ==(Object other)`
##### `int hashCode`
---
## mapSignal
Creates a [MapSignal](/types/mapsignal) initialized with the provided **map**.
This is a convenience helper function for creating reactive map signals.
```dart
import 'package:signals/signals.dart';
final settings = mapSignal({'theme': 'dark'});
```
---
## Page: SetSignal
Url: https://dartsignals.dev/packages/signals_core/value/set
Description: A reactive Signal that holds a Set and implements the Set interface.
---
A reactive [Signal](/types/signal) that holds a **Set** and implements the **Set** interface.
[SetSignal](/types/setsignal) lets you listen to changes on a set reactively and mutate it directly using
standard set operations (like add, addAll, remove, clear, etc.). Any mutations
automatically trigger reactive updates to all active listeners (e.g., inside an [effect](/types/effect)
or [computed](/types/computed)).
Additionally, [SetSignal](/types/setsignal) defines convenient operators:
- << injects/adds all items from another set into the current set.
- & forks/unions the set with another set into a new [SetSignal](/types/setsignal).
- | pipes/unions the set with another signal holding an iterable into a new [SetSignal](/types/setsignal).
### Example Usage
```dart
import 'package:signals/signals.dart';
void main() {
final numbers = setSignal({1, 2, 3});
effect(() {
print('Set content: $numbers, Length: ${numbers.length}');
}); // Prints: "Set content: {1, 2, 3}, Length: 3"
// Standard mutation (triggers updates)
numbers.add(4); // Prints: "Set content: {1, 2, 3, 4}, Length: 4"
// Removing an element (triggers updates)
numbers.remove(1); // Prints: "Set content: {2, 3, 4}, Length: 3"
// Set intersection (reactive query)
final common = numbers.intersection({3, 4, 5});
print(common); // Prints: {3, 4}
}
```
Mutating the collection directly calls the reactive set() routine under the hood automatically. You
do not need to assign numbers.value = ... manually!
### Constructors
View Constructors
##### `SetSignal(super.value, {SetSignalOptions? options, @Deprecated('Use options: SetSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: SetSignalOptions(name: ...) instead') String? debugLabel})`
Creates a [SetSignal](/types/setsignal) with the given **value**.
### Methods
View Methods
##### `SetSignal <<(Set other)`
Inject: Update current signal value with iterable
##### `SetSignal &(Set other)`
Fork: create a new signal with value is the concatenation of source signal and iterable parameter
##### `SetSignal |(Signal> other)`
Pipe: create a new signal by sending value from source to other
##### `bool ==(Object other)`
##### `int hashCode`
---
## setSignal
Creates a [SetSignal](/types/setsignal) initialized with the provided **set**.
This is a convenience helper function for creating reactive set signals.
```dart
import 'package:signals/signals.dart';
final mySet = setSignal({1, 2, 3});
```
---
## Page: ListSignal
Url: https://dartsignals.dev/packages/signals_core/value/list
Description: A reactive Signal that holds a List and implements the List interface.
---
A reactive [Signal](/types/signal) that holds a **List** and implements the **List** interface.
[ListSignal](/types/listsignal) lets you listen to changes on a list reactively and mutate it directly using
standard list operations (like add, addAll, remove, operators [] and []=, etc.).
Any mutations automatically trigger reactive updates to all active listeners (e.g., inside an
[effect](/types/effect) or [computed](/types/computed)).
Additionally, [ListSignal](/types/listsignal) defines convenient operators:
- << injects/adds all items from an iterable into the list.
- & forks/concatenates the list with an iterable into a new [ListSignal](/types/listsignal).
- | pipes/concatenates the list with another signal holding an iterable into a new [ListSignal](/types/listsignal).
### Example Usage
```dart
import 'package:signals/signals.dart';
void main() {
final numbers = listSignal([1, 2, 3]);
effect(() {
print('List content: $numbers, Length: ${numbers.length}');
}); // Prints: "List content: [1, 2, 3], Length: 3"
// Standard mutation (triggers updates)
numbers.add(4); // Prints: "List content: [1, 2, 3, 4], Length: 4"
// Update via index operator (triggers updates)
numbers[0] = 10; // Prints: "List content: [10, 2, 3, 4], Length: 4"
// Injection operator (triggers updates)
numbers << [5, 6]; // Prints: "List content: [10, 2, 3, 4, 5, 6], Length: 6"
}
```
Mutating the collection directly calls the reactive set() routine under the hood automatically. You
do not need to assign numbers.value = ... manually!
### Constructors
View Constructors
##### `ListSignal(super.value, {ListSignalOptions? options, @Deprecated('Use options: ListSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: ListSignalOptions(name: ...) instead') String? debugLabel})`
Creates a [ListSignal](/types/listsignal) with the given **value**.
### Methods
View Methods
##### `ListSignal <<(Iterable other)`
Inject: Update current signal value with iterable
##### `ListSignal &(Iterable other)`
Fork: create a new signal which value is the concatenation of source signal and iterable parameter
##### `ListSignal |(Signal> other)`
Pipe: create a new signal by sending value from source to other
##### `bool ==(Object other)`
##### `int hashCode`
---
## listSignal
Creates a [ListSignal](/types/listsignal) initialized with the provided **list**.
This is a convenience helper function for creating reactive list signals.
```dart
import 'package:signals/signals.dart';
final list = listSignal([1, 2, 3]);
```
---
## Page: IterableSignal
Url: https://dartsignals.dev/packages/signals_core/value/iterable
Description: A reactive Signal that holds an Iterable and implements the Iterable interface.
---
A reactive [Signal](/types/signal) that holds an **Iterable** and implements the **Iterable** interface.
[IterableSignal](/types/iterablesignal) allows you to listen to changes on an iterable collection reactively. It
exposes all standard **Iterable** properties and methods (like length, first, map, where, etc.)
directly on the signal itself. Calling these methods inside a reactive context (like an effect
or computed block) will automatically track them as dependencies.
### Example Usage
```dart
import 'package:signals/signals.dart';
void main() {
final items = iterableSignal([1, 2, 3]);
effect(() {
print('Items length: ${items.length}, First: ${items.first}');
}); // Prints: "Items length: 3, First: 1"
// Update the signal by assigning a new iterable
items.value = [10, 20, 30, 40]; // Prints: "Items length: 4, First: 10"
}
```
Direct mutation of the items inside the iterable will NOT trigger updates. To reactively mutate collections,
use specialized signals like ListSignal , SetSignal , or MapSignal .
### Constructors
View Constructors
##### `IterableSignal(super.value, {IterableSignalOptions? options, @Deprecated('Use options: IterableSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: IterableSignalOptions(name: ...) instead') String? debugLabel})`
Creates a [IterableSignal](/types/iterablesignal) with the given **value**.
### Methods
View Methods
##### `bool ==(Object other)`
##### `int hashCode`
---
## iterableSignal
Creates an [IterableSignal](/types/iterablesignal) holding the provided **iterable**.
This is a convenience function that instantiates an [IterableSignal](/types/iterablesignal), which delegates
all standard **Iterable** operations reactively and tracks changes.
### Example Usage
```dart
import 'package:signals/signals.dart';
final s = iterableSignal([1, 2, 3]);
print(s.length); // 3
```
---
## Page: ChangeStackSignal
Url: https://dartsignals.dev/packages/signals_core/value/change-stack
Description: A reactive Signal that records its history of values, allowing undo and redo operations.
---
A reactive [Signal](/types/signal) that records its history of values, allowing undo and redo operations.
[ChangeStackSignal](/types/changestacksignal) stores successive values of the signal in a double-ended queue.
This allows you to revert back to previous values using **undo** and re-apply undone values
using **redo**. You can also specify an optional **limit** parameter to cap the history queue size.
If you only need access to the initial and immediate previous values of a signal (without a full
history stack or undo/redo mechanisms), use the lightweight TrackedSignalMixin instead.
### Example Usage
```dart
import 'package:signals/signals.dart';
void main() {
final counter = ChangeStackSignal(0, limit: 5);
effect(() {
print('Counter: ${counter.value}');
}); // Prints: "Counter: 0"
counter.value = 1; // Prints: "Counter: 1"
counter.value = 2; // Prints: "Counter: 2"
print('Can Undo: ${counter.canUndo}'); // Prints: "Can Undo: true"
// Perform undo operation (automatically triggers reactive updates)
counter.undo(); // Prints: "Counter: 1"
counter.undo(); // Prints: "Counter: 0"
// Perform redo operation
counter.redo(); // Prints: "Counter: 1"
}
```
This class works best with values that are immutable or copied when updated.
If you mutate an object in-place directly without assigning a new value using the .value
setter or set(...), the history queue will store references to the same mutated object, and
undo/redo operations will not reflect changes correctly.
### Constructors
View Constructors
##### `ChangeStackSignal(super.value, {int? limit, ChangeSignalOptions? options, @Deprecated('Use options: ChangeSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: ChangeSignalOptions(name: ...) instead') String? debugLabel})`
Creates a [ChangeStackSignal](/types/changestacksignal) initialized with the provided **value**.
---
## changeStack
Creates a [ChangeStackSignal](/types/changestacksignal) initialized with the provided **value**.
This is a convenience helper function for creating reactive undo/redo history signals.
You can pass a **limit** to restrict the maximum history stack size.
```dart
import 'package:signals/signals.dart';
final s = changeStack(0, limit: 10);
s.value = 1;
s.undo(); // Returns to 0
```
---
## Page: SignalEquality
Url: https://dartsignals.dev/packages/signals_core/utilities/equality
Description: Defines the equality check algorithm used by signals to determine if a new value.
---
Defines the equality check algorithm used by signals to determine if a new value
actually differs from the current value.
By default, signals use standard Dart operator equality (==). However, you can configure
a signal to use different strategies, such as deep equality check for collections or custom comparator checks.
Strategies:
- **standard**: Default value equality (a == b).
- **identity**: Identity-based comparison (identical(a, b)).
- **deep**: Deep collection comparison for Lists, Maps, and Sets.
- **custom**: User-defined boolean comparison function.
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
void main() {
// Create a list signal using deep equality check
final items = signal(
[1, 2, 3],
options: SignalOptions(
equality: SignalEquality.deep(),
),
);
effect(() {
print('Items changed: ${items.value}');
});
// Reassigning an identical value structure does NOT trigger a rebuild!
items.value = [1, 2, 3];
// Triggers rebuild
items.value = [1, 2, 3, 4];
}
```
### Constructors
View Constructors
##### `SignalEquality()`
@nodoc
##### `SignalEquality.custom(bool Function(T a, T b) fn)`
Custom equality check
Uses a user-provided boolean function **fn** to check for equality.
### Methods
View Methods
##### `bool equals(Object? a, Object? b)`
Check if two values are equal
##### `static SignalEquality standard()`
Standard equality check (a == b)
Matches two objects if their standard == operator returns true.
This is the default strategy used by all signals.
##### `static SignalEquality identity()`
Identity equality check (identical(a, b))
Matches two objects only if they are the exact same instance in memory.
##### `static SignalEquality deep()`
Deep equality check
Matches collections (Lists, Maps, Sets) recursively by comparing their items.
---
## SignalIdentityEquality
Identity equality check (identical(a, b))
Matches two objects only if they point to the exact same instance in memory.
### Example Usage
```dart
final listA = [1, 2];
final listB = [1, 2];
final equality = SignalEquality.identity>();
print(equality.equals(listA, listB)); // false
print(equality.equals(listA, listA)); // true
```
### Constructors
View Constructors
##### `SignalIdentityEquality()`
Creates a new [SignalIdentityEquality](/types/signalidentityequality) instance.
### Methods
View Methods
##### `bool equals(Object? a, Object? b)`
---
## SignalDeepEquality
Deep equality check for collections
Recursively compares Lists, Maps, and Sets to see if their nested elements are equal.
### Example Usage
```dart
final equality = SignalEquality.deep();
print(equality.equals([1, [2, 3]], [1, [2, 3]])); // true
```
### Constructors
View Constructors
##### `SignalDeepEquality()`
Creates a new [SignalDeepEquality](/types/signaldeepequality) instance.
### Methods
View Methods
##### `bool equals(Object? a, Object? b)`
---
## SignalCustomEquality
Custom equality check using a custom function
Uses a custom comparison function to determine if two values of type **T** are equal.
### Example Usage
```dart
final equality = SignalEquality.custom((User a, User b) => a.id == b.id);
```
### Constructors
View Constructors
##### `SignalCustomEquality(this._fn)`
Creates a new [SignalCustomEquality](/types/signalcustomequality) instance.
### Methods
View Methods
##### `bool equals(Object? a, Object? b)`
---
## SignalStandardEquality
Standard equality check (a == b)
Matches two objects using the standard Dart operator ==.
### Example Usage
```dart
final equality = SignalEquality.standard();
print(equality.equals(5, 5)); // true
```
### Constructors
View Constructors
##### `SignalStandardEquality()`
Creates a new [SignalStandardEquality](/types/signalstandardequality) instance.
### Methods
View Methods
##### `bool equals(Object? a, Object? b)`
---
## Page: Model
Url: https://dartsignals.dev/packages/signals_core/utilities/model
Description: Creates a new model constructor with an instanced factory.
---
## createModel
Creates a new model constructor with an instanced factory.
A [SignalModel](/types/signalmodel) is a highly powerful architectural primitive designed to manage cohesive packages
of related state, business logic, actions, and side effects.
Under the hood, [SignalModel](/types/signalmodel) automatically tracks, scopes, and manages the lifecycle of any [Effect](/types/effect)s
instantiated during its factory execution. When the model is disposed (by calling model.dispose()),
all nested/captured effects are clean up automatically, ensuring complete prevention of memory leaks.
Furthermore, if the factory returns a standard Dart **Map**, and wrapInAction is enabled (default),
all nested function properties are automatically wrapped in batched [action](/types/action) transactions to optimize updates.
### 1. Type-Safe Models using Dart 3+ Records (Recommended)
The simplest and most built-in way to define a compile-safe model is to return a Dart **record** from your factory.
Records provide immediate type safety, autocomplete, and compile-time verification without any wrapper boilerplates.
```dart
import 'package:signals/signals.dart';
// Define the reactive model constructor returning a Record
final counterModel = createModel(() {
final count = signal(0);
// Captured nested side-effect (e.g. logging or syncing to local storage)
effect(() {
print('Nested logger: count is ${count.value}');
});
return (
count: count,
increment: () => count.value++,
);
});
void main() {
// Instantiate the model
final model = counterModel();
// Access properties type-safely via .value
print(model.value.count.value); // Prints: 0 (and registers effect print: Nested logger: count is 0)
model.value.increment(); // Prints: Nested logger: count is 1
// Dispose when done to clean up all captured nested effects
model.dispose();
}
```
### 2. Object-Oriented Style: Type-Safe Wrappers using Dart 3+ Extension Types
While records are great for lightweight structures, you can wrap the returned Map-based SignalModel in a standard Dart 3 **extension type** when you prefer a class-like API (e.g. implementing getters/setters or hiding subscript lookups).
```dart
import 'package:signals/signals.dart';
// 1. Define the reactive model constructor returning a Map
final counterModel = createModel(() {
final count = signal(0);
// Captured nested side-effect (e.g. logging or syncing to local storage)
effect(() {
print('Nested logger: count is ${count.value}');
});
return {
'count': count,
'increment': () => count.value++,
};
});
// 2. Create a premium, compile-safe extension type wrapper
extension type TypeSafeCounter(SignalModel> _model) {
int get count => (_model['count'] as Signal).value;
set count(int val) => (_model['count'] as Signal).value = val;
void increment() => (_model['increment'] as Function)();
void dispose() => _model.dispose();
}
void main() {
// 3. Instantiate and wrap the model
final counter = TypeSafeCounter(counterModel());
// Now you have a beautifully autocomplete-friendly, compile-safe API!
print(counter.count); // Prints: 0 (and registers effect print: Nested logger: count is 0)
counter.increment(); // Prints: Nested logger: count is 1
// Dispose when done to clean up all captured nested effects
counter.dispose();
}
```
Favor using Dart 3 records or extension types when defining models. They cost zero runtime overhead
while granting complete compile-safe parameters and autocomplete functionality.
---
## SignalModel
A premium wrapper for cohesive state packages constructed with [createModel](/types/createmodel).
It holds the instanced model **value** and all the [Effect](/types/effect)s that were captured
during its construction. Disposing the [SignalModel](/types/signalmodel) automatically disposes of all
nested/captured effects, completely avoiding memory leaks.
### Premium Pattern: Dart 3+ Extension Type Wrappers
To avoid unchecked subscript access like model['count'].value, wrap your model in an extension type:
```dart
extension type TypeSafeCounter(SignalModel> _model) {
int get count => (_model['count'] as Signal).value;
set count(int val) => (_model['count'] as Signal).value = val;
void increment() => (_model['increment'] as Function)();
void dispose() => _model.dispose();
}
```
### Constructors
View Constructors
##### `SignalModel(this.value, this._effects, {this.options = const SignalModelOptions()})`
Creates a new model instance.
### Properties
View Properties
##### `T value`
The instanced model value.
##### `SignalModelOptions options`
Options used to configure this model.
### Methods
View Methods
##### `dynamic [](Object? key)`
Access properties dynamically if the underlying **value** is a **Map**.
##### `void []=(dynamic key, dynamic val)`
Set properties dynamically if the underlying **value** is a **Map**.
##### `T call()`
Returns the **value** of this model. Alias for [.value]
##### `void dispose()`
Disposes of all captured effects.
---
## SignalModelOptions
Options for configuring a [SignalModel](/types/signalmodel).
Provides configuration for debug labeling (**name**) and whether to automatically wrap Map functions
in transaction-safe, batched actions (**wrapInAction**).
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
final options = const SignalModelOptions(
name: 'user-profile-model',
wrapInAction: true,
);
```
### Constructors
View Constructors
##### `SignalModelOptions({this.name, this.wrapInAction = true})`
Creates a new instance of [SignalModelOptions](/types/signalmodeloptions).
### Properties
View Properties
##### `String? name`
The name or debug label of the model.
##### `bool wrapInAction`
Whether to automatically wrap returned Map functions in actions.
Defaults to true.
### Methods
View Methods
##### `SignalModelOptions copyWith({String? name, bool? wrapInAction})`
Copy options with new values.
##### `bool ==(Object other)`
##### `int hashCode`
---
## SignalModelConstructor
A constructor for models that manages nested effects.
The model constructor starts capturing effects when called, storing them inside the returned [SignalModel](/types/signalmodel).
### Example Usage
```dart
import 'package:preact_signals/preact_signals.dart';
final myModel = SignalModelConstructor(() => 'data');
final model = myModel();
print(model.value); // Prints: data
```
### Constructors
View Constructors
##### `SignalModelConstructor(this._factory, {this.options = const SignalModelOptions()})`
Creates a new instance of [SignalModelConstructor](/types/signalmodelconstructor).
### Properties
View Properties
##### `SignalModelOptions options`
Options used to configure this constructor.
### Methods
View Methods
##### `SignalModel call()`
Instantiates a new [SignalModel](/types/signalmodel) instance.
---
## Page: SignalContainer
Url: https://dartsignals.dev/packages/signals_core/utilities/container
Description: Signal container used to create signals based on args.
---
Signal container used to create signals based on args
```dart
final container = readonlySignalContainer((e) {
return signal(Cache(e));
});
final cacheA = container('cache-a');
final cacheB = container('cache-b');
final cacheC = container('cache-c');
```
Example of settings and SharedPreferences:
```dart
class Settings {
final SharedPreferences prefs;
EffectCleanup? _cleanup;
Settings(this.prefs) {
_cleanup = effect(() {
for (final entry in setting.store.entries) {
final value = entry.value.peek();
if (prefs.getString(entry.key.$1) != value) {
prefs.setString(entry.key.$1, value).ignore();
}
}
});
}
late final setting = signalContainer(
(val) => signal(prefs.getString(val.$1) ?? val.$2),
cache: true,
);
Signal get darkMode => setting(('dark-mode', 'false'));
void dispose() {
_cleanup?.call();
setting.dispose();
}
}
void main() {
// Load or find instance
late final SharedPreferences prefs = ...;
// Create settings
final settings = Settings(prefs);
// Get value
print('dark mode: ${settings.darkMode}');
// Update value
settings.darkMode.value = 'true';
}
```
### Constructors
View Constructors
##### `SignalContainer(this._create, {this.cache = false, this.onEvict})`
Signal container used to create multiple signals via args
### Properties
View Properties
##### `bool cache`
If true then signals will be cached when created
##### `void Function(Arg key, S signal)? onEvict`
Optional callback when a signal is removed/evicted from the cache
##### `store`
Store of created signals (if cache is true)
### Methods
View Methods
##### `S call(Arg arg)`
Create the signal with the given args
##### `S? remove(Arg arg)`
Remove a signal from the cache
##### `bool containsKey(Arg arg)`
Check if signal is currently stored in the cache
##### `void clear()`
Clear the cache
##### `void dispose()`
Dispose of all created signals
##### `int length`
Returns the number of cached signals.
##### `bool isEmpty`
Returns true if the cache is empty.
##### `bool isNotEmpty`
Returns true if the cache is not empty.
##### `Iterable keys`
Returns all currently cached keys.
##### `Iterable values`
Returns all currently cached signals.
##### `Iterable> entries`
Returns all currently cached entries.
##### `S? lookup(Arg arg)`
Retrieve the cached signal for **arg** if it exists, without creating a new one if it is missing.
##### `void removeWhere(bool Function(Arg key, S signal) test)`
Filter and remove matching cached signals.
---
## readonlySignalContainer
Create a signal container used to instance signals based on args
```dart
final container = readonlySignalContainer((e) {
return signal(Cache(e));
});
final cacheA = container('cache-a');
final cacheB = container('cache-b');
final cacheC = container('cache-c');
```
The signals cannot be updated but allows for
using computed where the value is only derived from other signals.
---
## signalContainer
Create a signal container used to instance signals based on args
```dart
final container = signalContainer((e) {
return signal(Cache(e));
});
final cacheA = container('cache-a');
final cacheB = container('cache-b');
final cacheC = container('cache-c');
cacheA.value = 'a';
cacheB.value = 'b';
cacheC.value = 'c';
```
---
## streamSignalContainer
Create a signal container for StreamSignals based on args.
```dart
final container = streamSignalContainer((roomId) {
return streamSignal(() => listenToRoom(roomId));
});
```
---
## computedContainer
Create a signal container for computed signals based on args.
```dart
final container = computedContainer((arg) {
return computed(() => sourceSignal.value * arg);
});
```
---
## futureSignalContainer
Create a signal container for FutureSignals based on args.
```dart
final container = futureSignalContainer((id) {
return futureSignal(() => fetchPost(id));
});
```
---
## Page: SignalsObserver
Url: https://dartsignals.dev/packages/signals_core/utilities/observer
Description: You can observe all signal values in the dart application by providing an implementation of SignalsObserver:.
---
You can observe all signal values in the dart application by providing an implementation of SignalsObserver:
```dart
abstract class SignalsObserver {
void onSignalCreated(Signal instance);
void onSignalUpdated(Signal instance, dynamic value);
void onComputedCreated(Computed instance);
void onComputedUpdated(Computed instance, dynamic value);
static SignalsObserver? instance;
}
```
> There is a prebuilt LoggingSignalsObserver for printing updates to the console.
To add the observer override the instance at the start of the application:
```dart
void main() {
SignalsObserver.instance = LoggingSignalsObserver(); // or custom observer
...
}
```
This will have a slight performance hit since every update will be tracked via the observer. It is recommended to only set the SignalsObserver.instance in debug or profile mode.
### Properties
View Properties
##### `static SignalsObserver? instance`
The current observer instance.
### Methods
View Methods
##### `void onSignalCreated(Signal instance, T value)`
Called when a signal is created.
##### `void onSignalUpdated(Signal instance, T value)`
Called when a signal is updated.
##### `void onComputedCreated(Computed instance)`
Called when a computed is created.
##### `void onComputedUpdated(Computed instance, T value)`
Called when a computed is updated.
##### `void onEffectCreated(Effect instance)`
Called when a effect is created.
##### `void onEffectCalled(Effect instance)`
Called when a effect is called.
##### `void onEffectRemoved(Effect instance)`
Called when a effect is disposed.
---
## LoggingSignalsObserver
Logs all signals and computed changes to the console.
### Methods
View Methods
##### `void onComputedCreated(Computed instance)`
##### `void onComputedUpdated(Computed instance, T value)`
##### `void onSignalCreated(Signal instance, T value)`
##### `void onSignalUpdated(Signal instance, T value)`
##### `void onEffectCreated(Effect instance)`
##### `void onEffectCalled(Effect instance)`
##### `void onEffectRemoved(Effect instance)`
##### `void log(String message)`
Logs a message to the console.
---
## onSignalRead
Global callback when any signal is read.
---
## signalsDevToolsEnabled
Manually enable/disable signals devtools
---
## DevToolsSignalsObserver
Signals DevTools observer
### Constructors
View Constructors
##### `DevToolsSignalsObserver()`
Create a DevToolsSignalsObserver and register the VM service extensions.
### Methods
View Methods
##### `bool enabled`
Check if devTools is enabled
##### `enabled(bool value)`
Enable/Disable devTools
##### `void reassemble()`
Reload the signals devTools
##### `void onComputedCreated(Computed instance)`
##### `void onComputedUpdated(Computed instance, T value)`
##### `void onSignalCreated(Signal instance, T value)`
##### `void onSignalUpdated(Signal instance, T value)`
##### `void log(String message)`
Logs a message to the console.
##### `void onEffectCreated(Effect instance)`
##### `void onEffectCalled(Effect instance)`
##### `void onEffectRemoved(Effect instance)`
##### `Map getNodes()`
Returns a map representation of all active signals, computeds, and effects
in the reactive graph.
---
## disableSignalsDevTools
Disable the devtools
---
## reloadSignalsDevTools
Reload the devtools
---
## Page: PersistedSignal
Url: https://dartsignals.dev/packages/signals_core/utilities/persisted
Description: A Signal whose value is persistently stored in a key-value database.
---
A Signal whose value is persistently stored in a key-value database.
PersistedSignal allows application state (such as user preferences, theme options,
authentication tokens, and drafts) to automatically survive application restarts
without writing tedious boilerplate for manual loading and saving.
### Concrete Subclasses
For common primitive types, use the provided concrete classes:
- [PersistedBoolSignal](/types/persistedboolsignal) / [PersistedNullableBoolSignal](/types/persistednullableboolsignal)
- [PersistedIntSignal](/types/persistedintsignal) / [PersistedNullableIntSignal](/types/persistednullableintsignal)
- [PersistedDoubleSignal](/types/persisteddoublesignal) / [PersistedNullableDoubleSignal](/types/persistednullabledoublesignal)
- [PersistedNumSignal](/types/persistednumsignal) / [PersistedNullableNumSignal](/types/persistednullablenumsignal)
- [PersistedStringSignal](/types/persistedstringsignal) / [PersistedNullableStringSignal](/types/persistednullablestringsignal)
- [PersistedEnumSignal](/types/persistedenumsignal) / [PersistedNullableEnumSignal](/types/persistednullableenumsignal)
### Simple Usage Example
```dart
// 1. Create or obtain a key-value store adapter (like standard in-memory)
final localStore = SignalsInMemoryKeyValueStore();
// 2. Create the persisted signal with a unique key
final darkModeSignal = PersistedBoolSignal(
false, // Fallback initial value
key: 'settings.dark_mode',
store: localStore,
);
// 3. The value is automatically loaded asynchronously on instantiation.
// Mutating the value synchronously schedules an async save under the hood:
darkModeSignal.value = true; // Automatically persisted to store
```
### Custom Serialization / Complex Objects
To persist complex objects (e.g. custom classes), subclass [PersistedSignal](/types/persistedsignal)
and override the **decode** and **encode** methods, or mixin [PersistedSignalMixin](/types/persistedsignalmixin)
on a custom [Signal](/types/signal) class.
```dart
class User {
final String name;
final int age;
User(this.name, this.age);
Map toJson() => {'name': name, 'age': age};
factory User.fromJson(Map json) => User(json['name'], json['age']);
}
class PersistedUserSignal extends PersistedSignal {
PersistedUserSignal(
super.internalValue, {
required super.key,
required super.store,
});
@override
User decode(String value) => User.fromJson(jsonDecode(value));
@override
String encode(User value) => jsonEncode(value.toJson());
}
```
### Constructors
View Constructors
##### `PersistedSignal(super.internalValue, {required this.key, required this.store, PersistedSignalOptions? options, @Deprecated('Use options: PersistedSignalOptions(autoDispose: ...) instead') bool? autoDispose, @Deprecated('Use options: PersistedSignalOptions(name: ...) instead') String? debugLabel, bool autoInit = true})`
Creates a new PersistedSignal.
### Properties
View Properties
##### `String key`
##### `SignalsKeyValueStore store`
---
## SignalsKeyValueStore
An abstract class defining the persistence adapter contract for [PersistedSignal](/types/persistedsignal).
Implement this interface to bind PersistedSignal to your storage engine of
choice, such as local files, SQLite, SharedPreferences, Hive, or indexedDB.
### Example: Custom Shared Preferences Store (Flutter)
```dart
import 'package:shared_preferences/shared_preferences.dart';
import 'package:signals/signals.dart';
class SharedPreferencesStore implements SignalsKeyValueStore {
final SharedPreferences prefs;
SharedPreferencesStore(this.prefs);
@override
Future getItem(String key) async {
return prefs.getString(key);
}
@override
Future setItem(String key, String value) async {
await prefs.setString(key, value);
}
@override
Future removeItem(String key) async {
await prefs.remove(key);
}
}
```
### Properties
View Properties
##### `static SignalsKeyValueStore defaultStore`
The default store to be used if no store is provided.
### Methods
View Methods
##### `Future setItem(String key, String value)`
Sets an item in the store.
##### `Future getItem(String key)`
Gets an item from the store.
##### `Future removeItem(String key)`
Removes an item from the store.
---
## PersistedSignalMixin
A mixin that adds local persistence capabilities to a standard [Signal](/types/signal).
By mixing in PersistedSignalMixin on a Signal subclass, the signal
will automatically retrieve its stored state on boot and save its state whenever
.value is mutated.
Classes mixing in PersistedSignalMixin must implement:
- **key**: A unique identifier string for the key-value database.
- **store**: An implementation of [SignalsKeyValueStore](/types/signalskeyvaluestore).
### Serialization Customization
By default, the mixin uses standard JSON parsing (jsonDecode / jsonEncode).
If your data type T is not natively supported by JSON, override:
- **decode** to convert the raw string value back into type T.
- **encode** to serialize type T into a string.
### Properties
View Properties
##### `bool loaded`
Whether the signal has been loaded from the store.
### Methods
View Methods
##### `String key`
The key to use for storing the value.
##### `SignalsKeyValueStore store`
The store to use for storing the value.
##### `Future init()`
Initializes the signal by loading the value from the store.
##### `T value`
##### `value(T value)`
##### `Future load()`
Loads the value from the store.
##### `Future save(T value)`
Saves the value to the store.
##### `T decode(String value)`
Decodes the value from a string.
##### `String encode(T value)`
Encodes the value to a string.
---
## SignalsInMemoryKeyValueStore
An in-memory, volatile implementation of [SignalsKeyValueStore](/types/signalskeyvaluestore).
This serves as a fallback engine and does not persist across restarts/reload.
### Properties
View Properties
##### `store`
The in-memory store.
### Methods
View Methods
##### `Future getItem(String key)`
##### `Future removeItem(String key)`
##### `Future setItem(String key, String value)`
---
## PersistedNullableStringSignal
A PersistedSignal that stores a nullable string value.
> [!warning] An empty value is considered null
### Constructors
View Constructors
##### `PersistedNullableStringSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new NullableStringSignal.
### Methods
View Methods
##### `String? decode(String value)`
##### `String encode(String? value)`
---
## PersistedNullableIntSignal
A PersistedSignal that stores a nullable integer value.
### Constructors
View Constructors
##### `PersistedNullableIntSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new NullableIntSignal.
### Methods
View Methods
##### `int? decode(String value)`
##### `String encode(int? value)`
---
## PersistedNullableNumSignal
A PersistedSignal that stores a nullable numeric value.
### Constructors
View Constructors
##### `PersistedNullableNumSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new NullableNumSignal.
### Methods
View Methods
##### `num? decode(String value)`
##### `String encode(num? value)`
---
## PersistedNullableBoolSignal
A PersistedSignal that stores a nullable string value.
### Constructors
View Constructors
##### `PersistedNullableBoolSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new NullableBoolSignal.
### Methods
View Methods
##### `bool? decode(String value)`
##### `String encode(bool? value)`
---
## PersistedNullableDoubleSignal
A PersistedSignal that stores a nullable double value.
### Constructors
View Constructors
##### `PersistedNullableDoubleSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new NullableDoubleSignal.
### Methods
View Methods
##### `double? decode(String value)`
##### `String encode(double? value)`
---
## PersistedNullableEnumSignal
A PersistedSignal that stores a nullable enum value.
### Constructors
View Constructors
##### `PersistedNullableEnumSignal(super.val, String key, this.values, {SignalsKeyValueStore? store})`
Creates a new NullableEnumSignal.
### Properties
View Properties
##### `List values`
### Methods
View Methods
##### `T? decode(String value)`
##### `String encode(T? value)`
---
## PersistedIntSignal
A PersistedSignal that stores an integer value.
### Constructors
View Constructors
##### `PersistedIntSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new IntSignal.
### Methods
View Methods
##### `int decode(String value)`
##### `String encode(int value)`
---
## PersistedBoolSignal
A PersistedSignal that stores a boolean value.
### Constructors
View Constructors
##### `PersistedBoolSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new BoolSignal.
### Methods
View Methods
##### `bool decode(String value)`
##### `String encode(bool value)`
---
## PersistedDoubleSignal
A PersistedSignal that stores an double value.
### Constructors
View Constructors
##### `PersistedDoubleSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new DoubleSignal.
### Methods
View Methods
##### `double decode(String value)`
##### `String encode(double value)`
---
## PersistedNumSignal
A PersistedSignal that stores a numeric value.
### Constructors
View Constructors
##### `PersistedNumSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new NumSignal.
### Methods
View Methods
##### `num decode(String value)`
##### `String encode(num value)`
---
## PersistedStringSignal
A PersistedSignal that stores a string value.
### Constructors
View Constructors
##### `PersistedStringSignal(super.val, String key, {SignalsKeyValueStore? store})`
Creates a new StringSignal.
### Methods
View Methods
##### `String decode(String value)`
##### `String encode(String value)`
---
## PersistedEnumSignal
A PersistedSignal that stores an enum value.
### Constructors
View Constructors
##### `PersistedEnumSignal(super.val, String key, this.values, {SignalsKeyValueStore? store})`
Creates a new EnumSignal.
### Properties
View Properties
##### `List values`
### Methods
View Methods
##### `T decode(String value)`
##### `String encode(T value)`
---
## PersistedSignalOptions
Configuration options for a [PersistedSignal](/types/persistedsignal).
### Constructors
View Constructors
##### `PersistedSignalOptions({super.name, super.autoDispose, super.watched, super.unwatched})`
Creates a new [PersistedSignalOptions](/types/persistedsignaloptions) instance.
### Methods
View Methods
##### `PersistedSignalOptions copyWith({String? name, bool? autoDispose, void Function()? watched, void Function()? unwatched})`
##### `bool ==(Object other)`
##### `int hashCode`
---
## Page: signals_core
Url: https://dartsignals.dev/packages/signals_core/index
Description: The signals library exposes four core functions which are the building blocks to model any business logic you can think of.
---
> Version: `7.0.0`
## Installation
```bash
dart pub add signals_core
```
The `signals_core` package exposes the foundational building blocks of the entire Signals reactive framework. It is 100% platform-agnostic, zero-dependency, and can be integrated into any Dart codebase—including shelf servers, database layers, command-line scripts, or serverless functions.
## Key Features
- **📐 Signals & Computeds**: Declare reactive variables and lazy, cacheable derived states.
- **⚡ Effects**: Trigger side effects (like saving to databases, logging, or writing to files) automatically in response to dependency changes.
- **🗂 Advanced Collections**: Built-in reactive collections including `listSignal`, `setSignal`, `mapSignal`, and `iterableSignal`.
- **🕒 Async Bindings**: Easily bind streams and futures directly to reactive states using `futureSignal` and `streamSignal`.
- **🔄 Undo/Redo Change Stack**: Track value histories and enable instant undo/redo functionality using `changeStack`.
## Quick Start
```dart
import 'package:signals_core/signals_core.dart';
void main() {
final count = signal(0);
final doubleCount = computed(() => count.value * 2);
effect(() {
print('Double count: ${doubleCount.value}');
});
count.value = 5; // Prints: Double count: 10
}
```
## Package Contents
---
## Page: SignalsAvoidDeprecatedWatchExtension
Url: https://dartsignals.dev/packages/signals_lint/flutter/signals-avoid-deprecated-watch-extension
Description: A Dart static analysis rule that detects and warns against using the deprecated.
---
A Dart static analysis rule that detects and warns against using the deprecated
.watch(context) and .unwatch(context) extension methods in v7.
The .watch(context) extension was deprecated in signals v7 due to performance concerns
and unexpected side effects with Flutter's build lifecycle. Using it can trigger unnecessary
widget rebuilds. In v7, you must migrate to specialized reactive components like
SignalBuilder , SignalWidget , or SignalStatefulWidget .
### Examples
**Incorrect:**
```dart
@override
Widget build(BuildContext context) {
final value = counter.watch(context); // LINT: Deprecated watch extension
return Text('$value');
}
```
**Correct:**
```dart
@override
Widget build(BuildContext context) {
return SignalBuilder(
builder: (context) => Text('${counter.value}'), // OK
);
}
```
### Constructors
View Constructors
##### `SignalsAvoidDeprecatedWatchExtension()`
### Methods
View Methods
##### `void run(CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context)`
---
## Page: WrapWithSignalBuilder
Url: https://dartsignals.dev/packages/signals_lint/flutter/wrap-with-signal-builder
Description: An IDE quick-fix refactoring tool (Dart Assist) that automatically wraps any.
---
An IDE quick-fix refactoring tool (Dart Assist) that automatically wraps any
instantiated widget expression inside a [SignalBuilder](/types/signalbuilder) component.
Wrapping a widget inside SignalBuilder(builder: (context) => ...) optimizes rebuilding
performance by confining redraws strictly to the smallest possible sub-tree whenever
reactive signals read inside the builder change.
### How to use
1. Place your cursor on any widget constructor call (e.g., Text('...')).
2. Click the lightbulb icon or press your IDE's quick-fix shortcut (Alt+Enter or Cmd+.).
3. Select the **Wrap with SignalBuilder** assist option.
### Examples
**Before (Cursor on Text constructor):**
```dart
Widget build(BuildContext context) {
return Text('Counter: ${counter.value}');
}
```
**After (Apply Assist):**
```dart
Widget build(BuildContext context) {
return SignalBuilder(builder: (context) => Text('Counter: ${counter.value}'));
}
```
### Constructors
View Constructors
##### `WrapWithSignalBuilder()`
### Methods
View Methods
##### `void run(CustomLintResolver resolver, ChangeReporter reporter, CustomLintContext context, SourceRange target)`
---
## Page: ConvertStatelessToSignalWidget
Url: https://dartsignals.dev/packages/signals_lint/flutter/convert-stateless-to-signal-widget
Description: An IDE quick-fix refactoring tool (Dart Assist) that automatically converts a standard.
---
An IDE quick-fix refactoring tool (Dart Assist) that automatically converts a standard
StatelessWidget to extend the reactive [SignalWidget](/types/signalwidget) instead.
By extending SignalWidget instead of StatelessWidget, your widget automatically registers
fine-grained dependency tracking for any signals referenced within its build method. It
will rebuild automatically when their values change, removing the need for manual listener
code or wrapper components.
### How to use
1. Place your cursor on the widget class declaration (e.g., class MyWidget extends StatelessWidget).
2. Click the lightbulb icon or press your IDE's quick-fix shortcut (Alt+Enter or Cmd+.).
3. Select the **Convert to SignalWidget** assist option.
### Examples
**Before:**
```dart
class CounterDisplay extends StatelessWidget {
const CounterDisplay({super.key});
@override
Widget build(BuildContext context) {
return Text('Count: ${counter.value}');
}
}
```
**After (Apply Assist):**
```dart
class CounterDisplay extends SignalWidget {
const CounterDisplay({super.key});
@override
Widget build(BuildContext context) {
return Text('Count: ${counter.value}');
}
}
```
### Constructors
View Constructors
##### `ConvertStatelessToSignalWidget()`
### Methods
View Methods
##### `void run(CustomLintResolver resolver, ChangeReporter reporter, CustomLintContext context, SourceRange target)`
---
## Page: ConvertStatefulToSignalStatefulWidget
Url: https://dartsignals.dev/packages/signals_lint/flutter/convert-stateful-to-signal-stateful-widget
Description: An IDE quick-fix refactoring tool (Dart Assist) that automatically converts a standard.
---
An IDE quick-fix refactoring tool (Dart Assist) that automatically converts a standard
StatefulWidget to extend the reactive [SignalStatefulWidget](/types/signalstatefulwidget) instead.
By extending SignalStatefulWidget instead of StatefulWidget, your widget state automatically
registers fine-grained dependency tracking for any signals referenced within its build method. It
will rebuild automatically when their values change, removing the need for manual listener
lifecycle management or setState calls.
### How to use
1. Place your cursor on the widget class declaration (e.g., class MyWidget extends StatefulWidget).
2. Click the lightbulb icon or press your IDE's quick-fix shortcut (Alt+Enter or Cmd+.).
3. Select the **Convert to SignalStatefulWidget** assist option.
### Examples
**Before:**
```dart
class CounterWidget extends StatefulWidget {
const CounterWidget({super.key});
@override
State createState() => _CounterWidgetState();
}
```
**After (Apply Assist):**
```dart
class CounterWidget extends SignalStatefulWidget {
const CounterWidget({super.key});
@override
State createState() => _CounterWidgetState();
}
```
### Constructors
View Constructors
##### `ConvertStatefulToSignalStatefulWidget()`
### Methods
View Methods
##### `void run(CustomLintResolver resolver, ChangeReporter reporter, CustomLintContext context, SourceRange target)`
---
## Page: SignalsAvoidDeprecatedSignalsMixin
Url: https://dartsignals.dev/packages/signals_lint/flutter/signals-avoid-deprecated-signals-mixin
Description: A Dart static analysis rule that detects and warns against using the deprecated SignalsMixin in v7.
---
A Dart static analysis rule that detects and warns against using the deprecated SignalsMixin in v7.
In signals v7, SignalsMixin was deprecated to avoid unnecessary state tracking and
CPU/memory overhead. For optimal performance and a cleaner reactive codebase, you should
migrate to specialized widgets like SignalWidget , SignalStatefulWidget , or SignalBuilder .
### Examples
**Incorrect:**
```dart
class MyWidget extends StatefulWidget with SignalsMixin { // LINT: Deprecated SignalsMixin
@override
Widget build(BuildContext context) {
return Text('${counter.value}');
}
}
```
**Correct (using SignalWidget):**
```dart
class MyWidget extends SignalWidget {
const MyWidget({super.key});
@override
Widget build(BuildContext context) {
return Text('${counter.value}'); // OK: Rebuilds are handled implicitly and efficiently
}
}
```
### Constructors
View Constructors
##### `SignalsAvoidDeprecatedSignalsMixin()`
### Methods
View Methods
##### `void run(CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context)`
---
## Page: SignalsAvoidCreateInBuildMethod
Url: https://dartsignals.dev/packages/signals_lint/flutter/signals-avoid-create-in-build-method
Description: A Dart lint rule that checks and reports instances where a signal or computed.
---
A Dart lint rule that checks and reports instances where a signal or computed
variable is created directly inside Flutter's build(BuildContext context) method.
Creating a signal inside the build method is a severe reactive anti-pattern.
On every widget rebuild, a brand new signal instance is created, resetting its value
and breaking state persistence. This can lead to bugs, infinite rebuild loops, and
high memory consumption.
### Examples
**Incorrect:**
```dart
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final counter = signal(0); // LINT: Created in build() method
return Text('${counter.value}');
}
}
```
**Correct:**
```dart
class MyWidget extends StatelessWidget {
// Define signals outside the build method (e.g. as a field, in an InheritedWidget, or global)
final counter = signal(0);
@override
Widget build(BuildContext context) {
return Text('${counter.value}');
}
}
```
### How to suppress
If you have a highly specific edge case and need to ignore this lint, you can suppress it by adding
a line-level comment:
```dart
// ignore: signals_avoid_create_in_build_method
final mySignal = signal(42);
```
### Constructors
View Constructors
##### `SignalsAvoidCreateInBuildMethod()`
### Methods
View Methods
##### `void run(CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context)`
---
## Page: SignalsPreferNamedBuilder
Url: https://dartsignals.dev/packages/signals_lint/flutter/signals-prefer-named-builder
Description: A Dart static analysis rule that detects positional builder parameters in Watch.
---
A Dart static analysis rule that detects positional builder parameters in Watch
or SignalBuilder constructors and encourages using the named builder argument.
In signals v7, to make the API more readable and consistent with standard Flutter components,
Watch and SignalBuilder support the named builder parameter. Using positional
parameters can be harder to read and may be deprecated in future versions.
### Examples
**Incorrect:**
```dart
Watch((context) { // LINT: Positional builder argument
return Text('${counter.value}');
});
```
**Correct:**
```dart
Watch(
builder: (context) => Text('${counter.value}'), // OK: Named builder parameter
);
```
### Constructors
View Constructors
##### `SignalsPreferNamedBuilder()`
### Methods
View Methods
##### `void run(CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context)`
---
## Page: MigrateSignalsMixinToSignalStatefulWidget
Url: https://dartsignals.dev/packages/signals_lint/flutter/migrate-signals-mixin-to-signal-stateful-widget
Description: An IDE quick-fix refactoring tool (Dart Assist) that automatically migrates a deprecated.
---
An IDE quick-fix refactoring tool (Dart Assist) that automatically migrates a deprecated
SignalsMixin usage on a State class to extend the reactive [SignalStatefulWidget](/types/signalstatefulwidget) instead.
:::important
Since SignalsMixin is deprecated in signals v7, this automated quick-fix does two things:
1. Removes SignalsMixin from your State class's with clause.
2. Changes the parent widget class's superclass from StatefulWidget to SignalStatefulWidget.
This results in a cleaner, highly optimized codebase that runs with zero unnecessary overhead.
:::
### How to use
1. Place your cursor on the SignalsMixin name in the State's class signature.
2. Click the lightbulb icon or press your IDE's quick-fix shortcut (Alt+Enter or Cmd+.).
3. Select the **Migrate SignalsMixin to SignalStatefulWidget** assist option.
### Examples
**Before:**
```dart
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
@override
State createState() => _MyWidgetState();
}
class _MyWidgetState extends State with SignalsMixin { // LINT: Deprecated SignalsMixin
@override
Widget build(BuildContext context) {
return Text('${counter.value}');
}
}
```
**After (Apply Assist):**
```dart
class MyWidget extends SignalStatefulWidget {
const MyWidget({super.key});
@override
State createState() => _MyWidgetState();
}
class _MyWidgetState extends State {
@override
Widget build(BuildContext context) {
return Text('${counter.value}'); // OK
}
}
```
### Constructors