Flutter RawKeyboardListener listening twice?

14,281

Solution 1

Your callback is getting called for both keydown and keyup events with instances of following classes:

  • RawKeyDownEvent
  • RawKeyUpEvent

You can pass the whole object to handleKey, and filter based on runtime type of object. for example

  handleKey(RawKeyEvent key) {
    print("Event runtimeType is ${key.runtimeType}");
    if(key.runtimeType.toString() == 'RawKeyDownEvent'){
        RawKeyEventDataAndroid data = key.data as RawKeyEventDataAndroid;
        String _keyCode;
        _keyCode = data.keyCode.toString(); //keycode of key event (66 is return)

        print("why does this run twice $_keyCode");
    }
  }

  _buildTextComposer() {
      TextField _textField = new TextField(
          controller: _controller,
          onSubmitted: _handleSubmitted,
      );

      FocusScope.of(context).requestFocus(_textNode);

      return new RawKeyboardListener(
          focusNode: _textNode,
          onKey: handleKey,
          child: _textField
      );
  }

If this still does not help, check actual runtimeTypes logged from handleKey method, and filter by those.

Solution 2

You are right. RawKeyboardListener listens on raw keyboard events. Which means it returns down and up (or how the naming convention is on touchscreens). Knowing that you could simply create a if-statement and just get through the event once:

bool _tempKeyPressedOnce = false;
if (!_tempKeyPressedOnce) {
    // do stuff
    _tempKeyPressedOnce = true;
}

Solution 3

I prefer using is rather than accessing the runtime type:

onKey: (RawKeyEvent event) {
  if (event is RawKeyDownEvent) {
    // handle key down
  } else if (event is RawKeyUpEvent) {
    // handle key up
  }
},

Solution 4

Eyo, played around with the values variables and noticed that if you use the iskeypressed on the second time round it's false. Id hazard a guess that normally its either detecting the press and the release.

so

    RawKeyboardListener(
                focusNode: FocusNode(),
                autofocus: true,
                //includeSemantics: true,
                onKey: (value){

                  print("1) ${value.data}");
                  print("2) ${value.character.toString()}");
                  print("3) ${value.toString()}");
                  print("4) ${value.physicalKey.debugName}");
                  print("5) ${value.logicalKey.keyId}");
                  print("6) ${value.isKeyPressed(LogicalKeyboardKey.enter)}");


                  setState(() {
                   
                    ///add string to list and clear text or not ?
                    value.logicalKey == LogicalKeyboardKey.enter ? print("YES A") : 0;
                    value.isKeyPressed(LogicalKeyboardKey.enter) ? print("YES B") : 0;
                    }
                  );
                },

Results in a

    flutter: 1) Instance of 'RawKeyEventDataWindows'
flutter: 2) 

flutter: 3) RawKeyDownEvent#13d45(logicalKey: LogicalKeyboardKey#70028(keyId: "0x100070028", keyLabel: "Enter", debugName: "Enter"), physicalKey: PhysicalKeyboardKey#70028(usbHidUsage: "0x00070028", debugName: "Enter"))
flutter: 4) Enter
flutter: 5) 4295426088
flutter: 6) true
flutter: YES A
flutter: YES B
flutter:  NEXT SET
flutter: ***********************************
flutter: 1) Instance of 'RawKeyEventDataWindows'
flutter: 2) null
flutter: 3) RawKeyUpEvent#9dc07(logicalKey: LogicalKeyboardKey#70028(keyId: "0x100070028", keyLabel: "Enter", debugName: "Enter"), physicalKey: PhysicalKeyboardKey#70028(usbHidUsage: "0x00070028", debugName: "Enter"))
flutter: 4) Enter
flutter: 5) 4295426088
flutter: 6) false
flutter: YES A
flutter:  NEXT SET
flutter: ***********************************

Solution 5

This is how you can get it to work:

RawKeyboardListener(
  focusNode: FocusNode(),
  onKey: (event) {
    // Only taking key down event into consideration
    if (event.runtimeType == RawKeyDownEvent) {
      bool shiftPressed = event.isShiftPressed; // true: if shift key is pressed
    }
  },
  child: TextField(),
)
Share:
14,281

Related videos on Youtube

Jayden
Author by

Jayden

Updated on June 04, 2022

Comments

  • Jayden
    Jayden almost 2 years

    What I am trying to achieve is when viewing this widget, the RawKeyboardListener starts listening straight away when the TextField is not selected/in focus. It runs the HandleKey function to deal with what I want to do with the keyCode.

    The issue I am having is when running the app for the first time, the handleKey function seems to be running twice. So in the example below it would print why does this run twice $_keyCode TWICE when I only enter 1 key. I think it listens to keyUp AND keyDown. The result I want is for it to only run once...

    However, the code works fine as well when I select the TextField and do a regular submit with the emulator keyboard.

    I am struggling to understand why it only has a problem after interacting with the TextField. I feel like it needs a Future or await somewhere? but I have no idea.

    Please help.

    import 'package:flutter/material.dart';
    import 'package:flutter/services.dart';
    import 'dart:async';
    
    class KeyboardListener extends StatefulWidget {
    
        KeyboardListener();
    
        @override
        _RawKeyboardListenerState createState() => new _RawKeyboardListenerState();
    }
    
    class _RawKeyboardListenerState extends State<KeyboardListener> {
    
        TextEditingController _controller = new TextEditingController();
        FocusNode _textNode = new FocusNode();
    
    
        @override
            initState() {
            super.initState();
        }
        
        //Handle when submitting
        void _handleSubmitted(String finalinput) {
    
            setState(() {
                SystemChannels.textInput.invokeMethod('TextInput.hide'); //hide keyboard again
                _controller.clear();
            });
        }
    
        handleKey(RawKeyEventDataAndroid key) {
            String _keyCode;
            _keyCode = key.keyCode.toString(); //keycode of key event (66 is return)
    
            print("why does this run twice $_keyCode");
        }
    
        _buildTextComposer() {
            TextField _textField = new TextField(
                controller: _controller,
                onSubmitted: _handleSubmitted,
            );
    
            FocusScope.of(context).requestFocus(_textNode);
    
            return new RawKeyboardListener(
                focusNode: _textNode,
                onKey: (key) => handleKey(key.data),
                child: _textField
            );
        }
    
    
      @override
      Widget build(BuildContext context) {
        return new Scaffold(
          appBar: new AppBar(title: new Text("Search Item")),
          body: _buildTextComposer(),
        );
      }
    }
    
    • Mahesh Jamdade
      Mahesh Jamdade over 2 years
      That's intended to notify key press and key release.
  • Jayden
    Jayden almost 6 years
    But how do I access the RawKeyDownEvent or whatever I need to get to see if it is only a keydown or up so I can then use a bool on it
  • Bostrot
    Bostrot almost 6 years
    Something like this: RawKeyDownEvent keyDownEvent = key; keyDownEvent.data;
  • Kirill Karmazin
    Kirill Karmazin over 4 years
    afaik this listener only works for physical keyboards, it doesn't work for 'soft' keyboard, right?
  • Gyuri Majercsik
    Gyuri Majercsik almost 4 years
    I would rather use the instance-of kind operator: key is RawKeyDownEvent
  • TomTom101
    TomTom101 over 3 years
    Beware that this would not work in a production release where runtimeType will get optimized out, i.e. is not available.
  • lolbardsnin
    lolbardsnin almost 3 years
    ah just realised RawKeyUpEvent#9dc07 RawKeyDownEvent#13d45
  • Chris
    Chris almost 2 years
    Perfect! Best answer, although should explain what the others have already i.e. that two events are triggering