Writing Your Own Custom Flutter Hook

We are going to show how to create a custom hook with Flutter Hooks. As an example, we will rewrite a GraphQL query that retrieves race results for drivers in Formula 1. Why would you want to write your own Hook? Mateus discusses the benefits of hooks to React, the same benefits apply for Flutter. Luckily the creators of the Flutter Hooks library have made it really easy to write your own custom Hooks as we will show in this post.

Hooks are easier to work with and to test (as separated functions from React components*) and make the code look cleaner, easier to read — a related logic can be tightly coupled in a custom hook.

Mateus Rotz

Configuring the project

Before we can start with coding, we are going to add two dependencies to the project, namely Flutter Hooks. The latter is only needed for this example. If you are interested in writing your own hook and are doing nothing with GraphQL you can obviously skip that dependency.

dependencies:
  flutter_hooks: ^0.15.0 
  graphql_flutter: ^3.0.0

Do not forget to install the dependency, running the following command:

flutter pub get

That is it! We can now start with our own Custom Hook

Writing a custom hook

First let’s take a look at the code we want to rewrite, before we start by creating the hook.

  @override
  Widget build(BuildContext context) {
    return Query(
        options: QueryOptions(
          documentNode: gql("query")
        ),
        builder: (QueryResult result, {VoidCallback refetch, FetchMore fetchMore}) {
          if (result.loading) {
            return Text("Loading");
          }
    return ListView.builder(
        itemCount: result.data['Driver'].length,
        itemBuilder: (BuildContext context, int index) {
          return Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text(result.data['Driver'][index]['name']),
          );
        });
        });
  }

These are the steps we have to do for each query. The goals is to simplify this Widget with a hook to get the following result. Of course, we the Widget will have to extend the HookWidget to use the hooks.

    @override
  Widget build(BuildContext context) {
    final result = useQuery(context, fetchResults);
    if (result.loading) {
      return Text("Loading");
    }
    return ListView.builder(
        itemCount: result.data['Driver'].length,
        itemBuilder: (BuildContext context, int index) {
          return Padding(
            padding: const EdgeInsets.all(8.0),
            child: Text(result.data['Driver'][index]['name']),
          );
        });
  }

There are two ways to write your own hook. We are going to create a hook as a function. We create a new function. For this, we can combine two existing hooks, namely the useEffect and the useState hooks.

QueryResult useQuery(BuildContext context, String query) {
  final client = GraphQLProvider.of(context).value;
  final state = useState<QueryResult>(QueryResult(loading: true));
  useEffect(() {
    final promise = client.query(
      QueryOptions(documentNode: gql(query)),
    );
    promise.then((result) {
      state.value = result;
    });
    return () {};
  }, []);
  return state.value;
}

This was a simple example of writing your own custom hook. In one of the next blog posts, we will discuss writing a more complicated custom Hook using a class instead of a function as a basis. The code of this example can be found on Github. As always, if you have any questions, feel free to leave a comment.

Leave a Reply