How to use Flutter Hooks
Tags: flutter, mobile
Published: 2021-08-25 20:13:21

**Written by [Chidume Nnamdi](https://blog.logrocket.com/author/chidumennamdi/)** ✏️

Hooks, meet Flutter. Inspired by React Hooks and Dan Abramov's piece, _Making sense of React Hooks_, the developers at Dash Overflow decided to bring Hooks into Flutter.

Flutter widgets behave similarly to React components, as many of the lifecycles in a React component are present in a Flutter widget. According to the creators on their GitHub page:

> Hooks are a new kind of object that manages Widget life-cycles. They exist for one reason: increase the code-sharing between widgets by removing duplicates.

The `flutter_hooks` library provides a robust and clean way to manage a widget's lifecycle by increasing code-sharing between widgets and reducing duplicates in code.

The built-in Flutter Hooks include:

*   `useEffect`
*   `useState`
*   `useMemoized`
*   `useRef`
*   `useCallback`
*   `useContext`
*   `useValueChanged`

In this post, we’ll focus on three of these Hooks:

*   The `useState` Hook manages local states in apps
*   The `useEffect` Hook fetches data from a server and sets the fetch to the local state
*   The `useMemoized` Hook memoizes heavy functions to achieve optimal performance in an app

We’ll also learn how to create and use custom Hooks from `flutter_hooks` as well.

Now, let's see how we can install the `flutter_hooks` library below.

## Installing the `flutter_hooks` library

To use Flutter Hooks from the `flutter_hooks` library, we must install it by running the following command in a terminal inside a Flutter project:

```dart
flutter pub add flutter_hooks
```

This adds `flutter_hooks: VERSION_NUMER_HERE` in the `pubspec.yaml` file in the `dependencies` section.

Also, we can add `flutter_hooks` into the `dependencies` section in the `pubspec.yaml` file:

```dart
dependencies:
  flutter:
    sdk: flutter
  flutter_hooks:
```

After saving the file, Flutter installs the dependency. Next, import the `flutter_hooks` library:

```dart
import 'package:flutter_hooks/flutter_hooks.dart';
```

Now we are good to go!

## The `useState` Hook

Just like `useState` in React, `useState` in Flutter helps us create and manage state in a widget.

The `useState` Hook is called with the state we want to manage locally in a widget. This state passes to the `useState` Hook as a parameter. This state is the initial state because it can change during the lifetime of the widget:

```dart
final state = useState(0);
```

Here, `0` passes to `useState` and becomes the initial state.

Now, let's see how we can use it in a widget. We must first [convert Flutter's `counter` example](https://blog.logrocket.com/flutter-state-management-methods/) to use `useState`.

Here is Flutter's original `counter` example:

```dart
class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
```

Note that using the `StatefulWidget` makes maintaining state locally in a widget complex at times. We must also introduce another class that extends a `State` class, creating two classes for a `StatefulWidget`.

However, with Hooks, we only use one class to maintain our code, making it easier to maintain than `StatefulWidget`.

Below is the Hook equivalent:

```dart
class MyHomePage extends HookWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  Widget build(BuildContext context) {
    final _counter = useState(0);
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter.value',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _counter.value++,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}
```

The Hook example is shorter than its contemporary. Before using Flutter Hooks in a widget, however, the widget must extend `HookWidget`, which is provided by the `flutter_hooks` library.

By calling `useState` in the `build` method with `0`, we store the returned value in `_counter`. This `_counter` is an instance of `ValueNotifier`.

The state is now stored at the `.value` property of the `ValueNotifier`. So, the value of the `_counter` state is stored at `_counter.value`.

`useState` subscribes to the state in the `.value` property and when the value at `.value` is modified, the `useState` Hook rebuilds the widget to display the new value.

In the `FloatingActionButton`, the `_counter.value` increments if the button is pressed. This makes the state increase by `1`, and `useState` rebuilds the `MyHomePage` widget to display the new value.

## The `useEffect` Hook

The `useEffect` Hook in Flutter is the same as React's `useEffect` Hook. The Hook takes a function callback as a parameter and runs side effects in a widget:

```dart
useEffect( () {
    // side effects code here.
    //subscription to a stream, opening a WebSocket connection, or performing HTTP requests
});
```

Side effects can include a stream subscription, opening a WebSocket connection, or performing HTTP requests. They’re also done inside the Hook, so we can cancel them when a widget is disposed of.

The function callback must return a function and is called when the widget is disposed of. We can then cancel subscriptions or other cleanups in that function before the widget is removed from the UI and widget tree. Other cleanups include:

*   Unsubscribing from a stream
*   Canceling polling
*   Clearing timeouts
*   Canceling active HTTP connections
*   Canceling WebSockets connections

This prevents open connections — such as HTTP, WebSocket connections, open streams, and open subscriptions — in the widget from sticking around after the widget that opened them is destroyed and no longer in the widget tree:

```dart
useEffect( () {
    // side effects code here.
    // - Unsubscribing from a stream.
    // - Cancelling polling
    // - Clearing timeouts
    // - Cancelling active HTTP connections.
    // - Cancelling WebSockets conncetions.
        return () {
        // clean up code
    }
});
```

The function callback in `useEffect` is called synchronously, meaning it’s called every time the widget renders or rerenders.

### `keys` argument for `useEffect`

This Hook also has an optional second argument named `keys`. The `keys` argument is a list of values that determine whether the function callback in the `useEffect` Hook will be called or not.

`useEffect` compares the current values of `keys` against its previous values. If the values are different, `useEffect` runs the function callback. If only one value in `keys` remains the same, the function callback is not called:

```dart
useEffect( () {
    // side effects code here.
    return () {
        // clean up code
    }
}, [keys]);
```

## The `useMemoized` Hook

The `useMemoized` Hook is like `useMemo` in React: it memoizes/caches the instance of complex objects created from a builder function.

This function passes to the `useMemoized` Hook, then `useMemoized` calls and stores the result of the function. If a widget rerendering the function is not called, `useMemoized` is called and its previous result returns.

### `keys` argument for `useMemoized`

Similar to `useEffect`, the `useMemoized` Hook has a second optional argument called the `keys`:

```dart
const result = useMemoized(() {}, [keys]);
```

This `keys` argument is a list of dependencies, which determine whether the function passed to `useMemoized` executes when the widget rerenders.

When a widget rebuilds, `useMemoized` checks its `keys` to see whether the previous values changed. If at least one value changed, the function callback in the `useMemoized` Hook will be called, and the `result` renders the function call result.

If none of the values changed since they were last checked, `useMemoized` skips calling the function and uses its last value.

## Custom Hooks

`flutter_hooks` enables us to create our own custom Hooks through two methods: a function or class.

When creating custom Hooks, there are two rules to follow:

*   Using `use` as a prefix tells developers that the function is a Hook, not a normal function
*   Do not render Hooks conditionally, only render the Hook’s result conditionally

Using the function and class methods, we will create a custom Hook that prints a value with its debug value, just like React’s `useDebugValue` Hook.

Let’s begin with the function method.

### Function method

To begin with the function method, we must create a method using any of the built-in Hooks inside it:

```dart
ValueNotifier<T> useDebugValue([T initialState],debugLabel) {
    final state = useState(initialState);
    print(debugLabel + ": " + initialState);
    return state;
}
```

In the above code, using the built-in `useState` Hook holds the state in the function and prints the state’s `debugLabel` and value.

We can then return the `state`. So, using `debugLabel`, the state’s label prints in the console when the widget is mounted to the widget tree for the first time and when modifying the state value.

Next, let’s see how to use the `useDebugValue` Hook we created to print the `debutLabel` string and corresponding state when mounting and rebuilding the widget:

```dart
final counter = useDebugValue(0, "Counter");
final score = useDebugValue(10, "Score");

// Counter: 0
// Score: 10
```

### Class method

Now, let's use a class to recreate the `useDebugValue` custom Hook. This is done by creating a class that `extends` a `Hook` class:

```dart
ValueNotifier<T> useDebugValue<T>(T initialData, debugLabel) {
  return use(_StateHook(initialData: initialData, debugLabel));
}

class _StateHook<T> extends Hook<ValueNotifier<T>> {
  const _StateHook({required this.initialData, this.debugLabel});

  final T debugLabel;
  final T initialData;

  @override
  _StateHookState<T> createState() => _StateHookState();
}

class _StateHookState<T> extends HookState<ValueNotifier<T>, _StateHook<T>> {
  late final _state = ValueNotifier<T>(hook.initialData)
    ..addListener(_listener);

  @override
  void dispose() {
    _state.dispose();
  }

  @override
  ValueNotifier<T> build(BuildContext context) {
    print(this.debugLabel + ": " + _state.value);
      return _state;
  }

  void _listener() {
    setState(() {});
  }
}
```

In the above code, we have the `useDebugValue` function, which is our custom Hook. It accepts arguments, such as the `initialData` initial state value the Hook manages, and the state’s label, `debugLabel`.

The `_StateHook` class is where our Hook logic is written. When the `use` function is called and passed in the `_StateHook` class instance, it registers the `_StateHook` class to the Flutter runtime. We can then call `useDebugLabel` as a Hook.

So, whenever creating a Hook using the class method, the class must extend a Hook class. You can also use `Hook.use()` in place of `use()`.

## Conclusion

`flutter_hooks` brought a major change in how we build Flutter widgets by helping reduce the size of a codebase to a considerably smaller size.

As we have seen, `flutter_hooks` enables developers to do away with widgets like `StatefulWidget`, allowing them to write clean and maintainable code that’s easy to share and test.

---

## [LogRocket] (https://logrocket.com/signup/): Full visibility into your web apps

[![LogRocket Dashboard Free Trial Banner] (https://blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png)](https://logrocket.com/signup/)

[LogRocket] (https://logrocket.com/signup/) is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.

[Try it for free] (https://logrocket.com/signup/).