James Mark
James Mark

Reputation: 349

Flutter web - shortcuts and action widget is not listening the keyboard events at page level for providing the keyboard shortcuts

In my use case, I have multiple layouts in a particular page. for ex, in a page, i have layout(lets say layout-1) where i get the input data from user using list of textfield and then in the same page, i have another layout (lets say layout-2) in the bottom where table widget is being used to list row by row for the data that we received from user. similarly i have other layouts in the same page. I am trying to provide the keyboard shortcuts for these by using control + D, control + R etc. so the problem that i am facing is, if i focused on the texfield in layout-1 and then i press keyboard keys control + D this is focusing the expected widget correctly. but if i click outside of the texfield (anywhere in the screen on the particular page), then it loose the focus. now if i click the control + D, the shortcuts & action widget not listening the keyboard events.

In order to show this problem i just created simple code here and in this code if i click outside of the text field and if i try to use control + D, its not working.

for example, in the below code i have two textfields and i am focusing in these text fields based on shortcuts (this is just an example to explain the problem). control + R is to focus in textfield1 and control + D is to focus on textfield2. its working fine. but if i click somewhere outside of the text field and then if i try shortcuts, its not listening the keyboard events. it loose the focus.

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

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

  final FocusNode textfield1FocusNode = FocusNode();
  final FocusNode textfield2FocusNode = FocusNode();

  @override
  Widget build(BuildContext context) {
    return Shortcuts(
      shortcuts: {
        LogicalKeySet(LogicalKeyboardKey.controlLeft, LogicalKeyboardKey.keyR):
            Testfield1ShorcutIntent(),
        LogicalKeySet(LogicalKeyboardKey.controlLeft, LogicalKeyboardKey.keyD):
            Testfield2ShorcutIntent(),
      },
      child: Actions(
        actions: {
          Testfield1ShorcutIntent:
              CallbackAction<Testfield1ShorcutIntent>(onInvoke: (intent) {
            print('clicked cnrl + D');
            return textfield1FocusNode.requestFocus();
          }),
          Testfield2ShorcutIntent:
              CallbackAction<Testfield2ShorcutIntent>(onInvoke: (intent) {
            print('clicked cnrl + R');
            return textfield2FocusNode.requestFocus();
          }),
        },
        child: Focus(
          autofocus: true,
          child: Scaffold(
              appBar: AppBar(title: const Text('Keyboard shortcut testing')),
              body: Center(
                child: Card(
                  color: Colors.amber[50],
                  child: Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: SizedBox(
                      width: 300,
                      height: 200,
                      child: Column(
                        children: [
                          TextField(
                            focusNode: textfield1FocusNode,
                            decoration: const InputDecoration(
                              border: OutlineInputBorder(),
                            ),
                          ),
                          const SizedBox(height: 10),
                          TextField(
                            focusNode: textfield2FocusNode,
                            decoration: const InputDecoration(
                              border: OutlineInputBorder(),
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                ),
              )),
        ),
      ),
    );
  }
}

class Testfield1ShorcutIntent extends Intent {}

class Testfield2ShorcutIntent extends Intent {}

This code i am trying for flutter web. i am also attaching an image to show the problem. here if i focus either on textfield1 or texfield2 and then if i press control + D or control + R then the focus is being switched between the textfields as per the logic but i click outside of the textfield (anywhere in the yellow color card or outside of yellow color card) and then if i press then shortcuts are not working as its not even listening the keys. enter image description here

Note: I also tried using FocusScope widget instead of Focus widget but FocusScope widget is not allowing to focus outside. for ex. if i use FocusScope widget and if i click outside of the textfield, its not allowing because the focus is not coming out from the textfield. it continuously focusing on the same textfield widget.

Appreciate the response!.

Upvotes: 7

Views: 1870

Answers (2)

Aditya Bansal
Aditya Bansal

Reputation: 53

This is what worked for me

Wrap your scaffold with Focus scope (autofocus =true), and over that put your actions and shortcuts widget. now every time you will click on outside a focusable widget like a text field, button, etc, focus scope will take over the focus. This ensures the focus is always on your app.

@override
  Widget build(BuildContext context) {
    return Shortcuts(
      shortcuts: ...,
      child: Actions(
        actions: ...,
        child: FocusScope(
          autofocus: true,
          child: Scaffold(
             ...

I would recommend you to look at this thread: https://github.com/flutter/flutter/issues/111470#issuecomment-1302838704

Upvotes: 2

Anas
Anas

Reputation: 1073

  • Flutter Docs suggests using Stateful Widget for creating FocusNode
  • Flutter provide pre-build Intent to request focus -> RequestFocusIntent(focusNode1)

Try out the following code,

class TextFieldShortcut extends StatefulWidget {
  const TextFieldShortcut({super.key});

  @override
  State<TextFieldShortcut> createState() => _TextFieldShortcutState();
}

class _TextFieldShortcutState extends State<TextFieldShortcut> {

  late final FocusNode focusNode1, focusNode2;

  @override
  void initState() {
    super.initState();
    focusNode1 = FocusNode();
    focusNode2 = FocusNode();
  }

  @override
  void dispose() {
    focusNode1.dispose();
    focusNode2.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Shortcuts(
      shortcuts: {
        LogicalKeySet(LogicalKeyboardKey.controlLeft, LogicalKeyboardKey.keyM): 
        RequestFocusIntent(focusNode1),
        LogicalKeySet(LogicalKeyboardKey.controlLeft, LogicalKeyboardKey.keyQ): 
        RequestFocusIntent(focusNode2),
      },
      child: MaterialApp(
        home: Scaffold(
          body: Center(
            child: Column(
              children: [
                TextField(
                  autofocus: true,
                  focusNode: focusNode1,
                ),
                TextField(
                  focusNode: focusNode2,
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Upvotes: 1

Related Questions