0

I have a Widget called "CustomInput" that will be massively used through all my app and I'd like to each input to listen to his own FocusNode & apply some conditions whenever the input is focused / unfocused.

Sample example:

class CustomInput extends StatefulWidget {
  final String labelText;
  final FocusNode focusNode;
  final IconData icon;

  const CustomInput({String labelText, FocusNode focusNode, IconData icon})
      : this.labelText = labelText,
        this.focusNode = focusNode,
        this.icon = icon;

  @override
  _CustomInputState createState() => _CustomInputState();
}

class _CustomInputState extends State<CustomInput> {
  @override
  Widget build(BuildContext context) {
    return input();
  }

  TextFormField input() {
    return TextFormField(
      focusNode: widget.focusNode,
      decoration: InputDecoration(
        labelText: widget.labelText,
        labelStyle: TextStyle(color: Colors.black54),
        fillColor: Colors.white,
        filled: true,
        prefixIcon: prefixIcon(),
      ),
    );
  }

  Padding prefixIcon() {
    return Padding(
      padding: EdgeInsets.only(left: 5),
      child: Icon(
        widget.icon,
        color: widget.focusNode.hasFocus /// <------------- here
            ? Colors.red
            : Colors.black,
        size: 20,
      ),
    );
  }
}

The goal is to whenever the input is focused, the prefixIcon should assume the red color and when is not focused it should assume the black color. I call the Widget this way:

Column(children: [
    CustomInput(
      focusNode: emailFocusNode,
      icon: Icons.email_outlined,
    ),
    CustomInput(
      focusNode: passwordFocusNode,
      icon: Icons.lock,
    ),
    /// etc
]);

The problem is that the condition widget.focusNode.hasFocus is not triggering / always valuates to false. I have seen this post How to listen focus change in flutter? and also tried:

bool isFocused = false;

@override
void initState() {
    super.initState();

    widget.focusNode.addListener(() {
      isFocused = widget.focusNode.hasFocus;
    });
}

/// and then switch the prefixIcon function to 
Padding prefixIcon() {
    return Padding(
      padding: EdgeInsets.only(left: 5),
      child: Icon(
        widget.icon,
        color: this.isFocused
            ? Colors.red
            : Colors.black,
        size: 20,
      ),
    );
}

But the problem persists, it seems the focus listener doesn't get triggered.

Linesofcode
  • 5,327
  • 13
  • 62
  • 116
  • you do not need any listeners, set states etc: `class FocusNodeTest extends StatelessWidget { final FocusNode focusNode; FocusNodeTest({required this.focusNode}); @override Widget build(BuildContext context) { return AnimatedBuilder( animation: focusNode, builder: (context, child) { print(focusNode); return TextField( decoration: InputDecoration(icon: Icon(Icons.alarm, color: focusNode.hasFocus? Colors.red : Colors.black)), focusNode: focusNode, ); } ); } }` - the main idea is that `FocusNode` is `Listenable` so you can use `AnimatedBuilder` that takes `Listenable animation` as its input – pskink Mar 16 '22 at 07:05

1 Answers1

0

Solved. I forgot about the setState

Instead of:

@override
void initState() {
    super.initState();

    widget.focusNode.addListener(() {
      isFocused = widget.focusNode.hasFocus;
    });
}

Should be:

@override
void initState() {
    super.initState();

    widget.focusNode.addListener(() {
      setState(() { 
        isFocused = widget.focusNode.hasFocus 
      });
    });
}
Linesofcode
  • 5,327
  • 13
  • 62
  • 116