How to use HookWidget with useTextEditingController

Issue

I’m new to flutter development and trying to understand Riverpod.

I currently managed to make the following login form work with a StatefulWidget. Basically, the button becomes enabled if both fields are not empty and viceversa.

enter image description here

And this is the current code for it.

import 'package:flutter/material.dart';

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Login Demo',
      // theme: ThemeData(
      //   primarySwatch: Colors.blue,
      // ),
      home: Scaffold(
        body: Center(
          child: SizedBox(
            width: 400,
            child: MyLoginPage2(),
          ),
        ),
      ),
    );
  }
}

class MyLoginPage extends StatefulWidget {
  const MyLoginPage({Key? key}) : super(key: key);

  @override
  State<MyLoginPage> createState() > _MyLoginState();
}

class _MyLoginState extends State<MyLoginPage> {
  final emailController  TextEditingController(text: '');
  final passwordController  TextEditingController(text: '');

  @override
  void initState() {
    super.initState();
    emailController.addListener(() {
      setState(() {});
    });
    passwordController.addListener(() {
      setState(() {});
    });
  }

  @override
  void dispose() {
    emailController.dispose();
    passwordController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        TextField(
          controller: emailController,
          keyboardType: TextInputType.emailAddress,
        ),
        TextField(
          controller: passwordController,
          obscureText: true,
        ),
        areFieldsEmpty()
            ? const ElevatedButton(
                child: Text('Login disabled'),
                onPressed: null,
              )
            : ElevatedButton(
                child: const Text('Login enabled'),
                onPressed: () > print("this is where login happens"),
              )
      ],
    );
  }

  bool areFieldsEmpty() {
    return emailController.text.toString().isEmpty ||
        passwordController.text.toString().isEmpty;
  }
}

What I don’t like is how the listeners call setState directly just to trigger a widget refresh. Is this how would you accomplish such a behavior?

I read great things about Riverpod, but I don’t seem to grasp how to model the above behavior inheriting from HookWidget instead of StatefulWidget. Specifically, how to add the listeners to the text editing controllers.

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

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Login Demo',
      // theme: ThemeData(
      //   primarySwatch: Colors.blue,
      // ),
      home: Scaffold(
        body: Center(
          child: SizedBox(
            width: 400,
            child: MyLoginPage2(),
          ),
        ),
      ),
    );
  }
}

class MyLoginPage2 extends HookWidget {
  final emailController  useTextEditingController(text: '');
  final passwordController  useTextEditingController(text: '');

  MyLoginPage2({Key? key}) : super(key: key);

  // Pending questions
  // 1. Where do I add the listeners to the controllers?
  // 2. They are supposed to be auto dispose, right?

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        TextField(
          controller: emailController,
          keyboardType: TextInputType.emailAddress,
        ),
        TextField(
          controller: passwordController,
          obscureText: true,
        ),
        areFieldsEmpty()
            ? const ElevatedButton(
                child: Text('Login disabled'),
                onPressed: null,
              )
            : ElevatedButton(
                child: const Text('Login enabled'),
                onPressed: () > print("this is where login happens"),
              )
      ],
    );
  }

  bool areFieldsEmpty() {
    return emailController.text.toString().isEmpty ||
        passwordController.text.toString().isEmpty;
  }
}

Any help or tips will be appreciated.

Solution

To update the changes on HookWidget use useEffect(). We don’t have to worry about dispose until create our custom hookWidget.

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

  @override
  Widget build(BuildContext context) {
    final emailController  useTextEditingController(text: '');
    final passwordController  useTextEditingController(text: '');

    final _areFieldsEmpty 
        useState<bool>(true); // controll the button based on Text.isEmpty

    bool areFieldsEmpty() {
      return emailController.text.toString().isEmpty ||
          passwordController.text.toString().isEmpty;
    }

    useEffect(() {
      emailController.addListener(() {
        _areFieldsEmpty.value  areFieldsEmpty();
      });
      passwordController.addListener(() {
        _areFieldsEmpty.value  areFieldsEmpty();
      });
    }, [
      emailController,
      passwordController,
    ]);
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        TextField(
          controller: emailController,
          keyboardType: TextInputType.emailAddress,
        ),
        TextField(
          controller: passwordController,
          obscureText: true,
        ),
        _areFieldsEmpty.value
            ? const ElevatedButton(
                child: Text('Login disabled'),
                onPressed: null,
              )
            : ElevatedButton(
                child: const Text('Login enabled'),
                onPressed: () > print("this is where login happens"),
              )
      ],
    );
  }
}

Answered By – Yeasin Sheikh

Leave a Comment