📝 Reviews & Feedback
We value your feedback! Help us improve by sharing your experience with the package:
📋 Submit Review via Google Form
Your feedback helps make this package better for everyone in the Flutter community. Let us know what features you love and what we can improve!
🚀 Flutter Bloc Generator
✨ NOW WITH VSCODE EXTENSION! ✨
Get the "Fbloc Event Gen" extension from Visual Studio Marketplace to supercharge your development workflow!
Too much Unwanted freezed Bloc code... ??
Generate All state, event and even bloc code from a SINGLE VARIABLE
Update State with without having to Rememeber all those events.. in a single context.Set.. Fn()
With all options to CUSTOMISE your bloc and event code with out hastle
Then This is the Package u will ever need...
A powerful code generation package that supercharges your Flutter Bloc implementation with zero boilerplate! Generate events, states, and utilities automatically.
📚 Table of Contents
📦 Installation
Add to your pubspec.yaml
:
dependencies:
fbloc_event_gen: ^3.2.6
dev_dependencies:
build_runner: ^2.4.6
Run:
flutter pub get
✨ Features
- 🎯 Two Powerful Annotations
@GenerateEvents
- Generate events from factory constructors@GenerateStates
- Generate complete state management code
- 🔄 Automatic Generation
- Type-safe events and states
- BuildContext extensions
- Immutable state updates
- 🛡️ Built-in Safety
- Null safety support
- Equatable implementation
- Type checking
🎯 Usage
@GenerateStates
Use @GenerateStates
for complete state management generation. Define your state variables in the abstract class in the following format.
part of 'example_bloc.dart';
@generateStates
abstract class _$$ExampleState {
final bool isLoading = false;
final int counter = 0;
final String? data = "You have pushed the button this many times:";
final String? dss = null;
final List<String> listNm = List.generate(10, (index) => 'item $index');
final Map<String, int> mapgenerate = Map<String, int>.fromEntries(
['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
.map((e) => MapEntry<String, int>(e, int.parse(e))),
);
final Map<String?, String?> list = {};
final List<bool> selectedDays = [];
final Map<dynamic, dynamic>? test = {};
}
@GenerateEvents
Use @GenerateEvents
when you need event-only generation. Perfect for defining bloc events through factory constructors.
import 'package:flutter_bloc_generator/annotations.dart';
@generateEvents
abstract class ExampleEvent extends Equatable {
const ExampleEvent();
const factory ExampleEvent.userLoggedIn({required String userId,required String token,
bool? rememberMe,}) = UserLoggedIn;
const factory ExampleEvent.updateProfile({required String user,}) = UpdateProfile;
const factory ExampleEvent.logOut() = LogOut;
}
Implementation Example
Widget Usage
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Traditional way
BlocProvider.of<ExampleBloc>(context).add(
UpdateCounterEvent(counter: 42)
);
// Using generated extension (Cleaner!)
context.setExampleBlocState(
counter: 42
);
}
}
Bloc Class
class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {
ExampleBloc() : super(ExampleState.initial()) {
ExampleState.registerEvents(this);
on<UserLoggedIn>(_onUserLoggedIn);
on<UpdateProfile>(_onUpdateProfile);
on<LogOut>(_onLogOut);
}
void _onUserLoggedIn(UserLoggedIn event, Emitter<ExampleState> emit) {
emit(state.copyWith(isLoading: true));
}
void _onUpdateProfile(UpdateProfile event, Emitter<ExampleState> emit) {
emit(state.copyWith(isLoading: true));
}
void _onLogOut(LogOut event, Emitter<ExampleState> emit) {
emit(state.copyWith(isLoading: true));
}
}
🔍 Generated Code Examples
@GenerateEvents Generated Code
// **************************************************************************
// EventGenerator
// **************************************************************************
class UserLoggedIn extends ExampleEvent {
final String userId;
final String token;
final bool? rememberMe;
const UserLoggedIn(
{required this.userId, required this.token, this.rememberMe});
@override
List<Object?> get props => [userId, token, rememberMe];
}
class UpdateProfile extends ExampleEvent {
final String user;
const UpdateProfile({required this.user});
@override
List<Object?> get props => [user];
}
class LogOut extends ExampleEvent {
const LogOut();
@override
List<Object?> get props => [];
}
@GenerateStates Generated Code
// **************************************************************************
// StateGenerator
// **************************************************************************
// Events Generated for corresponding states in State Class
class UpdateIsLoadingEvent extends ExampleEvent {
final bool isLoading;
const UpdateIsLoadingEvent({required this.isLoading});
@override
List<Object?> get props => [isLoading];
}
class UpdateCounterEvent extends ExampleEvent {
final int counter;
const UpdateCounterEvent({required this.counter});
@override
List<Object?> get props => [counter];
}
/// A state class that represents the complete state of the ExampleBloc.
/// This class is immutable and extends Equatable for value comparison.
class ExampleState extends Equatable {
/// Indicates whether the bloc is currently processing an operation
final bool isLoading;
/// Counter value for tracking state changes
final int counter;
/// Optional data string that can be displayed to the user
final String? data;
/// Optional secondary data string
final String? dss;
/// List of string items
final List<String> listNm;
/// Map containing string keys and integer values
final Map<String, int> mapgenerate;
/// Map with nullable string keys and values
final Map<String?, String?> list;
/// List of boolean values representing selected days
final List<bool> selectedDays;
/// Optional map for storing dynamic key-value pairs
final Map<dynamic, dynamic>? test;
/// Creates a new instance of ExampleState with the given parameters.
/// All parameters except [data], [dss], and [test] are required.
const ExampleState({
required this.isLoading,
required this.counter,
this.data,
this.dss,
required this.listNm,
required this.mapgenerate,
required this.list,
required this.selectedDays,
this.test});
/// Creates the initial state of the ExampleBloc.
/// This method sets up default values for all state properties.
static ExampleState initial() {
return ExampleState(
isLoading: false,
counter: 0,
data: "You have pushed the button this many times:",
dss: null,
listNm: List.generate(10, (index) => 'item $index'),
mapgenerate: Map<String, int>.fromEntries(
['1', '2', '3', '4', '5', '6', '7', '8', '9', '10']
.map((e) => MapEntry<String, int>(e, int.parse(e))),
),
list: {},
selectedDays: [],
test: {});
}
/// Creates a copy of this state with the given parameters replaced.
/// If a parameter is not provided, the value from the current state is used.
ExampleState copyWith({
bool? isLoading,
int? counter,
String? data,
String? dss,
List<String>? listNm,
Map<String, int>? mapgenerate,
Map<String?, String?>? list,
List<bool>? selectedDays,
Map<dynamic, dynamic>? test}) {
return ExampleState(
isLoading: isLoading ?? this.isLoading,
counter: counter ?? this.counter,
data: data ?? this.data,
dss: dss ?? this.dss,
listNm: listNm ?? this.listNm,
mapgenerate: mapgenerate ?? this.mapgenerate,
list: list ?? this.list,
selectedDays: selectedDays ?? this.selectedDays,
test: test ?? this.test);
}
/// Creates a copy of this state with the ability to set specific fields to null.
/// The boolean parameters control whether the corresponding field should be set to null.
ExampleState copyWithNull({
bool? isLoading,
int? counter,
bool data = false,
bool dss = false,
List<String>? listNm,
Map<String, int>? mapgenerate,
Map<String?, String?>? list,
List<bool>? selectedDays,
bool test = false}) {
return ExampleState(
isLoading: isLoading ?? this.isLoading,
counter: counter ?? this.counter,
data: data ? null : this.data,
dss: dss ? null : this.dss,
listNm: listNm ?? this.listNm,
mapgenerate: mapgenerate ?? this.mapgenerate,
list: list ?? this.list,
selectedDays: selectedDays ?? this.selectedDays,
test: test ? null : this.test);
}
/// Registers all event handlers for the ExampleBloc.
/// This method sets up the event-to-state mapping for all possible state updates.
static void registerEvents(ExampleBloc bloc) {
bloc.on<UpdateIsLoadingEvent>((event, emit) {
emit(bloc.state.copyWith(isLoading: event.isLoading));
});
bloc.on<UpdatecounterEvent>((event, emit) {
emit(bloc.state.copyWith(counter: event.counter));
});
bloc.on<UpdateDataEvent>((event, emit) {
if (event.data == null) {
emit(bloc.state.copyWithNull(data: true));
} else {
emit(bloc.state.copyWith(data: event.data));
}
});
bloc.on<UpdateDssEvent>((event, emit) {
if (event.dss == null) {
emit(bloc.state.copyWithNull(dss: true));
} else {
emit(bloc.state.copyWith(dss: event.dss));
}
});
bloc.on<UpdateListNmEvent>((event, emit) {
emit(bloc.state.copyWith(listNm: event.listNm));
});
bloc.on<UpdateMapgenerateEvent>((event, emit) {
emit(bloc.state.copyWith(mapgenerate: event.mapgenerate));
});
bloc.on<UpdateListEvent>((event, emit) {
emit(bloc.state.copyWith(list: event.list));
});
bloc.on<UpdateSelectedDaysEvent>((event, emit) {
emit(bloc.state.copyWith(selectedDays: event.selectedDays));
});
bloc.on<UpdateTestEvent>((event, emit) {
if (event.test == null) {
emit(bloc.state.copyWithNull(test: true));
} else {
emit(bloc.state.copyWith(test: event.test));
}
});
}
/// Returns a list of all properties used for equality comparison.
@override
List<Object?> get props => [
isLoading,
counter,
data,
dss,
listNm,
mapgenerate,
list,
selectedDays,
test
];
}
/// Extension on BuildContext that provides convenient methods for updating the ExampleBloc state.
/// This extension simplifies state updates by providing a single method to update multiple state properties.
extension ExampleBlocContextExtension on BuildContext {
/// Updates the ExampleBloc state with the provided values.
/// Only the specified parameters will be updated; others will remain unchanged.
/// Uses UnspecifiedDataType.instance as a sentinel value to determine which parameters to update.
void setExampleBlocState({
Object? isLoading = UnspecifiedDataType.instance,
Object? counter = UnspecifiedDataType.instance,
Object? data = UnspecifiedDataType.instance,
Object? dss = UnspecifiedDataType.instance,
Object? listNm = UnspecifiedDataType.instance,
Object? mapgenerate = UnspecifiedDataType.instance,
Object? list = UnspecifiedDataType.instance,
Object? selectedDays = UnspecifiedDataType.instance,
Object? test = UnspecifiedDataType.instance,
}) {
final myBloc = read<ExampleBloc>(); // Read the MyBloc instance
if (isLoading != UnspecifiedDataType.instance) {
myBloc.add(UpdateIsLoadingEvent(isLoading: isLoading as bool?));
}
if (counter != UnspecifiedDataType.instance) {
myBloc.add(UpdateCounterEvent(counter: counter as int));
}
if (data != UnspecifiedDataType.instance) {
myBloc.add(UpdateDataEvent(data: data as String?));
}
if (dss != UnspecifiedDataType.instance) {
myBloc.add(UpdateDssEvent(dss: dss as String?));
}
if (iterable != UnspecifiedDataType.instance) {
myBloc.add(UpdateIterableEvent(iterable: iterable.cast<String>()));
}
if (listNm != UnspecifiedDataType.instance) {
myBloc.add(UpdateListNmEvent(listNm: listNm.cast<String>()));
}
if (mapgenerate != UnspecifiedDataType.instance) {
myBloc.add(
UpdateMapgenerateEvent(mapgenerate: mapgenerate.cast<String, int>()));
}
if (list != UnspecifiedDataType.instance) {
myBloc.add(UpdateListEvent(list: list.cast<String?, String?>()));
}
if (selectedDays != UnspecifiedDataType.instance) {
myBloc.add(
UpdateSelectedDaysEvent(selectedDays: selectedDays.cast<bool>()));
}
if (test != UnspecifiedDataType.instance) {
myBloc.add(UpdateTestEvent(test: test.cast<dynamic, dynamic>()));
}
}
}
🎯 Best Practices
-
State Variables
- Keep state classes focused and minimal
- Use meaningful variable names
- Consider nullability carefully
-
Event Generation
- Use descriptive factory constructor names
- Group related events together
- Document complex event parameters
-
State Updates
- Prefer extension methods for simple updates
- Use traditional events for complex logic
- Keep state immutable
🔄 Migration Guide
2.0.0 to 3.0.0
- Update annotation imports
- Rename existing state classes to include
_$$
prefix - Add initial values to state variables
- Run code generation
🤝 Contributing
We welcome contributions! Please see our contributing guide for details.
📝 License
This project is licensed under the MIT License - see the LICENSE file for details.