Skip to content

Context Extensions ​

The ReactonBuildContextExtension on BuildContext provides the primary API for using Reacton in Flutter widgets. These are the methods you will use most often.

dart
import 'package:flutter_reacton/flutter_reacton.dart';

context.watch<T>() ​

Reads a reacton's current value and subscribes the widget to future changes. When the reacton's value changes, the widget rebuilds.

dart
@override
Widget build(BuildContext context) {
  final count = context.watch(counterReacton);   // int
  final name = context.watch(nameReacton);       // String
  return Text('$name: $count');
}

Signature ​

dart
T watch<T>(ReactonBase<T> reacton)
ParameterTypeDescription
reactonReactonBase<T>Any reacton (writable, computed, selector, async, etc.)

Returns: The current value of type T.

When to Use ​

Use context.watch() inside build() methods whenever a widget needs to display or derive its output from a reacton's value.

You can watch multiple reactons in the same build(). The widget rebuilds when any of them change:

dart
@override
Widget build(BuildContext context) {
  final firstName = context.watch(firstNameReacton);
  final lastName = context.watch(lastNameReacton);
  return Text('$firstName $lastName');
}

context.read<T>() ​

Reads a reacton's current value without subscribing. The widget does not rebuild when the value changes.

dart
onPressed: () {
  final current = context.read(counterReacton);
  print('Current value: $current');
}

Signature ​

dart
T read<T>(ReactonBase<T> reacton)
ParameterTypeDescription
reactonReactonBase<T>Any reacton

Returns: The current value of type T.

When to Use ​

Use context.read() in event handlers, onPressed callbacks, lifecycle methods, and anywhere outside of build() where you need a one-time snapshot of the value.

DANGER

Never use context.read() inside build(). The widget will display stale data because it will not rebuild when the value changes. Use context.watch() instead.

dart
// BAD -- widget won't update when counter changes
@override
Widget build(BuildContext context) {
  final count = context.read(counterReacton); 
  return Text('$count');
}

// GOOD -- widget rebuilds when counter changes
@override
Widget build(BuildContext context) {
  final count = context.watch(counterReacton); 
  return Text('$count');
}

context.set<T>() ​

Writes a new value to a writable reacton.

dart
context.set(counterReacton, 42);
context.set(nameReacton, 'Alice');

Signature ​

dart
void set<T>(WritableReacton<T> reacton, T value)
ParameterTypeDescription
reactonWritableReacton<T>A writable reacton (not computed/readonly)
valueTThe new value

When to Use ​

Use context.set() when you already know the exact value to write, independent of the current value:

dart
FloatingActionButton(
  onPressed: () => context.set(counterReacton, 0), // Reset to 0
  child: const Icon(Icons.refresh),
)

context.update<T>() ​

Updates a writable reacton using a function that receives the current value and returns the new value.

dart
context.update(counterReacton, (count) => count + 1);
context.update(todosReacton, (todos) => [...todos, newTodo]);

Signature ​

dart
void update<T>(WritableReacton<T> reacton, T Function(T current) updater)
ParameterTypeDescription
reactonWritableReacton<T>A writable reacton
updaterT Function(T current)A function that receives the current value and returns the new value

When to Use ​

Use context.update() when the new value depends on the current value:

dart
// Increment
context.update(counterReacton, (c) => c + 1);

// Toggle
context.update(isDarkModeReacton, (dark) => !dark);

// Add to list
context.update(itemsReacton, (items) => [...items, newItem]);

context.reactonStore ​

Provides direct access to the ReactonStore instance from the nearest ReactonScope. Useful for advanced operations like batching, snapshots, or registering effects.

dart
final store = context.reactonStore;

store.batch(() {
  store.set(firstNameReacton, 'John');
  store.set(lastNameReacton, 'Doe');
});

Signature ​

dart
ReactonStore get reactonStore

Returns: The ReactonStore from the nearest ReactonScope.

How Subscription Tracking Works ​

Understanding the subscription mechanism helps explain why watch() and read() behave differently.

The Expando-Based Tracker ​

Reacton uses Dart's Expando class to attach a _ReactonSubscriptionTracker to each Element (the internal representation of a widget in the widget tree). This avoids requiring a custom base widget class.

When context.watch(reacton) is called:

  1. The BuildContext is cast to its underlying Element
  2. An Expando lookup retrieves (or creates) a _ReactonSubscriptionTracker for that element
  3. The tracker calls store.subscribe(reacton, callback) if not already subscribed
  4. The callback calls element.markNeedsBuild() to trigger a rebuild
  5. The current value is returned from the store
dart
// Simplified internal flow of context.watch()
T watch<T>(ReactonBase<T> reacton) {
  final store = ReactonScope.of(this);
  final element = this as Element;
  final tracker = _ReactonSubscriptionTracker.of(element);
  tracker.track(reacton, store); // Subscribe if not already
  return store.get(reacton);
}

Subscription Lifecycle ​

  • Created: On the first call to context.watch(reacton) for a given element
  • Deduplicated: Subsequent calls to context.watch() with the same reacton in the same element are no-ops (already tracked)
  • Cleaned up: When the element is unmounted, the tracker disposes all subscriptions

TIP

Because subscriptions are per-element, the same reacton can be watched by many widgets. Each gets its own independent subscription that only affects its own rebuilds.

API Summary ​

MethodSubscribes?Use InPurpose
context.watch(r)Yesbuild()Read value, rebuild on change
context.read(r)NoEvent handlersOne-time read
context.set(r, v)NoEvent handlersDirect write
context.update(r, fn)NoEvent handlersFunctional update
context.reactonStoreNoAnywhereDirect store access

What's Next ​

  • Widgets -- ReactonBuilder, ReactonConsumer, ReactonListener, ReactonSelector
  • ReactonScope -- How the store is provided to the tree
  • Form State -- Reactive form validation with FieldReacton

Released under the MIT License.