flutterflutter-desktop

How do I add hotkeys or key actions to items in a listview when they have focus?


I am using Flutter Desktop (Windows 10) with Flutter 3.0.1

In a flutter listview.builder, I can add InkWell to each item and when focused, I can press Enter or NumpadEnter keys to launch the onkey event.

But I want to add different keypresses like Numpad Add or even an A key press action to launch a different action, how do I do that?

basically, I can press Enter key when I have focus of an item to launch it's onTap event, but how can I add other kinds of key presses to launch different events?

currently I have only found this solution (wrap in RawKeyboardListener):

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.orange,
      ),
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: RawKeyboardListener(
          focusNode: FocusNode(),
          onKey: (event) {
            if (event.logicalKey.keyLabel == 'Arrow Down') {
              FocusScope.of(context).nextFocus();
            }
          },
          child: const TextField(
            autofocus: true,
          ),
        ),
      ),
      body: ListView.builder(
        itemBuilder: (context, index) {
          // remove rawKeyboardListener here to prevent weird navigation skips
          return RawKeyboardListener(
            focusNode: FocusNode(),
            onKey: (keyEvent) {
              if (keyEvent.logicalKey.keyLabel == 'Numpad Add') {
                print('numpad clicked item $index');
              }
            },
            child: InkWell(
              focusColor: Colors.blue,
              onTap: () => print('clicked item $index'),
              child: Padding(
                  padding: const EdgeInsets.all(8.0),
                  child:
                      SizedBox(height: 50, child: Text('this is item $index'))),
            ),
          );
        },
      ),
    );
  }
}

it works! BUT it has an issue where if I scroll pass it's vertical view, it then starts to skip item focus, like if I scroll down down down then scroll up then scroll down, the focus gets messed up.

You can copy paste the above code to test, only by removing the RawFocusKeyboard from each item in the listview, can solve this focus problem.

Has anyone found a better solution for this problem?


Solution

  • Instead of using RawKeyboardListener on the Inkwells, I used the Shortcuts and Actions widgets.

    ListView.builder > Shortcuts > Actions > Inkwell
    

    This fixed the scrolling errors and I can finally use a key shortcut other than space or Enter keys.

    the minor issue for future readers...

    When you are changing the focus of the items by using Tab, the Inkwell widgets can react to the shortcut keys like Enter key, Space key, and whatever you have assigned in Shortcuts and Actions widgets, but after you tap/click the Inkwells manually, no shortcuts will work (I suspect from my testings, the reason is the focus is not given to the Inkwell even if you manually tap it) I have yet to find the solution to this problem.