bloc_state_gen
https://github.com/user-attachments/assets/97d02d6d-98af-415f-8538-ef432f4c2ed1
A lightweight Dart package that generates convenient extensions for BLoC state classes, offering pattern matching and logging capabilities through simple annotations.
Features
🎯 Pattern Matching
- match: Complete state pattern matching requiring all cases to be handled
- matchSome: Partial pattern matching with default case handling
- Compile-time type safety
📝 Logging
- Built-in state logging functionality
- Debug-friendly state information
Installation
-
Add
bloc_state_gento yourpubspec.yaml:dependencies: bloc_state_gen: ^latest_version dev_dependencies: build_runner: ^latest_version -
Update your main cubit class to include the generated file:
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:bloc_state_gen/bloc_state_gen.dart'; part 'search_state.dart'; part 'search_state.g.dart'; class SearchCubit extends Cubit<SearchState> { SearchCubit() : super(const SearchInitial()); } -
Run the code generator:
flutter pub run build_runner build -
Ignore generated
.g.dartfiles in version control by adding the following to your.gitignore:# Ignore generated files *.g.dart
Usage
Basic Setup
-
Annotate your state class with
@BlocStateGen:part of 'search_cubit.dart'; @BlocStateGen() abstract class SearchState { const SearchState(); } class SearchInitial extends SearchState { const SearchInitial(); } class Searching extends SearchState { final String query; const Searching({ required this.query, }); } class SearchResults extends SearchState { final String query; final List<String> results; const SearchResults({ required this.query, required this.results, }); } class NoResults extends SearchState { final String query; const NoResults({ required this.query, }); } class SearchError extends SearchState { final String message; final String? query; const SearchError({ required this.message, this.query, }); } -
Run the code generator:
flutter pub run build_runner build
Feature Usage
1. match - Complete Pattern Matching
Requires handling all possible states:
Widget buildStateWidget(SearchState state) {
return state.match(
searchInitial: () => const StartSearch(),
searching: (query) => const CircularProgressIndicator(),
searchResults: (query, results) => DisplayList(items: results),
noResults: (query) => NoResultsWidget(query: query),
searchError: (message, query) => ErrorMessage(message: message),
);
}
2. matchSome - Partial Pattern Matching
Handle specific states with a default case:
String getDisplayText(SearchState state) {
return state.matchSome(
searchResults: (query, results) => 'Found ${results.length} results for: $query',
searchError: (message, query) => 'Error${query != null ? " for $query" : ""}: $message',
orElse: () => 'Idle...',
);
}
3. log - State Logging
Print state information for debugging:
void debugState(CounterState state) {
print(state.log()); // Outputs formatted state information
}
Customizing Generation
You can selectively enable/disable features using the @BlocStateGen annotation:
@BlocStateGen(
match: true, // Enable complete pattern matching
matchSome: true, // Enable partial pattern matching
log: true, // Enable logging functionality
)
abstract class SearchState {
const SearchState();
}
Best Practices
-
Complete Pattern Matching
- Use
matchwhen you need to handle all possible states - Ensures no state is accidentally forgotten
- Provides compile-time safety
- Use
-
Partial Pattern Matching
- Use
matchSomewhen you only need to handle specific states - Always provide a meaningful
orElsecase - Useful for selective state handling
- Use
-
Logging
- Enable logging during development for better debugging
- Use in conjunction with Flutter's debug mode:
if (kDebugMode) {
print(state.log());
}
Example Project
For a complete working example, check out our example project demonstrating:
- State class definition
- Extension generation
- Usage of all three core features
- Integration with Flutter UI
License
This project is licensed under the MIT License - see the LICENSE file for details.