Vamos a aprender BLoC paso a paso

 ¡Perfecto! Vamos a aprender BLoC paso a paso, empezando desde lo más básico para que entiendas el flujo de trabajo y las partes fundamentales. Vamos a construir una app simple de contador, ya que es un excelente punto de partida para entender cómo funcionan eventos, estados, y cómo conectarlos con la UI.



Paso 1: Configurar BLoC en el proyecto


1. Agrega el paquete flutter_bloc a tu proyecto:

Abre el archivo pubspec.yaml y añade:


dependencies:

  flutter_bloc: ^8.1.2


Luego, ejecuta:


flutter pub get



2. Estructura del proyecto:

Organiza tu proyecto para que el código sea modular y fácil de entender. Por ejemplo:


lib/

├── bloc/

  ├── counter_bloc.dart

  ├── counter_event.dart

  └── counter_state.dart

├── main.dart

└── ui/

    └── counter_screen.dart


Paso 2: Crea los componentes del BLoC


1. Define los eventos


Los eventos son acciones que el usuario dispara, como presionar un botón. Aquí definiremos los eventos para incrementar y decrementar el contador.


// lib/bloc/counter_event.dart


abstract class CounterEvent {}


// Evento para incrementar el contador

class IncrementCounter extends CounterEvent {}


// Evento para decrementar el contador

class DecrementCounter extends CounterEvent {}


2. Define los estados


Los estados son el resultado que queremos mostrar en la UI. En este caso, el estado será el valor actual del contador.


// lib/bloc/counter_state.dart


abstract class CounterState {}


// Estado que representa el valor del contador

class CounterValueState extends CounterState {

  final int counterValue;


  CounterValueState(this.counterValue);

}


3. Crea el BLoC


El BLoC conecta los eventos con los estados. Aquí procesamos la lógica de negocio: al recibir un evento, actualizamos el estado del contador.


// lib/bloc/counter_bloc.dart


import 'package:flutter_bloc/flutter_bloc.dart';

import 'counter_event.dart';

import 'counter_state.dart';


class CounterBloc extends Bloc<CounterEvent, CounterState> {

  CounterBloc() : super(CounterValueState(0)) {

    // Maneja IncrementCounter

    on<IncrementCounter>((event, emit) {

      final currentState = state as CounterValueState;

      emit(CounterValueState(currentState.counterValue + 1));

    });


    // Maneja DecrementCounter

    on<DecrementCounter>((event, emit) {

      final currentState = state as CounterValueState;

      emit(CounterValueState(currentState.counterValue - 1));

    });

  }

}


Paso 3: Conectar el BLoC con la UI


1. Configura el BlocProvider


El BlocProvider es necesario para crear y exponer el BLoC a la UI. Esto lo hacemos en el archivo main.dart:


// lib/main.dart


import 'package:flutter/material.dart';

import 'package:flutter_bloc/flutter_bloc.dart';

import 'bloc/counter_bloc.dart';

import 'ui/counter_screen.dart';


void main() {

  runApp(MyApp());

}


class MyApp extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      home: BlocProvider(

        create: (context) => CounterBloc(),

        child: CounterScreen(),

      ),

    );

  }

}


2. Construye la UI


La UI usará BlocBuilder para escuchar los estados del BLoC y actualizarse automáticamente cuando cambien.


// lib/ui/counter_screen.dart


import 'package:flutter/material.dart';

import 'package:flutter_bloc/flutter_bloc.dart';

import '../bloc/counter_bloc.dart';

import '../bloc/counter_event.dart';

import '../bloc/counter_state.dart';


class CounterScreen extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    final counterBloc = context.read<CounterBloc>();


    return Scaffold(

      appBar: AppBar(title: Text('Contador con BLoC')),

      body: Center(

        child: BlocBuilder<CounterBloc, CounterState>(

          builder: (context, state) {

            final counterValue = (state as CounterValueState).counterValue;

            return Text(

              'Valor: $counterValue',

              style: TextStyle(fontSize: 32),

            );

          },

        ),

      ),

      floatingActionButton: Row(

        mainAxisAlignment: MainAxisAlignment.end,

        children: [

          FloatingActionButton(

            onPressed: () => counterBloc.add(IncrementCounter()),

            child: Icon(Icons.add),

          ),

          SizedBox(width: 10),

          FloatingActionButton(

            onPressed: () => counterBloc.add(DecrementCounter()),

            child: Icon(Icons.remove),

          ),

        ],

      ),

    );

  }

}


Paso 4: Prueba tu app


1. Ejecuta la app.

2. Toca los botones + y - para disparar los eventos IncrementCounter y DecrementCounter.

3. Observa cómo la UI se actualiza automáticamente con los nuevos valores del contador.


Paso 5: Explicación del flujo


1. El usuario toca un botón (+ o -).

2. La UI dispara un evento (por ejemplo, IncrementCounter).

3. El evento llega al CounterBloc, que ejecuta la lógica para actualizar el estado.

4. El BLoC emite un nuevo estado (CounterValueState con un nuevo valor).

5. BlocBuilder escucha el cambio de estado y reconstruye la UI con el nuevo valor.


¿Qué aprendiste aquí?


1. Eventos: Representan lo que ocurre en la app (acciones del usuario).

2. Estados: Representan cómo debe lucir la app tras un evento.

3. BLoC: Es el puente entre eventos y estados, manejando la lógica de negocio.

4. BlocProvider: Expone el BLoC a los widgets.

5. BlocBuilder: Reconstruye la UI automáticamente cuando cambia el estado.


Próximos pasos


1. Haz un segundo ejemplo: Amplía esta app para que el contador no solo sume/resta, sino que tenga un botón de “reiniciar”.

2. Manejo de estados múltiples: Crea una lista de tareas con estados como “pendiente” o “completada”.

3. Firebase: Integra Firebase para sincronizar el contador o datos entre dispositivos.


Si necesitas ayuda para el siguiente paso, ¡avísame! 😊

Comentarios