Flutter how to make a selectable / focusable widget

912

The difference between your card with gesture detector and the ElevatedButton is that you don't have a FocusNode.

If you dig into the implementation details of the ElevatedButton you will find that it uses an InkWell with a FocusNode

final Widget result = ConstrainedBox(
  constraints: effectiveConstraints,
  child: Material(
    // ...
    child: InkWell(
      // ...
      focusNode: widget.focusNode,
      canRequestFocus: widget.enabled,
      onFocusChange: updateMaterialState(MaterialState.focused),
      autofocus: widget.autofocus,
      // ...
      child: IconTheme.merge(
        // ....
        child: Padding(
          padding: padding,
          child: // ...
          ),
        ),
      ),
    ),
  ),
);

So, if you replace GestureDetector with Inkwell, then the keyboard navigation would work.

InkWell(
  child: Card(
    shape: RoundedRectangleBorder(
      borderRadius: BorderRadius.circular(15.0),
    ),
    elevation: 10,
    child: const SizedBox(
      width: 200,
      height: 60,
      child: Center(
        child: Text(
          'Test 1',
        ),
      ),
    ),
  ),
  onTap: () {},
)

(Tested on Android TV emulator API 30, with keyboard an d-pad.)

References

Share:
912
Nicholas Muir
Author by

Nicholas Muir

Updated on January 01, 2023

Comments

  • Nicholas Muir
    Nicholas Muir over 1 year

    I am creating an android tv app. I was trying to work out for a long time why when I clicked the arrow up and down buttons on the remote it appeared to do nothing and it wasn't selecting any of the list item.

    Eventually I was able to work out that if I used an elevated button or other focusable widget on the list i could use the arrow keys and it would work fine. Previously I was using a card widget wrapped in a gesture detector.

    So I am wondering what the difference between a button and card with gesture detector is that stops the arrow keys from being able to select the item. I suspect it is the focus.

    This is what I was using that doesn't allow the up, down keys on the remote to select it:

    GestureDetector(
        child: Card(
          color: color,
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(15.0),
          ),
          elevation: 10,
          child: SizedBox(
              width: (width / numberOfCards) - padding * (numberOfCards - 1),
              height: (height / 2) - padding * 2,
              child: Center(child: Text(cardTitle, style: Theme.of(context).textTheme.bodyText1?.copyWith(fontSize: 16, fontWeight: FontWeight.bold, color: Colors.white),))),
        ),
        onTap: () => onCardTap(),
      ),
    

    And this is the button I replaced it with that then makes the up down keys and selection to work:

    ElevatedButton(
                      onPressed: () {},
                      child: Text('Test 1', style: Theme.of(context).textTheme.bodyText1?.copyWith(color: Colors.white, fontSize: 18, fontWeight: FontWeight.normal)),
                      style: ButtonStyle(
                          backgroundColor: MaterialStateProperty.all(Colors.grey.withOpacity(0.3)),
                          minimumSize: MaterialStateProperty.all(Size(60, 60)),
                          elevation: MaterialStateProperty.all(10),
                          shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: new BorderRadius.circular(50)),)),
                    ),
    

    In case its needed this is what I am using to pick up the key presses:

    Shortcuts(
      shortcuts: <LogicalKeySet, Intent>{
        LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
      },
    

    Thanks