This is the full developer documentation for Flutter Solid Framework # Welcome to Solid > Congrats on your interest in Solid! Let's make Flutter development even more enjoyable. Solid is a tiny framework built on top of Flutter that makes building apps easier and more enjoyable. The benefits of using Solid include: 1. **Don’t write boilerplate**: Solid generates boilerplate code for you, so you can focus on building your app. Inspired by SwiftUI. 2. **No state management/dependency injection manual work**: Solid has built-in state management and dependency injection. Just annotate your variables and Solid takes care of the rest. 3. **Fine-grained reactivity**: Solid’s reactivity system is inspired by SolidJS, allowing for efficient and fine-grained updates to your UI. Only the parts of the UI that depend on changed state are updated, leading to better performance. And the best is that you don’t have to think about it, Solid does it for you automatically. ## Example [Section titled “Example”](#example) You write this code, without any boilerplate and manual state management: source/counter.dart ```dart import 'package:flutter/material.dart'; import 'package:solid_annotations/solid_annotations.dart'; class Counter extends StatelessWidget { Counter({super.key}); @SolidState() int counter = 0; @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ Text('Date: ${DateTime.now()}'), Text('Counter is $counter'), ], ), ), floatingActionButton: FloatingActionButton( onPressed: () => counter++, child: const Icon(Icons.add), ), ); } } ``` You get this result, with real fine-grained reactivity: [![Demo of Solid fine-grained reactivity](/_astro/solid_demo.CRrIVc4O_Z2bPRRA.webp)](../../assets/solid_demo.gif) As you can see, the `DateTime.now()` text does not update when the counter changes, only the `Counter is X` text updates. This is because Solid tracks which parts of the UI depend on which state, and only updates those parts when the state changes, without any manual work from you. If this sounds interesting, check out the [getting started guide](/guides/getting-started) to learn how to set up Solid in your Flutter project! # Author > The author of Solid ![Image of Alexandru Mariuti](/ale.png) ### Alexandru Mariuti [ ](https://github.com/nank1ro)[](https://x.com/nank1ro) ## Alexandru Mariuti [Section titled “Alexandru Mariuti”](#alexandru-mariuti) I am an open source enthusiast, passionate about Dart and Flutter, and a strong believer in the power of community. Maintainer of: * [solidart](https://pub.dev/packages/solidart) and [flutter\_solidart](https://pub.dev/packages/flutter_solidart) (Dart/Flutter state management library inspired by [SolidJS](https://www.solidjs.com/)) * [shadcn\_ui (flutter port)](https://pub.dev/packages/shadcn_ui) (Design system inspired by [ui.shadcn.com](https://ui.shadcn.com/)) * [solid](https://pub.dev/packages/solid_generator) (Flutter framework, zero boilerplate, fine-grained reactivity) * [awesome\_flutter\_extensions](https://pub.dev/packages/awesome_flutter_extensions) (Useful Flutter extensions) * [disable\_web\_context\_menu](https://pub.dev/packages/disable_web_context_menu) (Disable native web context menu) * [disco](https://pub.dev/packages/disco) (Dependency injection library for Flutter) * [prompt\_parser](https://pypi.org/project/prompt-parser/) (Python library for parsing LLM prompts) ## Sponsorship [Section titled “Sponsorship”](#sponsorship) If you find Solid useful and would like to support its development, consider sponsoring the project on [GitHub Sponsors](https://github.com/sponsors/nank1ro/). I love building open-source software and your support helps me dedicate more time to improving Solid and adding new features. # FAQ > Frequently Asked Questions about Solid. ## Why using `StatelessWidget` instead of `StatefulWidget`? [Section titled “Why using StatelessWidget instead of StatefulWidget?”](#why-using-statelesswidget-instead-of-statefulwidget) The usage of `StatelessWidget` is intentional to reduce the boilerplate code you have to write. If the widget contains any solid annotation, it will be converted into a `StatefulWidget` by the code generator. Note You can still write `StatefulWidget` classes if you need, but you probably don’t have any reason to do it with Solid. ## Why my `StatelessWidget` is not immutable? [Section titled “Why my StatelessWidget is not immutable?”](#why-my-statelesswidget-is-not-immutable) The `StatelessWidget` you write is not immutable because it contains mutable variables. That’s why I suggest to add: analysis\_options.yaml ```yaml analyzer: errors: must_be_immutable: ignore ``` So you can ignore it. But **don’t worry**, the generated code will be immutable and follow all the best practices. The code you write in the `source` directory needs to be valid Dart code, even if it’s optimized later. ## Why not using Macros? [Section titled “Why not using Macros?”](#why-not-using-macros) The feature to add macros in Dart has been stopped, as you can [read here](https://blog.dart.dev/an-update-on-dart-macros-data-serialization-06d3037d4f12?gi=608cf334bf75) ## Why not using augmentations? [Section titled “Why not using augmentations?”](#why-not-using-augmentations) Augmentations in Dart have not been released yet, in addition, they are more limited than macros. For instance, you cannot augment a function that contains a body. So we can’t have fine grained reactivity in the `build` of our widgets. ## Why flutter\_solidart as state management? [Section titled “Why flutter\_solidart as state management?”](#why-flutter_solidart-as-state-management) I’m the author of [flutter\_solidart](https://pub.dev/packages/flutter_solidart) so I’m surely biased. But having a state manament library that has an automatic reactive system helps a lot. For instance, in solidart you can write: ```dart final counter = Signal(0); late final doubleCounter = Computed(() => counter.value * 2); ``` And it just works. In addition, `SignalBuilder` automatically reacts to any signal detected inside its builder. So I didn’t have to generate any complex transpiler. If I had to work with `ValueNotifier` and `ValueListenableBuilder`, the amount of complexity and boilerplate would have been enormous. ## Why writing code in the `source` directory and not on lib? [Section titled “Why writing code in the source directory and not on lib?”](#why-writing-code-in-the-source-directory-and-not-on-lib) Solid is not a simple code generator. In fact it doesn’t compile code, but it transpiles it. For instance, the code you write gets transformed in a working code. For the limitations of how Flutter works, your app needs to be built from the `lib` directory. So the code I transpile must be transpiled in the `lib` folder. Note The input and output directories live in `build.yaml` (`targets.$default.sources`) and in the builder’s `build_extensions`. The Solid builder hard-codes `^source/{{}}.dart` → `lib/{{}}.dart`, so changing the directories means forking the builder config in `packages/solid_generator/build.yaml`. ## Why did I created Solid? [Section titled “Why did I created Solid?”](#why-did-i-created-solid) You can learn more in my [blog post here](https://mariuti.com/posts/flutter-in-2025-forget-mvvm-embrace-fine-grained-reactivity-with-solid/) ## How can I use Solid with other generators? [Section titled “How can I use Solid with other generators?”](#how-can-i-use-solid-with-other-generators) Modify (or add) the `build.yaml` file in your project root. build.yaml ```yaml targets: $default: sources: - lib/** - $package$ - source/** ``` This way, other generators will also consider the `source` directory as input. Then run `dart run build_runner watch` once — `build_runner` invokes every registered builder, so Solid runs alongside `freezed`, `json_serializable`, etc. in the same process. Write your code in the `source` directory, using `part` statements or whatever the other generators need. # Effect > Learn to write reactive effects in Solid. The `@SolidEffect()` annotation will trigger a side effect whenever the reactive state variables it depends on change. ## Usage [Section titled “Usage”](#usage) source/effect\_example.dart ```dart class EffectExample extends StatelessWidget { EffectExample({super.key}); @SolidState() int counter = 0; @SolidEffect() void logCounter() { print('Counter changed: $counter'); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Effect')), body: Center(child: Text('Counter: $counter')), floatingActionButton: FloatingActionButton( onPressed: () => counter++, child: const Icon(Icons.add), ), ); } } ``` # Environment > Learn to inject environment providers in Solid. The `@SolidEnvironment()` annotation injects a value from the widget tree into your widget. It mirrors SwiftUI’s `@Environment` property wrapper: type-keyed, lookup happens on first access, and any reactive `@SolidState` fields on the injected type stay reactive in your `build` method. ## Usage [Section titled “Usage”](#usage) Annotate a `late` field with `@SolidEnvironment()` on a `StatelessWidget` or an existing `State`: source/counter\_display.dart ```dart class CounterDisplay extends StatelessWidget { CounterDisplay({super.key}); @SolidEnvironment() late Counter counter; @override Widget build(BuildContext context) { return Center(child: Text('Counter is ${counter.value}')); } } ``` `counter` is bound to the nearest ancestor `Provider` in the widget tree on first access. Because `Counter.value` is a `@SolidState` field, the read is reactive: only the `Text` widget rebuilds when `counter.value` changes — the same fine-grained reactivity you’d get from a same-class `@SolidState` read. Tip The lookup is lazy. The field initializer runs the first time the field is read, so it works inside `build`, a `@SolidEffect`, a `@SolidQuery` body, or any other context. ## Providing the instance [Section titled “Providing the instance”](#providing-the-instance) Given a `Counter` class that looks like this: source/counter.dart ```dart class Counter { @SolidState() int value = 0; } ``` You can provide it with `.environment()` extension on `Widget`, shipped by `solid_annotations`: source/main.dart ```dart class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: CounterDisplay().environment((_) => Counter()), ); } } ``` The type argument is inferred from the closure’s return type. Pass it explicitly only when you want consumers to read by a supertype: ```dart HomePage().environment((_) => RealAuthService()) ``` Chained calls nest providers in declaration order: ```dart HomePage() .environment((_) => Counter()) .environment((_) => Logger()) ``` *** Or `Provider` directly from `package:provider`: source/main.dart ```dart import 'package:provider/provider.dart'; class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( home: Provider( create: (_) => Counter(), child: CounterDisplay(), ), ); } } ``` To compose multiple providers, use `MultiProvider` from `package:provider`: ```dart MultiProvider( providers: [ Provider(create: (_) => Counter()), Provider(create: (_) => Logger()), ], child: CounterDisplay(), ) ``` # Getting started > Get started building amazing Flutter apps with Solid. ## Installation [Section titled “Installation”](#installation) 1. Add the runtime dependencies to your Flutter project ```bash flutter pub add solid_annotations flutter_solidart provider ``` [flutter\_solidart](https://pub.dev/packages/flutter_solidart) is the state-management runtime that backs Solid’s reactive primitives, and [provider](https://pub.dev/packages/provider) is the dependency-injection plumbing for `@SolidEnvironment`. You don’t have to learn either directly — Solid generates the boilerplate that uses them — but they appear in the generated `lib/` output, so they need to be in your pubspec. 2. Add the generator and `build_runner` as dev dependencies ```bash dart pub add --dev solid_generator build_runner ``` Solid ships as a `build_runner` builder — there is no separate CLI to install. 3. Update (or create) `build.yaml` at the project root so `build_runner` watches the `source/` folder. Most projects already have a `build.yaml` from other generators (`freezed`, `json_serializable`, …); just add `source/**` to the existing `sources` list. New projects can drop the file in as-is: build.yaml ```yaml targets: $default: sources: - source/** - lib/** - $package$ ``` 4. I’d also suggest installing the [very\_good\_analysis](https://pub.dev/packages/very_good_analysis) package to get amazing linting rules for your Flutter project. ```bash flutter pub add dev:very_good_analysis ``` Then in your `analysis_options.yaml` file, add the following line analysis\_options.yaml ```yaml include: package:very_good_analysis/very_good_analysis.yaml ``` This is an example of an `analysis_options.yaml` file analysis\_options.yaml ```yaml include: package:very_good_analysis/analysis_options.yaml analyzer: errors: must_be_immutable: ignore # Ignore immutability rule, because Solid generates immutable classes but you write mutable ones linter: rules: public_member_api_docs: false # Disable documentation requirement for public members # Same-package imports must be relative — `package:/…` resolves to `lib/`, # the generated realm, which is the wrong target for an authored file. always_use_package_imports: false prefer_relative_imports: true ``` ## AI assistant setup [Section titled “AI assistant setup”](#ai-assistant-setup) If you’re using an AI coding assistant (Claude Code, Cursor, Codex, GitHub Copilot, Amp, OpenHands, etc.), these three steps teach it the inverted `source/`-vs-`lib/` rule and Solid’s annotation contract — so it stops trying to edit `lib/` and silently losing your work. 1. Drop `AGENTS.md` at your app root. Most AI coding tools auto-load `AGENTS.md` at session start, so the rule applies to every interaction — even short or routine-looking ones — without depending on skill triggering. ```bash curl -o AGENTS.md https://raw.githubusercontent.com/nank1ro/solid/main/skills/solid/assets/AGENTS.md ``` If your tool only looks for `CLAUDE.md`, symlink it: ```bash ln -s AGENTS.md CLAUDE.md ``` 2. Install the Solid skill. ```bash npx skills add nank1ro/solid ``` Or copy `skills/solid/` from the [Solid repo](https://github.com/nank1ro/solid) into your editor’s skill location. The skill gives the agent deeper guidance (full annotation contract, scripts, troubleshooting) when it triggers on Solid-related work. `AGENTS.md` is the always-loaded baseline; the skill is the on-demand reference. 3. Point HTTP-docs tools at the LLM-friendly bundle (optional). If your tool fetches documentation over HTTP (Cursor `@docs`, ChatGPT custom GPTs, claude.ai web search, …), point it at [`/llms-full.txt`](https://solid.mariuti.com/llms-full.txt) — the full docs as a single LLM-friendly file. A short index lives at [`/llms.txt`](https://solid.mariuti.com/llms.txt). ## How it works [Section titled “How it works”](#how-it-works) The code you write is the source of truth, and Solid generates the boilerplate code needed to make the app working. There is an important difference between writing normal Flutter code and writing Solid code: In Flutter, the `lib` folder is the source of truth, while in Solid, the `source` folder is the source of truth. Note The `source` folder contains the code you write, and Solid generates the `lib` folder based on it. You never have to edit the `lib` folder manually, as it is generated by Solid. I decided not to use the `lib` folder directly because I don’t want to touch the original files, but I wanted to keep the same naming and structure, without adding complexity to you like `part` files or similar. I wanted to use `lib` as the input folder, but `flutter build` doesn’t allow building (as far as I know) an app from a different folder than `lib`. In addition, Solid is not a common generator because it transpiles your existing code to an optimized version of it. ## Configuration [Section titled “Configuration”](#configuration) To ensure Solid properly manages the lifecycle of your reactive atoms, configure `flutter_solidart` in your app’s `main()` function: source/main.dart ```dart import 'package:flutter/material.dart'; import 'package:flutter_solidart/flutter_solidart.dart'; import 'counter.dart'; void main() { SolidartConfig.autoDispose = false; runApp(const MaterialApp(home: CounterPage())); } ``` Setting `SolidartConfig.autoDispose = false` tells the library to let Solid manage disposal of reactive atoms manually, rather than automatically disposing them when they go out of scope. Note This is a temporary step. A future major release of `flutter_solidart` will disable `autoDispose` by default, at which point you won’t need to set this flag anymore. ## Usage [Section titled “Usage”](#usage) Run the generator with `build_runner`: ```bash # one-shot build dart run build_runner build # watch mode (recommended during development) dart run build_runner watch ``` ### Hot reload [Section titled “Hot reload”](#hot-reload) `flutter run` does not auto-reload when `build_runner` rewrites files under `lib/`, because no IDE save event fires for filesystem changes. There are two supported workflows: * Press `r` in the `flutter run` terminal after `build_runner` emits. * Use [`dashmonx`](https://pub.dev/packages/dashmonx), which wraps `flutter run` and triggers hot reload automatically when files under `lib/` change. Any `flutter run` flag passes through, e.g. `dashmonx -d chrome` for a web target. ### Clean up after generation [Section titled “Clean up after generation”](#clean-up-after-generation) Solid prioritises producing correct, runnable code over polishing it. The generated `lib/` output may miss some `const` opportunities, leave unused imports, or pick a non-preferred import form. Run `dart fix` after `build_runner` to apply the lint-driven fixes (`prefer_const_constructors`, `unnecessary_import`, `prefer_relative_imports`, …): ```bash dart fix --apply ``` In CI, chain the two commands so the generated output is always lint-clean: ```bash dart run build_runner build dart fix --apply ``` Note The Solid skill (installed via the AI assistant setup above) ships a `verify.sh` helper that runs both in one shot and reports PASS/FAIL. Note The next chapters assume you have set up Solid in your Flutter project, created the `source` folder, and are running `dart run build_runner watch` to regenerate `lib/` on every change. # Query > Learn to write queries in Solid. The `@SolidQuery()` annotation allows you to create reactive state based on asynchronous data sources, such as fetching data from a network. ## Usage [Section titled “Usage”](#usage) source/query\_example.dart ```dart class QueryExample extends StatelessWidget { const QueryExample({super.key}); @SolidQuery() Future fetchData() async { await Future.delayed(const Duration(seconds: 1)); return 'Fetched Data'; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Query')), body: Center( child: fetchData().when( ready: Text.new, loading: CircularProgressIndicator.new, error: (error, stackTrace) => Text('Error: $error'), ), ), ); } } ``` This example defines a `fetchData` method annotated with `@SolidQuery()`, which simulates fetching data asynchronously. The `when` method is used to handle the build different widgets based on the states of the query: `ready`, `loading`, and `error`. Tip You can also use the `maybeWhen` method to handle only specific states and provide a default widget for the `orElse` option. *** You can also use the `@SolidQuery()` annotation for streams. source/query\_example.dart ```dart @SolidQuery() Stream fetchData() { return Stream.periodic(const Duration(seconds: 1), (i) => i); } ``` And **magically**, you don’t have to change anything else. ## Reacting to state [Section titled “Reacting to state”](#reacting-to-state) A query can also depend on reactive state variables. source/query\_with\_source\_example.dart ```dart class QueryWithSourceExample extends StatelessWidget { QueryWithSourceExample({super.key}); @SolidState() String? userId; @SolidQuery(debounce: Duration(seconds: 1)) Future fetchData() async { if (userId == null) return null; await Future.delayed(const Duration(seconds: 1)); return 'Fetched Data for $userId'; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('QueryWithSource')), body: Center( child: fetchData().when( ready: (data) { if (data == null) { return const Text('No user ID provided'); } return Text(data); }, loading: CircularProgressIndicator.new, error: (error, stackTrace) => Text('Error: $error'), ), ), floatingActionButton: FloatingActionButton( onPressed: () => userId = 'user_${DateTime.now().millisecondsSinceEpoch}', child: const Icon(Icons.refresh), ), ); } } ``` Interesting Any time the `userId` variable changes, the `fetchData` query will be re-executed. Debounce As you can see, the `@SolidQuery()` annotation also supports a `debounce` parameter. `@SolidQuery(debounce: Duration(seconds: 1))` will wait for 1 second after the last change before executing the query. Caution A function annotated with `@SolidQuery()` must return either a `Future` or a `Stream` and **cannot have parameters**. ## Detect if a query is refreshing [Section titled “Detect if a query is refreshing”](#detect-if-a-query-is-refreshing) By default, when a query is re-executed due to a dependency change, it doesn’t enter the loading state again. Instead, it stays in the current state while the new data is being fetched. When the new data arrives, the state updates accordingly. This behavior is designed to provide a smoother user experience by avoiding unnecessary loading indicators during data refreshes. You can detect if a query is currently refreshing by using the `isRefreshing` property. ```dart fetchData().isRefreshing ``` This property returns `true` if the query is in the process of refreshing its data, allowing you to adjust your UI accordingly (e.g., showing a subtle loading indicator or a refresh icon). You can disable this behavior by setting the `useRefreshing` parameter to `false` in the `@SolidQuery()` annotation. ```dart @SolidQuery(useRefreshing: false) ``` In this case, the query will enter the loading state again when it is re-executed due to a dependency change. ## Manually refreshing a query [Section titled “Manually refreshing a query”](#manually-refreshing-a-query) You can manually refresh a query by calling the `refresh` method on it. ```dart fetchData.refresh(); ``` # State > Learn to write reactive state in Solid. The `@SolidState()` annotation allows you to create reactive state variables in your Solid widgets. When you annotate a variable with `@SolidState()`, Solid will generate the necessary boilerplate code to make that variable reactive. This means that whenever the variable changes, any UI elements that depend on it will automatically update. ## Usage [Section titled “Usage”](#usage) source/counter.dart ```dart import 'package:solid_annotations/solid_annotations.dart'; import 'package:flutter/material.dart'; class Counter extends StatelessWidget { Counter({super.key}); @SolidState() int counter = 0; @override Widget build(BuildContext context) { return Scaffold( body: Center( child: Text('Counter is $counter'), ), floatingActionButton: FloatingActionButton( onPressed: () => counter++, child: const Icon(Icons.add), ), ); } } ``` In this example, the `counter` variable is annotated with `@SolidState()`. Whenever the floating action button is pressed, the `counter` variable is incremented, and the text displaying the counter value will automatically update to reflect the new value. Interesting Only the `Text` widget that accesses the `counter` variable is rebuilt when `counter` changes, thanks to Solid’s fine-grained reactivity. ## Derived state [Section titled “Derived state”](#derived-state) You can also create computed properties that derive their values from reactive state variables. To do this, simply annotate a getter that accesses the reactive state variables with `@SolidState`. source/computed\_counter.dart ```dart class Counter extends StatelessWidget { Counter({super.key}); @SolidState() int counter = 0; @SolidState() int get doubleCounter => counter * 2; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Computed')), body: Center( child: Text('Counter: $counter, DoubleCounter: $doubleCounter'), ), floatingActionButton: FloatingActionButton( onPressed: () => counter++, child: const Icon(Icons.add), ), ); } } ``` In this example, the `doubleCounter` getter computes its value based on the `counter` variable. Whenever `counter` changes, `doubleCounter` will also update, and any UI elements that depend on it will automatically refresh. # Untracked > Read reactive state without re-running the surrounding build, effect, or query. By default, every read of a `@SolidState` field inside a reactive context — a `build` method, an `@SolidEffect` body, or an `@SolidQuery` body — **registers a dependency**. When the field changes, the surrounding computation re-runs: `build` re-renders the widget subtree at the read site, an effect re-fires, a query re-executes. That’s how fine-grained reactivity stays automatic. Occasionally you need to read the current value *without* subscribing. Solid covers two cases. ## Inside `on*` callbacks (automatic) [Section titled “Inside on\* callbacks (automatic)”](#inside-on-callbacks-automatic) Reads inside `onPressed`, `onTap`, `onChanged`, and other named-callback parameters starting with `on` are **automatically untracked**. Solid treats those as user-interaction handlers — they fire in response to gestures, not signal changes — so registering a dependency on every read inside them would be wrong. source/conditional\_dialog.dart ```dart class ConditionalDialog extends StatelessWidget { ConditionalDialog({super.key}); @SolidState() int counter = 0; @override Widget build(BuildContext context) { return FloatingActionButton( onPressed: () { if (counter > 10) showDialog(/*...*/); // read is untracked counter++; // writes are always untracked }, child: const Icon(Icons.add), ); } } ``` You don’t write anything special — Solid handles it. Just keep in mind the body reads a *snapshot*, not a live value. ## Manual opt-out: `field.untracked` [Section titled “Manual opt-out: field.untracked”](#manual-opt-out-fielduntracked) For one-off untracked reads outside a callback, append `.untracked` to the field reference. The most common case is reading a value once for key construction or imperative initialization, without rebuilding on later changes. source/keyed\_container.dart ```dart class KeyedContainer extends StatelessWidget { KeyedContainer({super.key}); @SolidState() int counter = 0; @override Widget build(BuildContext context) { return Container( // ValueKey reads counter once; it does NOT rebuild on counter changes. key: ValueKey(counter.untracked), child: const Text('hi'), ); } } ``` `.untracked` is a typed identity extension shipped by `solid_annotations` — `counter.untracked` has the same type as `counter` and at runtime is a no-op. Only the source-time presence matters: the generator rewrites `counter.untracked` to the underlying `untrackedValue` primitive and excludes the read from the dependency set, so `SignalBuilder` doesn’t wrap it inside `build` and an enclosing effect or query won’t re-fire on changes to it. Caution Inside string interpolations only the long form works: `'${counter.untracked}'`. The short form `'$counter.untracked'` parses as `${counter}` followed by a literal `.untracked` suffix — that read is tracked. ## When to reach for `.untracked` [Section titled “When to reach for .untracked”](#when-to-reach-for-untracked) * Building a one-time `Key` / `ValueKey` from a reactive field. * Inside a `@SolidEffect` that writes to a signal, reading the same signal’s current value to avoid a self-dependency loop: ```dart @SolidEffect() void recordHistory() { history = [...history.untracked, counter]; // counter is tracked, history is not } ``` * Logging / analytics calls that should not cause rebuilds. If you find yourself sprinkling `.untracked` everywhere, step back — usually a `@SolidState` getter (derived state) or a non-reactive plain field is the better tool.