Appearance
🔄 State Management - Cubit Standards
Table of Contents
Core Standards
State Definition Rules
- Always use
sealedclasses for state definitions - Use descriptive state names with feature prefix (e.g.,
UserLoading,UserSuccess) - Keep states immutable with final fields
- 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
- Always extend
BaseCubit<S, R>for repository integration - Implement
init()method for automatic initialization - Use
repoproperty for repository access - Check
isClosedbefore emitting states
dart
class UserCubit extends BaseCubit<UserState, UserRepo> {
UserCubit() : super(UserInitial());
@override
init() async {
await loadUser();
}
}Implementation Guidelines
Repository Integration Rules
- Always use
foldpattern for handling repository results - Emit loading state first before async operations
- Check
isClosedbefore emitting after async operations - 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)),
);
}- Use computed properties for derived data
- Chain operations when one depends on another
- Group related states with abstract base classes
Form Management Rules
- Use TextEditingController for form fields
- Use
GlobalKey<FormState>for validation - Validate before submission
- 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
- Use consistent navigation with
AppRouter.pop()after success - Refresh data after successful operations
- Handle errors silently or emit specific error states
- 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
- Use instance variables for non-UI state to avoid unnecessary rebuilds
- Implement proper cleanup in
close()method
State Optimization
- Use computed properties for derived data
- Emit specific states instead of generic ones
- Chain operations efficiently
- Avoid unnecessary state emissions