Why BlocBuilder is stuck in the initial state while using get_it?

Issue

I’m using flutter_bloc to manage the states of my app, and get_it to inject the needed dependencies following the idea suggested by the Reso Coder’s Flutter Clean Architecture Proposal.

Everything is working fine except that the bloc is not changing its state (it’s stuck in the initial state)

Here is the code of the involved classes:

The States

abstract class PaintingsState extends Equatable {
  final properties = const <dynamic>[];

  PaintingsState([properties]);

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

class PaintingsLoading extends PaintingsState {}

class PaintingsLoaded extends PaintingsState {
  final PaintingCardItems cardItems;

  PaintingsLoaded({@required this.cardItems}) : super([cardItems]);
}

class Error extends PaintingsState {
  final String message;

  Error({@required this.message}) : super([message]);
}

The Events

abstract class PaintingsEvent extends Equatable {
  const PaintingsEvent();

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

/// Tells the bloc that it needs to load the paintings from the PaintingsRepository
class GetPaintings extends PaintingsEvent {}

The Bloc

const String FILE_NOT_FOUND_MESSAGE = 'FileNotFound Failure';

class PaintingsBloc extends Bloc<PaintingsEvent, PaintingsState> {
  final GetPaintingCardItems getCardItems;

  PaintingsBloc({@required this.getCardItems}) : super(PaintingsLoading());

  @override
  Stream<PaintingsState> mapEventToState(PaintingsEvent event) async* {
    if (event is GetPaintings) {
      yield* _mapGetPaintingsToState();
    } 
  }

  Stream<PaintingsState> _mapGetPaintingsToState() async* {
    yield PaintingsLoading();
    final failureOrPaintingCardItems = await getCardItems(NoParams());
    yield failureOrPaintingCardItems.fold(
        (failure) => Error(message: _mapFailureToMessage(failure)),
        (paintingCardItems) => PaintingsLoaded(cardItems: paintingCardItems));
  }

  String _mapFailureToMessage(Failure failure) {
    switch (failure.runtimeType) {
      case FileNotFound:
        return FILE_NOT_FOUND_MESSAGE;
      default:
        return 'Unexpected error';
    }
  }
}

Dependencies injection

/// Ambient variable to access the service locator
final sl = GetIt.instance;

/// Set up all the objects you want to access later through the service locator [sl]
void setUpServiceLocator() {
  initFeatures();
}

void initFeatures() {
  //! Features - Paintings
  // Bloc
  sl.registerLazySingleton<PaintingsBloc>(() => PaintingsBloc(getCardItems: sl<GetPaintingCardItems>()));

  // Use cases
  sl.registerLazySingleton<GetPaintingCardItems>(() => GetPaintingCardItems(sl<PaintingsRepository>()));

  // Repository
  sl.registerLazySingleton<PaintingsRepository>(
      () => PaintingsRepositoryImpl(dataSource: sl<PaintingsDataSource>()));

  // Data sources
  sl.registerLazySingleton<PaintingsDataSource>(() => PaintingsDataSourceImpl());    
}

main.dart

void main() {
  // dependencies injection
  setUpServiceLocator();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider<PaintingsBloc>(
      create: (_) => sl<PaintingsBloc>(),
      child: MaterialApp(
        title: 'My Paintings',
        theme: appTheme,
        initialRoute: '/',
        onGenerateRoute: RouteGenerator.generateRoute,
      ),
    );
  }
}

Page where I use BlocBuilder

class PaintingsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
       ...
      ),
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Stack(
          children: <Widget>[
            SafeArea(
                child: Column(
                ...
                BlocBuilder<PaintingsBloc, PaintingsState>(
                  builder: (context, state) {
                    if(state is PaintingsLoading) {
                      return Container(
                        child: Center(
                           child: CircularProgressIndicator(),
                        ),
                      );
                    } else if(state is PaintingsLoaded) {
                      List<PaintingCardItem> _list = state.cardItems.paintingCardItems;
                      return Expanded(
                            child: SizedBox(
                              child: _list.length != 0
                                  ? ListCardView(
                                      cardItems: _list)
                                  : Container(
                                      child: Center(child: Text('Empty list'))),
                            ),
                          );
                    } else if(state is Error){
                      return Container(
                            child: Center(child: Text(state.message)));
                    } else {
                       return Container(
                            child: Center(child: Text('Unknown Error')));
                    }
                  }
                )
              ],
            ))
          ],
        ),
      ),
    );
  }
}

So, somehow the state of the bloc does not change from PaintingsLoading to either PaintingsLoaded or Error.

If someone can give me some idea to solve this problem, I will really appreciate it.

Solution

I solved it, I just needed to add the event to the bloc. So, my solution was to create another state called PaintingsInitialState like so:

The States

...
class PaintingsInitialState extends PaintingsState {}
...

Then in the Bloc, I just changed the constructor of the bloc.

PaintingsBloc({@required this.getCardItems}) : super(PaintingsInitialState());`

Finally, I added the following condition inside the builder parameter of the BlocBuilder.

if (state is PaintingsInitialState) {
  _paintingsBloc.add(GetPaintings());
}

I think that the information provided in the offitial site of the bloc library can be useful to understand how to use bloc pattern and libraries properly – particularly Flutter Wheather Tutorial.

Answered By – Yunet Luis Ruíz

Answer Checked By – Marie Seifert (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.