Dart 3.
Dart 3.13 changes the math here. By introducing primary constructors and this constructor body blocks, the friction of managing state in Flutter has plummeted. When you pair these language updates with BlocSignal (the signal-powered, synchronous evolution of BLoC), the architecture becomes incredibly lean.
Killing the event/state boilerplate
If you've done any serious Flutter work, you know the pain of defining sealed classes for events. You used to have to declare the field, then write the constructor to assign that field. It was redundant.
With primary constructors, you can collapse an entire event hierarchy into a few lines. Here is how the implementation looks now:
sealed class UserEvent {}
class UserFetchRequested(final String userId) extends UserEvent;
class UserUpdated({required final String name, required final int age}) extends UserEvent;
class UserLoggedOut() extends UserEvent;You keep the full type safety and the ability to use exhaustive switch expressions, but you stop wasting time writing this.userId = userId for the tenth time in a single file.
Cleaner dependency injection in CubitSignal
Dependency injection used to be a messy affair of forwarding arguments to super and re-declaring final fields. It made the top of every Cubit feel cluttered.
Now, the field declarations and super invocations happen directly in the class header. This removes the need for those awkward initializer lists.
class UserCubit(
final UserRepository repository,
final AnalyticsService analytics, {
final UserState initial = const UserInitial(),
}) extends CubitSignal(initialState: initial) {
Future loadUser(String id) async {
emit(const UserLoading());
try {
final user = await repository.fetchUser(id);
analytics.track('user_loaded', {'id': id});
emit(UserSuccess(user));
} catch (e, st) {
onError(e, st);
emit(UserError(e.toString()));
}
}
}The dependencies are injected and immediately available to all methods without the boilerplate of private field assignments.
Simplifying the AI workflow
From a prompt engineering perspective, this is a huge win. When using tools like Cursor or Claude Code to generate state management logic, less boilerplate means fewer tokens and less room for the AI to hallucinate redundant field declarations or miss a super-initializer.
If you're building a real-world app, I highly recommend auditing your current BLoCs. Moving to a primary constructor pattern doesn't just make the code shorter; it makes the intent of the class immediately obvious to anyone reading it. It's a complete guide to reducing cognitive load in your Flutter codebase.