{"id":14956369,"url":"https://github.com/rrousselgit/flutter_hooks","last_synced_at":"2025-05-12T20:48:31.285Z","repository":{"id":39526529,"uuid":"159863158","full_name":"rrousselGit/flutter_hooks","owner":"rrousselGit","description":"React hooks for Flutter. Hooks are a new kind of object that manages a Widget life-cycles. They are used to increase code sharing between widgets and as a complete replacement for StatefulWidget.","archived":false,"fork":false,"pushed_at":"2025-03-23T11:42:12.000Z","size":675,"stargazers_count":3225,"open_issues_count":26,"forks_count":183,"subscribers_count":31,"default_branch":"master","last_synced_at":"2025-05-10T20:01:49.925Z","etag":null,"topics":["code-reuse","dart","flutter","hacktoberfest","hook","widget"],"latest_commit_sha":null,"homepage":"","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/rrousselGit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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,"publiccode":null,"codemeta":null},"funding":{"github":"rrousselGit"}},"created_at":"2018-11-30T18:26:39.000Z","updated_at":"2025-05-08T07:04:51.000Z","dependencies_parsed_at":"2023-10-02T11:01:48.987Z","dependency_job_id":"4c43f36d-ab02-4651-81de-6c39c3eb58df","html_url":"https://github.com/rrousselGit/flutter_hooks","commit_stats":{"total_commits":341,"total_committers":68,"mean_commits":5.014705882352941,"dds":"0.39002932551319647","last_synced_commit":"319679b4ff8e4e224b3d9c29fd138623f750a24d"},"previous_names":[],"tags_count":53,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rrousselGit%2Fflutter_hooks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rrousselGit%2Fflutter_hooks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rrousselGit%2Fflutter_hooks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rrousselGit%2Fflutter_hooks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rrousselGit","download_url":"https://codeload.github.com/rrousselGit/flutter_hooks/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253819927,"owners_count":21969443,"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":["code-reuse","dart","flutter","hacktoberfest","hook","widget"],"created_at":"2024-09-24T13:12:54.996Z","updated_at":"2025-05-12T20:48:31.260Z","avatar_url":"https://github.com/rrousselGit.png","language":"Dart","readme":"[English](https://github.com/rrousselGit/flutter_hooks/blob/master/README.md) | [Português](https://github.com/rrousselGit/flutter_hooks/blob/master/packages/flutter_hooks/resources/translations/pt_br/README.md) | [한국어](https://github.com/rrousselGit/flutter_hooks/blob/master/packages/flutter_hooks/resources/translations/ko_kr/README.md) | [简体中文](https://github.com/rrousselGit/flutter_hooks/blob/master/packages/flutter_hooks/resources/translations/zh_cn/README.md) | [日本語](https://github.com/rrousselGit/flutter_hooks/blob/master/packages/flutter_hooks/resources/translations/ja_jp/README.md)\n\n[![Build](https://github.com/rrousselGit/flutter_hooks/workflows/Build/badge.svg)](https://github.com/rrousselGit/flutter_hooks/actions?query=workflow%3ABuild) [![codecov](https://codecov.io/gh/rrousselGit/flutter_hooks/branch/master/graph/badge.svg)](https://codecov.io/gh/rrousselGit/flutter_hooks) [![pub package](https://img.shields.io/pub/v/flutter_hooks.svg)](https://pub.dev/packages/flutter_hooks) [![pub package](https://img.shields.io/badge/Awesome-Flutter-blue.svg?longCache=true\u0026style=flat-square)](https://github.com/Solido/awesome-flutter)\n\u003ca href=\"https://discord.gg/6G6ZWkk3fQ\"\u003e\u003cimg src=\"https://img.shields.io/discord/765557403865186374.svg?logo=discord\u0026color=blue\" alt=\"Discord\"\u003e\u003c/a\u003e\n\n\u003cimg src=\"https://raw.githubusercontent.com/rrousselGit/flutter_hooks/master/packages/flutter_hooks/flutter-hook.svg?sanitize=true\" width=\"200\"\u003e\n\n# Flutter Hooks\n\nA Flutter implementation of React hooks: https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889\n\nHooks are a new kind of object that manage the life-cycle of a `Widget`. They exist\nfor one reason: increase the code-sharing _between_ widgets by removing duplicates.\n\n## Motivation\n\n`StatefulWidget` suffers from a big problem: it is very difficult to reuse the\nlogic of say `initState` or `dispose`. An obvious example is `AnimationController`:\n\n```dart\nclass Example extends StatefulWidget {\n  const Example({super.key, required this.duration});\n\n  final Duration duration;\n\n  @override\n  _ExampleState createState() =\u003e _ExampleState();\n}\n\nclass _ExampleState extends State\u003cExample\u003e with SingleTickerProviderStateMixin {\n  late final AnimationController _controller;\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = AnimationController(vsync: this, duration: widget.duration);\n  }\n\n  @override\n  void didUpdateWidget(Example oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (widget.duration != oldWidget.duration) {\n      _controller.duration = widget.duration;\n    }\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Container();\n  }\n}\n```\n\nAll widgets that desire to use an `AnimationController` will have to reimplement\nalmost all of this logic from scratch, which is of course undesired.\n\nDart mixins can partially solve this issue, but they suffer from other problems:\n\n- A given mixin can only be used once per class.\n- Mixins and the class share the same object.\\\n  This means that if two mixins define a variable under the same name, the result\n  may vary between compilation fails to unknown behavior.\n\n---\n\nThis library proposes a third solution:\n\n```dart\nclass Example extends HookWidget {\n  const Example({super.key, required this.duration});\n\n  final Duration duration;\n\n  @override\n  Widget build(BuildContext context) {\n    final controller = useAnimationController(duration: duration);\n    return Container();\n  }\n}\n```\n\nThis code is functionally equivalent to the previous example. It still disposes the\n`AnimationController` and still updates its `duration` when `Example.duration` changes.\nBut you're probably thinking:\n\n\u003e Where did all the logic go?\n\nThat logic has been moved into `useAnimationController`, a function included directly in\nthis library (see [Existing hooks](https://github.com/rrousselGit/flutter_hooks#existing-hooks)) - It is what we call a _Hook_.\n\nHooks are a new kind of object with some specificities:\n\n- They can only be used in the `build` method of a widget that mix-in `Hooks`.\n- The same hook can be reused arbitrarily many times.\n  The following code defines two independent `AnimationController`, and they are\n  correctly preserved when the widget rebuild.\n\n  ```dart\n  Widget build(BuildContext context) {\n    final controller = useAnimationController();\n    final controller2 = useAnimationController();\n    return Container();\n  }\n  ```\n\n- Hooks are entirely independent of each other and from the widget.\\\n  This means that they can easily be extracted into a package and published on\n  [pub](https://pub.dev/) for others to use.\n\n## Principle\n\nSimilar to `State`, hooks are stored in the `Element` of a `Widget`. However, instead\nof having one `State`, the `Element` stores a `List\u003cHook\u003e`. Then in order to use a `Hook`,\none must call `Hook.use`.\n\nThe hook returned by `use` is based on the number of times it has been called.\nThe first call returns the first hook; the second call returns the second hook,\nthe third call returns the third hook and so on.\n\nIf this idea is still unclear, a naive implementation of hooks could look as follows:\n\n```dart\nclass HookElement extends Element {\n  List\u003cHookState\u003e _hooks;\n  int _hookIndex;\n\n  T use\u003cT\u003e(Hook\u003cT\u003e hook) =\u003e _hooks[_hookIndex++].build(this);\n\n  @override\n  performRebuild() {\n    _hookIndex = 0;\n    super.performRebuild();\n  }\n}\n```\n\nFor more explanation of how hooks are implemented, here's a great article about\nhow it was done in React: https://medium.com/@ryardley/react-hooks-not-magic-just-arrays-cd4f1857236e\n\n## Rules\n\nDue to hooks being obtained from their index, some rules must be respected:\n\n### DO always prefix your hooks with `use`:\n\n```dart\nWidget build(BuildContext context) {\n  // starts with `use`, good name\n  useMyHook();\n  // doesn't start with `use`, could confuse people into thinking that this isn't a hook\n  myHook();\n  // ....\n}\n```\n\n### DO call hooks unconditionally\n\n```dart\nWidget build(BuildContext context) {\n  useMyHook();\n  // ....\n}\n```\n\n### DON'T wrap `use` into a condition\n\n```dart\nWidget build(BuildContext context) {\n  if (condition) {\n    useMyHook();\n  }\n  // ....\n}\n```\n\n---\n\n### About hot-reload\n\nSince hooks are obtained from their index, one may think that hot-reloads while refactoring will break the application.\n\nBut worry not, a `HookWidget` overrides the default hot-reload behavior to work with hooks. Still, there are some situations in which the state of a Hook may be reset.\n\nConsider the following list of hooks:\n\n```dart\nuseA();\nuseB(0);\nuseC();\n```\n\nThen consider that we edited the parameter of `HookB` after performing a hot-reload:\n\n```dart\nuseA();\nuseB(42);\nuseC();\n```\n\nHere everything works fine and all hooks maintain their state.\n\nNow consider that we removed `HookB`. We now have:\n\n```dart\nuseA();\nuseC();\n```\n\nIn this situation, `HookA` maintains its state but `HookC` gets hard reset.\nThis happens because, when a hot-reload is performed after refactoring, all hooks _after_ the first line impacted are disposed of.\nSo, since `HookC` was placed _after_ `HookB`, it will be disposed.\n\n## How to create a hook\n\nThere are two ways to create a hook:\n\n- A function\n\n  Functions are by far the most common way to write hooks. Thanks to hooks being\n  composable by nature, a function will be able to combine other hooks to create\n  a more complex custom hook. By convention, these functions will be prefixed by `use`.\n\n  The following code defines a custom hook that creates a variable and logs its value\n  to the console whenever the value changes:\n\n  ```dart\n  ValueNotifier\u003cT\u003e useLoggedState\u003cT\u003e([T initialData]) {\n    final result = useState\u003cT\u003e(initialData);\n    useValueChanged(result.value, (_, __) {\n      print(result.value);\n    });\n    return result;\n  }\n  ```\n\n- A class\n\n  When a hook becomes too complex, it is possible to convert it into a class that extends `Hook` - which can then be used using `Hook.use`.\\\n  As a class, the hook will look very similar to a `State` class and have access to widget\n  life-cycle and methods such as `initHook`, `dispose` and `setState`.\n\n  It is usually good practice to hide the class under a function as such:\n\n  ```dart\n  Result useMyHook() {\n    return use(const _TimeAlive());\n  }\n  ```\n\n  The following code defines a hook that prints the total time a `State` has been alive on its dispose.\n\n  ```dart\n  class _TimeAlive extends Hook\u003cvoid\u003e {\n    const _TimeAlive();\n\n    @override\n    _TimeAliveState createState() =\u003e _TimeAliveState();\n  }\n\n  class _TimeAliveState extends HookState\u003cvoid, _TimeAlive\u003e {\n    DateTime start;\n\n    @override\n    void initHook() {\n      super.initHook();\n      start = DateTime.now();\n    }\n\n    @override\n    void build(BuildContext context) {}\n\n    @override\n    void dispose() {\n      print(DateTime.now().difference(start));\n      super.dispose();\n    }\n  }\n  ```\n\n## Existing hooks\n\nFlutter_Hooks already comes with a list of reusable hooks which are divided into different kinds:\n\n### Primitives\n\nA set of low-level hooks that interact with the different life-cycles of a widget\n\n| Name                                                                                                     | Description                                                         |\n| -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |\n| [useEffect](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useEffect.html)             | Useful for side-effects and optionally canceling them.              |\n| [useState](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useState.html)               | Creates a variable and subscribes to it.                            |\n| [useMemoized](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useMemoized.html)         | Caches the instance of a complex object.                            |\n| [useRef](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useRef.html)                   | Creates an object that contains a single mutable property.          |\n| [useCallback](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useCallback.html)         | Caches a function instance.                                         |\n| [useContext](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useContext.html)           | Obtains the `BuildContext` of the building `HookWidget`.            |\n| [useValueChanged](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useValueChanged.html) | Watches a value and triggers a callback whenever its value changed. |\n\n### Object-binding\n\nThis category of hooks the manipulation of existing Flutter/Dart objects with hooks.\nThey will take care of creating/updating/disposing an object.\n\n#### dart:async related hooks:\n\n| Name                                                                                                             | Description                                                                         |\n| ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |\n| [useStream](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useStream.html)                     | Subscribes to a `Stream` and returns its current state as an `AsyncSnapshot`.       |\n| [useStreamController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useStreamController.html) | Creates a `StreamController` which will automatically be disposed.                  |\n| [useOnStreamChange](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useOnStreamChange.html)     | Subscribes to a `Stream`, registers handlers, and returns the `StreamSubscription`. |\n| [useFuture](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useFuture.html)                     | Subscribes to a `Future` and returns its current state as an `AsyncSnapshot`.       |\n\n#### Animation related hooks:\n\n| Name                                                                                                                     | Description                                                            |\n| ------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------- |\n| [useSingleTickerProvider](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useSingleTickerProvider.html) | Creates a single usage `TickerProvider`.                               |\n| [useAnimationController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useAnimationController.html)   | Creates an `AnimationController` which will be automatically disposed. |\n| [useAnimation](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useAnimation.html)                       | Subscribes to an `Animation` and returns its value.                    |\n\n#### Listenable related hooks:\n\n| Name                                                                                                                 | Description                                                                                         |\n| -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- |\n| [useListenable](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useListenable.html)                 | Subscribes to a `Listenable` and marks the widget as needing build whenever the listener is called. |\n| [useListenableSelector](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useListenableSelector.html) | Similar to `useListenable`, but allows filtering UI rebuilds                                        |\n| [useValueNotifier](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useValueNotifier.html)           | Creates a `ValueNotifier` which will be automatically disposed.                                     |\n| [useValueListenable](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useValueListenable.html)       | Subscribes to a `ValueListenable` and return its value.                                             |\n| [useOnListenableChange](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useOnListenableChange.html) | Adds a given listener callback to a `Listenable` which will be automatically removed.               |\n\n#### Misc hooks:\n\nA series of hooks with no particular theme.\n\n| Name                                                                                                                                   | Description                                                                                                                                                              |\n| -------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| [useReducer](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useReducer.html)                                         | An alternative to `useState` for more complex states.                                                                                                                    |\n| [usePrevious](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/usePrevious.html)                                       | Returns the previous argument called to [usePrevious].                                                                                                                   |\n| [useTextEditingController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useTextEditingController-constant.html)    | Creates a `TextEditingController`.                                                                                                                                       |\n| [useFocusNode](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useFocusNode.html)                                     | Creates a `FocusNode`.                                                                                                                                                   |\n| [useTabController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useTabController.html)                             | Creates and disposes a `TabController`.                                                                                                                                  |\n| [useScrollController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useScrollController.html)                       | Creates and disposes a `ScrollController`.                                                                                                                               |\n| [usePageController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/usePageController.html)                           | Creates and disposes a `PageController`.                                                                                                                                 |\n| [useFixedExtentScrollController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useFixedExtentScrollController.html) | Creates and disposes a `FixedExtentScrollController`.                                                                                                                    |\n| [useAppLifecycleState](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useAppLifecycleState.html)                     | Returns the current `AppLifecycleState` and rebuilds the widget on change.                                                                                               |\n| [useOnAppLifecycleStateChange](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useOnAppLifecycleStateChange.html)     | Listens to `AppLifecycleState` changes and triggers a callback on change.                                                                                                |\n| [useTransformationController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useTransformationController.html)       | Creates and disposes a `TransformationController`.                                                                                                                       |\n| [useIsMounted](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useIsMounted.html)                                     | An equivalent to `State.mounted` for hooks.                                                                                                                              |\n| [useAutomaticKeepAlive](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useAutomaticKeepAlive.html)                   | An equivalent to the `AutomaticKeepAlive` widget for hooks.                                                                                                              |\n| [useOnPlatformBrightnessChange](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useOnPlatformBrightnessChange.html)   | Listens to platform `Brightness` changes and triggers a callback on change.                                                                                              |\n| [useSearchController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useSearchController.html)                       | Creates and disposes a `SearchController`.                                                                                                                               |\n| [useWidgetStatesController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useWidgetStatesController.html)           | Creates and disposes a `WidgetStatesController`.                                                                                                                         |\n| [useExpansionTileController](https://api.flutter.dev/flutter/material/ExpansionTileController-class.html)                              | Creates a `ExpansionTileController`.                                                                                                                                     |\n| [useDebounced](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useDebounced.html)                                     | Returns a debounced version of the provided value, triggering widget updates accordingly after a specified timeout duration                                              |\n| [useDraggableScrollableController](https://api.flutter.dev/flutter/widgets/DraggableScrollableController-class.html)                   | Creates a `DraggableScrollableController`.                                                                                                                               |\n| [useCarouselController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useCarouselController.html)                   | Creates and disposes a **`CarouselController`**.                                                                                                                             |\n| [useTreeSliverController](https://pub.dev/documentation/flutter_hooks/latest/flutter_hooks/useTreeSliverController.html)               | Creates a `TreeSliverController`.                                                                                                                                        |\n| [useOverlayPortalController](https://api.flutter.dev/flutter/widgets/OverlayPortalController-class.html)                               | Creates and manages an `OverlayPortalController` for controlling the visibility of overlay content. The controller will be automatically disposed when no longer needed. |\n\n## Contributions\n\nContributions are welcomed!\n\nIf you feel that a hook is missing, feel free to open a pull-request.\n\nFor a custom-hook to be merged, you will need to do the following:\n\n- Describe the use-case.\n\n  Open an issue explaining why we need this hook, how to use it, ...\n  This is important as a hook will not get merged if the hook doesn't appeal to\n  a large number of people.\n\n  If your hook is rejected, don't worry! A rejection doesn't mean that it won't\n  be merged later in the future if more people show interest in it.\n  In the mean-time, feel free to publish your hook as a package on https://pub.dev.\n\n- Write tests for your hook\n\n  A hook will not be merged unless fully tested to avoid inadvertently breaking it\n  in the future.\n\n- Add it to the README and write documentation for it.\n\n## Sponsors\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://raw.githubusercontent.com/rrousselGit/freezed/master/sponsorkit/sponsors.svg\"\u003e\n    \u003cimg src='https://raw.githubusercontent.com/rrousselGit/freezed/master/sponsorkit/sponsors.svg'/\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n","funding_links":["https://github.com/sponsors/rrousselGit"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frrousselgit%2Fflutter_hooks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frrousselgit%2Fflutter_hooks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frrousselgit%2Fflutter_hooks/lists"}