flutter_event_component_system 0.0.1 copy "flutter_event_component_system: ^0.0.1" to clipboard
flutter_event_component_system: ^0.0.1 copied to clipboard

Powerful Event-Component-System state management for Flutter. Promotes clean architecture, testability, and includes visual debugging with Inspector

Flutter Event Component System (ECS) #

Apache 2.0 License Flutter Dart

A powerful and flexible Event-Component-System (ECS) architecture pattern implementation for Flutter applications. This package provides a reactive state management solution that promotes clean architecture, separation of concerns, and scalable application development.

๐ŸŒŸ Features #

Core Architecture #

  • ๐Ÿ—๏ธ Entity-Component-System Pattern: Clean separation between data (Components), behavior (Systems), and events
  • โšก Reactive Programming: Automatic UI updates when components change
  • ๐Ÿ”„ Event-Driven: Decoupled communication through events and reactive systems
  • ๐ŸŽฏ Type-Safe: Full TypeScript-like type safety with Dart generics
  • ๐Ÿงฉ Modular Design: Organize code into reusable features

Advanced Capabilities #

  • ๐Ÿ” Built-in Inspector: Real-time debugging and visualization tools
  • ๐Ÿ“Š Flow Analysis: Detect circular dependencies and cascade flows
  • ๐Ÿ“ˆ Performance Monitoring: Track system interactions and entity changes
  • ๐ŸŒ Graph Visualization: Interactive dependency graphs
  • ๐Ÿ“ Comprehensive Logging: Detailed system activity tracking

Developer Experience #

  • ๐Ÿ› ๏ธ Widget Integration: Seamless Flutter widget integration
  • ๐ŸŽจ Reactive Widgets: Automatic rebuilds on component changes
  • ๐Ÿ”ง Debugging Tools: Visual inspector with filtering and search
  • ๐Ÿ“‹ Cascade Analysis: Understand data flow and dependencies
  • โš™๏ธ Hot Reload Support: Full development workflow integration

๐Ÿš€ Quick Start #

Installation #

Add this package to your pubspec.yaml:

dependencies:
  flutter_event_component_system:
    git: 
      url: https://github.com/FlameOfUdun/flutter_event_component_system.git
      ref: main

Basic Usage #

1. Define Components and Events

// Components hold state data
class CounterComponent extends ECSComponent<int> {
  CounterComponent([super.value = 0]);
}

// Events trigger actions
class IncrementEvent extends ECSEvent {
  IncrementEvent();
}

2. Create Reactive Systems

// Systems define behavior and reactions
class IncrementCounterSystem extends ReactiveSystem {
  IncrementCounterSystem();

  @override
  Set<Type> get reactsTo => {IncrementEvent};
  
  @override
  Set<Type> get interactsWith => {CounterComponent};

  @override
  void react() {
    final counter = getEntity<CounterComponent>();
    counter.update(counter.value + 1);
  }
}

3. Organize into Features

class CounterFeature extends ECSFeature {
  CounterFeature() {
    // Add components
    addEntity(CounterComponent());
    addEntity(IncrementEvent());
    
    // Add systems
    addSystem(IncrementCounterSystem());
  }
}

4. Setup ECS Scope

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ECSScope(
      features: {
        CounterFeature(),
      },
      child: MaterialApp(
        home: CounterPage(),
      ),
    );
  }
}

5. Create Reactive Widgets

class CounterPage extends ECSWidget {
  @override
  Widget build(BuildContext context, ECSContext ecs) {
    final counter = ecs.watch<CounterComponent>();
    final incrementEvent = ecs.get<IncrementEvent>();
    
    return Scaffold(
      appBar: AppBar(title: Text('Counter')),
      body: Center(
        child: Text('Count: ${counter.value}'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: incrementEvent.trigger,
        child: Icon(Icons.add),
      ),
    );
  }
}

๐Ÿ›๏ธ Architecture Overview #

Core Concepts #

Entities

Base building blocks that can be either Components or Events:

  • Components: Hold state data with automatic change notification
  • Events: Trigger actions and system reactions

Systems

Define behavior and business logic:

  • InitializeSystem: Setup tasks on feature initialization
  • ExecuteSystem: Frame-based continuous execution
  • ReactiveSystem: React to entity changes
  • CleanupSystem: Cleanup tasks after each frame
  • TeardownSystem: Cleanup on feature disposal

Features

Organize related entities and systems into cohesive modules:

class UserAuthFeature extends ECSFeature {
  UserAuthFeature() {
    // Components
    addEntity(AuthStateComponent());
    addEntity(LoginCredentialsComponent());
    
    // Events  
    addEntity(LoginEvent());
    addEntity(LogoutEvent());
    
    // Systems
    addSystem(LoginUserReactiveSystem());
    addSystem(LogoutUserReactiveSystem());
  }
}

Manager

Central coordinator that:

  • Manages all features and their lifecycles
  • Coordinates system execution
  • Handles entity change notifications
  • Provides entity lookup and access

System Lifecycle #

graph TD
    A[Feature Added] --> B[Initialize Systems]
    B --> C[Execute Systems]
    C --> D[Cleanup Systems]
    D --> C
    D --> E[Teardown Systems]
    E --> F[Feature Disposed]

    G[Entity Changes]
    G --> H[Reactive Systems]
    H --> G
    B --> G
    C --> G
    D --> G
    E --> G

๐ŸŽฏ Advanced Features #

Reactive Widget Integration #

ECSWidget

Automatically rebuilds when watched components change:

class ProfileWidget extends ECSWidget {
  @override
  Widget build(BuildContext context, ECSContext ecs) {
    final user = ecs.watch<UserComponent>();
    final auth = ecs.watch<AuthStateComponent>();
    
    return Column(
      children: [
        Text('Welcome ${user.value.name}'),
        Text('Status: ${auth.value}'),
      ],
    );
  }
}

ECSBuilder

Functional approach for simple reactive widgets:

ECSBuilder<UserComponent>(
  builder: (context, ecs) {
    final user = ecs.watch<UserComponent>();
    return Text('Hello ${user.value.name}');
  },
)

ECSStatefulWidget

For complex widgets requiring local state:

class ComplexWidget extends ECSStatefulWidget {
  @override
  ECSState<ComplexWidget> createState() => _ComplexWidgetState();
}

class _ComplexWidgetState extends ECSState<ComplexWidget> {
  @override
  Widget build(BuildContext context) {
    final data = ecs.watch<DataComponent>();
    return YourComplexWidget(data: data.value);
  }
}

Event Handling and Listeners #

Direct Event Listening

class NotificationWidget extends ECSWidget {
  @override
  Widget build(BuildContext context, ECSContext ecs) {
    // Listen to specific entity
    ecs.listen<ErrorComponent>((entity) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text(entity.value.message)),
      );
    });
    
    return YourWidget();
  }
}

Lifecycle Callbacks

class LifecycleWidget extends ECSWidget {
  @override
  Widget build(BuildContext context, ECSContext ecs) {
    ecs.onEnter(() {
      print('Widget entered ECS context');
    });
    
    ecs.onExit(() {
      print('Widget exited ECS context');
    });
    
    return YourWidget();
  }
}

System Types and Usage #

Reactive Systems

Respond to entity changes:

class ValidationSystem extends ReactiveSystem {
  ValidationSystem();

  @override
  Set<Type> get reactsTo => {FormDataComponent};
  
  @override
  Set<Type> get interactsWith => {ValidationStateComponent};
  
  @override
  bool get reactsIf => true; // Conditional reactions
  
  @override
  void react() {
    final formData = getEntity<FormDataComponent>();
    final validation = getEntity<ValidationStateComponent>();
    
    // Validate form data
    final isValid = validateForm(formData.value);
    validation.update(isValid);
  }
}

Execute Systems

Continuous frame-based execution:

class TimerSystem extends ExecuteSystem {
  TimerSystem();

  @override
  Set<Type> get interactsWith => {TimerComponent};

  @override
  executesIf => true; // Conditional executions
  
  @override
  void execute(Duration elapsed) {
    final timer = getEntity<TimerComponent>();
    timer.update(timer.value + elapsed.inMilliseconds);
  }
}

Initialize/Teardown Systems

Setup and cleanup:

class DatabaseInitSystem extends InitializeSystem {
  @override
  Set<Type> get interactsWith => {DatabaseComponent};
  
  @override
  void initialize() {
    // Initialize database connection
    print('Database initialized');
  }
}

class DatabaseTeardownSystem extends TeardownSystem {
  @override
  Set<Type> get interactsWith => {DatabaseComponent};
  
  @override
  void teardown() {
    // Close database connection
    print('Database closed');
  }
}

๐Ÿ“Š Real-World Example #

User Authentication System #

// Components
class AuthStateComponent extends ECSComponent<AuthState> {
  AuthStateComponent() : super(AuthState.unauthenticated);
}

class LoginCredentialsComponent extends ECSComponent<LoginCredentials> {
  LoginCredentialsComponent() : super(LoginCredentials.empty());
}

// Events
class LoginEvent extends ECSEvent {
  LoginEvent();
}

class LogoutEvent extends ECSEvent {
  LogoutEvent();
}

// Systems
class LoginUserReactiveSystem extends ReactiveSystem {
  LoginUserReactiveSystem();

  @override
  Set<Type> get reactsTo => {LoginEvent};
  
  @override
  Set<Type> get interactsWith => {AuthStateComponent, LoginCredentialsComponent};

  @override
  void react() async {
    final credentials = getEntity<LoginCredentialsComponent>();
    final authState = getEntity<AuthStateComponent>();
    
    try {
      authState.update(AuthState.loading);
      
      final user = await authenticateUser(credentials.value);
      authState.update(AuthState.authenticated(user));
      
    } catch (error) {
      authState.update(AuthState.error(error.toString()));
    }
  }
}

// Feature
class UserAuthFeature extends ECSFeature {
  UserAuthFeature() {
    addEntity(AuthStateComponent());
    addEntity(LoginCredentialsComponent());
    addEntity(LoginEvent());
    addEntity(LogoutEvent());
    
    addSystem(LoginUserReactiveSystem());
    addSystem(LogoutUserReactiveSystem());
  }
}

// UI Integration
class LoginPage extends ECSWidget {
  @override
  Widget build(BuildContext context, ECSContext ecs) {
    final authState = ecs.watch<AuthStateComponent>();
    final credentials = ecs.get<LoginCredentialsComponent>();
    final loginEvent = ecs.get<LoginEvent>();
    
    return Scaffold(
      appBar: AppBar(title: Text('Login')),
      body: Column(
        children: [
          if (authState.value.isLoading)
            CircularProgressIndicator(),
            
          TextField(
            onChanged: (value) {
              credentials.update(
                credentials.value.copyWith(email: value)
              );
            },
            decoration: InputDecoration(labelText: 'Email'),
          ),
          
          ElevatedButton(
            onPressed: authState.value.isLoading ? null : loginEvent.trigger,
            child: Text('Login'),
          ),
          
          if (authState.value.hasError)
            Text(
              authState.value.error,
              style: TextStyle(color: Colors.red),
            ),
        ],
      ),
    );
  }
}

๐Ÿงช Testing #

Testing Components #

test('component notifies listeners on change', () {
  final component = TestComponent();
  bool notified = false;
  
  component.addListener(TestListener(() => notified = true));
  component.update(42);
  
  expect(notified, isTrue);
  expect(component.value, equals(42));
});

Testing Systems #

test('reactive system processes events', () {
  final manager = ECSManager();
  final feature = TestFeature();
  final system = TestReactiveSystem();
  
  feature.addEntity(TestEvent());
  feature.addSystem(system);
  manager.addFeature(feature);
  manager.activate();
  
  manager.getEntity<TestEvent>().trigger();
  
  expect(system.reacted, isTrue);
});

Testing Features #

test('feature manages entities and systems', () {
  final manager = ECSManager();
  final feature = TestFeature();
  final component = TestComponent();
  final system = TestSystem();
  
  feature.addEntity(component);
  feature.addSystem(system);
  manager.addFeature(feature);
  manager.activate();
  
  expect(feature.entities, contains(component));
  expect(feature.reactiveSystems[TestEvent], contains(system));
});

Widget Testing #

testWidgets('ECS widget rebuilds on component change', (tester) async {
  final feature = TestFeature();

  await tester.pumpWidget(
    ECSScope(
      features: {feature},
      child: TestECSWidget(),
    ),
  );
  
  final component = feature.getEntity<TestComponent>();
  component.update(100);

  await tester.pump();
  
  expect(find.text('100'), findsOneWidget);
});

๐ŸŽฏ Best Practices #

1. Feature Organization #

// โœ… Good: Organized by domain
features/
  user_auth_feature/
    components/
    events/
    systems/
    user_auth_feature.dart
  
// โŒ Avoid: Mixing concerns
features/
  all_components.dart
  all_events.dart
  all_systems.dart

2. Component Design #

// โœ… Good: Immutable data structures
class UserComponent extends ECSComponent<User> {
  UserComponent(super.value);
  
  void updateName(String name) {
    update(value.copyWith(name: name));
  }
}

// โŒ Avoid: Mutable data
class UserComponent extends ECSComponent<User> {
  UserComponent(super.value);
  
  void updateName(String name) {
    value.name = name; // Don't mutate directly
    notifyListeners(); // Manual notification
  }
}

3. System Granularity #

// โœ… Good: Single responsibility
class ValidateEmailSystem extends ReactiveSystem {
  @override
  Set<Type> get reactsTo => {EmailComponent};
  
  @override
  void react() {
    // Only validate email
  }
}

class ValidatePasswordSystem extends ReactiveSystem {
  @override
  Set<Type> get reactsTo => {PasswordComponent};
  
  @override
  void react() {
    // Only validate password
  }
}

// โŒ Avoid: Multiple responsibilities
class ValidateEverythingSystem extends ReactiveSystem {
  @override
  void react() {
    // Validate email, password, phone, etc.
  }
}

4. Error Handling #

// โœ… Good: Proper error handling
class LoginSystem extends ReactiveSystem {
  @override
  void react() async {
    try {
      final result = await authService.login();
      authState.update(AuthState.authenticated(result));
    } catch (error) {
      errorState.update(ErrorState.fromException(error));
    }
  }
}

Development Setup #

  1. Clone the repository

    git clone https://github.com/FlameOfUdun/flutter_event_component_system.git
    cd flutter_event_component_system
    
  2. Install dependencies

    flutter pub get
    
  3. Run tests

    flutter test
    
  4. Run example

    cd example
    flutter run
    

Code Style #

This project follows the Dart Style Guide and uses flutter_lints for code analysis.

๐Ÿ“„ License #

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

Copyright 2025 Ehsan Rashidi

๐Ÿ™ Acknowledgments #

  • Inspired by the Entity-Component-System pattern from game development
  • Built for the Flutter community with โค๏ธ

๐Ÿ“ž Support #


Made with โค๏ธ by FlameOfUdun

6
likes
0
points
130
downloads

Publisher

unverified uploader

Weekly Downloads

Powerful Event-Component-System state management for Flutter. Promotes clean architecture, testability, and includes visual debugging with Inspector

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter

More

Packages that depend on flutter_event_component_system