Open In App

Flutter Hooks – An Alternative to StatefulWidget Class

Last Updated : 23 Feb, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Flutter Hooks is a package that was introduced in Flutter v1.9 and provides an alternative way to manage state and other side effects in Flutter. Instead of using traditional stateful widgets, hooks allow you to use functional components to manage state and other side effects. In this article, we will explore how to use Flutter hooks to improve your Flutter development experience.

Introduction to Hooks

Hooks are functions that allow you to “hook” into Flutter’s state management and other features. These are special functions that you can hook into your state widgets, in order to remove boilerplate code and make code readable and reusable. There are several built-in hooks in the Flutter hooks package, including the `useState` hook, the `useEffect` hook, and the `useContext` hook. Each of these hooks serves a different purpose and can be used to solve different problems in your Flutter application. Hooks originally came from React.

Installing Flutter Hooks

To install the Flutter hooks package:

Add the following dependency to your pubspec.yaml file:

dependencies:
  flutter_hooks: ^0.18.5+1

Open a terminal window and navigate to the root directory of your Flutter project. Run the following command to add the Flutter Hooks package as a dependency in your project:

flutter pub add flutter_hooks

Then run the following command in your terminal to download the package:

flutter pub get

Once the package has been installed, you can start using it in your Flutter widgets by importing the flutter_hooks.dart file and use the hooks provided by the package.

import 'package:flutter_hooks/flutter_hooks.dart';

Get to know more about the hooks

useState hook

The useState hook is one of the most basic and commonly used hooks in the Flutter Hooks package. It allows you to add a state to your Flutter widgets, which can be used to store and manipulate data in response to user interactions or other events. With this, you do not need to use setState. Here’s a simple example of how to use the useState hook in a Flutter widget:

Dart




import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
  
void main() => runApp(MyApp());
  
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Hooks Example',
      home: Counter(),
    );
  }
}
  
class Counter extends HookWidget {
  final count = useState(0);
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '${count.value}',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => count.value++,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}


In this example, the useState hook is used to create a piece of state in the Counter widget. The useState hook returns a pair of values: the current state, and a setter function that can be used to update the state. In this case, the state is a simple integer that counts the number of times a button has been tapped. The value of the state is displayed in the UI, and the setter function is called every time the button is tapped, incrementing the state by one. This demonstrates how the useState hook can be used to add a state to your widgets and respond to user interactions.

useEffect hook

The useEffect hook is one of the most commonly used hooks in the Flutter Hooks package. It allows you to run side effects in response to changes in your widget’s state. For example, you can use the useEffect hook to update the UI, fetch data from an API, or listen to changes in the device’s orientation. With this hook, you do not need to worry about initializing something in your initState(), manually disposing, or stating didUpdateWidget on any changes, this hook alone will do everything for you.

Dart




import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
  
class HomePage extends HookWidget {
  @override
  Widget build(BuildContext context) {
    useEffect(
      () {
        // initState
        return () {
          // dispose
        };
      },
      [], // didUpdateWidget
      // null: fires in every change
      // empty list: fires only once on the first time
      // list with items: fires when any of the items on the list change.
    );
    return Container();
  }
}


Above is the way you can make use of useEffect the way you want to use it. Instead of stating separate functions for dispose(), didUpdateWidget(), and the initState(), with this, you can get a cleaner code. Here’s a simple example of how to use the useEffect hook in a Flutter widget:

Dart




import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
  
void main() => runApp(MyApp());
  
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Hooks Example',
      home: Counter(),
    );
  }
}
  
class Counter extends HookWidget {
  final count = useState(0);
  
  @override
  Widget build(BuildContext context) {
    useEffect(() {
      print('The count has changed to ${count.value}');
    }, [count.value]);
  
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '${count.value}',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => count.value++,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),
    );
  }
}


In this example, the useEffect hook is used to run a side effect every time the ‘count’ changes. The side effect simply prints a message to the console indicating the new value of the count. Note that the useEffect hook takes two arguments: a function that contains the side effect and a list of dependencies. The dependencies determine when the side effect should be rerun. In this case, the side effect will be rerun every time the count changes.

useContext hook

The useContext hook is used to access the nearest ancestor BuildContext that contains a particular type of InheritedWidget. The InheritedWidget is a special kind of widget in Flutter that allows you to share data with its descendants in the widget tree. The useContext hook will enable you to access this shared data from within a hook widget. Here’s a simple example of how to use the useContext hook:

Dart




import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
  
void main() => runApp(MyApp());
  
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Hooks Example',
      home: Counter(),
    );
  }
}
  
class Counter extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final theme = useContext(ThemeData);
  
    return Scaffold(
      body: Center(
        child: Text(
          '${theme.brightness}',
          style: Theme.of(context).textTheme.headline4,
        ),
      ),
    );
  }
}


In this example, the useContext hook is used to access the ThemeData from the nearest ancestor BuildContext that contains a Theme widget. The Theme widget is an InheritedWidget that is used to share the current ThemeData with its descendants in the widget tree. The useContext hook allows us to access this shared data in the Counter widget, even though it is not a direct descendant of the Theme widget.

By using the useContext hook, we can access shared data in a simple and concise way, without having to pass the data down the widget tree through constructors or callbacks. This makes it easier to manage and share data in your Flutter app, and helps to keep your code organized and maintainable.

useAnimation hook

The useAnimation hook is a hook provided by the Flutter Hooks package that makes it easy to work with animations in Flutter. The useAnimation hook allows you to create, control, and monitor an animation within a hook widget. With this, you do need to go through the hassle of extending the state class with SingleTickerProviderStateMixin, define AnimationController, override initState to initialize the animation controller, or remember to dispose of the controller, useAnimation will do all that for you. Here’s a simple example of how to use the useAnimation hook:

Dart




import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
  
void main() => runApp(MyApp());
  
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Hooks Example',
      home: FadeIn(),
    );
  }
}
  
class FadeIn extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final animation = useAnimation(CurvedAnimation(
      parent: useAnimationController(duration: Duration(seconds: 2)),
      curve: Curves.easeIn,
    ));
  
    return Scaffold(
      body: Center(
        child: FadeTransition(
          opacity: animation,
          child: Text(
            'Fade In',
            style: Theme.of(context).textTheme.headline4,
          ),
        ),
      ),
    );
  }
}


In this example, the useAnimation hook is used to create a CurvedAnimation with a specified curve and parent AnimationController. The useAnimationController hook is used to create and control the animation controller. The animation controller is used to control the progression of the animation over time. The useAnimation hook returns a Animation object, which can be used in conjunction with a FadeTransition widget to control the opacity of the text. The Animation object updates its value over time based on the progression of the animation controller.

By using the useAnimation hook, you can easily create and control animations in your Flutter app, without having to manage the animation state in a separate class. This makes it easier to manage and organize your animations and helps to keep your code concise and maintainable.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads