Search code examples
focusflutter

Flutter - Trouble preserving focus between routes


I have a form that uses FocusNodes to visually indicate which part of the form is active. One field extends a PopupRoute as a kind of pop up 'keyboard'. My problem is, when I press that field, the keyboard pops up but the visual effect of the focus doesn't occur.

Some debugging from the FocusNode's listeners shows it gets focus but immediately loses it. I think it is because the new PopupRoute has a new FocusScopeNode, so my FocusNode doesn't have focus any more.

How can I keep the field focused while in the other route? I've tried:

  • Using FocusScope.of(context).reparentIfNeeded(focusNode) in all the build methods, which didn't do anything (I don't really understand this function tbh)
  • Passing the current FocusScope.of(context) into my custom PopupRoute to use. This actually worked, but after it's popped, I can't focus anything anymore (I guess it gets disposed?)

Code-wise, I'm calling requestFocus on the field tap, and adding this listener in initState:

    widget.focusNode.addListener(() {
      print(widget.focusNode);
      if (widget.focusNode.hasFocus) {
        Navigator.of(context).push(
          CustomKeyboardPopupRoute(
            state: widget.state,
            position: //position stuff,
            focusScopeNode: FocusScope.of(context), //the second of my ideas which didn't quite work above
          )
        ).then((_) {
           widget.focusNode.unfocus();
        });
    }); 

Solution

  • You are on the right track, indeed this happens because of the FocusScopeNode.

    Make your keyboard route extend TransitionRoute:

    class CustomKeyboardPopupRoute extends TransitionRoute {
      @override
      bool get opaque => false;
    
      @override
      Duration get transitionDuration => Duration(milliseconds: 300);
    
      @override
      Iterable<OverlayEntry> createOverlayEntries() sync* {
        yield OverlayEntry(
          opaque: false,
          maintainState: true,
          builder: _buildKeyboard,
        );
      }
    
      Widget _buildKeyboard(BuildContext context) {
        final positionAnimation = Tween(begin: Offset(0.0, 1.0), end: Offset.zero).animate(animation);
        return SlideTransition(position: positionAnimation, child: Align(
          alignment: Alignment.bottomCenter,
          child: ...
        ),);
      }
    }