flutter_bloc: ^6.1.1 not changing state

Issue

I am using flutter_bloc: ^6.1.1 along with dependency injection get_it: ^5.0.6. The state is not changing when I submit event to the bloc.

nav_bar_bloc.dart

import 'dart:async';

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

import '../../../../../core/error/failures.dart';
import '../../../../../core/usecases/usecase.dart';
import '../../../domain/entities/fixtures_entities.dart';
import '../../../domain/entities/prediction_entities.dart';
import '../../../domain/usecases/get_fixtures.dart';
import '../../../domain/usecases/get_predictions.dart';

part 'nav_bar_event.dart';
part 'nav_bar_state.dart';

const String SERVER_FAILURE_MESSAGE = 'SERVER FAILURE. TRY AGAIN LATER';
const String CACHE_FAILURE_MESSAGE = 'CACHE FAILURE';

class NavBarBloc extends Bloc<NavBarEvent, NavBarState> {
  final GetBasicFixtures getBasicFixtures;
  final GetPredictions getPredictions;

  NavBarBloc({
    @required this.getBasicFixtures,
    @required this.getPredictions,
  }) : super(NavBarInitialState());

  @override
  Stream<NavBarState> mapEventToState(
    NavBarEvent event,
  ) async* {
    print(event);
    // FIXTURES EVENT
    // yield HomePageLoadingState();
    if (event is HomePageSelectEvent) {
      Either<Failure, List<BasicFixturesEntity>> response =
          await getBasicFixtures(NoParams());
      yield* response.fold((failure) async* {
        yield HomePageLoadFailureState(message: _mapFailureToMessage(failure));
      }, (fixtures) async* {
        print(fixtures[0].away);
        yield HomePageLoadSuccessState(fixtures: fixtures);
      });
    } // GET PREDICTIONS EVENT
    else if (event is PredictionPageSelectEvent) {
      yield PredictionPageLoadingState();
      Either<Failure, List<PredictionEntity>> response =
          await getPredictions(NoParams());
      yield* response.fold((failure) async* {
        yield PredictionPageLoadFailureState(
            message: _mapFailureToMessage(failure));
      }, (predictions) async* {
        yield PredictionPageLoadSuccessState(predictions: predictions);
      });
    } // NEWS EVENT
    else if (event is NewsPageSelectEvent) {
      yield NewsPageLoadingState();
      yield NewsPageLoadSuccessState();
    } // ACCOUNT EVENT
    else if (event is AccountPageSelectEvent) {
      yield AccountPageLoadingState();
      yield AccountPageLoadSuccessState();
    }
  }

  String _mapFailureToMessage(Failure failure) {
    switch (failure.runtimeType) {
      case ServerFailure:
        return SERVER_FAILURE_MESSAGE;
      case CacheFailure:
        return CACHE_FAILURE_MESSAGE;
      default:
        return 'UNEXPECTED ERROR';
    }
  }
}

nav_bar_event.dart

part of 'nav_bar_bloc.dart';

abstract class NavBarEvent extends Equatable {
  const NavBarEvent();

  @override
  List<Object> get props => [];
}

class HomePageSelectEvent extends NavBarEvent {}

class PredictionPageSelectEvent extends NavBarEvent {}

class NewsPageSelectEvent extends NavBarEvent {}

class AccountPageSelectEvent extends NavBarEvent {}

nav_bar_state.dart

part of 'nav_bar_bloc.dart';

abstract class NavBarState extends Equatable {
  const NavBarState();

  @override
  List<Object> get props => [];
}

class NavBarInitialState extends NavBarState {}

// Initial HOME PAGE [FIXTURES PAGE]
class HomePageLoadingState extends NavBarState {}

class HomePageLoadSuccessState extends NavBarState {
  final List<BasicFixturesEntity> fixtures;

  HomePageLoadSuccessState({@required this.fixtures});
  @override
  List<Object> get props => [fixtures];
}

class HomePageLoadFailureState extends NavBarState {
  final String message;

  HomePageLoadFailureState({@required this.message});

  @override
  List<Object> get props => [message];
}

// PREDICTION PAGE
class PredictionPageLoadingState extends NavBarState {}

class PredictionPageLoadSuccessState extends NavBarState {
  final List<PredictionEntity> predictions;

  PredictionPageLoadSuccessState({@required this.predictions});

  @override
  List<Object> get props => [predictions];
}

class PredictionPageLoadFailureState extends NavBarState {
  final String message;

  PredictionPageLoadFailureState({@required this.message});

  @override
  List<Object> get props => [message];
}

// NEWS PAGE
class NewsPageLoadingState extends NavBarState {}

class NewsPageLoadSuccessState extends NavBarState {}

class NewsPageLoadFailureState extends NavBarState {}

// ACCOUNT PAGE
class AccountPageLoadingState extends NavBarState {}

class AccountPageLoadSuccessState extends NavBarState {}

class AccountPageLoadailureState extends NavBarState {}

ui.dart

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

import '../../../../injection_containers/injection_container.dart';
import '../bloc/nav_bar_bloc/nav_bar_bloc.dart';
import '../widgets/core/navbar.dart';
import 'account_page/account_page.dart';
import 'fixtures_page/fixtures_page.dart';
import 'news_page/news_page.dart';
import 'prediction_page/prediction_page.dart';

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  @override
  void initState() {
    super.initState();
    sl<NavBarBloc>().add(HomePageSelectEvent());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        backgroundColor: Colors.black,
        bottomSheet: NavBar(),
        body: BlocProvider(
            create: (_) => sl<NavBarBloc>(),
            child:
                // ignore: missing_return
                BlocBuilder<NavBarBloc, NavBarState>(builder: (context, state) {
              print(state);
              // HOME PAGE aka. FIXTURES PAGE
              if (state is HomePageLoadFailureState) {
                return Center(child: Text(state.message));
              } else if (state is HomePageLoadSuccessState) {
                return FixturesPage(fixtures: state.fixtures);
              } else if (state is HomePageLoadingState) {
                return Center(child: CircularProgressIndicator());
              }
              // PREDICTION PAGE
              else if (state is PredictionPageLoadingState) {
                return Center(child: CircularProgressIndicator());
              } else if (state is PredictionPageLoadFailureState) {
                return Center(child: Text(state.message));
              } else if (state is PredictionPageLoadSuccessState) {
                return PredictionPage(predictions: state.predictions);
              }
              // ACCOUNT PAGE
              else if (state is AccountPageLoadingState) {
                return AccountPage();
              } else if (state is AccountPageLoadSuccessState) {
                return AccountPage();
              } else if (state is AccountPageLoadailureState) {
                return AccountPage();
              }
              // NEWS PAGE
              else if (state is NewsPageLoadingState) {
                return NewsPage();
              } else if (state is NewsPageLoadSuccessState) {
                return NewsPage();
              } else if (state is NewsPageLoadFailureState) {
                return NewsPage();
              } else if (state is NavBarInitialState) {
                return Center(
                    child: Text(
                  "loading",
                  style: TextStyle(color: Colors.white),
                ));
              }
            })));
  }
}

dependency_injection.dart

import 'package:connectivity/connectivity.dart';
import 'package:get_it/get_it.dart';
import 'package:http/http.dart' as http;
import 'package:shared_preferences/shared_preferences.dart';

import '../core/network/network_info.dart';
import '../features/predictor/data/datasources/local_data_source/fixtures_local_data_source.dart';
import '../features/predictor/data/datasources/local_data_source/prediction_local_data_source.dart';
import '../features/predictor/data/datasources/remote_data_source/fixtures_remote_data_source.dart';
import '../features/predictor/data/datasources/remote_data_source/prediction_remote_data_source.dart';
import '../features/predictor/data/datasources/token_access.dart';
import '../features/predictor/data/repositories/fixtures_repositories_impl.dart';
import '../features/predictor/data/repositories/prediction_repositories_impl.dart';
import '../features/predictor/domain/repositories/fixtures_repositories.dart';
import '../features/predictor/domain/repositories/prediction_repositories.dart';
import '../features/predictor/domain/usecases/get_fixtures.dart';
import '../features/predictor/domain/usecases/get_predictions.dart';
import '../features/predictor/domain/usecases/submit_prediction.dart';
import '../features/predictor/presentation/bloc/nav_bar_bloc/nav_bar_bloc.dart';
import '../features/predictor/presentation/bloc/prediction_submit_bloc/prediction_submit_bloc.dart';

GetIt sl = GetIt.instance;

Future<void> init() async {
  // NavBarBloc
  sl.registerFactory<NavBarBloc>(
      () => NavBarBloc(getBasicFixtures: sl(), getPredictions: sl()));
  // SubmitPredictionBloc
  sl.registerFactory<PredictionSubmitBloc>(
      () => PredictionSubmitBloc(submitPrediction: sl()));

  // Validate token
  sl.registerLazySingleton<ValidateToken>(() => ValidateTokenImpl());

  /// usecase
  ///
  // Fixtures
  sl.registerLazySingleton(() => GetBasicFixtures(repository: sl()));
  // Get-predictions
  sl.registerLazySingleton(() => GetPredictions(repository: sl()));
  // submit-prediction
  sl.registerLazySingleton(() => SubmitPrediction(predictionRepository: sl()));

  /// repository
  ///
  // Fixtures
  sl.registerLazySingleton<BasicFixturesRepository>(() =>
      BasicFixturesRepositoryImpl(
          localDataSource: sl(), networkInfo: sl(), remoteDataSource: sl()));
  // Prediction
  sl.registerLazySingleton<PredictionRepository>(() => PredictionRepositoryImpl(
      localDataSource: sl(), networkInfo: sl(), remoteDataSource: sl()));

  /// Data Source
  /// Remote Date Source
  ///
  // Fixtures
  sl.registerLazySingleton<BasicFixturesRemoteDataSource>(() =>
      BasicFixturesRemoteDataSourceImpl(client: sl(), validateToken: sl()));
  // Prediction
  sl.registerLazySingleton<PredictionsRemoteDataSource>(
      () => PredictionsRemoteDataSourceImpl(client: sl(), validateToken: sl()));

  /// Local Data Source
  ///
  // Fixtures
  sl.registerLazySingleton<BasicFixturesLocalDataSource>(
      () => BasicFixturesLocalDataSourceImpl(sharedPreferences: sl()));
  // Prediction
  sl.registerLazySingleton<PredictionsLocalDataSource>(
      () => PredictionLocalDataSourceImpl(sharedPreferences: sl()));

  // External
  sl.registerLazySingleton<NetworkInfo>(() => NetworkInfoImpl(sl()));
  final sharedPreferences = await SharedPreferences.getInstance();
  sl.registerLazySingleton(() => sharedPreferences);
  sl.registerLazySingleton(() => http.Client());
  sl.registerLazySingleton(() => Connectivity());
}

Everything is working fine except the bloc is is not changing its state. It’s stuck in initial state. The code is working properly as it is making API request to the server.

Solution

The problem is that you have registered NavBarBloc as a factory. So whenever you call sl<NavBarBloc>() a new instance of the bloc is created.

Thus when you have called sl<NavBarBloc>().add(HomePageSelectEvent()), the event is added to another instance, not the one you have passed to bloc builder.

UPDATE:
Two short examples:

  1. Using BlocProvider:
...
BlocProvider(
  create: (_) => sl<NavBarBloc>()..add(add(HomePageSelectEvent()),
  ...
)
...
  1. Saving the instance in you widget:
...
NavBarBloc navBarBloc;

@override
void initState() {
  super.initState();
  navBarBloc = sl<NavBarBloc>()..add(HomePageSelectEvent());
}

@override
void dispose() {
  navBarBloc.close();
  super.dispose();
}

@override
Widget build(BuildContext context) {
  return Scaffold(
    backgroundColor: Colors.black,
    bottomSheet: NavBar(),
    body: BlocProvider.value(
      value: navBarBloc,
...

Answered By – Ali Alizadeh

Answer Checked By – Gilberto Lyons (FlutterFixes Admin)

Leave a Reply

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