Prime-Holding / rx_bloc

A set of Flutter packages that help implement the BloC (Business Logic Component) design pattern using the power of reactive streams
https://pub.dev/packages/rx_bloc
63 stars 21 forks source link

Introduce the @RxCoordinatorBloc() annotation to the rx_bloc_generator #855

Open StanevPrime opened 2 weeks ago

StanevPrime commented 2 weeks ago

As a developer I want to use @RxCoordinatorBloc() and @RxCoordinatorBroadcastState() annotations so that I have central place for all annotated states and reduce the code I need to write in the CoordinatorBloc.

ACs

Step 1: Use the @RxCoordinatorBloc() annotation to enable coordinator-specific code generation.

The generator will scan for all @RxCoordinatorBloc() annotated blocs and will automatically generate states in the $CoordinatorBloc, such as $CoordinatorBloc.$profile.states.onProfileChanged

@RxCoordinatorBloc()
class CoordinatorBloc extends $CoordinatorBloc {}

coordinator_bloc.rxb.g.dart

abstract class _CoordinatorProfileBlocEvents {
    void setOnProfileChanged(ProfileModel model); //todo: find a better name
}

abstract class _CoordinatorProfileBlocStates {
  Stream<ProfileModel> get onProfileChanged;
}

abstract class _CoordinatorProfileBlocType extends RxBlocTypeBase {
  _CoordinatorProfileBlocEvents get events;

  _CoordinatorProfileBlocStates get states;
}

abstract class _CoordinatorProfileBloc extends RxBlocBase
    implements _CoordinatorProfileBlocEvents, _CoordinatorProfileBlocStates, _CoordinatorProfileBlocType {
  final _$setOnProfileChangedEvent = PublishSubject<ProfileModel>();
  void setOnProfileChanged(ProfileModel model) => _$setOnProfileChangedEvent.add(model);

  Stream<ProfileModel> get onProfileChanged => _$setOnProfileChangedEvent;
}

abstract class $CoordinatorBloc {
    late final _CoordinatorProfileBlocType $profile = _CoordinatorProfileBloc();
}

Step 2: Annotate the states that should be available in the coordinator bloc

abstract class ProfileBlocStates {
  @RxCoordinatorBroadcastState() // Choose better name
  Stream<ProfileModel> get onProfileChanged;
}

@RxBloc()
class ProfileBloc extends $ProfileBloc {
    ProfileBloc(CoordinatorBlocType coordinator): super(coordinator);
}

profile_bloc.rxb.g.dart

abstract class ProfileBlocType extends RxBlocTypeBase {
  ProfileBlocEvents get events;

  ProfileBlocStates get states;
}

/// $CounterBloc class - extended by the CounterBloc bloc
abstract class $ProfileBloc extends RxBlocBase
    implements ProfileBlocEvents, ProfileBlocStates, ProfileBlocType {

  $ProfileBloc(CoordinatorBlocType coordinatorBloc);

  final CoordinatorBlocType coordinatorBloc;

  late final Stream<int> _onProfileChangedState = 
 _mapToOnProfileChangedState()
    .doOnData(coordinatorBloc.$profile.events.setOnProfileChanged);

  @override
  Stream<int> get onProfileChanged => _onProfileChangedState;
  ...
}

Use cases

Use case 1: Use state listeners for debugging purposes through logs

class AppDependencies{

  late List<SingleChildWidget> providers = [
     ...
     _coordinatorBlocs,
  ];

  List<SingleChildWidget> get _coordinatorBlocs => [
        RxCoordinatorBlocProvider<CoordinatorBlocType>(
          create: (context) => CoordinatorBloc(),
          listeners: (bloc) => [
              // The states of all annotated blocs will be printed for debugging purposes.
              // Example: Coordinator: profile.onProfileChanged: {name: "Georgi Stanev"}
              if (kDebugMode) RxBlocLoggerListener(bloc), 
          ],
        ),
      ];
}

Use case 2: Use the generated states in the other blocs

@RxBloc()
class DashboardBloc extends $DashboardBloc {
    DashboardBloc(this._coordinatorBloc);

    /// The DashboardBloc can listen for the following state:  _coordinator.$profile.states.onProfileChanged
    final CoordinatorBlocType _coordinator
}
RomanovaPrime commented 12 hours ago

While investigating in this task I found a few points to be taken into consideration:

RomanovaPrime commented 12 hours ago

For now, this task will be put on hold. Since its implementation might coincide with the production release of macros, we likely won’t have the opportunity to use it before reworking the entire package to incorporate macros.