# 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. 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 | [![signals](https://img.shields.io/pub/v/signals.svg)](https://pub.dev/packages/signals) | | **signals_flutter** | UI-bound reactive components and .watch(context) extensions | Flutter Only | [![signals_flutter](https://img.shields.io/pub/v/signals_flutter.svg)](https://pub.dev/packages/signals_flutter) | | **signals_core** | Zero-dependency, ultra-fast reactive primitives | Pure Dart / Web | [![signals_core](https://img.shields.io/pub/v/signals_core.svg)](https://pub.dev/packages/signals_core) | | **signals_lint** | Real-time static analysis and IDE quick-fixes | Dev Tooling | [![signals_lint](https://img.shields.io/pub/v/signals_lint.svg)](https://pub.dev/packages/signals_lint) | | **preact_signals** | Direct low-overhead Dart port of Preact.js Signals | Core Engine | [![preact_signals](https://img.shields.io/pub/v/preact_signals.svg)](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: 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: 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
View Constructors ##### `MigrateSignalsMixinToSignalStatefulWidget()`
### Methods
View Methods ##### `void run(CustomLintResolver resolver, ChangeReporter reporter, CustomLintContext context, SourceRange target)`
--- ## Page: SignalsPreferUnifiedOptions Url: https://dartsignals.dev/packages/signals_lint/flutter/signals-prefer-unified-options Description: A Dart static analysis rule that detects deprecated positional or scattered named configuration. --- A Dart static analysis rule that detects deprecated positional or scattered named configuration parameters for signals and ensures they are bundled under a unified options object. :::important In signals v7, parameters like autoDispose, equals, or custom comparators are no longer passed directly as separate parameters to signal creators. Instead, they must be bundled into a unified, type-safe configuration object (such as [SignalOptions](/types/signaloptions), [ComputedOptions](/types/computedoptions), or [AsyncSignalOptions](/types/asyncsignaloptions)). ::: ### Examples **Incorrect:** ```dart final counter = signal(0, autoDispose: true); // LINT: Scattered configuration parameters ``` **Correct:** ```dart final counter = signal( 0, options: SignalOptions(autoDispose: true), // OK: Unified options object ); ``` ### Constructors
View Constructors ##### `SignalsPreferUnifiedOptions()`
### Methods
View Methods ##### `void run(CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context)`
--- ## Page: MigrateWatchToSignalBuilder Url: https://dartsignals.dev/packages/signals_lint/flutter/migrate-watch-to-signal-builder Description: An IDE quick-fix refactoring tool (Dart Assist) that automatically migrates a positional or named. --- An IDE quick-fix refactoring tool (Dart Assist) that automatically migrates a positional or named Watch constructor call to use the modern, type-safe [SignalBuilder](/types/signalbuilder) component. :::important The Watch widget was updated and aligned under the standard [SignalBuilder](/types/signalbuilder) paradigm in v7. This automated refactoring quick-fix updates your positional widget structure to a type-safe SignalBuilder(builder: (context) => ...) signature seamlessly. ::: ### How to use 1. Place your cursor on the Watch constructor call. 2. Click the lightbulb icon or press your IDE's quick-fix shortcut (Alt+Enter or Cmd+.). 3. Select the **Migrate Watch to SignalBuilder** assist option. ### Examples **Before:** ```dart Widget build(BuildContext context) { return Watch((context) => Text('${counter.value}')); // LINT: Positional Watch } ``` **After (Apply Assist):** ```dart Widget build(BuildContext context) { return SignalBuilder(builder: (context) => Text('${counter.value}')); // OK } ``` ### Constructors
View Constructors ##### `MigrateWatchToSignalBuilder()`
### Methods
View Methods ##### `void run(CustomLintResolver resolver, ChangeReporter reporter, CustomLintContext context, SourceRange target)`
--- ## Page: signals_lint Url: https://dartsignals.dev/packages/signals_lint/index Description: linter and developer tool for signals --- > Version: `7.0.0` ## Installation ```bash dart pub add signals_lint ``` The `signals_lint` package is a developer productivity toolkit containing custom compiler diagnostics, real-time IDE analysis rules, and automated quick-fixes. It guarantees that reactive signals are consumed according to best practices, preventing memory leaks and anti-patterns at code time. ## Key Features - **🛡 Anti-pattern Detection**: Warns when signals are instantiated directly inside build methods instead of state initializers. - **💡 Real-time IDE Quick-fixes**: Supports automated quick-fixes (like replacing standard state parameters with reactive watch methods). - **📝 Standardized Conventions**: Enforces uniform style guides and signal usage patterns across your team. ## Quick Start ```dart // analysis_options.yaml analyzer: plugins: - signals_lint ``` ## Package Contents --- ## Page: SignalsDevToolsExtension Url: https://dartsignals.dev/packages/signals_devtools_extension/flutter/devtools Description: The main widget of the Signals DevTools Extension. --- The main widget of the Signals DevTools Extension. Wraps the **SignalsExtensionHomePage** inside a DevToolsExtension container supplied by the devtools_extensions package. This allows it to: - Connect securely to the Dart VM Service. - Retrieve the application context and themes from DevTools. - Render custom debugging tabs directly as a pane in Flutter DevTools. ### UI Elements The UI is structured into two main diagnostic views: 1. **Updates Feed**: - Displays a live, chronological stream of all signal value updates, computed evaluations, and effect executions. - Includes previous and new values, timestamps, and active pause/resume capabilities to let developers snapshot reactions. 2. **Dependency Graph**: - An interactive dependency visualizer powered by GraphView. - Shows the relationship and flow of reactive nodes: Signals (sources) -> Computeds (intermediates) -> Effects (sinks). - Hovering or clicking on a node highlights its dependencies and current value. This panel leverages service extensions registered by signals_core in debug mode. It tracks creation, updates, and removal of nodes dynamically using standard event streams. ### Constructors
View Constructors ##### `SignalsDevToolsExtension({super.key})`
### Methods
View Methods ##### `Widget build(BuildContext context)`
--- ## $Node ### Constructors
View Constructors ##### `$Node({required this.id, required this.type, this.label, this.value, this.sources, this.targets})` ##### `$Node.fromJson(Map item)`
### Properties
View Properties ##### `int id` ##### `String type` ##### `String? label` ##### `String? value` ##### `String? sources` ##### `String? targets`
### Methods
View Methods ##### `String toString()`
--- ## BackgroundGrid ### Constructors
View Constructors ##### `BackgroundGrid({super.key, required this.backgroundColor, required this.foregroundColor})`
### Properties
View Properties ##### `Color backgroundColor` ##### `Color foregroundColor`
### Methods
View Methods ##### `Widget build(BuildContext context)`
--- ## NodeGraph ### Constructors
View Constructors ##### `NodeGraph({super.key})`
### Methods
View Methods ##### `State createState()`
--- ## NodesTable ### Constructors
View Constructors ##### `NodesTable({super.key})`
### Methods
View Methods ##### `Widget build(BuildContext context)`
--- ## NodeView ### Constructors
View Constructors ##### `NodeView({required this.nodeId, super.key})`
### Properties
View Properties ##### `int nodeId`
### Methods
View Methods ##### `Widget build(BuildContext context)`
--- ## SignalsExtensionHomePage ### Constructors
View Constructors ##### `SignalsExtensionHomePage({super.key})`
### Methods
View Methods ##### `State createState()`
--- ## SignalUpdate ### Constructors
View Constructors ##### `SignalUpdate({required this.type, required this.signalType, required this.signalName, required this.signalId, this.prevValue, this.newValue, required this.timestamp, this.depth = 0})`
### Properties
View Properties ##### `String type` ##### `String signalType` ##### `String signalName` ##### `int signalId` ##### `String? prevValue` ##### `String? newValue` ##### `DateTime timestamp` ##### `int depth`
--- ## UpdatesView ### Constructors
View Constructors ##### `UpdatesView({super.key})`
### Methods
View Methods ##### `Widget build(BuildContext context)`
--- ## Page: signals_devtools_extension Url: https://dartsignals.dev/packages/signals_devtools_extension/index Description: Signals DevTools extension. --- > Version: `1.1.0` ## Installation ```bash dart pub add signals_devtools_extension ``` The `signals_devtools_extension` package provides a premium, highly interactive debugging console and visualization dashboard directly embedded inside Flutter DevTools. It allows developers to inspect, audit, pause, and profile reactive states in real-time, instantly exposing dependency flows, memory lifecycles, and update cycles across all mobile, desktop, and web applications. ## Key Features - **📈 Real-Time Updates Timeline**: View a live, chronological stream of all signal value mutations, computation re-evaluations, and side effect executions complete with deep value diffs and microsecond-level timestamps. - **🕸 Interactive Physics Node Graph**: Render your entire application's reactive structure visually! Tracks the direct flow from raw input `Signals` to reactive intermediate `Computeds` down to final rendering sink `Effects` or widgets. - **⏸️ Reaction Pause & Replay**: Freeze the reactive event stream dynamically at any point. Snapshot current states, inspect value trees, and play/resume events to track sequential microtask mutations step-by-step. - **🔍 Deep Node Metadata Drawer**: Select any reactive node inside the graph or table to slide out a comprehensive inspector listing its ID, human-readable debug label, stringified value representation, and dynamic lists of upstream sources and downstream targets. - **🛡️ Diagnostic Logging & Coalescing**: Option to enable direct VM console print logging or group sequential microtask updates to inspect coalesced batch updates. ## Quick Start ```bash # 1. Start your Flutter application in Debug or Profile Mode: flutter run # 2. Open Dart DevTools inside your browser or IDE # 3. Locate and click the custom "Signals" tab in the main DevTools panel ``` ## Package Contents
💙 Flutter Components
--- ## Page: Untracked Url: https://dartsignals.dev/packages/signals/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/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/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/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/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'); ``` --- ## 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])`
--- ## 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`
--- ## 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 ```
--- ## 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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: TickerSignal Url: https://dartsignals.dev/packages/signals/signals/ticker-signal Description: Ticker signal used to drive animations and can create animation controllers. --- 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**
--- ## 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 } ``` --- ## Page: FlutterSignal Url: https://dartsignals.dev/packages/signals/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/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/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/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/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/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/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: 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/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/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/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/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/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/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/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/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/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/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/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/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/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: Lazy Signal Url: https://dartsignals.dev/packages/signals/extensions/lazy-signal Description: Creates a lazy, mutable FlutterSignal of type T whose value can be assigned later. --- ## lazySignal Creates a lazy, mutable [FlutterSignal](/types/fluttersignal) of type **T** whose value can be assigned later. Reading a lazy signal's value before it has been initialized will throw a runtime error. ### Example ```dart final database = lazySignal(); // Initialize later (e.g. in initState or async initialization): database.value = await connectToDatabase(); // Now it is safe to read: print('Connected to: ${database.value.host}'); ``` --- ## Page: Stateful Widget Convert Widget Extension Url: https://dartsignals.dev/packages/signals/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 Url: https://dartsignals.dev/packages/signals/index Description: Reactivity made simple. Do more by doing less. Supports Flutter and any Dart project including HTML/JS, CLI, Shelf Server, VM and more. --- > Version: `7.1.0` ## Installation ```bash dart pub add signals ``` The `signals` package is the main overarching package for the Signals framework. It exports the complete core reactive package (`signals_core`) along with full Flutter extensions (`signals_flutter`) to provide a seamless out-of-the-box state management developer experience. ## Key Features - **📦 Unified Import**: Direct access to all core signals, computations, batch updates, reactive collections, and Flutter builders. - **🎯 Full Reactivity**: The single source of truth for building extremely responsive, state-of-the-art Flutter and Dart applications. - **📈 Standardized Ecosystem**: Recommended package entry point for all standard mobile, web, desktop, and multiplatform Flutter development. ## Quick Start ```dart import 'package:signals/signals_flutter.dart'; // Access everything from a single package! final count = signal(0); final doubleCount = computed(() => count.value * 2); ``` ## Package Contents --- ## Page: SignalEffect Url: https://dartsignals.dev/packages/signals/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/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/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/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/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/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/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/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/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/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/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: signals_hooks Url: https://dartsignals.dev/packages/signals_hooks/index Description: flutter_hooks bindings for signals --- > Version: `7.1.0` ## Installation ```bash flutter pub add signals_hooks ``` The `signals_hooks` package provides seamless, type-safe bindings for the highly popular `flutter_hooks` package. It enables developers to declare, instantiate, and automatically clean up reactive signals directly inside hook-based functional widgets. ## Key Features - **🎣 Hook-based Signalling**: Instantly declare state with `useSignal()` inside functional Hook widgets. - **🌀 Automatic Disposal**: No manual cleanup or dispose overrides required; the hook manages the entire signal life cycle. - **⚡ useComputed & useAsyncComputed**: Create cacheable hook-scoped computations and fetch async states inside your Hook widgets. ## Quick Start ```dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals/signals_hooks.dart'; class HookCounter extends HookWidget { const HookCounter({super.key}); @override Widget build(BuildContext context) { // Creates a signal tied to the widget life cycle final count = useSignal(0); return ElevatedButton( onPressed: () => count.value++, child: Text('Count: ${count.value}'), ); } } ``` ## Package Contents --- ## Page: useIterableSignal Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-iterable-signal Description: Creates a new IterableSignal and subscribes to it. --- Creates a new [IterableSignal](/types/iterablesignal) and subscribes to it. An [IterableSignal](/types/iterablesignal) provides a reactive wrapper around a Dart **Iterable**, notifying subscribers and rebuilding **HookWidget**s when the iterable structure is mutated or replaced. ### Parameters - **value**: The initial iterable elements. - **options**: The options for the signal. - **keys**: A list of objects to watch. If any key changes, the iterable signal is re-created. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class IterableDisplay extends HookWidget { const IterableDisplay({super.key}); @override Widget build(BuildContext context) { final sequence = useIterableSignal(Iterable.generate(5)); return ListView( children: sequence.map((num) => ListTile(title: Text('Item $num'))).toList(), ); } } ```` --- ## Page: useLazySignal Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-lazy-signal Description: Creates a new Signal that starts uninitialized and subscribes to it. --- Creates a new [Signal](/types/signal) that starts uninitialized and subscribes to it. A lazy signal is a signal that doesn't have an initial value on creation. Reading its .value before setting it will throw a [LazySignalInitializationError](/types/lazysignalinitializationerror). Ideal for binding late-initialized state, such as data fetched in a lifecycle effect or passed asynchronously from platform channels. ### Parameters - **keys**: A list of objects to watch. If any key changes, the signal is re-created. - **options**: Optional configuration options for standard signals (e.g. name, autoDispose). ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class LateSetupWidget extends HookWidget { const LateSetupWidget({super.key}); @override Widget build(BuildContext context) { // Starts uninitialized safely final dataSignal = useLazySignal(); useEffect(() { Future.delayed(const Duration(seconds: 1), () { dataSignal.value = 'Initialized Data'; }); return null; }, []); return Text(dataSignal.value.isEmpty ? 'Uninitialized' : dataSignal.value); } } ```` --- ## Page: useConnect Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-connect Description: Creates a new Connect instance and automatically disposes of it when the widget unmounts. --- Creates a new [Connect](/types/connect) instance and automatically disposes of it when the widget unmounts. [Connect](/types/connect) connects one or more streams to feed a target signal. Clean up is fully automated: when the HookWidget is disposed, all connected streams are unsubscribed, and the connected signal is disposed. ### Parameters - [signal](/types/signal): The target [Signal](/types/signal) to feed. - **stream**: An optional initial stream to connect immediately. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class StreamingCounterWidget extends HookWidget { const StreamingCounterWidget({super.key}); @override Widget build(BuildContext context) { final counter = useSignal(0); // Automatically connects the periodic stream to the counter signal final periodicStream = useMemoized( () => Stream.periodic(const Duration(seconds: 1), (i) => i), ); useConnect(counter, periodicStream); return Text('Tick count: ${counter.value}'); } } ```` --- ## Page: useValueNotifierToSignal Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-value-notifier-to-signal Description: Creates a new mutable Signal from a ValueNotifier and subscribes to it. --- Creates a new mutable [Signal](/types/signal) from a **ValueNotifier** and subscribes to it. This provides interoperability with standard Flutter classes, allowing you to bridge a **ValueNotifier** into a fully reactive [Signal](/types/signal) container. Changes made to the **ValueNotifier** automatically update the signal, and changes made to the [signal](/types/signal) automatically write back to the **ValueNotifier**. This enables seamless bi-directional integration when working with external packages or existing widgets that rely heavily on ValueNotifier. ### Parameters - **value**: The **ValueNotifier** to bridge. - **keys**: A list of objects to watch. If any key changes, the signal is re-created. - **debugLabel**: An optional debug label. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class LegacyBridgeWidget extends HookWidget { const LegacyBridgeWidget({super.key}); @override Widget build(BuildContext context) { final notifier = useValueNotifier(0); // Bridge the standard ValueNotifier to a fully reactive Signal final countSignal = useValueNotifierToSignal(notifier); return Column( children: [ Text('ValueNotifier value: ${notifier.value}'), Text('Signal value: ${countSignal.value}'), ElevatedButton( onPressed: () => countSignal.value++, // Updates notifier automatically child: const Text('Increment Signal'), ), ], ); } } ```` --- ## Page: useValueListenableToSignal Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-value-listenable-to-signal Description: Creates a new ReadonlySignal from a ValueListenable and subscribes to it. --- Creates a new [ReadonlySignal](/types/readonlysignal) from a **ValueListenable** and subscribes to it. This provides read-only interoperability with standard Flutter classes, allowing you to bridge any **ValueListenable** (like **ScrollController**, **TextEditingController**, or other custom listenables) into a reactive [ReadonlySignal](/types/readonlysignal). Perfect for listening to UI framework states such as scroll offsets, keyboard visibility, or text inputs in a clean, functional reactive style. ### Parameters - **value**: The **ValueListenable** to bridge. - **keys**: A list of objects to watch. If any key changes, the signal is re-created. - **debugLabel**: An optional debug label. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class ScrollTrackerWidget extends HookWidget { const ScrollTrackerWidget({super.key}); @override Widget build(BuildContext context) { final scrollController = useScrollController(); // Bridge the listenable offset to a reactive ReadonlySignal final scrollSignal = useValueListenableToSignal(scrollController); return Scaffold( appBar: AppBar( title: Text('Scroll Position: ${scrollSignal.value.toStringAsFixed(1)}'), ), body: ListView.builder( controller: scrollController, itemCount: 100, itemBuilder: (context, idx) => ListTile(title: Text('Row $idx')), ), ); } } ```` --- ## Page: useListSignal Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-list-signal Description: Creates a new ListSignal and subscribes to it. --- Creates a new [ListSignal](/types/listsignal) and subscribes to it. A [ListSignal](/types/listsignal) provides a reactive wrapper around a standard Dart **List**. It tracks mutations (such as .add(), .remove(), or setting an element at an index) and automatically triggers rebuilds of the host **HookWidget** when they occur. Since Dart lists are objects, standard signals only track identity changes. ListSignal tracks in-place mutations as well, making it highly efficient for managing dynamic lists in your UI. ### Parameters - **value**: The initial list elements. - **keys**: A list of objects to watch. If any key changes, the list signal is re-created. - **debugLabel**: An optional debug label. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class TodoListWidget extends HookWidget { const TodoListWidget({super.key}); @override Widget build(BuildContext context) { // Creates a reactive list managed by this widget final todos = useListSignal([]); return Column( children: [ ElevatedButton( onPressed: () => todos.add('Task ${todos.length + 1}'), child: const Text('Add Task'), ), Expanded( child: ListView.builder( itemCount: todos.length, itemBuilder: (context, idx) => ListTile( title: Text(todos[idx]), trailing: IconButton( icon: const Icon(Icons.delete), onPressed: () => todos.removeAt(idx), ), ), ), ), ], ); } } ```` --- ## Page: useQueueSignal Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-queue-signal Description: Creates a new QueueSignal and subscribes to it. --- Creates a new [QueueSignal](/types/queuesignal) and subscribes to it. A [QueueSignal](/types/queuesignal) wraps a Dart **Queue**, providing reactive tracking for queue operations like adding items to the end or removing items from the front. Perfect for task pipelines, notification streams, or undo histories. ### Parameters - **value**: The initial queue items. - **options**: The options for the signal. - **keys**: A list of objects to watch. If any key changes, the queue signal is re-created. ### Example ````dart import 'dart:collection'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class NotificationQueueWidget extends HookWidget { const NotificationQueueWidget({super.key}); @override Widget build(BuildContext context) { final notifications = useQueueSignal(Queue()); return Column( children: [ ElevatedButton( onPressed: () => notifications.addLast('Alert at ${DateTime.now()}'), child: const Text('Enqueue Alert'), ), if (notifications.isNotEmpty) ElevatedButton( onPressed: () => notifications.removeFirst(), child: const Text('Dismiss Oldest Alert'), ), Expanded( child: ListView( children: notifications.map((note) => ListTile(title: Text(note))).toList(), ), ), ], ); } } ```` --- ## Page: useSignal Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-signal Description: Creates a new Signal that persists across widget rebuilds and subscribes to it. --- Creates a new [Signal](/types/signal) that persists across widget rebuilds and subscribes to it. The signal is instantiated once using **useMemoized** and automatically disposed of or cleaned up if necessary (signals created in this manner are managed by the Hook lifetime). The widget will automatically rebuild whenever this signal's value changes. If you want the signal to survive widget disposal (e.g. shared state), do not use useSignal. Instead, define your signal globally or in a controller, and subscribe to it using useExistingSignal or useSignalValue. ### Parameters - **value**: The initial value of the signal. - **keys**: A list of objects to watch for changes. If any key in this list changes, the signal is re-created with the current **value** as its initial value. - **debugLabel**: An optional debug label to identify the signal in developer tools. ### Returns A local [FlutterSignal](/types/fluttersignal) instance representing the reactive state. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class CounterHookWidget extends HookWidget { const CounterHookWidget({super.key}); @override Widget build(BuildContext context) { // Create a local signal managed by this HookWidget's lifecycle final count = useSignal(0, keys: const [], options: SignalOptions(name: 'localCounter')); return Scaffold( body: Center( child: Text( 'Count: ${count.value}', style: Theme.of(context).textTheme.headlineMedium, ), ), floatingActionButton: FloatingActionButton( onPressed: () => count.value++, tooltip: 'Increment', child: const Icon(Icons.add), ), ); } } ```` --- ## Page: useSignalValue Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-signal-value Description: Subscribes to an existing ReadonlySignal (or Signal) and returns its current value. --- Subscribes to an existing [ReadonlySignal](/types/readonlysignal) (or [Signal](/types/signal)) and returns its current value. This is a convenience hook that is equivalent to invoking useExistingSignal(signal).value. It triggers a rebuild of the host **HookWidget** whenever the signal's value changes. This hook is the preferred way to consume external/global signals when you only need read-only access to their values and want standard Flutter Hook ergonomics. ### Parameters - **value**: The existing [ReadonlySignal](/types/readonlysignal) or [Signal](/types/signal) instance to retrieve the value from and subscribe to. - **keys**: A list of objects to watch for changes. If any key changes, the hook will be re-evaluated and subscribe to the new signal. ### Returns The current value **T** of the signal. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; final cartItemCountSignal = signal(0); class CartBadge extends HookWidget { const CartBadge({super.key}); @override Widget build(BuildContext context) { // Directly extract and subscribe to the signal's value final itemCount = useSignalValue(cartItemCountSignal); return Badge( label: Text('$itemCount'), child: const Icon(Icons.shopping_cart), ); } } ```` --- ## Page: useStreamSignal Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-stream-signal Description: Creates a new StreamSignal and subscribes to it. --- Creates a new [StreamSignal](/types/streamsignal) and subscribes to it. A [StreamSignal](/types/streamsignal) wraps a standard Dart **Stream**, capturing emitted events, errors, and done notifications as reactive states (loading, data, error). The host widget is automatically rebuilt on every stream emission. Clean up is fully automated: when the HookWidget is disposed, the underlying stream subscription is cancelled to prevent memory leaks. ### Parameters - **value**: A callback returning the **Stream** to subscribe to. - **options**: An optional **StreamSignalOptions** to configure the stream signal. - **keys**: A list of objects to watch. If any key changes, the hook unsubscribes from the old stream and subscribes to the new one. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class ChatMessagesWidget extends HookWidget { final String channelId; const ChatMessagesWidget({required this.channelId, super.key}); @override Widget build(BuildContext context) { // Subscribes to chat stream, automatically switching streams if channelId changes final streamSignal = useStreamSignal( () => listenToChannel(channelId), keys: [channelId], ); return streamSignal.value.map( data: (message) => Text('New message: ${message.text}'), error: (err, stack) => Text('Stream error: $err'), loading: () => const Text('Connecting to channel...'), ); } } ```` --- ## Page: useSetSignal Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-set-signal Description: Creates a new SetSignal and subscribes to it. --- Creates a new [SetSignal](/types/setsignal) and subscribes to it. A [SetSignal](/types/setsignal) provides a reactive wrapper around a standard Dart **Set**. It tracks mutations (such as .add(), .remove(), .clear()) and automatically triggers rebuilds of the host **HookWidget**. Useful for managing collections of unique items, like active filters, selected item IDs, or tagged items in a multi-select interface. ### Parameters - **value**: The initial set elements. - **options**: The options for the signal. - **keys**: A list of objects to watch. If any key changes, the set signal is re-created. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class CategoryFilterWidget extends HookWidget { const CategoryFilterWidget({super.key}); @override Widget build(BuildContext context) { final selectedCategories = useSetSignal({}); void toggleCategory(String cat) { if (selectedCategories.contains(cat)) { selectedCategories.remove(cat); } else { selectedCategories.add(cat); } } return Row( children: ['Tech', 'News', 'Lifestyle'].map((cat) { final isSelected = selectedCategories.contains(cat); return FilterChip( label: Text(cat), selected: isSelected, onSelected: (_) => toggleCategory(cat), ); }).toList(), ); } } ```` --- ## Page: useLinkedSignal Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-linked-signal Description: Creates a new LinkedSignal that resets its value whenever its source computation changes. --- Creates a new [LinkedSignal](/types/linkedsignal) that resets its value whenever its source computation changes. A [LinkedSignal](/types/linkedsignal) is a specialized signal that "links" to another reactive source or computed value, automatically updating or resetting itself based on a custom computation function when the source changes. This is perfect for situations like editing a form field where the default value is derived from a dynamic source (e.g. an item index, a fetched database record) but the user can also edit the value locally. ### Parameters - **source**: A computation function that yields the source value of type **S**. - **options**: Configuration options for the linked signal, detailing how the source value **S** maps to the signal's inner value **T**. - **keys**: A list of objects to watch. If any key changes, the signal is re-created. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class ProfileEditor extends HookWidget { final String initialUsername; const ProfileEditor({required this.initialUsername, super.key}); @override Widget build(BuildContext context) { // LinkedSignal resets to initialUsername if the prop initialUsername changes, // but allows local modifications in the meantime. final usernameSignal = useLinkedSignal( () => initialUsername, keys: [initialUsername], options: LinkedSignalOptions( computation: (source, previous) => source, ), ); return Column( children: [ TextField( controller: useTextEditingController(text: usernameSignal.value) ..addListener(() { // Local modifications allowed usernameSignal.value = usernameSignal.value; }), ), Text('Live Username Signal: ${usernameSignal.value}'), ], ); } } ```` --- ## Page: useChangeStackSignal Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-change-stack-signal Description: Creates a new ChangeStackSignal and subscribes to it. --- Creates a new [ChangeStackSignal](/types/changestacksignal) and subscribes to it. A [ChangeStackSignal](/types/changestacksignal) maintains an undo/redo history stack of value updates. This enables effortless undo/redo mechanisms for user edits, document updates, or game actions. Use .undo() to step backwards, .redo() to step forwards, and check .canUndo and .canRedo to determine button enabling states. ### Parameters - **value**: The initial value of the stack. - **options**: The options for the signal. - **keys**: A list of objects to watch. If any key changes, the change stack is re-created. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class SketchPadWidget extends HookWidget { const SketchPadWidget({super.key}); @override Widget build(BuildContext context) { final colorStack = useChangeStackSignal(Colors.red, limit: 10); return Column( children: [ Container( width: 100, height: 100, color: colorStack.value, ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( icon: const Icon(Icons.undo), onPressed: colorStack.canUndo ? () => colorStack.undo() : null, ), IconButton( icon: const Icon(Icons.redo), onPressed: colorStack.canRedo ? () => colorStack.redo() : null, ), ], ), Wrap( children: [Colors.blue, Colors.green, Colors.orange, Colors.purple].map((col) { return GestureDetector( onTap: () => colorStack.value = col, child: Container( width: 40, height: 40, color: col, margin: const EdgeInsets.all(4), ), ); }).toList(), ), ], ); } } ```` --- ## Page: useTrackedSignal Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-tracked-signal Description: Creates a new TrackedSignal and subscribes to it. --- Creates a new [TrackedSignal](/types/trackedsignal) and subscribes to it. A [TrackedSignal](/types/trackedsignal) extends standard signals by maintaining a record of its previous value alongside its current value. This allows you to inspect history changes, calculate delta differences, or perform custom transition animations. You can access both the current value using .value and the previous value using .previousValue. ### Parameters - **value**: The initial value of the signal. - **options**: The options for the signal. - **keys**: A list of objects to watch. If any key changes, the signal is re-created. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class DeltaCounterWidget extends HookWidget { const DeltaCounterWidget({super.key}); @override Widget build(BuildContext context) { final count = useTrackedSignal(0); return Column( children: [ Text('Current: ${count.value}'), Text('Previous: ${count.previousValue ?? "N/A"}'), ElevatedButton( onPressed: () => count.value++, child: const Text('Increment'), ), ], ); } } ```` --- ## Page: useExistingSignal Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-existing-signal Description: Subscribes to an existing ReadonlySignal (or Signal) and returns it. --- Subscribes to an existing [ReadonlySignal](/types/readonlysignal) (or [Signal](/types/signal)) and returns it. This is useful when you have a signal that is defined globally, inside a controller, or provided via a dependency injection framework (like provider, get_it, or riverpod), and you want to reactive-bind the **HookWidget** to rebuild whenever the signal emits a new value. If you only need the value of the signal inside the build method and do not need the signal instance itself, consider using useSignalValue instead. ### Parameters - **value**: The existing [ReadonlySignal](/types/readonlysignal) or [Signal](/types/signal) instance to subscribe to. - **keys**: A list of objects to watch for changes. If any of the keys change, the hook is re-evaluated, unsubscribing from the old signal and subscribing to the new one. ### Returns The same signal instance passed in **value**, allowing you to interact with it directly (e.g., read its .value or write to it if it is a mutable [Signal](/types/signal)). ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; // A globally defined signal or one located in a service final globalThemeMode = signal(ThemeMode.light); class ThemeSelectorWidget extends HookWidget { const ThemeSelectorWidget({super.key}); @override Widget build(BuildContext context) { // Bind to the existing global signal final themeMode = useExistingSignal(globalThemeMode); return SwitchListTile( title: Text('Dark Mode: ${themeMode.value == ThemeMode.dark ? "On" : "Off"}'), value: themeMode.value == ThemeMode.dark, onChanged: (isDark) { themeMode.value = isDark ? ThemeMode.dark : ThemeMode.light; }, ); } } ```` --- ## Page: useAsyncSignal Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-async-signal Description: Creates a new mutable AsyncSignal initialized with a specific AsyncState and subscribes to it. --- Creates a new mutable [AsyncSignal](/types/asyncsignal) initialized with a specific [AsyncState](/types/asyncstate) and subscribes to it. Unlike [useFutureSignal](/types/usefuturesignal) and [useStreamSignal](/types/usestreamsignal), an [AsyncSignal](/types/asyncsignal) exposes a mutable container where you can manually publish asynchronous states (e.g. AsyncState.loading(), AsyncState.data(...), or AsyncState.error(...)). This is perfect for manually managed async workflows, like handling a login button click where you want to transitions states explicitly under your control. ### Parameters - **value**: The initial [AsyncState](/types/asyncstate) (e.g. AsyncState.loading() or AsyncState.data(initialValue)). - **keys**: A list of objects to watch. If any key changes, the async signal will be re-created. - **debugLabel**: An optional debug label. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class LoginButton extends HookWidget { const LoginButton({super.key}); @override Widget build(BuildContext context) { // Start in idle data state final loginState = useAsyncSignal(AsyncState.data(null)); Future performLogin() async { loginState.value = AsyncState.loading(); try { await apiLogin(); loginState.value = AsyncState.data(null); } catch (err, stack) { loginState.value = AsyncState.error(err, stack); } } return loginState.value.map( data: (_) => ElevatedButton( onPressed: performLogin, child: const Text('Login'), ), error: (err, _) => Column( children: [ Text('Error: $err', style: const TextStyle(color: Colors.red)), ElevatedButton(onPressed: performLogin, child: const Text('Retry')), ], ), loading: () => const CircularProgressIndicator(), ); } } ```` --- ## Page: useComputed Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-computed Description: Creates a new Computed signal and subscribes to it. --- Creates a new [Computed](/types/computed) signal and subscribes to it. This hook is useful for deriving state within a **HookWidget** based on other signals or values. The callback **value** is evaluated lazily and cached. It is recomputed only when the dependent signals inside the callback change, or if any of the hook **keys** change. The host widget will rebuild whenever the derived value changes. Use computed signals to keep build methods clean and prevent unnecessary re-computations of expensive operations (e.g., filtering, sorting, or formatting data). ### Parameters - **value**: The computation callback that derives the computed value. - **keys**: A list of objects to watch for changes. If any key changes, the computed signal is re-created. - **debugLabel**: An optional debug label to identify the computed signal in developer tools. ### Returns A [FlutterComputed](/types/fluttercomputed) signal containing the derived value. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class DerivedStateWidget extends HookWidget { const DerivedStateWidget({super.key}); @override Widget build(BuildContext context) { final firstName = useSignal('John'); final lastName = useSignal('Doe'); // Derived full name computed from the individual signals final fullName = useComputed( () => '${firstName.value} ${lastName.value}', keys: [firstName, lastName], ); return Column( children: [ Text('Full Name: ${fullName.value}'), ElevatedButton( onPressed: () => firstName.value = 'Jane', child: const Text('Change First Name'), ), ], ); } } ```` --- ## Page: useSignalProvider Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-signal-provider Description: A custom hook to retrieve a generic signal from a SignalProvider ancestor higher up in the widget tree. --- A custom hook to retrieve a generic signal from a [SignalProvider](/types/signalprovider) ancestor higher up in the widget tree. Under the hood, this uses the BuildContext to look up the provider and registers a reactive dependency. Supports standard core signals from package:signals_core/signals_core.dart as well as Flutter-native signals. ### Example Usage ```dart class CounterDisplayWidget extends HookWidget { const CounterDisplayWidget({super.key}); @override Widget build(BuildContext context) { final counter = useSignalProvider>(); if (counter == null) return const Text('Not found'); return Text('Value: ${counter.value}'); } } ``` --- ## Page: useFutureSignal Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-future-signal Description: Creates a new FutureSignal and subscribes to it. --- Creates a new [FutureSignal](/types/futuresignal) and subscribes to it. A [FutureSignal](/types/futuresignal) wraps a standard Dart **Future** in a reactive state container, allowing you to easily read the asynchronous operation's progress (loading, error, data) directly in your build method without needing FutureBuilder. The signal automatically manages the lifecycle of the future and rebuilds the host widget when the future transitions from loading to data or error. ### Parameters - **value**: A callback returning the **Future** to execute. - **keys**: A list of objects to watch for changes. If any key in this list changes, the existing future is cancelled/ignored and a new future is created and subscribed to. - **initialValue**: An optional initial value to display before the future completes. - **dependencies**: An optional list of signals that this future depends on. If any dependency changes, the future is automatically re-run. - **lazy**: If true (default), the future won't start executing until the signal's value is first read. - **debugLabel**: An optional debug label to identify this signal in devtools. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class UserProfileWidget extends HookWidget { final int userId; const UserProfileWidget({required this.userId, super.key}); @override Widget build(BuildContext context) { // Re-fetches user profile if userId changes final userSignal = useFutureSignal( () => fetchUserProfile(userId), keys: [userId], ); return userSignal.value.map( data: (user) => ListTile( title: Text(user.name), subtitle: Text(user.email), ), error: (err, stack) => Text('Error: $err'), loading: () => const CircularProgressIndicator(), ); } } ```` --- ## Page: useAsyncComputed Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-async-computed Description: Creates a new FutureSignal from a computed async value and subscribes to it. --- Creates a new [FutureSignal](/types/futuresignal) from a computed async value and subscribes to it. This hook allows you to construct an async computation that automatically reruns whenever its reactive dependencies (e.g. other signals accessed via .value) or hook keys change. It combines the capabilities of computed/derived state with asynchronous futures, serving as a powerful replacement for complex chain reactions of streams or state notifier combinations. ### Parameters - **value**: The async computation function that depends on one or more reactive signals. - **keys**: A list of objects to watch. If any key changes, the computation will be re-created. - **dependencies**: A list of dependency signals to monitor. If any dependency signal fires, the async computation will rerun. - **lazy**: If true (default), execution is delayed until the signal is first read. - **initialValue**: An optional initial value before the computation resolves. - **debugLabel**: An optional debug label. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class SearchResultsWidget extends HookWidget { const SearchResultsWidget({super.key}); @override Widget build(BuildContext context) { final searchQuery = useSignal(''); // Re-runs search query automatically whenever searchQuery.value changes final resultsSignal = useAsyncComputed( () async { if (searchQuery.value.isEmpty) return []; return await performSearch(searchQuery.value); }, options: AsyncSignalOptions(dependencies: [searchQuery]), ); return Column( children: [ TextField( onChanged: (val) => searchQuery.value = val, decoration: const InputDecoration(labelText: 'Search'), ), Expanded( child: resultsSignal.value.map( data: (results) => ListView.builder( itemCount: results.length, itemBuilder: (context, idx) => ListTile(title: Text(results[idx].title)), ), error: (err, _) => Text('Error: $err'), loading: () => const Center(child: CircularProgressIndicator()), ), ), ], ); } } ```` --- ## Page: useSignalEffect Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-signal-effect Description: Creates a new reactive effect and registers it with the widget's lifecycle. --- Creates a new reactive [effect](/types/effect) and registers it with the widget's lifecycle. The effect is automatically created on build and is disposed of when the widget is unmounted. Any reactive signal accessed inside the callback **cb** is tracked as a dependency, and the effect is executed automatically whenever those signals change. Avoid modifying signals inside the effect callback that could trigger infinite loops or unintentional side effects. ### Parameters - **cb**: The effect callback to execute when tracked dependencies change. - **keys**: A list of objects to watch. If any key changes, the effect is disposed and re-created. - **onDispose**: An optional callback executed when the effect itself is disposed. - **debugLabel**: An optional debug label to identify the effect in developer tools. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class LoggerWidget extends HookWidget { const LoggerWidget({super.key}); @override Widget build(BuildContext context) { final count = useSignal(0); // Set up a side effect to log value changes useSignalEffect( () { debugPrint('The counter value is now: ${count.value}'); }, keys: [count], onDispose: () => debugPrint('Logger effect disposed'), ); return TextButton( onPressed: () => count.value++, child: Text('Count: ${count.value}'), ); } } ```` --- ## Page: useMapSignal Url: https://dartsignals.dev/packages/signals_hooks/hooks/use-map-signal Description: Creates a new MapSignal and subscribes to it. --- Creates a new [MapSignal](/types/mapsignal) and subscribes to it. A [MapSignal](/types/mapsignal) provides a reactive wrapper around a standard Dart **Map**. It tracks in-place mutations (such as entry addition, value updates, or key removals) and automatically triggers rebuilds of the host **HookWidget**. Excellent for storing state dictionaries, form configurations, cached records, or key-value structures that require fine-grained reactivity. ### Parameters - **value**: The initial map entries. - **options**: The options for the signal. - **keys**: A list of objects to watch. If any key changes, the map signal is re-created. ### Example ````dart import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:signals_hooks/signals_hooks.dart'; class DynamicFormWidget extends HookWidget { const DynamicFormWidget({super.key}); @override Widget build(BuildContext context) { final formData = useMapSignal({}); return Column( children: [ TextField( decoration: const InputDecoration(labelText: 'Email'), onChanged: (val) => formData['email'] = val, ), TextField( decoration: const InputDecoration(labelText: 'Password'), onChanged: (val) => formData['password'] = val, ), Text('Live Form Data: ${formData.value}'), ], ); } } ```` --- ## Page: SignalStatefulHookWidget Url: https://dartsignals.dev/packages/signals_hooks/widgets/signal-stateful-hook-widget Description: A premium stateful widget that both supports Flutter Hooks and implicitly tracks and rebuilds on signal changes. --- A premium stateful widget that both supports Flutter Hooks and implicitly tracks and rebuilds on signal changes. Stateful counterpart of [SignalHookWidget](/types/signalhookwidget). ### Example Usage ```dart class CounterWidget extends SignalStatefulHookWidget { const CounterWidget({super.key}); @override State createState() => _CounterWidgetState(); } class _CounterWidgetState extends State { @override Widget build(BuildContext context) { // Standard flutter hooks work here: final controller = useTextEditingController(); // Reactive signals work here as well: final counter = useSignal(0); return Column( children: [ TextField(controller: controller), Text('Count: $counter'), ElevatedButton( onPressed: () => counter.value++, child: const Text('Increment'), ), ], ); } } ``` ### Constructors
View Constructors ##### `SignalStatefulHookWidget({super.key})` Constructor for [SignalStatefulHookWidget](/types/signalstatefulhookwidget).
### Methods
View Methods ##### `StatefulElement createElement()`
--- ## Page: SignalHookBuilder Url: https://dartsignals.dev/packages/signals_hooks/widgets/signal-hook-builder Description: A premium, localized builder widget that behaves exactly like SignalHookWidget. --- A premium, localized builder widget that behaves exactly like [SignalHookWidget](/types/signalhookwidget) but in a builder format. Allows mixing hooks and implicit signal tracking within a localized subtree builder callback. ### Example Usage ```dart final count = signal(0); Widget build(BuildContext context) { return Scaffold( body: Center( child: SignalHookBuilder( builder: (context) { // Call standard hooks inside the builder: final controller = useTextEditingController(); final focusNode = useFocusNode(); // Access signals implicitly. Only this subtree will rebuild when count changes: return Column( children: [ TextField(controller: controller, focusNode: focusNode), Text('Counter: ${count.value}'), ], ); }, ), ), ); } ``` ### Constructors
View Constructors ##### `SignalHookBuilder({super.key, required this.builder})` Creates a [SignalHookBuilder](/types/signalhookbuilder) widget.
### Properties
View Properties ##### `Widget Function(BuildContext context) builder` The widget builder callback.
### Methods
View Methods ##### `Widget build(BuildContext context)`
--- ## Page: SignalHookWidget Url: https://dartsignals.dev/packages/signals_hooks/widgets/signal-hook-widget Description: A premium reactive widget that both supports Flutter Hooks and implicitly tracks and rebuilds on signal changes. --- A premium reactive widget that both supports Flutter Hooks and implicitly tracks and rebuilds on signal changes. SignalHookWidget 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. At the same time, you can call any hooks (like useFocusNode, useTextEditingController, etc.) within **build**. This provides the ultimate developer experience, combining the power of implicit, boilerplate-free signal tracking with the rich lifecycle management of Flutter Hooks. ### Dual Reactivity Example ```dart final globalCartCount = signal(0); class MyDualWidget extends SignalHookWidget { const MyDualWidget({super.key}); @override Widget build(BuildContext context) { // 1. Declare hooks seamlessly: final controller = useTextEditingController(); final focusNode = useFocusNode(); final localCounter = useSignal(0); // 2. Implicit tracking of external/global signals: // No .watch(context) needed! Just accessing .value tracks it. return Column( children: [ TextField(controller: controller, focusNode: focusNode), Text('Global Signal: ${globalCartCount.value}'), Text('Local Signal: ${localCounter.value}'), ], ); } } ``` ### Constructors
View Constructors ##### `SignalHookWidget({super.key})` Constructor for [SignalHookWidget](/types/signalhookwidget).
### Methods
View Methods ##### `StatelessElement createElement()` ##### `Widget build(BuildContext context)` Subclasses override this method to define their widget tree. For backward compatibility, this defaults to calling **buildWidget**. ##### `Widget buildWidget(BuildContext context)` Subclasses override this method instead of **build** to define their widget tree.
--- ## Page: Untracked Url: https://dartsignals.dev/packages/preact_signals/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: Effect Url: https://dartsignals.dev/packages/preact_signals/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/preact_signals/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/preact_signals/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`
--- ## 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`
--- ## 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'); ``` --- ## 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`
--- ## 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()`
--- ## Page: Computed Url: https://dartsignals.dev/packages/preact_signals/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/preact_signals/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/preact_signals/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: SignalEquality Url: https://dartsignals.dev/packages/preact_signals/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/preact_signals/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: preact_signals Url: https://dartsignals.dev/packages/preact_signals/index Description: Dart port of Preact.js Signals --- > Version: `7.0.1` ## Installation ```bash dart pub add preact_signals ``` The `preact_signals` package is a direct, ultra-high-performance Dart port of Preact.js Signals (v7.0.0). It brings fine-grained reactive programming to Dart VM, command-line interfaces, server environments, and web targets with maximum memory efficiency and minimal execution overhead. ## Key Features - **⚡ High Performance**: Built on a highly optimized, double-linked reactive graph that automatically caches derived values. - **📦 Memory Efficient**: Automatic memory cleanup and garbage collection of inactive nodes. - **🎯 Dynamic Dependency Tracking**: Automatically tracks which signals are read during execution and re-evaluates effects surgically only when those specific dependencies update. - **🧪 Batching Updates**: Group multiple signal updates together into a single transaction so reactions and UI redraws occur only once. ## Quick Start ```dart import 'package:preact_signals/preact_signals.dart'; void main() { final count = signal(0); final isEven = computed(() => count.value.isEven); // Automatically tracks and prints when dependencies update final dispose = effect(() { print('Count is ${count.value}, isEven: ${isEven.value}'); }); count.value = 1; count.value = 2; dispose(); } ``` ## Package Contents
🛠️ Utilities