2

I have a flutter app where I need to look up details after a barcode has been scanned into a TextFormField (using a physical Android mobile device). The scanner just sends the barcodes as keyboard input (with '\n' at the end).

I am using a RawKeyboardListener and it works fine when I manually enter text and press enter, however, the scanner sends the keystrokes at a much higher rate and the TextEditingController is still empty when the Enter key arrives at the RawKeyboardListener.

I am currently using a Future.delayed to wait 500 ms before trying to read the text and this works, but I do not like this solution very much (too fragile):

handleKey(RawKeyEvent event) {
  var isEnter = event.logicalKey.keyId == LogicalKeyboardKey.enter.keyId || event.logicalKey.keyId == 1108101563381;
  if (event.runtimeType.toString() == 'RawKeyDownEvent' && isEnter) {
    print('- Text: ' + _textController.text);                 <--- Empty here
    Future.delayed(const Duration(milliseconds: 500), () {
      print('- Text after delay: ' + _textController.text);   <--- '013803266986' here
    });
  }
}

My question: What is a more robust method to get the scanned barcode (text) after the Enter key?

Here is a full runnable sample:

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

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  TextEditingController _textController = new TextEditingController();
  FocusNode _textNode = new FocusNode();

  handleKey(RawKeyEvent event) {
    var isEnter = event.logicalKey.keyId == LogicalKeyboardKey.enter.keyId || event.logicalKey.keyId == 1108101563381;
    if (event.runtimeType.toString() == 'RawKeyDownEvent' && isEnter) {
      print('- Text: ' + _textController.text);
      Future.delayed(const Duration(milliseconds: 500), () {
        print('- Text after delay: ' + _textController.text);
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: SizedBox(
          width: double.infinity,
          height: 300,
          child: RawKeyboardListener(
            focusNode: FocusNode(),
            onKey: (event) => handleKey(event),
            child: TextFormField(
              controller: _textController,
              focusNode: _textNode,
            ),
          ),
        ),
      ),
    );
  }
}

Note: This method does not work for me (since it makes the text field grow): Detect 'enter key' press in flutter

Lars335
  • 1,730
  • 2
  • 22
  • 35
  • I will withdraw my answer as it doesn’t solve your question. However I recommend you update the question with the link to the issue track and explain that you are looking for a workaround as the api has a bug, not for the official way it should work. – Kent Dec 10 '19 at 19:11
  • RawKeyboardListener seems to have issues with focus when used with TextField. Here's an open issue on GitHub https://github.com/flutter/flutter/issues/67915 – Omatt Jul 22 '21 at 15:12

1 Answers1

0

A more robust way to delay code until the next framework tick would be WidgetsBinding.instance.scheduleFrameCallback. Pass your closure here instead of Future.delayed.


Also may be worth trying the Shortcuts class instead of RawKeyboardListener; following this example.

I just discovered it and generally seems like it could replace RawKeyboardListener for a lot of solutions.

jayjw
  • 677
  • 7
  • 12