hooked_bloc

Creator: coderz1093

Last updated:

Add to Cart

Description:

hooked bloc

Hooked Bloc #
Flutter package that simplifies injection and usage of Bloc/Cubit.
The library is based on the concept of hooks originally introduced in React Native and adapted to Flutter.
Flutter hooks allow you to extract view's logic into common
use cases and reuse them, which makes writing widgets faster and easier.
Contents #


Motivation
Setup
Basics

useBloc
useBlocFactory
useBlocBuilder
useBlocComparativeBuilder
useBlocListener
useBlocComparativeListener
useActionListener


Contribution

Motivation #
When you want to use Bloc/Cubit in your application you have to provide an instance of the object down the widgets tree
for state receivers. This is mostly achieved by BlocBuilder along with BlocProvider and enlarges complexity of the
given widget.
Each time you have to use BlocBuilder, BlocListener or BlocSelector. What if we could use the power of Flutter
hooks?
So, instead of this:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: ...,
body: BlocProvider<RealLifeCubit>(
create: (context) =>
RealLifeCubit()
..loadData(),
child: BlocListener<RealLifeCubit, hooked.BuildState>(
listenWhen: (_, state) => state is ErrorState,
listener: (context, state) {
// Show some view on event
},
child: BlocBuilder<RealLifeCubit, hooked.BuildState>(
buildWhen: (_, state) =>
[LoadedState, LoadingState, ShowItemState]
.contains(state.runtimeType),
builder: (BuildContext context, hooked.BuildState state) {
return // Build your widget using `state`
},
),
),
),
);
}

copied to clipboard
We can have this:
@override
Widget build(BuildContext context) {
final cubit = useBloc<RealLifeCubit>();

useBlocListener<RealLifeCubit, BuildState>(cubit, (cubit, value, context) {
// Show some view on event
}, listenWhen: (state) => state is ErrorState);

final state = useBlocBuilder(
cubit,
buildWhen: (state) =>
[LoadedState, LoadingState, ShowItemState].contains(
state.runtimeType,
),
);

return // Build your widget using `state`
}
copied to clipboard
This code is functionally equivalent to the previous example. It still rebuilds the widget in the proper way and the
right time. Whole logic of finding adequate Cubit/Bloc and providing current state is hidden in useBloc
and useBlocBuilder hooks.
Full example can be found in here
Setup #
Install package
Run command:
flutter pub add hooked_bloc
copied to clipboard
Or manually add the dependency in the pubspec.yaml
dependencies:
# Library already contains flutter_hooks package
hooked_bloc:
copied to clipboard
After that you can (it's optional) initialize the HookedBloc:
void main() async {
// With GetIt or Injectable
await configureDependencies();

runApp(
HookedBlocConfigProvider(
injector: () => getIt.get,
builderCondition: (state) => state != null, // Global build condition
listenerCondition: (state) => state != null, // Global listen condition
child: const MyApp(),
)
);

// Or you can omit HookedBlocInjector(...)
// and allow library to find the cubit in the widget tree
}
copied to clipboard
Then you can simply start writing your widget with hooks
// Remember to inherit from HookWidget
class MyApp extends HookWidget {
const MyApp({Key? key}) : super(key: key);

@override
Widget build(BuildContext context) {
// At start obtain a cubit instance
final cubit = useBloc<CounterCubit>();
// Then observe state's updates
// `buildWhen` param will override builderCondition locally
final state = useBlocBuilder(cubit, buildWhen: (state) => state <= 10);
// Create a listener for the side-effect
useBlocListener(cubit, (cubit, value, context) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text("Button clicked"),
));
});

// Build widget's tree without BlocProvider
return MaterialApp(
home: Scaffold(
floatingActionButton: FloatingActionButton(
onPressed: () => cubit.increment(), // Access cubit in tree
child: const Icon(Icons.add),
),
// Consume state without BlocBuilder
body: Center(child: Text("The button has been pressed $state times")),
),
);
}
}
copied to clipboard
Basics #
Existing hooks #
Hooked Bloc already comes with a few reusable hooks:


Name
Description


useBloc
Returns required Cubit/Bloc


useBlocFactory
Returns expected Cubit/Bloc by creating it via provided factory


useBlocBuilder
Returns current Cubit/Bloc state - similar to BlocBuilder


useBlocComparativeBuilder
Returns current Cubit/Bloc state basing on comparison result


useBlocListener
Invokes callback - similar to BlocListener


useBlocComparativeListener
Invokes callback basing on state comparison result


useActionListener
Invokes callback, but independent of Bloc/Cubit state


useBloc #
useBloc hook tries to find Cubit using the cubit provider, or - if not specified - looks into the widget tree.
@override
Widget build(BuildContext context) {
// The hook will provide the expected object
final cubit = useBloc<SimpleCubit>(
// For default hook automatically closes cubit
closeOnDispose: true,
);

return // Access provided cubit
}
copied to clipboard
useBlocFactory #
useBlocFactory hook tries to find factory using provided injection method and then returns cubit created by it
class SimpleCubitFactory extends BlocFactory<SimpleCubit> {
bool _value = true;

@override
SimpleCubit create() {
return _value ? SimpleCubitA() : SimpleCubitB();
}

// This is example method which you can add to configure your cubit creation on run
void configure(bool value) {
_value = value;
}
}

@override
Widget build(BuildContext context) {
// The hook will provide the expected object
final cubit = useBlocFactory<SimpleCubit, SimpleCubitFactory>(
onCubitCreate: (cubitFactory) {
cubitFactory.configure(false);
}
);

return // Access provided cubit
}
copied to clipboard
useBlocBuilder #
useBlocBuilder hook rebuilds the widget when new state appears

final CounterCubit cubit = CounterCubit("My cubit");

@override
Widget build(BuildContext context) {
// The state will be updated along with the widget
// For default the state will be updated basing on `builderCondition`
final int state = useBlocBuilder(cubit);

return // Access provided state
}

copied to clipboard
useBlocComparativeBuilder #
useBlocComparativeBuilder hook rebuilds the widget for a new state and positive comparison result

final CounterCubit cubit = CounterCubit("My cubit");

@override
Widget build(BuildContext context) {
// The state will be updated along with the widget
// We can compare state's changes to allow rebuild
final state = useBlocComparativeBuilder(
cubit,
buildWhen: (int previous, int current) {
return current != previous;
},
);

return // Access provided state
}

copied to clipboard
useBlocListener #
useBlocListener hook allows to observe cubit's states that represent action (e.g. show Snackbar)

final EventCubit cubit = EventCubit();

@override
Widget build(BuildContext context) {
// Handle state as event independently of the view state
useBlocListener(cubit, (_, value, context) {
_showMessage(context, (value as ShowMessage).message);
}, listenWhen: (state) => state is ShowMessage);

return // Build your widget
}
copied to clipboard
useBlocComparativeListener #
useBlocComparativeListener hook allows to observe and compare cubit's states that represent action (e.g. show Snackbar)

final EventCubit cubit = EventCubit();

@override
Widget build(BuildContext context) {
// Handle state as event independently of the view state
// We can compare state changes to allow listener function to be called
useBlocComparativeListener(
cubit,
(_, value, context) {
_showMessage(context, (value as ShowMessage).message);
},
listenWhen: (previousState, currentState) => previousState is! ShowMessage && currentState is ShowMessage,
);

return // Build your widget
}
copied to clipboard
useActionListener #
useActionListener hook is similar to the useBlocListener but listens to the stream different than state's stream
and can be used for actions that require a different flow of notifying.
Because of that your bloc/cubit must use BlocActionMixin
class MessageActionCubit extends EventCubit with BlocActionMixin<String, BuildState> {

// The method used to publish events
@override
void dispatch(String action) {
super.dispatch(action);
}
}
copied to clipboard
Then, consume results as you would do with useBlocListener
@override
Widget build(BuildContext context) {
// Handle separate action stream with values other than a state type
useActionListener(
cubit,
(String action) {
_showMessage(context, action);
},
// If you need, you can filter actions
actionWhen: (previousAction, action) => true,
);

return // Build your widget
}

copied to clipboard
Instead of BlocActionMixin you can use one of our classes: ActionCubit or ActionBloc
class MessageActionCubit extends ActionCubit<BuildState, String> {
// The method used to publish events
@override
void dispatch(String action) {
super.dispatch(action);
}
}
copied to clipboard
class MessageActionBloc extends ActionBloc<BuildState, BlocEvent, String> {
// The method used to publish events
@override
void dispatch(String action) {
super.dispatch(action);
}
}
copied to clipboard
Contribution #
We accept any contribution to the project!
Suggestions of a new feature or fix should be created via pull-request or issue.
feature request: #


Check if feature is already addressed or declined


Describe why this is needed
Just create an issue with label enhancement and descriptive title. Then, provide a description and/or example code.
This will help the community to understand the need for it.


Write tests for your hook
The test is the best way to explain how the proposed hook should work. We demand a complete test before any code is
merged in order to ensure cohesion with existing codebase.


Add it to the README and write documentation for it
Add a new hook to the existing hooks table and append sample code with usage.


Fix #


Check if bug was already found


Describe what is broken
The minimum requirement to report a bug fix is a reproduction path. Write steps that should be followed to find a
problem in code. Perfect situation is when you give full description why some code doesn't work and a solution code.


Write tests for your hook
The test should show that your fix corrects the problem. You can start with straightforward test and then think about
potential edge cases or other places that can be broken.


Add it to the README and write documentation for it
If your fix changed behavior of the library or requires any other extra steps from user, this should be fully
described in README.


Contributors #

License

For personal and professional use. You cannot resell or redistribute these repositories in their original state.

Customer Reviews

There are no reviews.