What is copyWith in BLoC?
In BLoC, states are immutable objects. The copyWith method is a common pattern that allows you to create a new instance of a state class by copying existing values and replacing only the fields you want to change. This ensures immutability while providing a convenient way to update state without having to manually pass all fields. It's typically combined with Equatable to enable correct equality comparisons and avoid unnecessary widget rebuilds.
Why Use copyWith?
- Immutability – Preserves the benefits of immutable state: predictable, easy to reason about, and safe for concurrency.
- Clean Code – Reduces boilerplate when updating only a few fields.
- Type Safety – Avoids mistakes like forgetting to copy a field.
- Performance – Works seamlessly with
Equatableto prevent unnecessary rebuilds. - Maintainability – Adding new fields to a state class requires minimal changes to update code.
Basic Implementation
Here’s a typical state class using Equatable with a custom copyWith method.
Now you can update state immutably in your BLoC:
Handling Nested Objects
When your state contains other objects, you need to ensure those objects are also copied immutably. Use copyWith on nested objects as well.
Using copyWith with Collections
Lists, maps, and sets are also immutable. When updating, you should create new collections rather than mutating existing ones.
Alternative: Freezed for Boilerplate Reduction
If you find writing copyWith manually repetitive, consider using the freezed package. It generates copyWith, toString, and Equatable implementation automatically.
Best Practices
- Always use copyWith – Avoid directly modifying fields; always create new instances.
- Use nullable parameters – Make all copyWith parameters optional and use
??to fall back to current values. - Combine with Equatable – Ensures that
BlocBuilderonly rebuilds when the state actually changes. - Freeze collections – Use
List.unmodifiableorconst []to prevent accidental mutations. - Don't mutate nested objects in place – Always create a new instance of nested objects when updating them.
- Use
freezedfor complex states – Saves time and reduces bugs. - Name your copyWith method consistently – Always call it
copyWithfor familiarity.
Common Mistakes
- ❌ Mutating collections directly –
state.todos.add(todo)doesn't trigger updates; use[...state.todos, todo]. - ❌ Forgetting to include new fields in copyWith – When you add a field to a state, you must also add it to copyWith and props.
- ❌ Not using Equatable – Without it,
BlocBuildermay rebuild even when copyWith produces a new instance with the same data. - ❌ Calling copyWith inside build methods – This can cause infinite rebuilds; do it inside BLoCs or event handlers.
- ❌ Using default parameters incorrectly – If a field should be set to
null, you need a way to differentiate; consider usingrequiredwith sentinel values.
Conclusion
The copyWith pattern is essential for working with immutable state in BLoC. It reduces boilerplate, maintains immutability, and pairs perfectly with Equatable for efficient UI updates. Whether you write it manually or generate it with Freezed, mastering copyWith will make your BLoC code cleaner and more maintainable.