The easy way to manage the state of Flutter using flutter_hooks

The easy way to manage the state of Flutter using flutter_hooks

02-Oct-2023 - Sophoun

I saw a lot of people talking about state management in Flutter like bloc, provider, riverpod, and others. But not of those people mention about flutter_hooks package.

In this small blog post, I will explain to you how it's easy to just use only flutter_hooks to manage your Flutter state.

What is flutter_hook?

In their page of flutter_hooks the package on pub.dev you can see in the first line, they said:

And the second line said:

Yes, it exists for a reason. Flutter hooks are similar to react hooks as they've mentioned in the first line. If you're familiar with React it will be easy to help you but if not, that's okay. I will explain you in this blog.

Hooks

Hooks is a state management solution for React applications and it's easy to use. Then someone in the Flutter community brought this technique to the Flutter echo system.

In Flutter everything is a widget this way is similar to React, everything is a React component.

How to use it?

To start using flutter_hooks you have to add dependencies to your pubspec.yaml file like below.

dependencies: flutter_hooks: ${latest_version}

Go to the package info page for more details: https://pub.dev/packages/flutter_hooks/install

HookWidget

After adding the dependency you can start using it.

Flutter Hook has its own special widget called HookWidget all widgets that want to use hook must be extended to this widget and override build function as normal.

Example:

import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; class HomeWidget extends HookWidget { Widget build(BuildContext context) { // TODO: implement build throw UnimplementedError(); } }

After you extend to HookWidget you can use other hook helper method to archive your state management.

Build-in hooks

Flutter_hooks has delivered with build-in some useful hooks like the below.

NameDescription
useEffectUseful for side effects and optionally canceling them.
useStateCreates a variable and subscribes to it.
useMemoizedCaches the instance of a complex object.
useRefCreates an object that contains a single mutable property.
useCallbackCaches a function instance.
useContextObtains the BuildContext of the building HookWidget.
useValueChangedWatches a value and triggers a callback whenever its value changes.

This is just a few, but in most case, you just need some 2 or 3 hooks to achieve your goal.

useState()

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

import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; class HomeWidget extends HookWidget { const HomeWidget({super.key}); 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.

  1. You start with the declaration of the state with the default value.

  2. Access the state value in the text widget to display any changes.

  3. Get the value and add it to the existing floating action button.

In the above example, build the function will be rebuilt when you change the value of count. But the count the hook will hold your latest value in it during the rebuild. This is cool, right?

useMemorized()

In some cases you just want the hook to call it once when the first widget is built. It could be an API call to display data on the screen, you don't need it to call again when it rebuilds. I will use the same example as above.

Example

import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; class HomeWidget extends HookWidget { const HomeWidget({super.key}); Future<String> getDummyJson() async { return (await http.get(Uri.parse("https://dummyjson.com/products?limit=1"))) .body; } Widget build(BuildContext context) { final result = useMemoized(() => getDummyJson()); return Scaffold( body: FutureBuilder<String>( future: result, builder: (context, snapshot) { return Center( child: Text("Result: ${snapshot.data}"), ); }, ), ); } }

In the above example, I have a helper function that calls dummyjson.com. This API will respond to a string of JSON value after it gets the result.

  1. I declare function using useMemorized() hook to ensure function call only one time during the widget lifecycle.

  2. I access the value using FutureBuilder to get the value from API.

  3. Show result in Text widget.

Conclutions

As you can see it's easy to archive the tasks using flutter_hooks package. If you're familiar with React you will be feeling at home at this point. I hope you will consider this package for your next project. If you want to get more detail about this package please check the package via this link: https://pub.dev/packages/flutter_hooks. Good bye, see you next time.