{"id":13550951,"url":"https://github.com/Iteo/hooked_bloc","last_synced_at":"2025-04-03T01:30:44.823Z","repository":{"id":39635577,"uuid":"454301943","full_name":"Iteo/hooked_bloc","owner":"Iteo","description":null,"archived":false,"fork":false,"pushed_at":"2025-01-28T11:37:19.000Z","size":349,"stargazers_count":63,"open_issues_count":1,"forks_count":5,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-03-24T02:51:51.729Z","etag":null,"topics":["bloc","flutter","flutter-hooks","hooks","state-management"],"latest_commit_sha":null,"homepage":"https://www.iteo.com","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Iteo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2022-02-01T07:50:05.000Z","updated_at":"2025-01-28T11:37:23.000Z","dependencies_parsed_at":"2024-01-16T10:25:04.469Z","dependency_job_id":"8f41c7ca-ae74-4da9-be28-953842532dd7","html_url":"https://github.com/Iteo/hooked_bloc","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Iteo%2Fhooked_bloc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Iteo%2Fhooked_bloc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Iteo%2Fhooked_bloc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Iteo%2Fhooked_bloc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Iteo","download_url":"https://codeload.github.com/Iteo/hooked_bloc/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246837679,"owners_count":20841902,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bloc","flutter","flutter-hooks","hooks","state-management"],"created_at":"2024-08-01T12:01:40.143Z","updated_at":"2025-04-03T01:30:43.392Z","avatar_url":"https://github.com/Iteo.png","language":"Dart","funding_links":[],"categories":["Dart"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n\u003cimg width=\"200\" src=\"https://github.com/Iteo/hooked_bloc/raw/main/hooked_bloc_icon.png\"\u003e\n\u003cbr /\u003e\u003cbr /\u003e\n\n[![Build](https://github.com/Iteo/hooked_bloc/workflows/Build/badge.svg)](https://github.com/Iteo/hooked_bloc/actions?query=workflow%3ATest)\n\u0026nbsp;\n[![codecov](https://codecov.io/gh/Iteo/hooked_bloc/branch/main/graph/badge.svg)](https://codecov.io/gh/Iteo/hooked_bloc)\n\u0026nbsp;\n[![pub package](https://img.shields.io/pub/v/hooked_bloc.svg)](https://pub.dartlang.org/packages/hooked_bloc) \u0026nbsp;\n[![stars](https://img.shields.io/github/stars/Iteo/hooked_bloc.svg?style=flat\u0026logo=github\u0026colorB=deeppink\u0026label=stars)](https://github.com/Iteo/hooked_bloc)\n\u0026nbsp;\n[![GitHub license](https://img.shields.io/github/license/Iteo/hooked_bloc)](https://github.com/Iteo/hooked_bloc/blob/main/LICENSE) \u0026nbsp;\n\n\u003c/div\u003e\n\n---\n\n# Hooked Bloc\n\nFlutter package that simplifies injection and usage of \u003ca href=\"https://pub.dev/packages/flutter_bloc\"\u003e Bloc/Cubit\u003c/a\u003e.\nThe library is based on the concept of hooks originally introduced in React Native and adapted to Flutter.\n\u003ca href=\"https://github.com/rrousselGit/flutter_hooks\"\u003eFlutter hooks\u003c/a\u003e allow you to extract view's logic into common\nuse cases and reuse them, which makes writing widgets faster and easier.\n\n## Contents\n\n\u003c!-- pub.dev accepts anchors only with lowercase --\u003e\n\n- [Motivation](#motivation)\n- [Setup](#setup)\n- [Basics](#basics)\n  - [useBloc](#usebloc)\n  - [useBlocFactory](#useblocfactory)\n  - [useBlocBuilder](#useblocbuilder)\n  - [useBlocComparativeBuilder](#usebloccomparativebuilder)\n  - [useBlocListener](#usebloclistener)\n  - [useBlocComparativeListener](#usebloccomparativelistener)\n  - [useActionListener](#useactionlistener)\n- [Contribution](#contribution)\n\n## Motivation\n\nWhen you want to use Bloc/Cubit in your application you have to provide an instance of the object down the widgets tree\nfor state receivers. This is mostly achieved by `BlocBuilder` along with `BlocProvider` and enlarges complexity of the\ngiven widget.\n\nEach time you have to use `BlocBuilder`, `BlocListener` or `BlocSelector`. What if we could use the power of Flutter\nhooks?\n\nSo, instead of this:\n\n```dart\n  @override\nWidget build(BuildContext context) {\n  return Scaffold(\n    appBar: ...,\n    body: BlocProvider\u003cRealLifeCubit\u003e(\n      create: (context) =\u003e\n      RealLifeCubit()\n        ..loadData(),\n      child: BlocListener\u003cRealLifeCubit, hooked.BuildState\u003e(\n        listenWhen: (_, state) =\u003e state is ErrorState,\n        listener: (context, state) {\n          // Show some view on event\n        },\n        child: BlocBuilder\u003cRealLifeCubit, hooked.BuildState\u003e(\n          buildWhen: (_, state) =\u003e\n              [LoadedState, LoadingState, ShowItemState]\n                  .contains(state.runtimeType),\n          builder: (BuildContext context, hooked.BuildState state) {\n            return // Build your widget using `state`\n          },\n        ),\n      ),\n    ),\n  );\n}\n\n```\n\nWe can have this:\n\n```dart\n  @override\nWidget build(BuildContext context) {\n  final cubit = useBloc\u003cRealLifeCubit\u003e();\n\n  useBlocListener\u003cRealLifeCubit, BuildState\u003e(cubit, (cubit, value, context) {\n    // Show some view on event\n  }, listenWhen: (state) =\u003e state is ErrorState);\n\n  final state = useBlocBuilder(\n    cubit,\n    buildWhen: (state) =\u003e\n        [LoadedState, LoadingState, ShowItemState].contains(\n          state.runtimeType,\n        ),\n  );\n\n  return // Build your widget using `state`\n}\n```\n\nThis code is functionally equivalent to the previous example. It still rebuilds the widget in the proper way and the\nright time. Whole logic of finding adequate Cubit/Bloc and providing current state is hidden in `useBloc`\nand `useBlocBuilder` hooks.\n\nFull example can be found in \u003ca href=\"https://github.com/Iteo/hooked_bloc/tree/main/example\"\u003ehere\u003c/a\u003e\n\n## Setup\n\nInstall package\n\nRun command:\n\n```shell\nflutter pub add hooked_bloc\n```\n\nOr manually add the dependency in the `pubspec.yaml`\n\n```yaml\ndependencies:\n  # Library already contains flutter_hooks package\n  hooked_bloc:\n```\n\nAfter that you can (it's optional) initialize the HookedBloc:\n\n```dart\nvoid main() async {\n  // With GetIt or Injectable\n  await configureDependencies();\n\n  runApp(\n    HookedBlocConfigProvider(\n      injector: () =\u003e getIt.get,\n      builderCondition: (state) =\u003e state != null, // Global build condition\n      listenerCondition: (state) =\u003e state != null, // Global listen condition\n      child: const MyApp(),\n    )\n  );\n\n  // Or you can omit HookedBlocInjector(...)\n  // and allow library to find the cubit in the widget tree\n}\n```\n\nThen you can simply start writing your widget with hooks\n\n```dart\n// Remember to inherit from HookWidget\nclass MyApp extends HookWidget {\n  const MyApp({Key? key}) : super(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    // At start obtain a cubit instance\n    final cubit = useBloc\u003cCounterCubit\u003e();\n    // Then observe state's updates\n    // `buildWhen` param will override builderCondition locally\n    final state = useBlocBuilder(cubit, buildWhen: (state) =\u003e state \u003c= 10);\n    // Create a listener for the side-effect\n    useBlocListener(cubit, (cubit, value, context) {\n      ScaffoldMessenger.of(context).showSnackBar(\n          const SnackBar(content: Text(\"Button clicked\"),\n          ));\n    });\n\n    // Build widget's tree without BlocProvider\n    return MaterialApp(\n      home: Scaffold(\n        floatingActionButton: FloatingActionButton(\n          onPressed: () =\u003e cubit.increment(), // Access cubit in tree\n          child: const Icon(Icons.add),\n        ),\n        // Consume state without BlocBuilder\n        body: Center(child: Text(\"The button has been pressed $state times\")),\n      ),\n    );\n  }\n}\n```\n\n## Basics\n\n### Existing hooks\n\nHooked Bloc already comes with a few reusable hooks:\n\n\u003ctable\u003e\n  \u003ctr\u003e\n      \u003cth\u003eName\u003c/th\u003e\n      \u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003euseBloc\u003c/td\u003e\n    \u003ctd\u003eReturns required Cubit/Bloc\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003euseBlocFactory\u003c/td\u003e\n    \u003ctd\u003eReturns expected Cubit/Bloc by creating it via provided factory\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003euseBlocBuilder\u003c/td\u003e\n    \u003ctd\u003eReturns current Cubit/Bloc state - similar to BlocBuilder\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003euseBlocComparativeBuilder\u003c/td\u003e\n    \u003ctd\u003eReturns current Cubit/Bloc state basing on comparison result\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003euseBlocListener\u003c/td\u003e\n    \u003ctd\u003eInvokes callback - similar to BlocListener\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003euseBlocComparativeListener\u003c/td\u003e\n    \u003ctd\u003eInvokes callback basing on state comparison result\u003c/td\u003e\n  \u003c/tr\u003e\n\n  \u003ctr\u003e\n    \u003ctd\u003euseActionListener\u003c/td\u003e\n    \u003ctd\u003eInvokes callback, but independent of Bloc/Cubit state\u003c/td\u003e\n  \u003c/tr\u003e\n\n\u003c/table\u003e\n\n### useBloc\n\n`useBloc` hook tries to find Cubit using the cubit provider, or - if not specified - looks into the widget tree.\n\n```dart\n  @override\nWidget build(BuildContext context) {\n  // The hook will provide the expected object\n  final cubit = useBloc\u003cSimpleCubit\u003e(\n    // For default hook automatically closes cubit\n    closeOnDispose: true,\n  );\n\n  return // Access provided cubit\n}\n```\n\n### useBlocFactory\n\n`useBlocFactory` hook tries to find factory using provided injection method and then returns cubit created by it\n\n```dart\nclass SimpleCubitFactory extends BlocFactory\u003cSimpleCubit\u003e {\n  bool _value = true;\n\n  @override\n  SimpleCubit create() {\n    return _value ? SimpleCubitA() : SimpleCubitB();\n  }\n\n  // This is example method which you can add to configure your cubit creation on run\n  void configure(bool value) {\n    _value = value;\n  }\n}\n\n@override\nWidget build(BuildContext context) {\n  // The hook will provide the expected object\n  final cubit = useBlocFactory\u003cSimpleCubit, SimpleCubitFactory\u003e(\n    onCubitCreate: (cubitFactory) {\n      cubitFactory.configure(false);\n    }\n  );\n\n  return // Access provided cubit\n}\n```\n\n### useBlocBuilder\n\n`useBlocBuilder` hook rebuilds the widget when new state appears\n\n```dart\n\nfinal CounterCubit cubit = CounterCubit(\"My cubit\");\n\n@override\nWidget build(BuildContext context) {\n  // The state will be updated along with the widget\n  // For default the state will be updated basing on `builderCondition`\n  final int state = useBlocBuilder(cubit);\n\n  return // Access provided state\n}\n\n```\n\n### useBlocComparativeBuilder\n\n`useBlocComparativeBuilder` hook rebuilds the widget for a new state and positive comparison result\n\n```dart\n\nfinal CounterCubit cubit = CounterCubit(\"My cubit\");\n\n@override\nWidget build(BuildContext context) {\n  // The state will be updated along with the widget\n  // We can compare state's changes to allow rebuild\n  final state = useBlocComparativeBuilder(\n    cubit,\n    buildWhen: (int previous, int current) {\n      return current != previous;\n    },\n  );\n\n  return // Access provided state\n}\n\n```\n\n### useBlocListener\n\n`useBlocListener` hook allows to observe cubit's states that represent action (e.g. show Snackbar)\n\n```dart\n\nfinal EventCubit cubit = EventCubit();\n\n@override\nWidget build(BuildContext context) {\n  // Handle state as event independently of the view state\n  useBlocListener(cubit, (_, value, context) {\n    _showMessage(context, (value as ShowMessage).message);\n  }, listenWhen: (state) =\u003e state is ShowMessage);\n\n  return // Build your widget\n}\n```\n\n### useBlocComparativeListener\n\n`useBlocComparativeListener` hook allows to observe and compare cubit's states that represent action (e.g. show Snackbar)\n\n```dart\n\nfinal EventCubit cubit = EventCubit();\n\n@override\nWidget build(BuildContext context) {\n  // Handle state as event independently of the view state\n  // We can compare state changes to allow listener function to be called\n  useBlocComparativeListener(\n    cubit,\n    (_, value, context) {\n       _showMessage(context, (value as ShowMessage).message);\n    },\n    listenWhen: (previousState, currentState) =\u003e previousState is! ShowMessage \u0026\u0026 currentState is ShowMessage,\n    );\n\n  return // Build your widget\n}\n```\n\n### useActionListener\n\n`useActionListener` hook is similar to the `useBlocListener` but listens to the stream different than state's stream\nand can be used for actions that require a different flow of notifying.\n\nBecause of that your bloc/cubit must use `BlocActionMixin`\n\n```dart\nclass MessageActionCubit extends EventCubit with BlocActionMixin\u003cString, BuildState\u003e {\n\n  // The method used to publish events\n  @override\n  void dispatch(String action) {\n    super.dispatch(action);\n  }\n}\n```\n\nThen, consume results as you would do with `useBlocListener`\n\n```dart\n  @override\nWidget build(BuildContext context) {\n  // Handle separate action stream with values other than a state type\n  useActionListener(\n    cubit, \n    (String action) {\n      _showMessage(context, action);\n    },\n    // If you need, you can filter actions\n    actionWhen: (previousAction, action) =\u003e true,\n  );\n\n  return // Build your widget\n}\n\n```\n\nInstead of `BlocActionMixin` you can use one of our classes: `ActionCubit` or `ActionBloc`\n\n```dart\nclass MessageActionCubit extends ActionCubit\u003cBuildState, String\u003e  {\n  // The method used to publish events\n  @override\n  void dispatch(String action) {\n    super.dispatch(action);\n  }\n}\n```\n\n```dart\nclass MessageActionBloc extends ActionBloc\u003cBuildState, BlocEvent, String\u003e  {\n  // The method used to publish events\n  @override\n  void dispatch(String action) {\n    super.dispatch(action);\n  }\n}\n```\n\n## Contribution\n\nWe accept any contribution to the project!\n\nSuggestions of a new feature or fix should be created via pull-request or issue.\n\n### feature request:\n\n- Check if feature is already addressed or declined\n\n- Describe why this is needed\n\n  Just create an issue with label `enhancement` and descriptive title. Then, provide a description and/or example code.\n  This will help the community to understand the need for it.\n\n- Write tests for your hook\n\n  The test is the best way to explain how the proposed hook should work. We demand a complete test before any code is\n  merged in order to ensure cohesion with existing codebase.\n\n- Add it to the README and write documentation for it\n\n  Add a new hook to the existing hooks table and append sample code with usage.\n\n### Fix\n\n- Check if bug was already found\n\n- Describe what is broken\n\n  The minimum requirement to report a bug fix is a reproduction path. Write steps that should be followed to find a\n  problem in code. Perfect situation is when you give full description why some code doesn't work and a solution code.\n\n- Write tests for your hook\n\n  The test should show that your fix corrects the problem. You can start with straightforward test and then think about\n  potential edge cases or other places that can be broken.\n\n- Add it to the README and write documentation for it\n\n  If your fix changed behavior of the library or requires any other extra steps from user, this should be fully\n  described in README.\n\n## Contributors\n\n\u003cdiv align=\"left\"\u003e\n  \u003ca href=\"https://github.com/Iteo/hooked_bloc/graphs/contributors\"\u003e\n   \u003cimg src=\"https://contrib.rocks/image?repo=Iteo/hooked_bloc\"/\u003e\n  \u003c/a\u003e\n\u003c/div\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FIteo%2Fhooked_bloc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FIteo%2Fhooked_bloc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FIteo%2Fhooked_bloc/lists"}