I am using BLOC to make a simple counter app , but my counter is not updating when I am incrementing / decrementing it? I used BlocBuilder to build UI

Issue

I have wrapped MaterialApp under the BlocProvider and also I have used BlocBuilder for building the UI ( counter text )
I have two floating action buttons each of which call Incrementevent() and Decrementevent() respectively but still, the UI doesn’t change.
please suggest a solution.

My main.dart file

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

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => CounterBloc(),
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: MyHomePage(title: 'Flutter Demo Home Page'),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final _counterBloc = CounterBloc();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: BlocBuilder<CounterBloc, CounterState>(
        builder: (context, state) {
          return Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Text(
                  'You have pushed the button this many times:',
                ),
                Text(
                  '${state.counter}',
                  style: Theme.of(context).textTheme.headline4,
                ),
              ],
            ),
          );
        },
      ),
      floatingActionButton: Row(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: () => _counterBloc.add(Incrementevent()),
            tooltip: 'Decrement',
            child: Icon(Icons.remove),
          ),
          SizedBox(width: 10),
          FloatingActionButton(
            onPressed: () => _counterBloc.add(Decrementevent()),
            tooltip: 'Increment',
            child: Icon(Icons.add),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    // TODO: implement dispose
    _counterBloc.close();
    super.dispose();
  }
}

My counter_event file

part of 'counter_bloc.dart';

@immutable
abstract class CounterEvent {}

class Incrementevent extends CounterEvent {}

class Decrementevent extends CounterEvent {}

My counter_state file

part of 'counter_bloc.dart';

@immutable
abstract class CounterState {
  final int counter;
  const CounterState(this.counter);
}

class CounterInitial extends CounterState {
  CounterInitial(int counter) : super(counter);
}

My counter_bloc file

import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';

part 'counter_event.dart';
part 'counter_state.dart';

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

  @override
  Stream<CounterState> mapEventToState(
    CounterEvent event,
  ) async* {
    if (event is Incrementevent) {
      yield CounterInitial(state.counter + 1);
    } else if (event is Decrementevent) {
      yield CounterInitial(state.counter - 1);
    }
  }
}

Solution

TL;DR You are creating two instances of the same BLoC, hence by triggering events on one BLoC, the other does not get updated.

In the MyApp widget, you create a BLoC instance and provide it to the widget tree using the BlocProvider – everything is perfect here.

However, in the _MyHomePageState, you are creating another instance of the same CounterBloc and using it to add events. It means, that you are operating on a different BLoC than you provided before.

To resolve this, you should get the injected BLoC instance and operate on it. Since the BLoC library depends on provider, you could resolve the injected BLoC like this:

@override
Widget build(BuildContext context) {
  final counterBloc = context.watch<CounterBloc>();
  ...
  floatingActionButton: Row(
    mainAxisAlignment: MainAxisAlignment.end,
    children: [
      FloatingActionButton(
        onPressed: () => counterBloc.add(Incrementevent()),
        tooltip: 'Decrement',
        child: Icon(Icons.remove),
      ),
      SizedBox(width: 10),
      FloatingActionButton(
        onPressed: () => counterBloc.add(Decrementevent()),
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    ],
  ),
  ...
}

Or this:

@override
Widget build(BuildContext context) {
  ...
  floatingActionButton: Row(
    mainAxisAlignment: MainAxisAlignment.end,
    children: [
      FloatingActionButton(
        onPressed: () => context.read<CounterBloc>().add(Incrementevent()),
        tooltip: 'Decrement',
        child: Icon(Icons.remove),
      ),
      SizedBox(width: 10),
      FloatingActionButton(
        onPressed: () => context.read<CounterBloc>().add(Decrementevent()),
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    ],
  ),
  ...
}

Answered By – mkobuolys

Answer Checked By – Timothy Miller (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *