Essentials

Widgets

Learn how to integrate Shard with Flutter widgets.

ShardProvider

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>();

ShardBuilder

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'),
)

ShardSelector

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'),
)

Performance Tips

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'),
)

AsyncShardBuilder

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'),
)

Using ShardBuilder with AsyncValue

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.

Next Steps

Learn about Debounce & Throttle to optimize state updates.