Flutter BLoC Overview

A portrait painting style image of a pirate holding an iPhone.

by The Captain

on
June 13, 2023

Flutter Bloc: An Overview and Example

Flutter BLoC is a popular state management library for developing reactive applications. It is built on top of the RxDart package and provides a simple way to manage application state and trigger UI updates in response to changes in that state.

Core Concepts of Flutter Bloc

The core concepts of Flutter Bloc include:

  • Events: These are input actions that the user triggers or that come from the application state.
  • States: These represent the current state of the application. UI widgets are rebuilt in response to changes in state.
  • Blocs: These are objects that take in events and emit new states based on those events.
  • UI Widgets: These are the visual elements that make up your app. In a Flutter Bloc application, they typically use the BlocBuilder widget to listen for state changes.

Example: Building a Counter App

Let's build a simple counter app to demonstrate how to use Flutter BLoC. We'll have two buttons, one to increment the counter and another to decrement it. The updated count will be displayed on the screen in real-time.

First, we'll create a new Flutter project.

flutter create counter_app
cd counter_app

Now let's add the dependencies for rxdart and bloc.

dependencies:
  flutter:
    sdk: flutter
  rxdart: "^0.25.0"
  bloc: "^7.0.0"

Next, let's create the CounterEvent and CounterState classes.

abstract class CounterEvent {}

class IncrementEvent extends CounterEvent {}

class DecrementEvent extends CounterEvent {}

abstract class CounterState {}

class InitialCounterState extends CounterState {
  final int count;

  InitialCounterState(this.count);
}

class UpdatedCounterState extends CounterState {
  final int count;

  UpdatedCounterState(this.count) : super();
}

Our InitialCounterState will represent the initial state of the counter app with a count of zero. The UpdatedCounterState will represent the state after the count has been incremented or decremented.

Now let's create the CounterBloc itself.

class CounterBloc extends Bloc<CounterEvent, CounterState> {
  CounterBloc() : super(InitialCounterState(0));

  @override
  Stream<CounterState> mapEventToState(CounterEvent event) async* {
    if (event is IncrementEvent) {
      yield UpdatedCounterState(state.count + 1);
    } else if (event is DecrementEvent) {
      yield UpdatedCounterState(state.count - 1);
    }
  }
}

The CounterBloc extends the Bloc class and provides the implementation for the mapEventToState method. This is where we handle incoming events and map them to new states. We have two event types in this example, one for incrementing the count and one for decrementing it. When an event is received, a new UpdatedCounterState is emitted with the updated count value.

Last of all, let's create the UI of our application using the BlocBuilder widget, which listens for updates to the state of our CounterBloc and rebuilds the UI accordingly.

class CounterApp extends StatelessWidget {
  final CounterBloc _counterBloc = CounterBloc();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Counter App'),
        ),
        body: BlocBuilder<CounterBloc, CounterState>(
          bloc: _counterBloc,
          builder: (BuildContext context, CounterState state) {
            if (state is UpdatedCounterState) {
              return Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Text(
                    'Count:',
                    style: TextStyle(fontSize: 20),
                  ),
                  Text(
                    state.count.toString(),
                    style: TextStyle(fontSize: 50),
                  ),
                  SizedBox(
                    height: 20,
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      FloatingActionButton(
                          onPressed: () {
                            _counterBloc.add(DecrementEvent());
                          },
                          child: Icon(Icons.remove)),
                      SizedBox(width: 20),
                      FloatingActionButton(
                          onPressed: () {
                            _counterBloc.add(IncrementEvent());
                          },
                          child: Icon(Icons.add)),
                    ],
                  )
                ],
              );
            } else {
              return Center(
                child: CircularProgressIndicator(),
              );
            }
          },
        ),
      ),
    );
  }
}

The UI that we create is a simple column of widgets that includes the current count, two buttons to increment and decrement the count, and a loading spinner for initial state. To update the count, we add the corresponding CounterEvent to the CounterBloc using the add() method. The BlocBuilder widget handles the rest, listening for state changes and updating the UI accordingly.

Wrapping Up

Flutter BLoC is a powerful technique for managing state in your Flutter applications. By following the core concepts and example above, you'll be well on your way to building scalable, reactive applications.