Skip to content

🔄 State Management - Cubit Standards

Table of Contents

Core Standards

State Definition Rules

  1. Always use sealed classes for state definitions
  2. Use descriptive state names with feature prefix (e.g., UserLoading, UserSuccess)
  3. Keep states immutable with final fields
  4. Group related states using abstract base classes when needed
dart
sealed class UserState {}
final class UserInitial extends UserState {}
final class UserLoading extends UserState {}
final class UserSuccess extends UserState {
  final User user;
  UserSuccess(this.user);
}

BaseCubit Requirements

  1. Always extend BaseCubit<S, R> for repository integration
  2. Implement init() method for automatic initialization
  3. Use repo property for repository access
  4. Check isClosed before emitting states
dart
class UserCubit extends BaseCubit<UserState, UserRepo> {
  UserCubit() : super(UserInitial());

  @override
  init() async {
    await loadUser();
  }
}

Implementation Guidelines

Repository Integration Rules

  1. Always use fold pattern for handling repository results
  2. Emit loading state first before async operations
  3. Check isClosed before emitting after async operations
  4. Handle both success and failure cases explicitly
dart
Future<void> loadData() async {
  emit(DataLoading());
  final result = await repo.getData();
  if (isClosed) return;
  
  result.fold(
    (failure) => emit(DataError(failure.message)),
    (data) => emit(DataSuccess(data)),
  );
}
  1. Use computed properties for derived data
  2. Chain operations when one depends on another
  3. Group related states with abstract base classes

Form Management Rules

  1. Use TextEditingController for form fields
  2. Use GlobalKey<FormState> for validation
  3. Validate before submission
  4. Emit specific states for different form actions
dart
class SignInCubit extends BaseCubit<SignInState, AuthRepo> {
  final emailController = TextEditingController();
  final formKey = GlobalKey<FormState>();

  Future<void> signIn() async {
    if (!formKey.currentState!.validate()) return;
    emit(SignInLoading());
    // ... rest of implementation
  }
}

CRUD Operations Rules

  1. Use consistent navigation with AppRouter.pop() after success
  2. Refresh data after successful operations
  3. Handle errors silently or emit specific error states
  4. Use named parameters for better readability
dart
Future<void> save({required DateTime date, required String description}) async {
  final result = await repo.createExpenses(/* params */);
  result.fold(
    (f) {}, // Handle error silently
    (s) async {
      AppRouter.pop(); // Navigate back
      await getExpenses(); // Refresh data
    },
  );
}

Performance Rules

Memory Management

  1. Use instance variables for non-UI state to avoid unnecessary rebuilds
  2. Implement proper cleanup in close() method

State Optimization

  1. Use computed properties for derived data
  2. Emit specific states instead of generic ones
  3. Chain operations efficiently
  4. Avoid unnecessary state emissions