Provides a shard to the widget tree and manages its lifecycle.
// Create new shard
ShardProvider<CounterShard>(
create: () => CounterShard(),
child: MyWidget(),
)
// Provide existing shard
ShardProvider.value(
value: existingShard,
child: MyWidget(),
)
// Access shard
final shard = context.read<CounterShard>();
Rebuilds when state changes. Provides the current state to the builder.
ShardBuilder<CounterShard, int>(
builder: (context, count) => Text('Count: $count'),
)
// Conditional rebuilding
ShardBuilder<CounterShard, int>(
buildWhen: (prev, curr) => curr % 2 == 0,
builder: (context, count) => Text('Even: $count'),
)
// Listener callback
ShardBuilder<CounterShard, int>(
listener: (prev, curr) => print('$prev → $curr'),
builder: (context, count) => Text('Count: $count'),
)
// Control listening
ShardBuilder<CounterShard, int>(
listenWhen: (prev, curr) => curr > 10,
builder: (context, count) => Text('Count: $count'),
)
Recommended approach. Rebuilds only when the selected value changes. Optimizes performance by avoiding unnecessary rebuilds.
// Select part of state
ShardSelector<TodoShard, TodoState, int>(
selector: (state) => state.todos.length,
builder: (context, count) => Text('Count: $count'),
)
// Complex selection
ShardSelector<TodoShard, TodoState, List<Todo>>(
selector: (state) => state.todos.where((t) => !t.completed).toList(),
builder: (context, activeTodos) => Text('Active: ${activeTodos.length}'),
)
// Simple state access
ShardSelector<CounterShard, int, int>(
selector: (state) => state,
builder: (context, count) => Text('Count: $count'),
)
Prefer ShardSelector over ShardBuilder for better performance:
// ❌ Rebuilds on any state change
ShardBuilder<TodoShard, TodoState>(
builder: (context, state) => Text('Count: ${state.todos.length}'),
)
// ✅ Only rebuilds when count changes
ShardSelector<TodoShard, TodoState, int>(
selector: (state) => state.todos.length,
builder: (context, count) => Text('Count: $count'),
)
Use buildWhen for conditional rebuilding with ShardBuilder:
ShardBuilder<TodoShard, TodoState>(
buildWhen: (prev, curr) => prev.todos.length != curr.todos.length,
builder: (context, state) => Text('Count changed'),
)
For async operations with FutureShard or StreamShard, use AsyncShardBuilder with separate callbacks:
AsyncShardBuilder<UserShard, User>(
onLoading: (context) => CircularProgressIndicator(),
onData: (context, user) => Text('Hello, ${user.name}'),
onError: (context, error, stackTrace) => Text('Error: $error'),
)
You can also use ShardBuilder with AsyncValue and pattern matching for more control:
ShardBuilder<UserShard, AsyncValue<User>>(
builder: (context, asyncValue) {
return switch (asyncValue) {
AsyncLoading() => CircularProgressIndicator(),
AsyncData(:final value) => Text('Hello, ${value.name}'),
AsyncError(:final error) => Text('Error: $error'),
};
},
)
Learn more in Async Values.
Learn about Debounce & Throttle to optimize state updates.