Shard<T> is a generic shard that holds typed state. It extends ChangeNotifier and provides state management with built-in lifecycle management and mixin support.
class CounterShard extends Shard<int> {
CounterShard() : super(0);
void increment() {
emit(state + 1);
}
void decrement() {
emit(state - 1);
}
}
Use emit() to update state. This will notify all listeners:
void updateValue(int newValue) {
emit(newValue);
}
By default, emit() performs an equality check before updating state. If the new state equals the current state, no notification is triggered. This prevents unnecessary widget rebuilds and improves performance.
class CounterShard extends Shard<int> {
CounterShard() : super(0);
void increment() => emit(state + 1); // Notifies listeners
void emitSame() => emit(state); // Skipped - no rebuild
}
Override stateEquals() to provide custom equality logic for complex state objects:
class UserShard extends Shard<UserState> {
UserShard() : super(UserState.empty());
@override
bool stateEquals(UserState a, UserState b) {
// Only compare specific fields
return a.id == b.id && a.name == b.name;
}
}
Use emitForce() to bypass equality check and always notify listeners:
void forceRefresh() {
emitForce(state); // Always notifies, even if state is same
}
emitForce() is useful when your state object is mutable and has been modified in place, or when you need to force a UI refresh.Override onInit() to perform initialization when the shard is created, and dispose() to clean up resources when the shard is disposed:
class TimerShard extends Shard<int> {
Timer? _timer;
TimerShard() : super(0);
@override
void onInit() {
super.onInit();
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
emit(state + 1);
});
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
}
Shard provides built-in observer methods that you can override to handle state changes and errors:
class CounterShard extends Shard<int> {
CounterShard() : super(0);
@override
void onChange(int previousState, int currentState) {
print('State: $previousState → $currentState');
super.onChange(previousState, currentState);
}
@override
void onError(Object error, StackTrace? stackTrace) {
print('Error: $error');
super.onError(error, stackTrace);
}
void increment() => emit(state + 1);
}
Use addError to report errors without stopping execution:
void riskyOperation() {
try {
// risky code
} catch (e, st) {
addError(e, st); // Reports error, doesn't block
}
emit(state + 1); // Still executes
}
Learn more about Observing a Shard for advanced observer patterns and global error handling.
Shard provides built-in debounce and throttle mixins to optimize state updates and reduce unnecessary operations.
Delays execution until after a period of inactivity. Perfect for search inputs and form validation.
class SearchShard extends Shard<SearchState> {
SearchShard() : super(SearchState(query: '', results: []));
void updateQuery(String query) {
// Update UI immediately
emit(state.copyWith(query: query));
// Debounce actual search
debounce('search', () {
performSearch(query);
}, duration: const Duration(milliseconds: 500));
}
Future<void> performSearch(String query) async {
if (query.isEmpty) {
emit(state.copyWith(results: []));
return;
}
final results = await searchApi(query);
emit(state.copyWith(results: results));
}
}
Limits execution to once per time period. The first call executes immediately, subsequent calls are ignored until the period expires. Perfect for API calls and scroll events.
class InfiniteScrollShard extends Shard<ScrollState> {
void loadMore() {
throttle('loadMore', () {
_performLoadMore();
}, duration: const Duration(seconds: 1));
}
Future<void> _performLoadMore() async {
if (state.isLoading || !state.hasMore) return;
emit(state.copyWith(isLoading: true));
try {
final items = await fetchItems(state.page + 1);
emit(state.copyWith(
items: [...state.items, ...items],
page: state.page + 1,
isLoading: false,
));
} catch (e) {
emit(state.copyWith(isLoading: false, error: e.toString()));
}
}
}
Now that you understand core concepts, learn about Persistent Shard to add state persistence.