blocz 1.5.0
blocz: ^1.5.0 copied to clipboard
Flutter bloc helpers
blocz #
A command-line interface (CLI) tool to speed up Flutter app development by scaffolding BLoC pattern components.
About #
blocz helps you quickly generate BLoC, Event, and State files in a structured directory, saving you time and keeping your codebase consistent. The tool also supports adding events to an existing BLoC.
Features #
- Generate BLoC, Event, and State with a single command.
- Automatically create a domain-based directory structure.
- The generated code is compatible with popular packages like
flutter_bloc,freezed, andinjectable. - Supports quickly adding new events to a BLoC.
- Automatically import events and handlers from an API service file or Protobuf (
.proto) file. - Repository Scaffolding: Automatically generate repository interfaces and implementations synced with your API/Proto methods.
- Robust & Surgical Updates: Uses Dart AST (Abstract Syntax Tree) to accurately identify and update method calls, preserving your manual changes.
How it Works #
1. BLoC Scaffolding (make) #
The make command creates the initial BLoC structure.
2. Adding Events (add:event) #
The add:event command adds or updates events in an existing BLoC.
3. API to BLoC Generation (--apiPath) #
When --apiPath is provided, blocz parses the Dart API file to scaffold comprehensive Event and State classes based on the API's public methods.
Note
blocz uses the Dart analyzer package to parse your code into an Abstract Syntax Tree (AST). This allows it to perform "surgical" updates—replacing only the necessary bits (like method arguments) while leaving the rest of your custom logic untouched.
Prerequisites #
Run these commands in your Flutter project directory to add the required dependencies:
flutter pub add flutter_bloc freezed_annotation injectable get_it
flutter pub add --dev build_runner freezed injectable_generator
Installation #
Activate blocz as a global tool to use it from anywhere:
dart pub global activate blocz
Usage #
1. Create BLoC, Event, and State #
Use the make command to generate the necessary components.
blocz make --domain <domain_name> --name <bloc_name> [--apiPath <path_to_api_file>] [--writeDir <custom_path>]
--domain(or-d): The domain or feature of the BLoC (e.g.,pet,product).--name(or-n)(optional): The name of the BLoC, or a sub-domain/sub-feature name (e.g.,authentication,profile).--apiPath(or-a)(optional): Optional path to an API service file. If provided,bloczwill automatically generate and implement events for all public methods in that file.--writeDir(or-w)(optional): Custom directory to generate files. Defaults tolib/features/<domain>/presentation/bloc.
writeDir Template Support
You can use template variables in the --writeDir path:
{{DOMAIN}}or{{domain}}: Replaced by the domain name in snake_case.{{Domain}}: Replaced by the domain name in PascalCase.
Example:
blocz make --domain pet --writeDir "lib/App/screens/{{DOMAIN}}_page/bloc"
Examples:
Basic BLoC creation:
blocz make --domain pet
Generated files tree
lib/features/pet/presentation/bloc/
├── pet_bloc.dart
├── pet_event.dart
└── pet_state.dart
This command creates the BLoC structure. You will then need to run build_runner.
BLoC creation with automatic event implementation from an API file:
Example with OpenAPI generator:
export MY_PET_API_PACKAGE_NAME="my_pet_api"
export MY_PET_API_DIR="./apis/$MY_PET_API_PACKAGE_NAME"
rm -fr $MY_PET_API_DIR || true # remove old
mkdir -p $MY_PET_API_DIR # create if not exists
npx @openapitools/openapi-generator-cli generate
-i https://petstore.swagger.io/v2/swagger.json
-g dart
--additional-properties=pubName=$MY_PET_API_PACKAGE_NAME
-o $MY_PET_API_DIR
cd $MY_PET_API_DIR
&& dart pub get
&& (dart run build_runner build || true)
&& cd "$(git rev-parse --show-toplevel)"
ls -lh "./apis/$MY_PET_API_PACKAGE_NAME/lib/api/"
# in your pubspec.yaml
dependencies:
my_pet_api: # Added local API package
path: ./apis/my_pet_api
blocz make --domain pet --apiPath ./apis/my_pet_api/lib/api/pet_api.dart
This command will create the BLoC files and also automatically add events and handlers for all methods found in pet_api.dart.
// $PROJECT/lib/features/pet/presentation/bloc/pet_event.dart
part of 'pet_bloc.dart';
@freezed
sealed class PetEvent with _$PetEvent {
const factory PetEvent.loading() = _PetEventLoading;
const factory PetEvent.addPet(Pet body) = _AddPetRequested;
const factory PetEvent.deletePet(int petId, {String? apiKey}) = _DeletePetRequested;
const factory PetEvent.findPetsByStatus(List<String> status) = _FindPetsByStatusRequested;
const factory PetEvent.findPetsByTags(List<String> tags) = _FindPetsByTagsRequested;
const factory PetEvent.getPetById(int petId) = _GetPetByIdRequested;
const factory PetEvent.updatePet(Pet body) = _UpdatePetRequested;
const factory PetEvent.updatePetWithForm(int petId, {String? name, String? status}) = _UpdatePetWithFormRequested;
const factory PetEvent.uploadFile(int petId, {String? additionalMetadata, MultipartFile? file}) = _UploadFileRequested;
}
// $PROJECT/lib/features/pet/presentation/bloc/pet_state.dart
part of 'pet_bloc.dart';
@freezed
sealed class PetState with _$PetState {
const factory PetState.initial() = _InitialDone;
const factory PetState.loading() = _Loading;
const factory PetState.failure(String message) = _Failure;
const factory PetState.addPetResult() = _AddPetResult;
const factory PetState.deletePetResult() = _DeletePetResult;
const factory PetState.findPetsByStatusResult(List<Pet>? data) = _FindPetsByStatusResult;
const factory PetState.findPetsByTagsResult(List<Pet>? data) = _FindPetsByTagsResult;
const factory PetState.getPetByIdResult(Pet? data) = _GetPetByIdResult;
const factory PetState.updatePetResult() = _UpdatePetResult;
const factory PetState.updatePetWithFormResult() = _UpdatePetWithFormResult;
const factory PetState.uploadFileResult(ApiResponse? data) = _UploadFileResult;
}
Important: Since the generated files use freezed, you need to run build_runner after generation:
dart run build_runner build --delete-conflicting-outputs
2. Add an Event #
Use the add:event command to add a new event to an existing BLoC.
blocz add:event --domain <domain_name> --name <sub_domain_name> <options>
Options:
--name <sub_domain_name>(or-n): The name of the BLoC or sub-domain (e.g.,profile).--event <event_name>: Adds a single, specified event.--apiPath <path_to_api_file>: Scans the API file and generates events and handlers for all public methods.--apiPath <path_to_api_file> --method <method_name>: Generates an event and handler for only one specified method from the API file.--writeDir <custom_path>(or-w): Custom directory where the BLoC files are located.
Examples:
Add a simple event:
blocz add:event --domain pet --event bark
Add all events from an API file:
blocz add:event --domain pet --apiPath ./apis/my_pet_api/lib/api/pet_api.dart
Add a single event from a specific API method:
blocz add:event --domain pet --name profile --event UpdateAvatar --apiPath ./apis/my_pet_api/lib/api/pet_api.dart --method uploadFile
Protobuf Support
You can generate BLoC events and states directly from a .proto file:
blocz add:event -d user_proto -a proto/user.proto --update
Repository Scaffolding
Generate repository interfaces and implementations automatically:
blocz add:event -d user -a lib/api/example_api.dart --update --repository
This command will create:
lib/features/user/presentation/repository/user_repository.dart(Interface)lib/features/user/presentation/repository/user_repository_impl.dart(Implementation)
The repository will be automatically populated with methods matching your API/Proto definitions.
This command will update the corresponding BLoC files to add the new event(s).
Other Commands #
blocz also provides many helper commands for parsing Dart source code. Use blocz --help to see all available commands.