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

2

8

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!.

Wrathful answered 23/8, 2022 at 15:50 Comment(1)
I face the same issue. I have a workaround for the web using dart:html and window.onKeyDown.listen() but I would prefer a clean solution.Artima
L
2

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

Lafayette answered 28/2, 2023 at 15:52 Comment(0)
T
1
  • 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,
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
Transparent answered 30/10, 2022 at 15:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.