Lachlan
Lachlan

Reputation: 515

How to detect key presses in flutter without a RawKeyboardListener

I'm trying to detect key presses like "Enter", "Delete" and "Backspace" within flutter. My issue with using a RawKeyboardListener is that it takes focus away from any child widgets.

For example

RawKeyboardListener(
  focusNode: _focusNode,
  onKey: handleKey,
  child: TextField()
)

This makes it impossible to detect both key presses and use the Textfield at the same time.

Does anyone have a alternative way for detecting key presses.

Thanks

Upvotes: 21

Views: 13386

Answers (3)

Joshua Wade
Joshua Wade

Reputation: 5293

Flutter has a keyboard service that provides Flutter with raw key events from hardware keyboards, and you can ask this service to give you key events as well.

A benefit of this approach is that it doesn't prevent existing Flutter focus nodes from receiving events, so you don't have to manually handle keyboard event propagation.

This works on desktop, but note that it doesn't work for mobile (iOS / Android).

You can add a listener to this service like so:

import 'package:flutter/services.dart';

ServicesBinding.instance.keyboard.addHandler(_onKey);

...where _onKey is a bool Function(KeyEvent). Here's an example event handler:

bool _onKey(KeyEvent event) {
  final key = event.logicalKey.keyLabel;

  if (event is KeyDownEvent) {
    print("Key down: $key");
  } else if (event is KeyUpEvent) {
    print("Key up: $key");
  } else if (event is KeyRepeatEvent) {
    print("Key repeat: $key");
  }

  return false;
}

See here for more info: https://api.flutter.dev/flutter/services/HardwareKeyboard/addHandler.html

Note that this event handler will always receive key events, even if a text box is currently in focus.


Here's an example of using this technique in a widget:

import 'package:flutter/services.dart';

// ...

class MyApp extends StatefulWidget {
  // ...
}

class _MyAppState extends State<MyApp> {
  // ...

  bool _onKey(KeyEvent event) {
    final key = event.logicalKey.keyLabel;

    if (event is KeyDownEvent) {
      print("Key down: $key");
    } else if (event is KeyUpEvent) {
      print("Key up: $key");
    } else if (event is KeyRepeatEvent) {
      print("Key repeat: $key");
    }

    return false;
  }

  @override
  void initState() {
    super.initState();
    ServicesBinding.instance.keyboard.addHandler(_onKey);
  }

  @override
  void dispose() {
    ServicesBinding.instance.keyboard.removeHandler(_onKey);
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // ...
  }
}

Upvotes: 14

Arenukvern
Arenukvern

Reputation: 534

You can use dart:ui and set method window.onKeyData in initState in a stateful widget

Be careful with that method as if you have any focusable nodes on the screen you will need to pass events to them too, otherwise, for example, TextField will not work.

  @override
  void initState() {
    window.onKeyData = (final keyData) {
      if (keyData.logical == LogicalKeyboardKey.escape.keyId) {
        widget.onPressed();

        return true;
      }

      /// Let event pass to other focuses if it is not the key we looking for
      return false;
    };
    super.initState();
  }

  @override
  void dispose() {
    window.onKeyData = null;
    super.dispose();
  }

Upvotes: 2

Robin Bozan
Robin Bozan

Reputation: 92

You can use the following from dart_html:

    window.onKeyPress.listen((KeyboardEvent e) {
      print(e.charCode.toString() + " " + new String.fromCharCode(e.charCode));
    });

Upvotes: 5

Related Questions