Ad

Flutter Keyboard Listener Builds Compounded On Each Refresh

- 1 answer

New to Flutter. I have a keyboard listener wrapping my Scaffold body to detect Enter and backspace key.

I noticed however: upon every page refresh, there will be additional listeners causing double firing of events. i.e. increment function triggered within the listener will double

  • 1st open: every 1 'enter' keypress: 1 count
  • refresh: every 1 'enter' keypress: 2 counts
  • refresh again: every 1 'enter' keypress: 4 counts
  • refresh again: every 1 'enter' keypress: 8 counts

I have to kill the browser and reopen the page to return to the initial state.

What's happening here? any best way to resolve this?

My code (selected relevant segments):

class MyHomePageState extends State<MyHomePage> {
  static int counter = 0;

  void _incrementCounter() {
    setState(() {
      counter++;
    });
  }

  void _decrementCounter() {
    setState(() {
      if (counter > 0) {
        counter--;
     }
    });
  }

. . .

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      bottomSheet: Container(
        padding: EdgeInsets.all(5.0),
        child: Text(
          'Some text',
          style: const TextStyle(
            fontSize: 12.0,
          ),
      textAlign: TextAlign.center,
    ),
  ),
  body: RawKeyboardListener(
    focusNode: FocusNode(),
    onKey: (RawKeyEvent event) {
      if (event.isKeyPressed(LogicalKeyboardKey.enter) ||
          event.isKeyPressed(LogicalKeyboardKey.numpadEnter)) {
        _incrementCounter();
      }

      if (event.isKeyPressed(LogicalKeyboardKey.backspace)) {
        _decrementCounter();
      }
    },
    autofocus: true,
    child: Stack(
      alignment: Alignment.center,
      children: <Widget>[
        Container(
          decoration: const BoxDecoration(.......

Thanks in advance!

Ad

Answer

Not the most beautiful solution but I managed to resolve this by separating one of the button and disposing the listener

_buildDefectButton() {
    // ElevatedButton _button =
    FocusScope.of(context).requestFocus(_focusNode);
    return RawKeyboardListener(
      focusNode: _focusNode,
      autofocus: true,
      onKey: (RawKeyEvent event) => {
        if (event is RawKeyDownEvent)
          {
            _handleKeyPressed(_focusNode, event),
          }
      },
      child: ElevatedButton.icon(
        style: ButtonStyle(
          padding:
              MaterialStateProperty.all<EdgeInsets>(const EdgeInsets.all(20)),
        ),
        label: const Text(
          'Button pressed!',
          style: TextStyle(
            fontSize: 20.0,
          ),
        ),
        onPressed: _incrementCounter,
        icon: const Icon(Icons.sentiment_very_dissatisfied),
      ),
    );
  }


//Handle when keyPressed
  _handleKeyPressed(FocusNode _focusNode, RawKeyEvent event) {
    if (event is RawKeyDownEvent) {
      if (event.isKeyPressed(LogicalKeyboardKey.enter) ||
          event.isKeyPressed(LogicalKeyboardKey.numpadEnter)) {
        _incrementCounter();
        // return KeyEventResult.handled;
      }

      if (event.isKeyPressed(LogicalKeyboardKey.backspace)) {
        _decrementCounter();
        // return KeyEventResult.handled;
      }
    }
    // return KeyEventResult.ignored;
  }

and just adding

final FocusNode _focusNode = FocusNode();

@override
  void initState() {
    super.initState();
}

and

@override
  void dispose() {
    // The attachment will automatically be detached in dispose().
    _focusNode.dispose();
    super.dispose();
  }

I did face some confusing situation in between if i return KeyEventResult.handled which causes a double count whenever the keyboard button is pressed, but just handling the keyboard press without returning result and disposing the keynode at the end resolve my issue that listener will compound upon every refresh. Hope this helps anybody who might face similar confusion in the future.

Ad
source: stackoverflow.com
Ad