flutter_bloc_kit 0.0.3
flutter_bloc_kit: ^0.0.3 copied to clipboard
A flutter_bloc-based architecture (Result, UseCase, Bloc/Event/State separation) and recommended folder structure, extending flutter_basic_kit_library.
flutter_bloc_kit #
Read this in other languages: 한국어
A flutter_basic_kit_library extension that provides a flutter_bloc-based architecture separating data / domain / presentation layers. It mirrors flutter_provider_kit / flutter_riverpod_kit's folder structure, implemented with flutter_bloc.
What's included #
Result<T>— aSuccess/Failuresealed class, handled with Dart 3 pattern matching (switch)UseCase<Output, Params>— base class the use cases underdomain/use_case/extend- Re-exports the whole
flutter_blocpackage (no need to add it separately)
Bloc<Event, State> already separates event and state on its own, so this package doesn't add an extra base class like MviViewModel in the provider/riverpod kits.
Install #
dependencies:
flutter_bloc_kit: ^0.0.1
Scaffold the structure #
After installing, one command scaffolds the recommended folders plus a minimal, ready-to-run home feature into your project:
dart run flutter_bloc_kit:init # creates presentation/home/
dart run flutter_bloc_kit:init login # feature name as an argument (presentation/login/)
What it generates:
data/data_source,data/repository,domain/model,domain/repository,domain/use_case— empty layer folders (.gitkeep)presentation/<feature>/— minimalstate/event/bloc/screenstubsdi/injector.dart— a manualbuild<Feature>Bloc()core/routing/route_paths.dart+core/routing/router.dart— ago_routerconfig routingRoutePaths.<feature>to<Feature>Screen
It also runs flutter pub add go_router for you. flutter_bloc is already bundled (re-exported) and provides BlocProvider, so no provider dependency is needed.
The folder structure is identical to
flutter_provider_kit/flutter_riverpod_kit; only the contents ofpresentation/(bloc vs view_model) differ by state-management choice.
pub gethas no auto-run hook (unlike npm'spostinstall), so this is a one-off command you run once after installing. Existing files are never overwritten.
Recommended folder structure #
example/ implements a "photo search" feature end to end (with mock data) using this structure.
lib/
data/
data_source/
photo_api.dart # external API calls (mocked here)
repository/
photo_repository_impl.dart # implements the domain's abstract interface
domain/
model/
photo.dart # freezed model
repository/
photo_repository.dart # abstract interface
use_case/
get_photos_use_case.dart # extends UseCase<Output, Params>
presentation/
home/
components/
photo_widget.dart # widget local to home
home_screen.dart # BlocProvider + BlocConsumer
home_event.dart # events sent to the bloc (SearchRequested, ...)
home_state.dart # freezed state class (a separate file from the bloc)
home_bloc.dart # Bloc<HomeEvent, HomeState>
di/
injector.dart # manual wiring of repository/use_case/bloc
main.dart
test/
data/
photo_api_test.dart
ui/
home_bloc_test.dart
Layer rules #
- domain is pure business logic with no dependency on Flutter or bloc.
repository/holds only abstract interfaces; the real implementation lives indata/repository/. - data implements
domain's interfaces and isolates real API/DB calls insidedata_source/. - presentation is split into per-feature folders (
home/,detail/, ...), each bundling its ownevent/state/bloc/screen/components.home_state.dartstays a separate file fromhome_bloc.dart, and freezed generates==/copyWithfor it. - di is currently manual, function-based wiring (
injector.dart). The structure is kept swappable forget_it/injectable(already bundled viaflutter_basic_kit_library) once that's needed — not decided yet.
Usage #
class HomeBloc extends Bloc<HomeEvent, HomeState> {
HomeBloc(this._getPhotosUseCase) : super(const HomeState()) {
on<SearchRequested>(_onSearchRequested);
}
final GetPhotosUseCase _getPhotosUseCase;
Future<void> _onSearchRequested(SearchRequested event, Emitter<HomeState> emit) async {
emit(state.copyWith(isLoading: true, errorMessage: null));
switch (await _getPhotosUseCase(event.query)) {
case Success(:final data):
emit(state.copyWith(photos: data, isLoading: false));
case Failure(:final error):
emit(state.copyWith(isLoading: false, errorMessage: error.toString()));
}
}
}
// screen
BlocConsumer<HomeBloc, HomeState>(
listener: (context, state) { /* one-off effects, e.g. a snackbar */ },
builder: (context, state) { /* UI */ },
)
See example/ for the full working app.
Under consideration #
- A NestJS-CLI-style code generator for
repository/use_caseCRUD boilerplate (not started, to be discussed separately) - Whether to move DI to
get_it/injectable
Related packages #
| Package | Status |
|---|---|
| flutter_basic_kit_library | Published |
| flutter_provider_kit | Published |
| flutter_riverpod_kit | Published |
| flutter_bloc_kit | this package |
Versioning #
This package follows Semantic Versioning. See CHANGELOG.md for the full history.