DEV Community

vmodal_ai
vmodal_ai

Posted on

Advanced BLoC Architecture for Production Flutter Apps

Advanced BLoC Architecture for Production Flutter Apps

As Flutter applications grow, state management becomes difficult when UI code, API calls, business rules, and persistence are mixed together.

BLoC provides a structured way to separate these responsibilities.

A production architecture can look like:

UI
 |
BLoC / Cubit
 |
Use Case
 |
Repository
 |
Data Source
 |
REST API / Database / Local Storage
Enter fullscreen mode Exit fullscreen mode

Feature-first project structure

A scalable project can use:

lib/
  core/
    error/
    network/
    storage/

  features/
    authentication/
      data/
      domain/
      presentation/

    profile/
      data/
      domain/
      presentation/

    chat/
      data/
      domain/
      presentation/
Enter fullscreen mode Exit fullscreen mode

This keeps related code together.

Events and states

A login feature might define:

sealed class LoginEvent {}

class LoginSubmitted extends LoginEvent {
  final String email;
  final String password;

  LoginSubmitted({
    required this.email,
    required this.password,
  });
}
Enter fullscreen mode Exit fullscreen mode

States:

sealed class LoginState {}

class LoginInitial extends LoginState {}

class LoginLoading extends LoginState {}

class LoginSuccess extends LoginState {}

class LoginFailure extends LoginState {
  final String message;

  LoginFailure(this.message);
}
Enter fullscreen mode Exit fullscreen mode

BLoC

class LoginBloc extends Bloc<LoginEvent, LoginState> {
  final LoginUseCase login;

  LoginBloc(this.login) : super(LoginInitial()) {
    on<LoginSubmitted>(_onLoginSubmitted);
  }

  Future<void> _onLoginSubmitted(
    LoginSubmitted event,
    Emitter<LoginState> emit,
  ) async {
    emit(LoginLoading());

    try {
      await login(
        email: event.email,
        password: event.password,
      );

      emit(LoginSuccess());
    } catch (e) {
      emit(LoginFailure(e.toString()));
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

The widget does not need to know how authentication works.

Repository pattern

abstract class AuthRepository {
  Future<void> login({
    required String email,
    required String password,
  });
}
Enter fullscreen mode Exit fullscreen mode

Implementation:

class AuthRepositoryImpl implements AuthRepository {
  final AuthRemoteDataSource remote;

  AuthRepositoryImpl(this.remote);

  @override
  Future<void> login({
    required String email,
    required String password,
  }) {
    return remote.login(
      email: email,
      password: password,
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

This abstraction makes testing easier.

Avoid contradictory state flags

Avoid large combinations such as:

isLoading
hasError
isLoggedIn
hasData
Enter fullscreen mode Exit fullscreen mode

because they can produce invalid combinations.

Prefer explicit states or immutable state objects.

Managing concurrency

For search or API operations, event transformers can control concurrency.

Conceptually:

User types:
a
ab
abc
abcd

Instead of sending four expensive requests,
cancel obsolete requests when appropriate.
Enter fullscreen mode Exit fullscreen mode

Choose the correct behavior for each feature:

  • sequential
  • restartable
  • droppable
  • concurrent

Dependency injection

Inject dependencies instead of creating them inside BLoCs:

BlocProvider(
  create: (_) => LoginBloc(loginUseCase),
  child: LoginPage(),
)
Enter fullscreen mode Exit fullscreen mode

This improves testability and separation of concerns.

Testing BLoCs

Test the state transition:

Event
  ↓
BLoC
  ↓
Expected states
Enter fullscreen mode Exit fullscreen mode

For example:

LoginSubmitted
  -> LoginLoading
  -> LoginSuccess
Enter fullscreen mode Exit fullscreen mode

and:

LoginSubmitted
  -> LoginLoading
  -> LoginFailure
Enter fullscreen mode Exit fullscreen mode

Production responsibility matrix

Layer Responsibility
UI Rendering and interaction
BLoC State transitions
Use Case Business rules
Repository Data abstraction
Data Source API/database communication
Model Data representation

Conclusion

Advanced BLoC architecture is not about adding more files. It is about making responsibilities explicit.

A good production architecture should make it easy to answer:

Where does this business rule belong?

If the answer is clear, the codebase becomes easier to maintain, test, and extend.

Useful Links

SDK Flutter: https://github.com/v-modal/vmodal_sdk_flutter

SDK Android: https://github.com/v-modal/vmodal_sdk_android

Discord: https://discord.gg/K72z28KUx

Top comments (0)