Search code examples
flutterdartflutter-layoutflutter-state

Not able to change the visibility icon to visibility_off icon or vice versa in flutter


Summary

I was building an app in flutter where I was trying to keep 3 functions linked to one build function, so I have a navigation bar to navigate between these 3 functions (containing the layout).

Issue

While developing the 3rd layout which is basically a form, where I am unable to change the icon wrapped with GestureDetector(Function:- to toggle the visibility of password, hence changing the icon 👁).

Complete Code

class _MyHomePageState extends State<MyHomePage> {
  final globalKey = GlobalKey<ScaffoldState>();

  final _passwordTextController = TextEditingController();
 
  bool _hidePassword = true;  //initial assigning value
  List<Widget>? pages;
  int _currentIndex = 0;

  void initState() {
    super.initState();
    _hidePassword = true; //calling at the starting of the app
    pages = <Widget>[
      homeNavPage(context),
      servicePage(),
      manageUser(context), //function I am facing 
    ];
  }

  void _onItemTapped(int index) {
    setState(() {
      MyHomePage._selectedIndex = index;
    });
  }

  @override
  Widget build(BuildContext context) {
    var _keyboardVisible = MediaQuery.of(context).viewInsets.bottom != 0;

    return Scaffold(
        body: pages![_currentIndex],

        //navigation bar
        floatingActionButtonLocation:
            FloatingActionButtonLocation.miniCenterFloat,
        floatingActionButton: (!_keyboardVisible)
            ? Container(
                clipBehavior: Clip.hardEdge,
                decoration: BoxDecoration(
                    borderRadius: BorderRadius.all(Radius.circular(10))),
                margin: EdgeInsets.only(left: 15.0, right: 15.0),
                child: NavigationBarTheme(
                  data: NavigationBarThemeData(
                      indicatorColor: Colors.cyan.withOpacity(0.5),
                      labelTextStyle: MaterialStateProperty.all(const TextStyle(
                        fontSize: 12,
                        fontWeight: FontWeight.bold,
                        color: Colors.white,
                      ))),
                  child: NavigationBar(
                    backgroundColor: Colors.black.withOpacity(0.8),
                    selectedIndex: _currentIndex,
                    animationDuration: const Duration(seconds: 1),
                    labelBehavior:
                        NavigationDestinationLabelBehavior.alwaysShow,
                    onDestinationSelected: (int newIndex) {
                      setState(() {
                        _currentIndex = newIndex;
                      });
                    },
                    destinations: [
                      NavigationDestination(
                        selectedIcon:
                            Icon(Icons.home_rounded, color: Colors.white),
                        icon: Icon(Icons.home_outlined, color: Colors.white),
                        label: 'Home',
                      ),
                      NavigationDestination(
                        selectedIcon:
                            Icon(Icons.design_services, color: Colors.white),
                        icon: Icon(Icons.design_services_outlined,
                            color: Colors.white),
                        label: 'Service',
                      ),
                      NavigationDestination(
                        selectedIcon: Icon(Icons.manage_accounts_rounded,
                            color: Colors.white),
                        icon: Icon(Icons.manage_accounts_outlined,
                            color: Colors.white),
                        label: 'Manage Profile',
                      ),
                    ],
                  ),
                ),
              )
            : Container(),
      );
  }

  //1
  Stack homeNavPage(BuildContext context) {
   //Some codes...
  }

  //2
  servicePage() {
    //some codes...
  }

  //3 function which is creating issue
  manageUser(BuildContext context) {

    void _togglePasswordView() {
      setState(() {
        _hidePassword = !_hidePassword;
      });
    }
    

    // while testing this only runs at the starting
    print('outside $_hidePassword');


    final _formkey = GlobalKey<FormState>();
    return Scaffold(
      body: SingleChildScrollView(
        child: Container(
          color: Colors.white,
          child: Column(
            children: [
              Padding(
                padding: const EdgeInsets.all(40.0),
                child: Form(
                  key: _formkey,
                  child: Column(
                    children: [
                      // some extra codes

                      TextFormField(
                        keyboardType: TextInputType.text,
                        decoration: InputDecoration(
                          floatingLabelBehavior: FloatingLabelBehavior.never,
                          icon: Icon(Icons.password),
                          // hintText: 'Enter your password',
                          labelText: 'Password',
                          suffixIcon: GestureDetector(

                            //works as usual whenever toggle happens
                            onTap: () {
                              print('1. $_hidePassword');
                              _togglePasswordView();
                              print('2. $_hidePassword');
                            },
                            //icon doesn't change 
                            child: Icon(_hidePassword == true
                                ? Icons.visibility_off
                                : Icons.visibility),
                          ),
                        ),
                        controller: _passwordTextController,
                        // focusNode: _focusPassword,

                        //no changes happens
                        obscureText: _hidePassword,
                        validator: (value) =>
                            Validator.validatePassword(password: value!),
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Code Specific Having Issue

                      TextFormField(
                       //some codes
                          suffixIcon: GestureDetector(

                            //works as usual whenever toggle happens
                            onTap: () {
                              print('1. $_hidePassword');
                              _togglePasswordView();
                              print('2. $_hidePassword');
                            },
                            //icon doesn't change 
                            child: Icon(_hidePassword == true
                                ? Icons.visibility_off
                                : Icons.visibility),
                          ),
                        ),
                        //no changes happens
                        obscureText: _hidePassword,
                       
                      ),

Testing

I have performed testing by using the 3 print statement where,

  1. print('outside $_hidePassword'); = runs once during initialization.
  2. other 2 print statements present inside the onTap: () of GestureDetector works as usual changing the values.
  3. But there is no change found in the icon and obscureText state.

Screenshot

issue SS

Already specified all the details, if required please ask, and I will furnish you with other details.

Thank you.


Solution

  • You need to update state to change icon but calling setState is to expensive.So use StatefulBuilder like this:

    StatefulBuilder(builder: (context, innerSetState) {
              return TextFormField(
                //some codes
                decoration: InputDecoration(
                  suffixIcon: GestureDetector(
                    onTap: () {
                      print('1. $_hidePassword');
                      innerSetState(() {
                        _hidePassword = !_hidePassword;
                      });
    
                      print('2. $_hidePassword');
                    },
                    child: Icon(_hidePassword == true
                        ? Icons.visibility_off
                        : Icons.visibility),
                  ),
                ),
                obscureText: _hidePassword,
              );
            }),