<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Christopher Nwosu-Madueke</title>
    <description>The latest articles on DEV Community by Christopher Nwosu-Madueke (@lordchris).</description>
    <link>https://dev.to/lordchris</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F882983%2Ffa1ef41a-9e5c-4d41-816b-3a42f6753b96.jpeg</url>
      <title>DEV Community: Christopher Nwosu-Madueke</title>
      <link>https://dev.to/lordchris</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/lordchris"/>
    <language>en</language>
    <item>
      <title>Understanding Flutter Hooks useListenable and useListenableSelector</title>
      <dc:creator>Christopher Nwosu-Madueke</dc:creator>
      <pubDate>Thu, 29 Sep 2022 16:32:43 +0000</pubDate>
      <link>https://dev.to/lordchris/understanding-flutter-hooks-uselistenable-and-uselistenableselector-546c</link>
      <guid>https://dev.to/lordchris/understanding-flutter-hooks-uselistenable-and-uselistenableselector-546c</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Have you ever wondered how you can update your UI when there are data changes without explicitly writing listeners for those data? In this article, you will learn what the &lt;code&gt;flutter_hooks&lt;/code&gt; package is and how to get these desired results. We will explore the "useListenable" and "useListenableSelector" methods the package provides us.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://pub.dev/packages/flutter_hooks" rel="noopener noreferrer"&gt;&lt;code&gt;flutter_hooks&lt;/code&gt;&lt;/a&gt; is a flutter package that gives you access to pre-written hooks. Hooks are objects that help you manage the lifecycle of a widget as well as increase code reusability of widgets. An example is when we use a &lt;code&gt;TextEditingController&lt;/code&gt; or an &lt;code&gt;AnimationController&lt;/code&gt;, we would have to write the &lt;code&gt;initState&lt;/code&gt; logic, call it in the required place in the build method and then dispose of it. &lt;code&gt;flutter_hooks&lt;/code&gt; helps simplify that process. It gives us an easier way of managing all of that while ensuring there are no memory leaks by disposing it when no longer in use.&lt;/p&gt;

&lt;p&gt;Flutter hooks, according to the documentation, are grouped into 2 types: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Primitive hooks&lt;/li&gt;
&lt;li&gt;Object-binding hooks&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Object-binding hooks are further divided into 4 types:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Dart:async related hooks&lt;/li&gt;
&lt;li&gt;Animation related hooks&lt;/li&gt;
&lt;li&gt;Listenable related hooks&lt;/li&gt;
&lt;li&gt;Misc hooks&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the sake of this article, we will only describe Listenable related hooks.&lt;br&gt;
&lt;strong&gt;Listenable related hooks&lt;/strong&gt; are hooks that have functions relating to Listenable data. These functions can be either subscribing to or creating Listenables. The hooks under these are&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;useListenable&lt;/li&gt;
&lt;li&gt;useListenableSelector&lt;/li&gt;
&lt;li&gt;useValueNotifier&lt;/li&gt;
&lt;li&gt;useValueListenable&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What are useListenable and useListenableSelector methods?
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;useListenable&lt;/em&gt; and &lt;em&gt;useListenableSelector&lt;/em&gt; are two of the four listenable related hooks which are used to trigger rebuilds when the state of a listenable data is changed. The difference between the two is that while &lt;em&gt;useListenable&lt;/em&gt; triggers a rebuild every time the listener is called, &lt;em&gt;useListenableSelector&lt;/em&gt; allows you to specify the value of the Listenable that should trigger a rebuild. &lt;em&gt;useListenableSelector&lt;/em&gt; listens to the listenable data but gives you the ability to select when the rebuild should happen.&lt;/p&gt;

&lt;p&gt;You have been seeing &lt;em&gt;Listenable&lt;/em&gt; a lot. You must be wondering what it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Listenable
&lt;/h2&gt;

&lt;p&gt;If you have been into Flutter for a while, you might have noticed some widgets have the &lt;code&gt;addListener&lt;/code&gt; and &lt;code&gt;removeListener&lt;/code&gt; methods, those widgets are Listenables. Listenable is an interface that provides those two methods. There are classes that implement this Listenable class, such as &lt;code&gt;ChangeNotifier&lt;/code&gt; and &lt;code&gt;ValueNotifier&lt;/code&gt;. In summary, any class that extends or implements either the &lt;code&gt;ChangeNotifier&lt;/code&gt; or &lt;code&gt;ValueNotifier&lt;/code&gt; class are by inheritance Listenables. Example is &lt;code&gt;TextEditingController&lt;/code&gt; which extends &lt;code&gt;ValueNotifier&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I will be showing the implementation using a two screen layout made with stateless widgets so we don't have to call &lt;code&gt;setState&lt;/code&gt;. The first screen will have a text field and a button which only shows when the user inputs &lt;strong&gt;"Flutter Hooks"&lt;/strong&gt;. The second screen will have a single button that toggles the theme of the screen between light and dark modes. Now let's dive into the actual implementation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flutter Implementation
&lt;/h2&gt;

&lt;p&gt;First step is to create the flutter project. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

flutter create hooks_example


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, we go to the &lt;code&gt;pubspec.yaml&lt;/code&gt; file in the project root folder and add the &lt;code&gt;flutter_hooks&lt;/code&gt; package to it. The dependencies section of our &lt;code&gt;pubspec.yaml&lt;/code&gt; will then look like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

dependencies:
  cupertino_icons: ^1.0.2
  flutter:
    sdk: flutter
  flutter_hooks: ^0.18.5+1


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The latest version of flutter_hooks at the time of creating this article was &lt;code&gt;0.18.5+1&lt;/code&gt;. Next we create the two files in the lib folder &lt;code&gt;first_screen.dart&lt;/code&gt; and &lt;code&gt;second_screen.dart&lt;/code&gt;.&lt;br&gt;
Starting with the first screen we add the following code to &lt;code&gt;first_screen.dart&lt;/code&gt;. &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import 'package:flutter/material.dart';

import 'second_screen.dart';

class FirstScreen extends StatelessWidget {
  const FirstScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Colors.blueGrey,
        title: const Text("Hooks - First Screen"),
      ),
      body: Column(
        children: [
          const Expanded(
            flex: 3,
            child: Center(
              child: Padding(
                padding: EdgeInsets.all(8.0),
                child: TextField(
                  decoration: InputDecoration(
                    labelText: "Enter Text...",
                    enabledBorder: OutlineInputBorder(),
                    focusedBorder: OutlineInputBorder(),
                  ),
                ),
              ),
            ),
          ),
          Expanded(
            flex: 2,
            child: Center(
              child: OutlinedButton(
                onPressed: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(builder: (_) =&amp;gt; const SecondScreen()),
                  );
                },
                style: OutlinedButton.styleFrom(
                  padding:
                      const EdgeInsets.symmetric(horizontal: 25, vertical: 20),
                  backgroundColor: Colors.blueGrey,
                ),
                child: const Text(
                  "Proceed",
                  style: TextStyle(
                    color: Colors.white,
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note that there will be an error under the &lt;code&gt;SecondScreen()&lt;/code&gt; class, this is because we have not created the second screen yet. Not to worry, we will get to that now. The code below is then added to &lt;code&gt;second_screen.dart&lt;/code&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import 'package:flutter/material.dart';

class SecondScreen extends StatelessWidget {
  const SecondScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        automaticallyImplyLeading: true,
        backgroundColor: Colors.blueGrey,
        title: const Text("Hooks - Second Screen"),
      ),
      body: Center(
        child: OutlinedButton(
          onPressed: () {},
          style: OutlinedButton.styleFrom(
            padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 20),
            backgroundColor: Colors.blueGrey,
          ),
          child: const Text(
            "Change Theme",
            style: TextStyle(
              color: Colors.white,
            ),
          ),
        ),
      ),
    );
  }
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;After this, the error in the first screen should be gone. Next we change our &lt;code&gt;main.dart&lt;/code&gt; to look like this.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Hooks Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const FirstScreen(),
    );
  }
}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here, we clean up the default code the project comes with upon creation and change the value of the home property of &lt;code&gt;MaterialApp&lt;/code&gt; to the &lt;code&gt;FirstScreen&lt;/code&gt; widget. When we run the code, we will have both screens looking like:&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fymes4mhe7p2sxq7esscf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fymes4mhe7p2sxq7esscf.png" alt="Image of the first screen"&gt;&lt;/a&gt; &lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo03pykz712kz5dpq85pg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo03pykz712kz5dpq85pg.png" alt="Image of the second screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we have our base screens, let us proceed to add the logic to the first screen. We replace the different sections of the code to look like below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

// 1. 
class FirstScreen extends HookWidget {
  const FirstScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
// 2.  
   final controller = useTextEditingController();
// 3. 
    final buttonIsVisible = useListenableSelector(
        controller, () =&amp;gt; controller.text == "Flutter Hooks");
    return Scaffold(

'''

'''
// 4. 
              child: Padding(
                padding: const EdgeInsets.all(8.0),
                child: TextField(
                  controller: controller,
                  decoration: const InputDecoration(
                    labelText: "Enter Text...",
                    enabledBorder: OutlineInputBorder(),
                    focusedBorder: OutlineInputBorder(),
                  ),
                ),
              ),
'''

'''
// 5.    
         child: Visibility(
              visible: buttonIsVisible,
              child: Center(
                child: OutlinedButton(
                  onPressed: () {
                    Navigator.push(
                      context,
                      MaterialPageRoute(builder: (_) =&amp;gt; const SecondScreen()),
                    );
                  },
                  style: OutlinedButton.styleFrom(
                    padding: const EdgeInsets.symmetric(
                        horizontal: 25, vertical: 20),
                    backgroundColor: Colors.blueGrey,
                  ),
                  child: const Text(
                    "Proceed",
                    style: TextStyle(
                      color: Colors.white,
                    ),
                  ),
                ),
              ),
            ),
'''


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;What happened here is that we &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;changed StatelessWidget to HookWidget to be able to use the different hooks&lt;/li&gt;
&lt;li&gt;created a text controller using one of the Misc hooks&lt;/li&gt;
&lt;li&gt;created a listenableSelector to listen for changes to the text controller but only rebuild when the selector result changes from true to false and vise versa&lt;/li&gt;
&lt;li&gt;added the controller to the text field&lt;/li&gt;
&lt;li&gt;then made the button visibility dependent on the &lt;code&gt;TextEditingValue&lt;/code&gt; of the text controller using the &lt;code&gt;Visibility&lt;/code&gt; widget.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the second widget we make similar adjustments to the existing code and the code should look like this.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

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

ValueNotifier&amp;lt;bool&amp;gt; isDarkMode = ValueNotifier(true);

class SecondScreen extends HookWidget {
  const SecondScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    useListenable(isDarkMode);
    return Scaffold(
      backgroundColor: isDarkMode.value ? Colors.grey[900] : Colors.white,
      appBar: AppBar(
        automaticallyImplyLeading: true,
        backgroundColor: isDarkMode.value ? Colors.grey : Colors.blueGrey,
        title: const Text("Hooks - Second Screen"),
      ),
      body: Center(
        child: OutlinedButton(
          onPressed: () {
            isDarkMode.value = !isDarkMode.value;
          },
          style: OutlinedButton.styleFrom(
            padding: const EdgeInsets.symmetric(horizontal: 25, vertical: 20),
            backgroundColor: isDarkMode.value ? Colors.grey : Colors.blueGrey,
          ),
          child: const Text(
            "Change Theme",
            style: TextStyle(
              color: Colors.white,
            ),
          ),
        ),
      ),
    );
  }
}



&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here, we &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;create a global simple ValueNotifier variable &lt;code&gt;isDarkMode&lt;/code&gt; which holds a boolean value, it is made global so we can have a constant constructor for the &lt;code&gt;SecondScreen&lt;/code&gt; class.&lt;/li&gt;
&lt;li&gt;make use of a ternary operator to specify the theme colors everywhere we need the theme change to reflect. &lt;/li&gt;
&lt;li&gt;also added a function that toggles the &lt;code&gt;isDarkMode&lt;/code&gt; value to the &lt;code&gt;onPressed&lt;/code&gt; property of the &lt;code&gt;Change Theme&lt;/code&gt; button.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now that we are done with the "dirty work", our final result will be this &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq1h9eab2510115urtzzc.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq1h9eab2510115urtzzc.gif" alt="Final result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We notice that the first screen is only rebuilt when the value of &lt;code&gt;buttonIsValid&lt;/code&gt;changes while the second screen rebuilds every time the &lt;code&gt;isDarkMode&lt;/code&gt; value changes.&lt;/p&gt;

&lt;p&gt;You can check out the complete code &lt;a href="https://github.com/Lord-Chris/hooks_example" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;You have learnt a summary about &lt;code&gt;flutter_hooks&lt;/code&gt;, what &lt;code&gt;Listenables&lt;/code&gt; are, how &lt;code&gt;useListenable&lt;/code&gt; and &lt;code&gt;useListenableSelector&lt;/code&gt; can be used to customize and limit rebuilds of widgets, and when to use one over the other. Check out the official &lt;a href="https://pub.dev/packages/flutter_hooks" rel="noopener noreferrer"&gt;flutter_hooks&lt;/a&gt; and &lt;a href="https://docs.flutter.dev/" rel="noopener noreferrer"&gt;flutter&lt;/a&gt; documentation for further study.&lt;/p&gt;

&lt;p&gt;If you have any questions, feel free to reach out to me on Twitter: &lt;a href="https://twitter.com/lord_chris__" rel="noopener noreferrer"&gt;@lord_chris__&lt;/a&gt; or LinkedIn: &lt;a href="https://linkedin.com/in/lord-chris" rel="noopener noreferrer"&gt;@lord-chris&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
