A computed shard derives its value from one or more Listenable sources and recomputes whenever any of them notifies. Sources can be other Shards (which are ChangeNotifiers), ValueNotifiers, Animations — anything that is Listenable.
There are two forms:
computedShard function (recommended) for most cases.ComputedShard subclass when you need a named type to provide via ShardProvider.Pass the list of sources and a compute closure that returns the derived value. The value is recomputed whenever any source notifies:
// Derived value from one or more Listenable sources, recomputed on any notify:
final total = computedShard(
[cart],
() => cart.state.items.fold(0, (sum, i) => sum + i.price),
);
This is the recommended default because the compute closure captures already-constructed sources. It sidesteps the constructor-ordering trap of the subclass form entirely — there are no subclass fields to initialize before the value is first computed.
When you need a named type — for example, to register and resolve it through ShardProvider — extend ComputedShard<T>. Pass the sources and an initial value to the base constructor, and override compute() to derive the value from the current source states:
class CartTotal extends ComputedShard<int> {
CartTotal(this.cart) : super([cart], 0);
final CartShard cart;
@override
int compute() => cart.state.items.fold(0, (sum, i) => sum + i.price);
}
Provide it with ShardProvider:
ShardProvider<CartTotal>(
create: () => CartTotal(context.read<CartShard>()),
child: const CheckoutSummary(),
)
compute() runs from the base constructor's body, so any field it reads must be set via an initializing formal or the initializer list — not assigned later in the subclass constructor body. The computedShard function form avoids this entirely.In the example above, cart is set via the initializing formal this.cart, so it is available by the time the base constructor invokes compute().
A ComputedShard removes its source listeners on dispose.
Next: Persistent Shard.