fbloc_event_gen 3.2.7 copy "fbloc_event_gen: ^3.2.7" to clipboard
fbloc_event_gen: ^3.2.7 copied to clipboard

Generates STATE, EVENT AND BLOC from a SINGLE LINE OF STATE VARIABLE and can generate custom event classes from factory constructors. Eliminates boilerplate code in Flutter BLoC development.

🚀 Flutter Bloc Generator #

A revolutionary Flutter package that eliminates verbose boilerplate in BLoC pattern development. Generate complete STATE, EVENT, and BLOC implementations from a single line of state variable declaration. With support for custom event classes through factory constructors, it dramatically reduces development time while maintaining type safety and best practices.

[Flutter Bloc Generator Banner]


Pub Version GitHub Discord

Define States with @generateStates

@generateStates
abstract class _$$BaseState {
  final bool active = false;
  final bool? isLoading = false;
  final bool isError = false;
}

Define Events with @generateEvents


@generateEvents
abstract class BaseEvent extends Equatable {
  const BaseEvent();
}

Use Generated Code - Simple Context Extension

ElevatedButton(
  onPressed: () {
    context.setBaseBlocState(active: isActive);
  },
  child: Text('Active'),
), // ElevatedButton

🚀 Why Choose fbloc_event_gen? #

VSCode Extension Available! #

Get the Fbloc Event Gen extension from Visual Studio Marketplace to supercharge your development workflow!

Key Benefits #

  • Zero Boilerplate: Generate all state, event, and even bloc code from a SINGLE VARIABLE
  • Simple State Updates: Update state without remembering event names - just use context.set{BlocName}State()
  • Fully Customizable: All options to customize your bloc and event code without hassle
  • Type-Safe: Automatic type checking and null safety support
  • Production Ready: Built for apps of any scale - from small projects to large enterprise applications

Community #

Join our growing community on Discord to get support, share feedback, and stay updated with the latest features!

📚 Table of Contents #

  1. Quick Start
  2. Installation
  3. Core Features
  4. Usage Guide
  5. What Gets Generated
  6. Best Practices
  7. Migration Guide
  8. Contributing
  9. License

⚡ Quick Start #

Get up and running in just 3 steps!

Step 1: Add Dependencies #

dependencies:
  fbloc_event_gen: ^3.2.7
  equatable: ^2.0.7
  flutter_bloc: # Any Bloc Version

dev_dependencies:
  build_runner: ^2.4.6

Step 2: Create Your Files #

counter_bloc.dart (main file)

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fbloc_event_gen/annotations.dart';
import 'package:equatable/equatable.dart';

part 'counter_event.dart';
part 'counter_state.dart';
part 'counter_bloc.g.dart';

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(CounterState.initial()) {
    // ⚠️ IMPORTANT: Register auto-generated events
    CounterState.registerEvents(this);
  }
}

counter_state.dart

part of 'counter_bloc.dart';

@generateStates
abstract class _$$CounterState {
  final int count = 0;
  final bool isLoading = false;
}

counter_event.dart

part of 'counter_bloc.dart';

@generateEvents
abstract class CounterEvent extends Equatable {
  const CounterEvent();
}

Step 3: Run Code Generation #

dart run build_runner build --delete-conflicting-outputs --use-polling-watcher

That's it! Now use context.setCounterBlocState(count: 5) anywhere in your widgets! 🎉


📦 Installation #

Add to pubspec.yaml #

dependencies:
  fbloc_event_gen: ^3.2.7
  equatable: ^2.0.7
  flutter_bloc:   # Any Bloc Version above Required for BLoC pattern

dev_dependencies:
  build_runner: ^2.4.6

Install Packages #

flutter pub get

Setup Build Runner (Optional - Watch Mode) #

For automatic code generation on file changes:

dart run build_runner watch --delete-conflicting-outputs --use-polling-watcher

Or for one-time generation:

dart run build_runner build --delete-conflicting-outputs

✨ Core Features #

Two Annotations #

@GenerateStates
Complete state management

@GenerateEvents
Custom event classes

Auto Generation #

  • Type-safe events
  • Immutable states
  • Context extensions
  • copyWith methods

Built-in Safety #

  • Null safety
  • Equatable support
  • Type checking
  • Documentation

📖 Usage Guide #

⚠️ IMPORTANT NOTE
When using @GenerateStates, you MUST call {YourState}.registerEvents(this) in your bloc constructor.
Without this, the context.set{YourBloc}State() extension won't work!

class YourBloc extends Bloc<YourEvent, YourState> {
  YourBloc() : super(YourState.initial()) {
    YourState.registerEvents(this);  // ← Don't forget this!
  }
}

Using @GenerateStates #

@GenerateStates is your all-in-one solution for complete state management. Simply define your state variables with initial values, and let the generator create everything else!

📝 Define Your State (counter_state.dart)

part of 'counter_bloc.dart';

@generateStates
abstract class _$$CounterState {
  final int counter = 0;
  final bool isLoading = false;
  final String? message = null;
  final List<String> items = const [];
  final Map<String, dynamic> data = const {};
}

📝 Create Your Bloc (counter_bloc.dart - main file)

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fbloc_event_gen/annotations.dart';
import 'package:equatable/equatable.dart';

part 'counter_event.dart';
part 'counter_state.dart';
part 'counter_bloc.g.dart';

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(CounterState.initial()) {
    // ⚠️ CRITICAL: This line is REQUIRED for context.setCounterBlocState() to work
    CounterState.registerEvents(this);
  }
}

📝 Define Your Event (counter_event.dart)

part of 'counter_bloc.dart';

@generateEvents
abstract class CounterEvent extends Equatable {
  const CounterEvent();
}

What You Get

  • CounterState class with all properties
  • CounterState.initial() factory constructor
  • copyWith() method for immutable updates
  • copyWithNull() for setting nullable fields to null
  • Auto-generated events for each property
  • context.setCounterBlocState() extension method
  • registerEvents() method for easy bloc setup
  • Full Equatable implementation

Using @GenerateEvents #

@GenerateEvents is perfect when you need custom event classes with factory constructors. Great for complex user actions!

📝 Main Bloc File (auth_bloc.dart)

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fbloc_event_gen/annotations.dart';
import 'package:equatable/equatable.dart';

part 'auth_event.dart';
part 'auth_state.dart';
part 'auth_bloc.g.dart';

class AuthBloc extends Bloc<AuthEvent, AuthState> {
  AuthBloc() : super(AuthState.initial()) {
    AuthState.registerEvents(this);
    on<LoginEvent>(_onLogin);
    on<LogoutEvent>(_onLogout);
  }
  
  void _onLogin(LoginEvent event, Emitter<AuthState> emit) {
    // Your login logic
  }
  
  void _onLogout(LogoutEvent event, Emitter<AuthState> emit) {
    // Your logout logic
  }
}

📝 Define Your Events (auth_event.dart)

part of 'auth_bloc.dart';

@generateEvents
abstract class AuthEvent extends Equatable {
  const AuthEvent();
  
  // Login event with required and optional parameters
  const factory AuthEvent.login({
    required String email,
    required String password,
    bool? rememberMe,
  }) = LoginEvent;
  
  // Simple logout event
  const factory AuthEvent.logout() = LogoutEvent;
  
  // Update profile event
  const factory AuthEvent.updateProfile({
    required String name,
    String? avatarUrl,
  }) = UpdateProfileEvent;
}

📝 Define Your State (auth_state.dart)

part of 'auth_bloc.dart';

@generateStates
abstract class _$$AuthState {
  final bool isAuthenticated = false;
  final String? userId = null;
  final String? token = null;
}

What You Get

  • Complete event classes (LoginEvent, LogoutEvent, etc.)
  • Proper constructors with required/optional parameters
  • Equatable implementation with props
  • Immutable and type-safe events

Complete Implementation #

Here's a full example showing how everything works together:

1. Main Bloc File (counter_bloc.dart)

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:fbloc_event_gen/annotations.dart';
import 'package:equatable/equatable.dart';

part 'counter_event.dart';
part 'counter_state.dart';
part 'counter_bloc.g.dart';

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(CounterState.initial()) {
    // Register auto-generated state update events
    CounterState.registerEvents(this);
    
    // Handle custom events
    on<IncrementEvent>(_onIncrement);
    on<DecrementEvent>(_onDecrement);
    on<ResetEvent>(_onReset);
  }
  
  void _onIncrement(IncrementEvent event, Emitter<CounterState> emit) {
    emit(state.copyWith(count: state.count + 1));
  }
  
  void _onDecrement(DecrementEvent event, Emitter<CounterState> emit) {
    emit(state.copyWith(count: state.count - 1));
  }
  
  void _onReset(ResetEvent event, Emitter<CounterState> emit) {
    emit(state.copyWith(count: 0));
  }
}

2. State File (counter_state.dart)

part of 'counter_bloc.dart';

@generateStates
abstract class _$$CounterState {
  final int count = 0;
  final bool isLoading = false;
  final String? errorMessage = null;
}

3. Event File (counter_event.dart)

part of 'counter_bloc.dart';

@generateEvents
abstract class CounterEvent extends Equatable {
  const CounterEvent();
  
  const factory CounterEvent.increment() = IncrementEvent;
  const factory CounterEvent.decrement() = DecrementEvent;
  const factory CounterEvent.reset() = ResetEvent;
}

💡 File Structure: These 3 files will generate counter_bloc.g.dart containing all the generated code.

4. Use in Widget

class CounterWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // Display state
        BlocBuilder<CounterBloc, CounterState>(
          builder: (context, state) {
            return Text('Count: ${state.count}');
          },
        ),
        
        // Traditional way - using custom events
        ElevatedButton(
          onPressed: () => context.read<CounterBloc>().add(
            const CounterEvent.increment()
          ),
          child: Text('Increment'),
        ),
        
        // Generated way - direct state updates
        ElevatedButton(
          onPressed: () => context.setCounterBlocState(
            count: 100,
            isLoading: true,
          ),
          child: Text('Set to 100'),
        ),
      ],
    );
  }
}

🔍 What Gets Generated #

Understanding what code is generated helps you leverage the full power of fbloc_event_gen!

For @GenerateEvents #

When you define:

@generateEvents
abstract class AuthEvent extends Equatable {
  const AuthEvent();
  
  const factory AuthEvent.login({
    required String email,
    required String password,
    bool? rememberMe,
  }) = LoginEvent;
  
  const factory AuthEvent.logout() = LogoutEvent;
}

You automatically get:

// **************************************************************************
// EventGenerator
// **************************************************************************

class LoginEvent extends AuthEvent {
  final String email;
  final String password;
  final bool? rememberMe;

  const LoginEvent({
    required this.email,
    required this.password,
    this.rememberMe,
  });

  @override
  List<Object?> get props => [email, password, rememberMe];
}

class LogoutEvent extends AuthEvent {
  const LogoutEvent();

  @override
  List<Object?> get props => [];
}

Benefits: Type-safe, immutable events with automatic Equatable implementation!


For @GenerateStates #

When you define:

@generateStates
abstract class _$$CounterState {
  final int count = 0;
  final bool isLoading = false;
  final String? message = null;
}

You automatically get:

🎯 State Class with Auto-Generated Events
// Auto-generated events for each state property
class UpdateCountEvent extends CounterEvent {
  final int count;
  const UpdateCountEvent({required this.count});

  @override
  List<Object?> get props => [count];
}

class UpdateIsLoadingEvent extends CounterEvent {
  final bool isLoading;
  const UpdateIsLoadingEvent({required this.isLoading});

  @override
  List<Object?> get props => [isLoading];
}

class UpdateMessageEvent extends CounterEvent {
  final String? message;
  const UpdateMessageEvent({required this.message});

  @override
  List<Object?> get props => [message];
}
📦 Complete State Class
class CounterState extends Equatable {
  final int count;
  final bool isLoading;
  final String? message;

  const CounterState({
    required this.count,
    required this.isLoading,
    this.message,
  });

  // Factory constructor for initial state
  static CounterState initial() {
    return const CounterState(
      count: 0,
      isLoading: false,
      message: null,
    );
  }

  // copyWith method for immutable updates
  CounterState copyWith({
    int? count,
    bool? isLoading,
    String? message,
  }) {
    return CounterState(
      count: count ?? this.count,
      isLoading: isLoading ?? this.isLoading,
      message: message ?? this.message,
    );
  }

  // copyWithNull for nullable fields
  CounterState copyWithNull({bool message = false}) {
    return CounterState(
      count: this.count,
      isLoading: this.isLoading,
      message: message ? null : this.message,
    );
  }

  // Auto-registers event handlers
  static void registerEvents(CounterBloc bloc) {
    bloc.on<UpdateCountEvent>((event, emit) {
      emit(bloc.state.copyWith(count: event.count));
    });

    bloc.on<UpdateIsLoadingEvent>((event, emit) {
      emit(bloc.state.copyWith(isLoading: event.isLoading));
    });

    bloc.on<UpdateMessageEvent>((event, emit) {
      if (event.message == null) {
        emit(bloc.state.copyWithNull(message: true));
      } else {
        emit(bloc.state.copyWith(message: event.message));
      }
    });
  }

  @override
  List<Object?> get props => [count, isLoading, message];
}
🔧 BuildContext Extension
extension CounterBlocContextExtension on BuildContext {
  /// Updates the CounterBloc state with the provided values.
  /// Only specified parameters will be updated; others remain unchanged.
  void setCounterBlocState({
    Object? count = UnspecifiedDataType.instance,
    Object? isLoading = UnspecifiedDataType.instance,
    Object? message = UnspecifiedDataType.instance,
  }) {
    final bloc = read<CounterBloc>();
    
    if (count != UnspecifiedDataType.instance) {
      bloc.add(UpdateCountEvent(count: count as int));
    }

    if (isLoading != UnspecifiedDataType.instance) {
      bloc.add(UpdateIsLoadingEvent(isLoading: isLoading as bool));
    }

    if (message != UnspecifiedDataType.instance) {
      bloc.add(UpdateMessageEvent(message: message as String?));
    }
  }
}

Benefits:

  • Complete state class with all properties
  • initial() factory with default values
  • copyWith() for immutable updates
  • copyWithNull() for nullable fields
  • Auto-generated update events
  • registerEvents() for easy setup
  • Context extensions for easy state updates
  • Full documentation comments

💡 Best Practices #

State Management #

DO

@generateStates
abstract class _$$UserState {
  final String username = '';
  final bool isAuthenticated = false;
  final String? profileUrl = null;
}

Use meaningful, descriptive names
Provide default values
Consider nullability carefully

DON'T

@generateStates
abstract class _$$UserState {
  final String u = '';
  final bool b = false;
  final String? x;
}

Avoid cryptic variable names
Don't skip default values
Don't over-use nullable types

Event Naming #

DO

@generateEvents
abstract class AuthEvent {
  const factory AuthEvent.loginWithEmail({
    required String email,
    required String password,
  }) = LoginWithEmailEvent;
}

Use descriptive names
Follow verb-noun pattern
Group related events

DON'T

@generateEvents
abstract class AuthEvent {
  const factory AuthEvent.e1({
    String? x,
    String? y,
  }) = Event1;
}

Avoid generic names
Don't use numbered events
Missing documentation

When to Use What #

Use Case Recommendation Why?
Simple state updates context.set{Bloc}State() Cleaner, less code
Complex business logic Custom events + handlers Better separation of concerns
Multiple state changes copyWith() in bloc Atomic state updates
Nullable field reset copyWithNull() Explicitly set to null
Initial setup {State}.initial() Consistent defaults

File Organization #

lib/
├── blocs/
│   ├── auth/
│   │   ├── auth_bloc.dart        # Main bloc file (imports, bloc class)
│   │   ├── auth_event.dart       # Event definitions with @generateEvents
│   │   ├── auth_state.dart       # State definitions with @generateStates
│   │   └── auth_bloc.g.dart      # Generated file (auto-generated events + states)
│   └── counter/
│       ├── counter_bloc.dart     # Main bloc file (imports, bloc class)
│       ├── counter_event.dart    # Event definitions with @generateEvents
│       ├── counter_state.dart    # State definitions with @generateStates
│       └── counter_bloc.g.dart   # Generated file (auto-generated events + states)

File Structure Breakdown:

File Purpose Contains
{name}_bloc.dart Main file Imports, part declarations, Bloc class
{name}_state.dart State definition part of + @generateStates
{name}_event.dart Event definition part of + @generateEvents
{name}_bloc.g.dart Generated code Auto-generated events, states, extensions

🔄 Migration Guide #

Migrating from 2.x to 3.x #

Step 1: Update Dependencies

dependencies:
  fbloc_event_gen: ^3.2.7  # Update version

Step 2: Update State Class Names

Before:

@generateStates
abstract class MyState {
  // ...
}

After:

@generateStates
abstract class _$$MyState {  // Add _$$ prefix
  // ...
}

Step 3: Add Initial Values

Before:

abstract class _$$MyState {
  final int counter;
  final bool isLoading;
}

After:

abstract class _$$MyState {
  final int counter = 0;           // Add defaults
  final bool isLoading = false;     // Add defaults
}

Step 4: Run Code Generation

dart run build_runner clean
dart run build_runner build --delete-conflicting-outputs

🤝 Contributing #

We love contributions! 💙

How to Contribute #

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Ways to Contribute #

  • Report bugs
  • Suggest new features
  • Improve documentation
  • Add tests
  • Fix issues

See our Contributing Guide for more details.


📝 License #

This project is licensed under the MIT License - see the LICENSE file for details.


🙏 Acknowledgments #

Special thanks to all our contributors and the Flutter community for their support!

Show Your Support #

If you like this package, please give it a ⭐ on GitHub!

Get in Touch #


Made with ❤️ for the Flutter Community

4
likes
0
points
112
downloads

Publisher

verified publishernavaneethk.dev

Weekly Downloads

Generates STATE, EVENT AND BLOC from a SINGLE LINE OF STATE VARIABLE and can generate custom event classes from factory constructors. Eliminates boilerplate code in Flutter BLoC development.

Repository (GitHub)
View/report issues

Topics

#bloc-generator #bloc-event-generator #bloc-state-generator #build-runner-generator

License

unknown (license)

Dependencies

analyzer, build, build_runner, equatable, source_gen

More

Packages that depend on fbloc_event_gen