Domain Events with Freezed Unions in Flutter

Summary
Summary
Summary
Summary

Model domain events in Flutter as Freezed unions to gain exhaustiveness, immutability, and concise pattern matching. Define events with Freezed, handle them centrally with when or map, emit from UI controllers, and write tests that assert event sequences and resulting states. This pattern improves predictability and testability in mobile development.

Model domain events in Flutter as Freezed unions to gain exhaustiveness, immutability, and concise pattern matching. Define events with Freezed, handle them centrally with when or map, emit from UI controllers, and write tests that assert event sequences and resulting states. This pattern improves predictability and testability in mobile development.

Model domain events in Flutter as Freezed unions to gain exhaustiveness, immutability, and concise pattern matching. Define events with Freezed, handle them centrally with when or map, emit from UI controllers, and write tests that assert event sequences and resulting states. This pattern improves predictability and testability in mobile development.

Model domain events in Flutter as Freezed unions to gain exhaustiveness, immutability, and concise pattern matching. Define events with Freezed, handle them centrally with when or map, emit from UI controllers, and write tests that assert event sequences and resulting states. This pattern improves predictability and testability in mobile development.

Key insights:
Key insights:
Key insights:
Key insights:
  • Modeling Domain Events With Freezed: Freezed unions let you declare a closed set of immutable events with generated equality and helpers.

  • Handling Events With Pattern Matching: Use when or map to force exhaustive, type-safe handling of every event variant.

  • Emitting Events In Flutter Widgets: Keep UI thin—construct Freezed events in widget handlers and forward them to a controller or state manager.

  • Testing And Reasoning About Events: Generated equality and verbose toString make it simple to assert event sequences and state transitions in tests.

Introduction

Domain events are a natural fit for designing predictable, testable business logic in Flutter mobile development. They represent things that happened in the domain — e.g., UserLoggedIn, ItemAddedToCart — and decouple intent from effect. Freezed unions (sealed classes) give you a compact, type-safe way to model those events and handle them exhaustively. This tutorial shows how to model domain events with Freezed, match on them with pattern matching, emit them from UI code, and write tests that reason about event flows.

Modeling Domain Events With Freezed

Use Freezed to declare a union of events. Each union variant encodes the data relevant to that event. Freezed generates equality, copyWith, and pattern-matching helpers which reduce boilerplate and improve compiler-safety.

Example event union for a shopping flow:

@freezed
class CartEvent with _$CartEvent {
  const factory CartEvent.itemAdded(String productId, int quantity) = ItemAdded;
  const factory CartEvent.itemRemoved(String productId) = ItemRemoved;
  const factory CartEvent.checkoutRequested() = CheckoutRequested;
}

This single declaration gives you a closed set of events. The Dart analyzer will warn when you forget to handle a variant. That safety is valuable in mobile development where UI state transitions must be predictable.

Handling Events With Pattern Matching

Once events are defined, handle them using when or map. when returns values and requires all cases; maybeWhen allows partial handling. Prefer when in reducers or command handlers to force exhaustiveness.

Reducer-style handler example:

CartState onEvent(CartState state, CartEvent event) {
  return event.when(
    itemAdded: (id, qty) => state.addItem(id, qty),
    itemRemoved: (id) => state.removeItem(id),
    checkoutRequested: () => state.markCheckout(),
  );
}

This approach keeps business logic centralized, making unit testing straightforward. Use map when you need access to the concrete subclass instance (rare for simple events).

Emitting Events In Flutter Widgets

In Flutter mobile development, events are commonly emitted from UI interactions and handled by a state manager (Bloc, Riverpod, Provider, or your own). Keep UI thin: it should construct events and forward them to a controller. This separation improves reuse and testability.

Example using a simple ChangeNotifier or controller:

  • Define an EventBus/Controller that exposes a method handleEvent(CartEvent).

  • From a button press, create the corresponding Freezed union and pass it.

Because events are immutable and typed, it's easy to trace what happened during a UI session. Freezed unions also play nicely with Streams: you can publish events into a stream and derive state using scan-like operators.

Testing And Reasoning About Events

Freezed unions simplify assertions in tests. You can assert that an observer received an ItemAdded event with the expected productId and quantity. Because equality is generated, deep comparisons are concise.

Example test assertions:

  • Use when to apply the same reducer logic and compare resulting states.

  • Use a fake EventSink to capture emitted events and assert the sequence.

Tips for robust tests:

  • Keep event creation simple: use builder helpers or factory functions to make tests readable.

  • Test handlers exhaustively: verify every union variant maps to an expected state change.

  • Use freezed generated toString in failure messages for quick diagnosis.

Freezed also interacts well with code generation for serialization if you need to persist events or send them over the wire, but for pure local domain events you usually avoid serialization to keep the model focused.

Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.

Conclusion

Modeling domain events as Freezed unions in Flutter brings clarity and safety to event-driven flows in mobile development. You get exhaustive pattern matching, immutable, comparable types, and minimal boilerplate — all of which make UI code thinner and business logic easier to test. Start by modeling your important domain occurrences as unions, handle them with when/map in centralized handlers, and emit events from UI controllers. This pattern scales from small apps to complex state machines while keeping intent explicit and behavior predictable.

Introduction

Domain events are a natural fit for designing predictable, testable business logic in Flutter mobile development. They represent things that happened in the domain — e.g., UserLoggedIn, ItemAddedToCart — and decouple intent from effect. Freezed unions (sealed classes) give you a compact, type-safe way to model those events and handle them exhaustively. This tutorial shows how to model domain events with Freezed, match on them with pattern matching, emit them from UI code, and write tests that reason about event flows.

Modeling Domain Events With Freezed

Use Freezed to declare a union of events. Each union variant encodes the data relevant to that event. Freezed generates equality, copyWith, and pattern-matching helpers which reduce boilerplate and improve compiler-safety.

Example event union for a shopping flow:

@freezed
class CartEvent with _$CartEvent {
  const factory CartEvent.itemAdded(String productId, int quantity) = ItemAdded;
  const factory CartEvent.itemRemoved(String productId) = ItemRemoved;
  const factory CartEvent.checkoutRequested() = CheckoutRequested;
}

This single declaration gives you a closed set of events. The Dart analyzer will warn when you forget to handle a variant. That safety is valuable in mobile development where UI state transitions must be predictable.

Handling Events With Pattern Matching

Once events are defined, handle them using when or map. when returns values and requires all cases; maybeWhen allows partial handling. Prefer when in reducers or command handlers to force exhaustiveness.

Reducer-style handler example:

CartState onEvent(CartState state, CartEvent event) {
  return event.when(
    itemAdded: (id, qty) => state.addItem(id, qty),
    itemRemoved: (id) => state.removeItem(id),
    checkoutRequested: () => state.markCheckout(),
  );
}

This approach keeps business logic centralized, making unit testing straightforward. Use map when you need access to the concrete subclass instance (rare for simple events).

Emitting Events In Flutter Widgets

In Flutter mobile development, events are commonly emitted from UI interactions and handled by a state manager (Bloc, Riverpod, Provider, or your own). Keep UI thin: it should construct events and forward them to a controller. This separation improves reuse and testability.

Example using a simple ChangeNotifier or controller:

  • Define an EventBus/Controller that exposes a method handleEvent(CartEvent).

  • From a button press, create the corresponding Freezed union and pass it.

Because events are immutable and typed, it's easy to trace what happened during a UI session. Freezed unions also play nicely with Streams: you can publish events into a stream and derive state using scan-like operators.

Testing And Reasoning About Events

Freezed unions simplify assertions in tests. You can assert that an observer received an ItemAdded event with the expected productId and quantity. Because equality is generated, deep comparisons are concise.

Example test assertions:

  • Use when to apply the same reducer logic and compare resulting states.

  • Use a fake EventSink to capture emitted events and assert the sequence.

Tips for robust tests:

  • Keep event creation simple: use builder helpers or factory functions to make tests readable.

  • Test handlers exhaustively: verify every union variant maps to an expected state change.

  • Use freezed generated toString in failure messages for quick diagnosis.

Freezed also interacts well with code generation for serialization if you need to persist events or send them over the wire, but for pure local domain events you usually avoid serialization to keep the model focused.

Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.

Conclusion

Modeling domain events as Freezed unions in Flutter brings clarity and safety to event-driven flows in mobile development. You get exhaustive pattern matching, immutable, comparable types, and minimal boilerplate — all of which make UI code thinner and business logic easier to test. Start by modeling your important domain occurrences as unions, handle them with when/map in centralized handlers, and emit events from UI controllers. This pattern scales from small apps to complex state machines while keeping intent explicit and behavior predictable.

Build Flutter Apps Faster with Vibe Studio

Build Flutter Apps Faster with Vibe Studio

Build Flutter Apps Faster with Vibe Studio

Build Flutter Apps Faster with Vibe Studio

Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.

Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.

Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.

Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.

Other Insights

Other Insights

Other Insights

Other Insights

Join a growing community of builders today

Join a growing community of builders today

Join a growing community of builders today

Join a growing community of builders today

Join a growing community of builders today

28-07 Jackson Ave

Walturn

New York NY 11101 United States

© Steve • All Rights Reserved 2025

28-07 Jackson Ave

Walturn

New York NY 11101 United States

© Steve • All Rights Reserved 2025

28-07 Jackson Ave

Walturn

New York NY 11101 United States

© Steve • All Rights Reserved 2025

28-07 Jackson Ave

Walturn

New York NY 11101 United States

© Steve • All Rights Reserved 2025

28-07 Jackson Ave

Walturn

New York NY 11101 United States

© Steve • All Rights Reserved 2025

28-07 Jackson Ave

Walturn

New York NY 11101 United States

© Steve • All Rights Reserved 2025