CodeBlanks Logo Icon CodeBlanks

State classes

Model UI state with state classes.

What are state classes?

State classes are immutable objects that model the current state of the interface. Each distinct UI state is expressed as a separate state class: loading, success, error, and so on. This allows safe state checks like if (state is LoginSuccess).

abstract class LoginState {}
class LoginLoading extends LoginState {}
class LoginSuccess extends LoginState {
  final String user;
  LoginSuccess(this.user);
}

Abstract state and subclasses

Deriving states from an abstract class groups common behavior and makes state checks safer. When you define every possible state as a separate class, you handle each case individually with switch or if (state is ...). This makes it harder to miss a case and keeps the code readable.

abstract class LoginState {}
class LoginInitial extends LoginState {}
class LoginLoading extends LoginState {}
class LoginSuccess extends LoginState {}

Practice Write a LoginBloc and state classes

Define LoginEvent and LoginSubmittedEvent. Write the LoginState abstract class with LoginLoading and LoginSuccess (holds a username) subclasses. LoginBloc should start with LoginLoading and its LoginSubmittedEvent handler should emit LoginSuccess('Ayşe'). In main, trigger the event, wait, and print the username on success.

Starter code

main.dart
import 'package:bloc/bloc.dart';

// LoginEvent ve LoginSubmittedEvent sınıflarını tanımla

// LoginState soyut sınıfını ve alt sınıflarını tanımla
// LoginLoading ve LoginSuccess (kullanıcı adı tutar)

class LoginBloc extends Bloc<LoginEvent, LoginState> {
  LoginBloc() : super(LoginLoading()) {
    // event işleyici ekle
  }
}

void main() async {
  // oluştur, event ekle, bekle, durumu kontrol et
}

Sample solution

main.dart
import 'package:bloc/bloc.dart';

abstract class LoginEvent {}
class LoginSubmittedEvent extends LoginEvent {}

abstract class LoginState {}
class LoginLoading extends LoginState {}
class LoginSuccess extends LoginState {
  final String user;
  LoginSuccess(this.user);
}

class LoginBloc extends Bloc<LoginEvent, LoginState> {
  LoginBloc() : super(LoginLoading()) {
    on<LoginSubmittedEvent>((event, emit) =>
      emit(LoginSuccess('Ayşe')));
  }
}

void main() async {
  final bloc = LoginBloc();
  bloc.add(LoginSubmittedEvent());
  await Future.delayed(Duration(milliseconds: 10));
  final state = bloc.state;
  if (state is LoginSuccess) {
    print(state.user);
  }
}

Expected output

Ayşe

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 How should state classes be designed?

  • Immutable, with a separate class per state Correct answer
  • No state class, use a Map instead
  • Mutable, with flags inside a single class
  • A separate state class for each widget

Explanation: States should be immutable and each possible state should be modeled with its own class; a single class with flags is error-prone.

2 Define a state class

Question
class LoginSuccess extends ____ {
  final String user;
  LoginSuccess(this.user);
}
Answer
class LoginSuccess extends LoginState {
  final String user;
  LoginSuccess(this.user);
}

Explanation: LoginSuccess is a state, so it derives from the base state class LoginState; not from Bloc or Event.

3 State parts

Term Meaning
abstract class LoginState Base state class
class LoginLoading extends Loading state
final String user State data
if (state is LoginSuccess) State check

All lessons in Advanced