Get in touch
or send us a question?

Flutter Counter Tutorial With BLoC Pattern part 1

In the following tutorial, we’re going to build a Counter in Flutter using the Bloc library.



We’ll start off by creating a brand new Flutter project

flutter create flutter_counter

We can then go ahead and replace the contents of pubspec.yaml with

name: flutter_counter
description: A new Flutter project.

publish_to: "none" # Remove this line if you wish to publish to

version: 1.0.0+1

  sdk: ">=2.7.0 <3.0.0"

    sdk: flutter
  bloc: ^6.1.0
  flutter_bloc: ^6.1.0

    sdk: flutter
  bloc_test: ^7.1.0
  mockito: ^4.0.0
  integration_test: ^1.0.0

  uses-material-design: true

and then install all of our dependencies

flutter packages get

Project Structure

├── lib
│   ├── app.dart
│   ├── counter
│   │   ├── counter.dart
│   │   ├── cubit
│   │   │   └── counter_cubit.dart
│   │   └── view
│   │       ├── counter_page.dart
│   │       └── counter_view.dart
│   ├── counter_observer.dart
│   └── main.dart
├── pubspec.lock
├── pubspec.yaml

The application uses a feature-driven directory structure. This project structure enables us to scale the project by having self-contained features. In this example we will only have a single feature (the counter itself) but in more complex applications we can have hundreds of different features.


The first thing we’re going to take a look at is how to create a BlocObserver which will help us observe all state changes in the application.

Let’s create lib/counter_observer.dart:

import 'package:bloc/bloc.dart';

/// {@template counter_observer}
/// [BlocObserver] for the counter application which
/// observes all [Cubit] state changes.
/// {@endtemplate}
class CounterObserver extends BlocObserver {
  void onChange(Cubit cubit, Change change) {
    print('${cubit.runtimeType} $change');
    super.onChange(cubit, change);

In this case, we’re only overriding onChange to see all state changes that occur.

NoteonChange works the same way for both Bloc and Cubit instances.


Next, let’s replace the contents of main.dart with:

import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';

import 'app.dart';
import 'counter_observer.dart';

void main() { = CounterObserver();
  runApp(const CounterApp());
Copy to clipboardErrorCopied

We’re initializing the CounterObserver we just created and calling runApp with the CounterApp widget which we’ll look at next.

Counter App

CounterApp will be a MaterialApp and is specifying the home as CounterPage.

import 'package:flutter/material.dart';

import 'counter/counter.dart';

/// {@template counter_app}
/// A [MaterialApp] which sets the `home` to [CounterPage].
/// {@endtemplate}
class CounterApp extends MaterialApp {
  /// {@macro counter_app}
  const CounterApp({Key key}) : super(key: key, home: const CounterPage());

Note: We are extending MaterialApp because CounterApp is a MaterialApp. In most cases, we’re going to be creating StatelessWidget or StatefulWidget instances and composing widgets in build but in this case there are no widgets to compose so it’s simpler to just extend MaterialApp.

Here is end for part 1. Try to practice and I will continue in part 2 soon. See you later <3 <3 <3