skip to Main Content

I initially created one Cubit to manage the state of my screen, but as it became too complex, I refactored it into four separate Cubits. Now, to make the screen function as intended, I had to implement communication between these Cubits using listeners and buidlwhen, which has resulted in messy and difficult-to-read code. Is there a better way to handle this?

To illustrate, imagine a screen with four sections:

  • The first three sections contain buttons or actions.
  • Pressing a button or any action calls a data provider, shows different loading states, and updates other sections.
  • The final section displays a list that updates based on these actions.

When the screen initially loads, there is a full-screen loading indicator. The same happens if any error occurs. After any interaction, there are different loading indicators.

Due to the nature of this screen, which results in many combinations of loading and state changes, I’ve tried implementing the strategy pattern. Do you have some examples of complex Flutter apps using BLoC? Do you have any suggestions for writing complex screens in a clean way?

I cant provide a code example, but I hope you get the idea.

2

Answers


  1. Generally, I follow the principle that there is only one BLoC (or Cubit in your case) per screen. This makes it easier to manage and expand everything. If you have different "sections" in your UI, I would approach it as follows. Your state class could look like this:

    part of 'test_bloc.dart';
    
    enum TestStatus {
      initial,
      loading,
      success,
      failure,
    }
    
    extension TestStatusX on TestStatus {
      bool get isInitial => this == TestStatus.initial;
      bool get isLoading => this == TestStatus.loading;
      bool get isSuccess => this == TestStatus.success;
      bool get isFailure => this == TestStatus.failure;
    }
    
    class TestState {
      TestStatus status;
      Failure? failure;
      TestState({required this.status, this.failure});
    
      TestState copyWith({
        TestStatus? status,
        Failure? failure,
      }) {
        return TestState(
          status: status ?? this.status,
          failure: failure ?? this.failure,
        );
      }
    }
    

    For individual sections, I would create corresponding variables that you can control through them: clientsLoading, sectionLoading, etc., name them as you wish. This allows you to manage specific areas of your page while still utilizing the global state with enums. However, these status variables do not define the overall state of your page; they merely control the internal sections of your page.

    If you have multiple states within individual sections beyond the "default" state or when loading, you can indeed create a second enum for each section with their respective states. Then, you can specify these in the state class. This way, you can also represent multiple states for the sections.

    With this structure, you should have complete flexibility.

    Login or Signup to reply.
  2. As it says in the docs, block to block communication should happen either in the UI or in the repository.

    So, for the first case you could organize your UI something like this:

    class FeaturePage extends StatelessWidget {
      const FeaturePage({
        super.key,
      });
    
      @override
      Widget build(BuildContext context) => MultiBlocProvider(
            providers: [
              BlocProvider(create: (_) => FirstCubit()),
              BlocProvider(create: (_) => SecondCubit()),
              BlocProvider(create: (_) => ThirdCubit()),
              BlocProvider(create: (_) => FourthCubit()),
            ],
            child: const FeatureView(),
          );
    }
    
    class FeatureView extends StatelessWidget {
      const FeatureView({
        super.key,
      });
    
      @override
      Widget build(BuildContext context) {
        return MultiBlocListener(
          listeners: [
            BlocListener<FirstCubit, FirstCubitState>(
              listener: (context, firstCubitState) {
                //do stuff here
              },
            ),
            BlocListener<SecondCubit, SecondCubitState>(
              listener: (context, secondCubitState) {
                //do stuff here
              },
            ),
            BlocListener<ThirdCubit, ThirdCubitState>(
              listener: (context, thirdCubitState) {
                //do stuff here
              },
            ),
            BlocListener<FourthCubit, FourthCubitState>(
              listener: (context, fourthCubitState) {
                //do stuff here
              },
            ),
          ],
          child: const Column(
            children: [
              FirstView(),
              SecondView(),
              ThirdView(),
              FourthView(),
            ],
          ),
        );
      }
    }
    

    where:

    class FirstCubitState {}
    
    class FirstCubit extends Cubit<FirstCubitState> {
      FirstCubit() : super(FirstCubitState());
    }
    
    class SecondCubitState {}
    
    class SecondCubit extends Cubit<SecondCubitState> {
      SecondCubit() : super(SecondCubitState());
    }
    
    class ThirdCubitState {}
    
    class ThirdCubit extends Cubit<ThirdCubitState> {
      ThirdCubit() : super(ThirdCubitState());
    }
    
    class FourthCubitState {}
    
    class FourthCubit extends Cubit<FourthCubitState> {
      FourthCubit() : super(FourthCubitState());
    }
    

    also there are widgets:

    class FirstView extends StatelessWidget {
      const FirstView({
        super.key,
      });
    
      @override
      Widget build(BuildContext context) {
        return BlocBuilder<FirstCubit, FirstCubitState>(
          builder: (context, state) => ..first view UI,
        );
      }
    }
    ... and other SecondView, ThirdView, FourthView accordingly.
    

    And there is second case always. You could manage your data in repository without any bloc to bloc directly communication.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search