flutterdartflutter-textformfieldcustom-widgets

Implementing a Visibility Toggle for Password Field in Flutter TextFormField


I'm working on a Flutter app where I need to implement a password field within a TextFormField that includes a visibility toggle (eye icon) to show/hide the password. Despite attempting to integrate this feature, I'm facing challenges with passing the visibility state to my custom TextFormField widget.

Here's what I currently have and what I aim to achieve (with images illustrating my design goal):

Current Design:

enter image description here

Target Design with Visibility Toggle:

enter image description here enter image description here

Relevant Code Snippets:

Profile.dart snippet showcasing the password TextFormField:

profile.dart

class _ProfileState extends State<Profile> {
  Auth _auth = Auth.signUp;
  final _signUpFormKey = GlobalKey<FormState>();
  final _signInFormKey = GlobalKey<FormState>();

  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();
  final TextEditingController _nameController = TextEditingController();

  @override
  void dispose() {
    super.dispose();
    _emailController.dispose();
    _passwordController.dispose();
    _nameController.dispose();
  }

  bool isHiddenPassword = true;

 ...child: Form(
                            key: _signUpFormKey,
 ...CustomTextField(
                                  controller: _passwordController,
                                  hintText: 'Password',
                                  style: GoogleFonts.nunito(
                                    color: const Color(0xff3B3B3B),
                                    fontStyle: FontStyle.normal,
                                    fontWeight: FontWeight.w500,
                                    fontSize: 20.0,
                                  ),
                                ),

custom_textfield.dart

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

class CustomTextField extends StatefulWidget {
  final TextEditingController controller;
  final String hintText;
  final TextStyle style;
  const CustomTextField({
    Key? key,
    required this.controller,
    required this.hintText,
    required this.style,
  }) : super(key: key);

  @override
  State<CustomTextField> createState() => _CustomTextFieldState();
}

class _CustomTextFieldState extends State<CustomTextField> {
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: TextFormField(
        style: GoogleFonts.nunito(
          fontStyle: FontStyle.normal,
          fontWeight: FontWeight.w500,
          fontSize: 20.0,
            
          
        ),
        controller: widget.controller,
        decoration: InputDecoration(
          hintText: widget.hintText,
          border: const OutlineInputBorder(
              borderSide: BorderSide(
            color: Color(0xff3B3B3B),
          )),
          enabledBorder: const OutlineInputBorder(
            borderSide: BorderSide(
              color: Color(0xff3B3B3B),
            ),
          ),
        ),
        validator: (val) {
          return null;
        },
      ),
    );
  }
}

This is my toggle:

  bool isHiddenPassword = true;
TextField(
                            obscureText: isHiddenPassword,
                            cursorColor: const Color(0xff3B3B3B),
                            decoration: InputDecoration(
                              hintText: '8+ Characters',
                              hintStyle: const TextStyle(
                                  fontSize: 20.0, color: Color(0xffD7D7D7)),
                              suffixIcon: InkWell(
                                onTap: _togglePasswordView,
                                child: isHiddenPassword
                                    ? const Icon(
                                        Icons.visibility_off,
                                        color: Color(0xff3B3B3B),
                                      )
                                    : const Icon(
                                        Icons.visibility,
                                        color: Colors.grey,
                                      ),
                              ),
                              enabledBorder: const UnderlineInputBorder(
                                borderSide:
                                    BorderSide(color: Color(0xffD7D7D7)),
                              ),
                              focusedBorder: const UnderlineInputBorder(
                                borderSide:
                                    BorderSide(color: Color(0xffD7D7D7)),
                              ),
                            ),
                            style: const TextStyle(
                              fontSize: 20,
                              decoration: TextDecoration.none,
                              decorationStyle: TextDecorationStyle.dotted,
                              decorationColor: Color(0xffF6F6F6),
                              fontStyle: FontStyle.normal,
                              fontWeight: FontWeight.normal,
                              color: Color.fromRGBO(59, 59, 59, 1),
                            ),
                          ),
                  
  void _togglePasswordView() {
    setState(() {
      isHiddenPassword = !isHiddenPassword;
    });
  }

My Question:

How can I ensure the visibility toggle (eye icon) properly toggles the visibility of the password in my custom TextFormField widget? I'm having difficulty passing the isHiddenPassword state and the _togglePasswordView function to control the visibility from my Profile widget to the custom CustomTextField widget.

Any guidance or examples on how to achieve this functionality would be greatly appreciated. Thank you in advance for your help!


Solution

  • While isHiddenPassword is just needed and update on the _CustomTextFieldState you dont need to worry about parent widget. But if you like to use it for all textFiled, It would be better to add a widget params to check if it password type or not. I am setting default is false.

    class CustomTextField extends StatefulWidget {
      final TextEditingController controller;
      final String hintText;
      final TextStyle style;
      final bool isPasswordFiled;
    
      const CustomTextField({
        Key? key,
        required this.controller,
        required this.hintText,
        required this.style,
        this.isPasswordFiled = false,
      }) : super(key: key);
    
      @override
      State<CustomTextField> createState() => _CustomTextFieldState();
    }
    
    class _CustomTextFieldState extends State<CustomTextField> {
      bool isHiddenPassword = true;
      @override
      Widget build(BuildContext context) {
        return Padding(
          padding: const EdgeInsets.all(16.0),
          child: TextFormField(
            obscureText: widget.isPasswordFiled && isHiddenPassword,
            style: GoogleFonts.nunito(
              fontStyle: FontStyle.normal,
              fontWeight: FontWeight.w500,
              fontSize: 20.0,
            ),
            controller: widget.controller,
            decoration: InputDecoration(
              suffixIcon: widget.isPasswordFiled
                  ? InkWell(
                      onTap: () {
                        setState(() {
                          isHiddenPassword = !isHiddenPassword;
                        });
                      },
                      child: isHiddenPassword
                          ? const Icon(
                              Icons.visibility_off,
                              color: Color(0xff3B3B3B),
                            )
                          : const Icon(
                              Icons.visibility,
                              color: Colors.grey,
                            ),
                    )
                  : null,
              hintText: widget.hintText,
              border: const OutlineInputBorder(
                  borderSide: BorderSide(
                color: Color(0xff3B3B3B),
              )),
              enabledBorder: const OutlineInputBorder(
                borderSide: BorderSide(
                  color: Color(0xff3B3B3B),
                ),
              ),
            ),
            validator: (val) {
              return null;
            },
          ),
        );
      }
    }