Flutter hide onscreenkeyboard on android Barcode Scanner - android

iam developing a Flutter app to acquire numbers via an extrernal Bluetooth Scanner which acts like a normal Keyboard.
If i tap on the TextInputField the native android Keyboard pops up to enter text, which is fine, but i need to supress the keyboard since iam going to scan Barcodes and i need the full Screen to see my scanned Barcodes.
#Barcodes: these are only 12-digit long numbers.
so far,
axp

//I created this solution based it works for iOS and Android both.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class QRCodeScreen extends StatefulWidget {
QRCodeScreen({Key key}) : super(key: key);
#override
_QRCodeScreenState createState() => _QRCodeScreenState();
}
class _QRCodeScreenState extends State<QRCodeScreen> {
FocusNode node = NoKeyboardEditableTextFocusNode();
TextEditingController scanController = TextEditingController();
#override
void initState() {
node.requestFocus();
Future.delayed(
const Duration(milliseconds: 300),
() {
SystemChannels.textInput.invokeMethod('TextInput.invisible');
}
);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Container(
child: IgnorePointer(
child: Padding(
padding: const EdgeInsets.all(5.0),
child: Column(
children: [
TextField(
controller: scanController,
focusNode: node,
autofocus: true,
decoration: InputDecoration(hintText: "WAITING FOR QR CODE..."),
),
SizedBox(height:50),
],
),
),
),
),
);
}
#override
void dispose() {
node.dispose();
super.dispose();
}
}
class NoKeyboardEditableTextFocusNode extends FocusNode {
#override
bool consumeKeyboardToken() {
return false;
}
}

My scanner is an Android phone, here is what I've done:
Implemented Kotlin code to hide keyboard
Created a method channel
Invoke method in Flutter where I needed to hide soft keyboard
https://github.com/flutter/flutter/issues/44681#issuecomment-962110920

Related

Keyboard immediately disappear when onTap event fired in Flutter

I'm developing a flutter app using Bloc, Division, and Get for the navigation.
I have a text field reusable component that I use in most of the fields that I create, the text field works properly on iOS without any issues, but on Android the Keyboard immediately disappear when I tap the field.
Here are the things that I've done but it still doesn't work:
Not using text field from the component, but directly create another one on the page
Create my own testing page that only used to test the text field
Using resizeToAvoidBottomInset: false, defined the controller as a static final
The only thing that works is when I create another flutter project from the start.
my-testing.dart
import 'package:flutter/material.dart';
class MyTesting extends StatefulWidget {
const MyTesting({Key? key}) : super(key: key);
#override
State<MyTesting> createState() => _MyTestingState();
}
class _MyTestingState extends State<MyTesting> {
late TextEditingController controller;
#override
void initState() {
controller = TextEditingController();
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Just testing'),
),
body: Padding(
padding: const EdgeInsets.all(20),
child: TextField(
controller: controller,
),
),
);
}
}

Flutter TextField doesn't get physical keyboard focus after rebuild

I am trying to clear a the TextField whenever user presses a space and then it should be cleared and get the focus again for next word. I want to type the content word by word. I am using ValueListenableBuilder for managing the state. Below code can re-produce the issue.
Issue:- When I press space on physical keyboard it looses the physical keyboard focus. Works fine with phone's virtual keyboard. But my user requirement is to use physical keyboard connected to the host mobile phone.
void main() {
runApp(const TestScreen());
}
class TestScreen extends StatefulWidget {
const TestScreen({Key? key}) : super(key: key);
#override
State<TestScreen> createState() => _TestScreenState();
}
class _TestScreenState extends State<TestScreen> {
final ValueNotifier<String> _notifier = ValueNotifier("");
final FocusNode _fnode = FocusNode();
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("Text Demo")),
body: ValueListenableBuilder<String>(
valueListenable: _notifier,
builder: (context, value, child) {
// print("textfield is being built.");
return TextField(
focusNode: _fnode,
textAlign: TextAlign.center,
autofocus: true,
cursorColor: Colors.blue,
decoration: const InputDecoration(),
controller: TextEditingController(text: value),
maxLines: 1,
keyboardType: TextInputType.multiline,
onChanged: (text) {
if (text.contains(" ")) {
_notifier.value = "";
_notifier.notifyListeners();
_fnode.requestFocus();
}
},
);
}),
),
);
}
}

How to use barcode_scan widget as a child to some other widget?

I am using barcode_scan widget in my flutter app when I call Scan method this widget takes up the whole screen where it show the camera, I want to show that camera view inside another widget.
You can use package https://pub.dev/packages/last_qr_scanner or https://pub.dev/packages/qr_code_scanner
They both use platform view within Flutter
full example code of last_qr_scanner
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:last_qr_scanner/last_qr_scanner.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
const MyApp({
Key key,
}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
var qrText = "";
var controller;
#override
void initState() {
super.initState();
}
void _onQRViewCreated(QRViewController controller) {
this.controller = controller;
final channel = controller.channel;
controller.init(qrKey);
channel.setMethodCallHandler((MethodCall call) async {
switch (call.method) {
case "onRecognizeQR":
dynamic arguments = call.arguments;
setState(() {
qrText = arguments.toString();
});
}
});
}
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new Scaffold(
appBar: new AppBar(
title: new Text('Barcode Scanner Example'),
),
body: Column(
children: <Widget>[
Expanded(
child: LastQrScannerPreview(
key: qrKey,
onQRViewCreated: _onQRViewCreated,
),
flex: 4,
),
Expanded(
child: Text("This is the result of scan: $qrText"),
flex: 1,
),
Expanded(
child: RaisedButton(
onPressed: () {
this.controller.toggleTorch();
},
child: Text("Toggle Torch"),
),
flex: 1,
)
],
),
),
);
}
}
Your camera view must be a flutter widget to be embedded in another widget.
You can use this package which outputs the camera preview on a flutter texture and use the Mobile Vision API to detect QR codes and barcodes : https://github.com/rmtmckenzie/flutter_qr_mobile_vision

Flutter Bluetooth External Barcode Scanner

I need to use an external barcode scanner which is connected via Bluetooth to my device (it is recognized as a keyboard). It works well and I can get the content of a barcode inside a TextField.
The problem is that I need to set the focus to the TextField in order to get the content of the barcode... Is there a way for the current screen to listen to the keyboard event that way I could get the data without wasting time selecting the TextField?
Might be too late but you can set the TextField autofocus prop to true.
TextField(
autofocus:true,
)
You can use this example code for RawKeyboardListener:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'RawKeyboardListener';
#override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatefulWidget(),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
#override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
final FocusNode _focusNode = FocusNode();
String _controller = '';
String? _message;
#override
void dispose() {
_focusNode.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
FocusScope.of(context).requestFocus(_focusNode);
final TextTheme textTheme = Theme.of(context).textTheme;
return Container(
color: Colors.white,
alignment: Alignment.center,
child: DefaultTextStyle(
style: textTheme.bodyText1!,
child: RawKeyboardListener(
focusNode: _focusNode,
onKey: (RawKeyEvent event) {
if (event is RawKeyDownEvent) {
if (event.physicalKey == PhysicalKeyboardKey.enter) {
print('ENTER');
setState(() {
_message = _controller;
_controller = '';
});
} else {
print(
'_handleKeyEvent Event data keyLabel ${event.data.keyLabel}');
_controller += event.data.keyLabel;
}
print('controller: $_controller');
}
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
_message ?? 'Press a key',
),
Text(
'${_message?.length}',
),
],
),
),
),
);
}
}
You can use TextEditingController for TextFormField

Hide On-Screen Keyboard when tapping outside of the Text Field (Anywhere on the screen) in Flutter [duplicate]

I am collecting user input with a TextFormField and when the user presses a FloatingActionButton indicating they are done, I want to dismiss the on screen keyboard.
How do I make the keyboard go away automatically?
import 'package:flutter/material.dart';
class MyHomePage extends StatefulWidget {
MyHomePageState createState() => new MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
TextEditingController _controller = new TextEditingController();
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.send),
onPressed: () {
setState(() {
// send message
// dismiss on screen keyboard here
_controller.clear();
});
},
),
body: new Container(
alignment: FractionalOffset.center,
padding: new EdgeInsets.all(20.0),
child: new TextFormField(
controller: _controller,
decoration: new InputDecoration(labelText: 'Example Text'),
),
),
);
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
void main() {
runApp(new MyApp());
}
For Flutter version 2 or latest :
Since Flutter 2 with null safety this is the best way:
FocusManager.instance.primaryFocus?.unfocus();
Note: using old ways leads to some problems like keep rebuild states;
For Flutter version < 2 :
As of Flutter v1.7.8+hotfix.2, the way to go is:
FocusScope.of(context).unfocus();
Comment on PR about that:
Now that #31909 (be75fb3) has landed, you should use
FocusScope.of(context).unfocus() instead of
FocusScope.of(context).requestFocus(FocusNode()), since FocusNodes are
ChangeNotifiers, and should be disposed properly.
-> DO NOT use ̶r̶e̶q̶u̶e̶s̶t̶F̶o̶c̶u̶s̶(̶F̶o̶c̶u̶s̶N̶o̶d̶e̶(̶)̶ anymore.
F̶o̶c̶u̶s̶S̶c̶o̶p̶e̶.̶o̶f̶(̶c̶o̶n̶t̶e̶x̶t̶)̶.̶r̶e̶q̶u̶e̶s̶t̶F̶o̶c̶u̶s̶(̶F̶o̶c̶u̶s̶N̶o̶d̶e̶(̶)̶)̶;̶
Read more about the FocusScope class in the flutter docs.
Note: This answer is outdated. See the answer for newer versions of Flutter.
You can dismiss the keyboard by taking away the focus of the TextFormField and giving it to an unused FocusNode:
FocusScope.of(context).requestFocus(FocusNode());
Solution with FocusScope doesn't work for me.
I found another:
import 'package:flutter/services.dart';
SystemChannels.textInput.invokeMethod('TextInput.hide');
It solved my problem.
For Flutter 1.17.3 (stable channel as of June 2020), use
FocusManager.instance.primaryFocus.unfocus();
Following code helped me to hide keyboard
void initState() {
SystemChannels.textInput.invokeMethod('TextInput.hide');
super.initState();
}
To dismiss the keyboard (1.7.8+hotfix.2 and above) just call the method below:
FocusScope.of(context).unfocus();
Once the FocusScope.of(context).unfocus() method already check if there is focus before dismiss the keyboard it's not needed to check it. But in case you need it just call another context method: FocusScope.of(context).hasPrimaryFocus
Looks like different approaches for different version. I am using Flutter v1.17.1 and the below works for me.
onTap: () {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
currentFocus.focusedChild.unfocus();
}
}
GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child:Container(
alignment: FractionalOffset.center,
padding: new EdgeInsets.all(20.0),
child: new TextFormField(
controller: _controller,
decoration: new InputDecoration(labelText: 'Example Text'),
),
), })
try this on tap gesture
None of the above solutions don't work for me.
Flutter suggests this -
Put your widget inside new GestureDetector() on which tap will hide keyboard and onTap use FocusScope.of(context).requestFocus(new FocusNode())
class Home extends StatelessWidget {
#override
Widget build(BuildContext context) {
var widget = new MaterialApp(
home: new Scaffold(
body: new Container(
height:500.0,
child: new GestureDetector(
onTap: () {
FocusScope.of(context).requestFocus(new FocusNode());
},
child: new Container(
color: Colors.white,
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
new TextField( ),
new Text("Test"),
],
)
)
)
)
),
);
return widget;
}}
For me, the Listener above App widget is the best approach I've found:
Listener(
onPointerUp: (_) {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
currentFocus.focusedChild.unfocus();
}
},
child: MaterialApp(
title: 'Flutter Test App',
theme: theme,
...
),
)
This may simplify the case. Below code will work only if keyboard is open
if(FocusScope.of(context).isFirstFocus) {
FocusScope.of(context).requestFocus(new FocusNode());
}
As in Flutter everything is a widget, I decided to wrap the FocusScope.of(context).unfocus(); approach in a short utility widget.
Just create the KeyboardHider widget:
import 'package:flutter/widgets.dart';
/// A widget that upon tap attempts to hide the keyboard.
class KeyboardHider extends StatelessWidget {
/// Creates a widget that on tap, hides the keyboard.
const KeyboardHider({
required this.child,
Key? key,
}) : super(key: key);
/// The widget below this widget in the tree.
final Widget child;
#override
Widget build(BuildContext context) {
return GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => FocusScope.of(context).unfocus(),
child: child,
);
}
}
Now, you can wrap any widget (very convenient when using a good IDE) with the KeyboardHider widget, and then when you tap on something, the keyboard will close automatically. It works well with forms and other tappable areas.
class SimpleWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return KeyboardHider(
/* Here comes a widget tree that eventually opens the keyboard,
* but the widget that opened the keyboard doesn't necessarily
* takes care of hiding it, so we wrap everything in a
* KeyboardHider widget */
child: Container(),
);
}
}
You can use unfocus() method from FocusNode class.
import 'package:flutter/material.dart';
class MyHomePage extends StatefulWidget {
MyHomePageState createState() => new MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
TextEditingController _controller = new TextEditingController();
FocusNode _focusNode = new FocusNode(); //1 - declare and initialize variable
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.send),
onPressed: () {
_focusNode.unfocus(); //3 - call this method here
},
),
body: new Container(
alignment: FractionalOffset.center,
padding: new EdgeInsets.all(20.0),
child: new TextFormField(
controller: _controller,
focusNode: _focusNode, //2 - assign it to your TextFormField
decoration: new InputDecoration(labelText: 'Example Text'),
),
),
);
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
home: new MyHomePage(),
);
}
}
void main() {
runApp(new MyApp());
}
To summarize, this is a working solution for Flutter 1.17:
Wrap your Widget like this:
GestureDetector(
onTap: FocusScope.of(context).unfocus,
child: YourWidget(),
);
if you use CustomScrollView, just put,
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag,
You can wrap your widget with "GestureDetector", then assign "FocusScope.of(context).unfocus()" to its onTap function
GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: child,
);
_dismissKeyboard(BuildContext context) {
FocusScope.of(context).requestFocus(new FocusNode());
}
#override
Widget build(BuildContext context) {
return new GestureDetector(
onTap: () {
this._dismissKeyboard(context);
},
child: new Container(
color: Colors.white,
child: new Column(
children: <Widget>[/*...*/],
),
),
);
}
Call this function when you needed
void hideKeyboard(BuildContext context) {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
FocusManager.instance.primaryFocus?.unfocus();
}
}
You can also declare a focusNode for you textfield and when you are done you can just call the unfocus method on that focusNode
and also dispose it
class MyHomePage extends StatefulWidget {
MyHomePageState createState() => new MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
TextEditingController _controller = new TextEditingController();
/// declare focus
final FocusNode _titleFocus = FocusNode();
#override
void dispose() {
_titleFocus.dispose();
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(),
floatingActionButton: new FloatingActionButton(
child: new Icon(Icons.send),
onPressed: () {
setState(() {
// send message
// dismiss on screen keyboard here
_titleFocus.unfocus();
_controller.clear();
});
},
),
body: new Container(
alignment: FractionalOffset.center,
padding: new EdgeInsets.all(20.0),
child: new TextFormField(
controller: _controller,
focusNode: _titleFocus,
decoration: new InputDecoration(labelText: 'Example Text'),
),
),
);
}
}
FocusScope.of(context).unfocus() has a downside when using with filtered listView.
Apart from so many details and concisely, use keyboard_dismisser package in https://pub.dev/packages/keyboard_dismisser will solve all the problems.
I have created this function to my base code, so far works well!!
void hideKeyword(BuildContext context) {
FocusScopeNode currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus && currentFocus.focusedChild != null) {
currentFocus.focusedChild.unfocus();
}
}
FocusScope.of(context).unfocus(); doesn't work.
This code works for me at flutter ver 2.2.3 and null safety.
WidgetsBinding.instance?.focusManager.primaryFocus?.unfocus()
Source: https://github.com/flutter/flutter/issues/20227#issuecomment-512860882
For example, put this code in MyAppState to apply hide keyboard when touch outside for whole app.
return GestureDetector(
onTap: () =>
WidgetsBinding.instance?.focusManager.primaryFocus?.unfocus(),
child: MaterialApp(
title: 'Flutter Demo',
theme: getTheme(),
home: _body(),
),
);
Use SystemChannels.textInput.invokeMethod('TextInput.hide');. It will close/dismiss the keyboard when the screen loads.
void initState() {
super.initState();
SystemChannels.textInput.invokeMethod('TextInput.hide');
}
====== Dismiss the keyboard after clicking out of the TextField =======
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(), //this will dismiss keyboard
child: Scaffold(
body: SafeArea(
.........
====== Dismiss the keyboard when scrolling the screen =======
ListView(
keyboardDismissBehavior: ScrollViewKeyboardDismissBehavior.onDrag, //this will dismiss
children: [
..........
The SingleChildScrollView widget also have this property.
You can use this one.
FocusScope.of(context).requestFocus(FocusNode());
And you can use this onTap of GestureDetector or InkWell like this.
`GestureDetector(
onTap: () {`
// THIS FOCUS SCOPE WILL CLOSE THE KEYBOARD
FocusScope.of(context).requestFocus(FocusNode());
forgotPasswordAPI(emailController.text);
},``
add this code inside build widget
FocusScope.of(context).requestFocus(FocusNode());
If your keyboard still won't turn off , don't forget add focusNode to TextField. The above information was helpful, but forgetting to add focusNode bothered me a bit. Here an example.
TextField(
focusNode: FocusNode(),
textController: _controller,
autoFocus: false,
textStyle: TextStyle(fontSize: 14),
onFieldSubmitted: (text) {},
onChanged: (text) {},
hint: 'Enter the code',
hintColor: CustomColors.mediumGray,
suffixAsset: _voucherController.text.length == 7
? Assets.ic_approved_voucher
: null,
isIcon: false,
isObscure: false,
maxLength: 7,
)
closeKeyboard(BuildContext context) {
var currentFocus = FocusScope.of(context);
if (!currentFocus.hasPrimaryFocus) {
currentFocus.unfocus();
}
}
#override
Widget build(BuildContext context) {
_keyboardVisible = MediaQuery.of(context).viewInsets.bottom != 0;
size = MediaQuery.of(context).size;
return GestureDetector(
onTap: () {
closeKeyboard(context);
},
child: Scaffold(
backgroundColor: Colors.white,
body: Container(
width: double.maxFinite,
height: double.maxFinite,
child: _buildUI(vm)),
),
);
}
try using a TextEditingController.
at the begining,
final myController = TextEditingController();
#override
void dispose() {
// Clean up the controller when the widget is disposed.
myController.dispose();
super.dispose();
}
and in the on press event,
onPressed: () {
myController.clear();}
this will dismiss the keybord.
If you use TextField(maxLines: null) and just want to show Done button ON the screen keyboard to hide it, the code below works.
TextField(
keyboardType: TextInputType.text,
maxLines: null,
)
Side note: why in the first place doesn't the keyboard show Done button? The reason is found in the implementation of TextField:
keyboardType = keyboardType ?? (maxLines == 1 ? TextInputType.text : TextInputType.multiline),

Categories

Resources