Skip to content

Troubleshooting ​

Practical fixes for the most common issues when setting up and running Reacton. If something here does not match what you are seeing, open an issue on GitHub.

Installation & Setup ​

flutter pub get fails with dependency conflicts ​

Make sure your project meets the minimum SDK constraints documented in Installation:

  • Dart SDK: >= 3.0.0
  • Flutter: >= 3.10.0 (for flutter_reacton)

If you previously pinned an older version of Reacton, update every Reacton package in pubspec.yaml in lock-step:

yaml
dependencies:
  flutter_reacton: ^0.2.0
  reacton: ^0.2.0

dev_dependencies:
  reacton_test: ^0.2.0
  reacton_generator: ^0.2.0
  reacton_lint: ^0.2.0

Then:

bash
flutter pub get

No ReactonScope found in widget tree ​

You tried to call context.watch(), context.read(), context.set(), or context.update() from a widget that has no ReactonScope ancestor.

dart
void main() {
  runApp(
    ReactonScope(           // ← must wrap anything that uses context.watch
      child: const MyApp(),
    ),
  );
}

If you have multiple MaterialApp instances (e.g. inside a navigator), put ReactonScope above the top-level MaterialApp.

Build runner can't find reacton_generator ​

For generator-based workflows:

bash
dart run build_runner build --delete-conflicting-outputs

If the command fails:

  • Confirm reacton_generator and build_runner are in dev_dependencies.
  • Re-run flutter pub get.
  • Delete the .dart_tool/build/ cache and run build_runner clean followed by the build command.

See Code Generation for the full setup.

Runtime & Reactivity ​

My widget does not rebuild when I update a reacton ​

Five checks, in order:

  1. Are you using context.watch() and not context.read()? read deliberately does not subscribe.
  2. Is the watch call inside build()? Subscriptions are tracked via the BuildContext — calling watch() in initState or event handlers does not subscribe.
  3. Did the value actually change (by ==)? By default Reacton skips notifications when the new value equals the old one. Set a custom equals in ReactonOptions if you need stricter comparison, or reach for an observable collection when mutating the same list.
  4. Are you mutating a list/map in place? list.add(...) does not change the reference — wrap with a new list or use ObservableList.
  5. Is your widget inside the ReactonScope subtree? A sibling above the scope will not see updates.

Infinite rebuild loop ​

Typical cause: an effect() that writes to a reacton it also reads.

dart
// ❌ infinite loop — effect reads `counter`, then writes to it
createEffect((read) {
  final n = read(counter);
  store.set(counter, n + 1);
});

Fix: break the cycle. Read in the effect, write from a separate trigger (a button callback, a timer, another reacton that you read but do not write).

StateError: No reacton with ref id … ​

Thrown by ReactonStore.getByRef(refId) when a ref does not resolve. Common causes:

  • You passed an outdated ReactonRef from a previous store.
  • You called store.remove(ref) and then tried to read the same ref.

Solution: always read/write through the reacton object itself (store.get(myReacton)) rather than raw refs, unless you are writing low-level tooling.

StateError: Module of type X is not installed. ​

You called store.moduleOf<MyModule>() before calling store.installModule(MyModule()). Install the module once, at app start:

dart
final store = ReactonStore();
store.installModule(AuthModule());
store.installModule(CartModule());
runApp(ReactonScope(store: store, child: const MyApp()));

StateError: Field "…" not found in form "…" ​

The form reacton does not know about that field name. Register every field when building the form, and reference fields by the exact string you registered.

dart
final loginForm = formReacton(
  fields: {
    'email':    fieldReacton(''),
    'password': fieldReacton(''),
  },
);

Effect cleanup never runs ​

Return a cleanup function from the effect body:

dart
createEffect((read) {
  final sub = stream.listen((e) => ...);
  return () => sub.cancel();      // ← this is the cleanup
});

Without a returned cleanup, Reacton assumes there is nothing to dispose.

Async ​

Query stays in AsyncLoading forever ​

  • Check that your queryFn actually completes (await someFuture — not bare Future construction).
  • If you throw from queryFn, the value transitions to AsyncError, not loading. If you see loading forever, nothing resolved.
  • Use DevTools → Timeline to confirm the query even started.

QueryCancelledException in production ​

Queries cancel automatically when their dependent reactons change or the subscriber is disposed. Check ctx.isCancelled at yield points:

dart
reactonQuery<User>(
  queryFn: (ctx) async {
    final partial = await api.fetchProfile();
    ctx.throwIfCancelled();
    final full = await api.fetchOrders(partial.id);
    return User.merge(partial, full);
  },
);

Treat QueryCancelledException as "nothing to do", never as a bug to report to the user.

Retries don't happen ​

RetryPolicy only retries when shouldRetry(error) returns true. The default retries on all errors, but if you set a custom shouldRetry, make sure it returns true for the errors you actually want to retry.

Flutter / Widget issues ​

Widget throws during hot reload ​

Reactons declared as top-level final variables survive hot reload, but their values in the ReactonStore do not — the store is recreated.

If you see stale data after hot reload, restart the app (R in the terminal, or the restart button in your IDE). Use a persistence adapter if you need state to survive app restarts.

ReactonScope is not found in tests ​

Wrap the widget under test:

dart
testWidgets('shows counter', (tester) async {
  await tester.pumpWidget(
    ReactonScope(
      store: TestReactonStore(),
      child: const MaterialApp(home: CounterPage()),
    ),
  );
});

See Widget Testing for more patterns.

DevTools ​

DevTools extension does not appear ​

  1. Add reacton_devtools to dev_dependencies.
  2. Run the app with DevTools enabled (flutter run prints a URL; open it, or use the IDE's "Open DevTools" button).
  3. Look for the Reacton tab next to Performance/Memory.

If the tab still does not show, confirm your Flutter version is >= 3.10 and your DevTools is >= 2.28.

DevTools shows stale data ​

Click Refresh in the Reacton tab, or disconnect and reconnect DevTools. Some views cache until the next frame.

CI / deploy ​

vitepress build fails in GitHub Actions ​

The docs deploy workflow uses Node 20 with an npm cache keyed to docs/package-lock.json. If builds fail:

  • Re-run the workflow — transient npm registry hiccups are common.
  • Bump vitepress locally, commit the updated package-lock.json.
  • Check the base: '/reacton/' setting in .vitepress/config.ts matches your Pages URL.

flutter pub publish --dry-run reports warnings ​

The repo ships pre-configured pubspecs for publish. If warnings appear:

  • Confirm the package has a short description (60–180 chars).
  • Confirm homepage/repository URLs are public and resolve.
  • Run dart analyze — pub.dev scores drop on analyzer warnings.

Still stuck? ​

Released under the MIT License.