lightweight_bloc

A lightweight implementation of the Bloc pattern

Purpose

This package loosely follows the Bloc pattern with minimized boilerplate code as the top priority.

Insipiration

lightweight_bloc is heavily inspired by flutter_bloc. If you want a highly scalable and featrue-rich bloc package, use it instead.

Usage

BlocProvider

Provider a Bloc to all the nodes below it in the widget trees.

BlocProvider(
    child: AScreen()
    builder: (context) => ABloc()
)

Then in the child widgets, you can access ABloc like this

final aBloc = BlocProvider.of<ABloc>(context);

Bloc

Handles business logic and provides States to observers. Every bloc should:

  • Extend the Bloc class and pass in it’s State class.
class ABloc extends Bloc<AState>{
}
  • Provides initialState, which is the state that this Bloc starts with.
AState get initialState => AState(state: Loading)
  • Define event handling functions, usually starts with “on…”,
  • Use latestState to access the latest State object up until then.
  • Then use update to emits a new State. This State should usually be a clone from the previous State, with different State identifier/data.
void onUserRefreshPage() async {
  if(latestState.stateId == AState.DoneLoading){
    updateState(latestState.copyWith(stateId: AState.Loading);
    // Fetch new data
  }
}

State

Data output of a Bloc. Every state should:

  • Have State identifiers.
  • Have a copyWith function to clone the current State with some new data.
class AState {
  static const String Loading = "AStateLoading";
  static const String DoneLoading = "AStateDoneLoading";
  
  String stateId;
  
  AState copyWith({String stateId}) => AState(
    stateId: stateId ?? this.stateId
  );
}

BlocListener

A flutter widget provides a listener function that is guaranteed to be called only once every time a bloc’s state changes. This should be used to handle one-time effecs in the UI like navigation, showing a dialog, using controllers.

 return BlocListener<ABloc, AState>(
  child: ...
  listener: (context, bloc, state){
    if(state.stateId == AState.DoneLoading){
      showSnackBar("New data loaded");
    }
  }
 );

BlocWidgetBuilder

A flutter widget provides a builder function to render the widgets with the associated State.

 return BlocListener<ABloc, AState>(
  child: ...
  builder: (context, bloc, state){
    if(state.stateId == AState.DoneLoading){
      return _buildPage(state);
    } else {
      return _buildLoadingIndicator();
    }
  }
 );

Tips and Tricks

Nullable

Sometimes with new events, we want to clear some data in the State. So we might do something like latestState.copyWith(data: null). However, copyWith doesn’t understand and thinks we didn’t pass any value to it.

class AState {
  int data;
  
  // Passing null is useless, copyWith will always get the current data
  AState copyWith({int data}) => AState(
    data: data ?? this.data
  );
}

In these cases, we can use Nullable to differentiate between passing a null value and not passing any value at all.

class AState {
  int data;
  
  // Passing null is useless, copyWith will always get the current data
  AState copyWith({Nullable<int> data}) => AState(
    data: data != null ? data.value : this.value
  );
}

Plugin

Dart Data Class to generate data class with copyWith function.

Libraries

lightweight_bloc