I've seen many people discussing state management in Flutter, mentioning bloc, provider, riverpod, and others. However, few of them mention the flutter_hooks package.
In this short blog post, I will explain how easy it is to use flutter_hooks for managing your Flutter state.
flutter_hooks?On the flutter_hooks package page on pub.dev, you'll see in the first line that they state:
And the second line said:
Widget. They exist for one reason: to increase code-sharing between widgets by removing duplication.Yes, they exist for a reason. Flutter hooks are similar to React hooks, as mentioned in the first line. If you're familiar with React, this will be easy to grasp, but if not, that's perfectly fine. I will explain it in this blog post.
Hooks are a state management solution for React applications and are easy to use. This technique was then brought to the Flutter ecosystem by someone in the Flutter community.
In Flutter, everything is a widget, similar to how everything is a React component in React.
To start using flutter_hooks you have to add dependencies to your pubspec.yaml file like below.
yamldependencies: flutter_hooks: ${latest_version}
Go to the package info page for more details: https://pub.dev/packages/flutter_hooks/install
After adding the dependency you can start using it.
Flutter Hooks has its own special widget called HookWidget. All widgets that intend to use hooks must extend this widget and override the build function as usual.
Example:
dartimport 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; class HomeWidget extends HookWidget { @override Widget build(BuildContext context) { // TODO: implement build throw UnimplementedError(); } }
After extending HookWidget, you can use other hook helper methods to achieve your state management.
flutter_hooks comes with several built-in useful hooks, such as:
| Name | Description |
|---|---|
| useEffect | Useful for side effects and optionally canceling them. |
| useState | Creates a variable and subscribes to it. |
| useMemoized | Caches the instance of a complex object. |
| useRef | Creates an object that contains a single mutable property. |
| useCallback | Caches a function instance. |
| useContext | Obtains the BuildContext of the building HookWidget. |
| useValueChanged | Watches a value and triggers a callback whenever its value changes. |
These are just a few examples, but in most cases, you'll only need two or three hooks to achieve your goal.
useState() is the most common one that I always use. It helps us to hold the state when the flutter widget is rebuilt. See below
dartimport 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; class HomeWidget extends HookWidget { const HomeWidget({super.key}); @override Widget build(BuildContext context) { final count = useState(0); return Scaffold( body: Center( child: Text("Count: ${count.value}"), ), floatingActionButton: FloatingActionButton( child: const Icon(Icons.add), onPressed: () { count.value++; }), ); } }
As you can see above it's really easy to use useState hooks.
You start by declaring the state with a default value.
Access the state's value in the text widget to display any changes.
Get the value and use it with the existing floating action button.
In the example above, the build function will be rebuilt when you change the value of count. However, the count hook will retain its latest value during the rebuild. This is quite useful, isn't it?
In some cases, you might want the hook to be called only once when the widget is first built. This could be for an API call to display data on the screen, which you wouldn't want to call again on rebuilds. I will use the same example as above.
Example
dartimport 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; class HomeWidget extends HookWidget { const HomeWidget({super.key}); FuturegetDummyJson() async { return (await http.get(Uri.parse("https://dummyjson.com/products?limit=1"))) .body; } @override Widget build(BuildContext context) { final result = useMemoized(() => getDummyJson()); return Scaffold( body: FutureBuilder ( future: result, builder: (context, snapshot) { return Center( child: Text("Result: ${snapshot.data}"), ); }, ), ); } }
In the example above, I have a helper function that calls dummyjson.com. This API will respond with a JSON string after it retrieves the result.
I declare the function using the useMemoized() hook to ensure it's called only once during the widget's lifecycle.
I access the value using FutureBuilder to retrieve the data from the API.
Display the result in a Text widget.
As you can see, it's easy to achieve these tasks using the flutter_hooks package. If you're familiar with React, you'll feel right at home. I hope you'll consider this package for your next project. For more details about this package, please check the link: https://pub.dev/packages/flutter_hooks. Goodbye for now, and see you next time!