DEV Community

Cover image for Exploring hooks Package: Object-Binding Hooks and How to Use Them”P.2”
مصعب يوسف
مصعب يوسف

Posted on Originally published at blog.stackademic.com on

Exploring hooks Package: Object-Binding Hooks and How to Use Them”P.2”

Exploring hooks Package: Object-Binding Hooks and How to Use Them”P.2”

he flutter_hooks package is a powerful toolkit for Flutter developers looking to manage state in a more concise and readable way. By allowing functions to 'hook into' the Flutter lifecycle, this package provides a variety of hooks that help reduce boilerplate code, making applications easier to maintain and more efficient. In this article, we will focus on a specific category of hooks available in flutter_hooks called Object-Binding Hooks. These hooks simplify the management of Streams, Futures, and other objects by automatically handling their lifecycle binding, ensuring that resources are properly allocated and disposed of.

What Are Object-Binding Hooks?

Object-binding hooks provide tools to manage commonly used asynchronous Flutter features such as Streams and Futures. They simplify the process of binding these objects to the widget lifecycle, automatically handling disposal and allowing developers to focus more on core functionality. Let’s explore the object-binding hooks offered by the flutter_hooks package:

  1. useStream: Subscribes to a Stream and returns its current state as an AsyncSnapshot.
  2. useStreamController: Creates a StreamController which will be automatically disposed of when the widget is removed from the widget tree.
  3. useOnStreamChange: Subscribes to a Stream, registers handlers, and returns the StreamSubscription.
  4. useFuture: Subscribes to a Future and returns its current state as an AsyncSnapshot.

Each of these hooks has its unique functionality, offering convenience and simplifying the handling of asynchronous programming in Flutter. Below, we will discuss each of these hooks in detail along with practical examples.

Using Object-Binding Hooks in Practice

To better understand how object-binding hooks work, let’s explore each of them through practical examples.

1. useStream Hook

The useStream hook subscribes to a Stream and returns its current state as an AsyncSnapshot. This makes it easy to handle the state of a Stream within your widget without having to manually subscribe and dispose of it.

Consider the following example, where we are using a Stream that emits values every second:

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

Stream<int> numberStream() async* {
  int counter = 0;
  while (true) {
    await Future.delayed(Duration(seconds: 1));
    yield counter++;
  }
}
class StreamExample extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final snapshot = useStream(numberStream());
    return Scaffold(
      appBar: AppBar(title: Text('useStream Example')),
      body: Center(
        child: Text(
          snapshot.hasData ? 'Stream Value: ${snapshot.data}' : 'Loading...',
          style: TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

In this example:

  • useStream(numberStream()) automatically manages the subscription to the numberStream and updates the widget with new data.
  • The AsyncSnapshot allows us to check if data is available and display the corresponding UI.

2. useStreamController Hook

The useStreamController hook is used to create a StreamController that will automatically be disposed of when the widget is removed. This makes it easy to manage the lifecycle of a StreamController.

Here’s an example of using useStreamController:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class StreamControllerExample extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final controller = useStreamController<int>();
    return Scaffold(
      appBar: AppBar(title: Text('useStreamController Example')),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          StreamBuilder<int>(
            stream: controller.stream,
            builder: (context, snapshot) {
              return Text(
                snapshot.hasData ? 'StreamController Value: ${snapshot.data}' : 'No Data',
                style: TextStyle(fontSize: 24),
              );
            },
          ),
          SizedBox(height: 20),
          ElevatedButton(
            onPressed: () => controller.add((snapshot.hasData ? snapshot.data! : 0) + 1),
            child: Text('Add Value to Stream'),
          ),
        ],
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

In this example:

  • useStreamController<int>() creates a StreamController of type int and ensures that it is properly disposed of when the widget is removed.
  • The button adds a value to the Stream, and the StreamBuilder listens to the controller and displays the current value.

3. useOnStreamChange Hook

The useOnStreamChange hook subscribes to a Stream, registers handlers for events, and returns the StreamSubscription. It is helpful when you need to perform side effects on stream changes.

Here is a simple example that uses useOnStreamChange to listen to a stream and perform a side effect:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

class OnStreamChangeExample extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final streamController = useStreamController<int>();
    useOnStreamChange(streamController.stream, (event) {
      print('Stream event: $event'); // Log every new event from the stream
    });
    return Scaffold(
      appBar: AppBar(title: Text('useOnStreamChange Example')),
      body: Center(
        child: ElevatedButton(
          onPressed: () => streamController.add((streamController.hasListener ? 0 : 0) + 1),
          child: Text('Add Stream Event'),
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

In this example:

  • useOnStreamChange is used to perform an action (printing the event to the console) whenever a new event is added to the stream.
  • This hook can be useful for triggering side effects in response to changes in a stream.

4. useFuture Hook

The useFuture hook subscribes to a Future and returns its current state as an AsyncSnapshot. This makes it convenient to handle asynchronous computations without managing state manually.

Consider the following example that waits for a Future to complete and then displays the result:

import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';

Future<String> fetchData() async {
  await Future.delayed(Duration(seconds: 2));
  return 'Data Loaded';
}
class FutureExample extends HookWidget {
  @override
  Widget build(BuildContext context) {
    final snapshot = useFuture(fetchData());
    return Scaffold(
      appBar: AppBar(title: Text('useFuture Example')),
      body: Center(
        child: Text(
          snapshot.connectionState == ConnectionState.done
              ? 'Result: ${snapshot.data}'
              : 'Loading...',
          style: TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

In this example:

  • useFuture(fetchData()) subscribes to the fetchData Future and returns an AsyncSnapshot.
  • Depending on the connectionState, the UI updates accordingly—showing either the loading state or the fetched data.

Conclusion

Object-binding hooks in the flutter_hooks package provide a clean and efficient way to manage asynchronous data sources such as Streams and Futures in Flutter. By using hooks like useStream, useStreamController, useOnStreamChange, and useFuture, developers can write more readable and maintainable code, reduce boilerplate, and ensure proper lifecycle management.

Try experimenting with these hooks in your next Flutter project to see how they can simplify managing asynchronous programming, improve your workflow, and enhance your app’s performance. Whether you are dealing with data streams, handling side effects, or waiting for Future results, flutter_hooks can greatly enhance your development workflow.

Top comments (0)