All keys are unresponsive when using HardwareKeyboard in Flutter - flutter

I have the android device you see in the picture. This device has an orange button on the back. I want to trigger a certain function when this button is pressed. I just try to use the HardwareKeyboard in Flutter as below and get a solution, but I don't get a response.enter image description here
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
#override
void initState() {
HardwareKeyboard.instance.addHandler(key_handler);
super.initState();
}
bool key_handler(event) {
print(event.physicalKey.debugName);
// In this part, when a button is clicked, it should return certain responses to me.
if (event is KeyDownEvent) {
if (event.physicalKey.usbHidUsage == PhysicalKeyboardKey.audioVolumeDown.usbHidUsage) {
_incrementCounter();
} else if (event.physicalKey.usbHidUsage ==
PhysicalKeyboardKey.audioVolumeUp.usbHidUsage) {
_decrementCounter();
}
}
return true;
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
void _decrementCounter() {
setState(() {
_counter--;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'Hit a Volume UP/Down key:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
TextField(
decoration: InputDecoration(
hintText: "Once touch here and show a software key."),
),
],
),
),
);
}
}
The code above increments or decrements the count by 1 unit when the volume up and down buttons are pressed. It works.
The button I need is known as f4 on the back. But when I click on this button I don't get any response.
I think Flutter has this problem because the codes of this part are not complete. Because there is no response from any button except the volume up and down buttons, the back button.
There is no Plugin that I can find to solve this issue. There used to be a plugin called hardware_buttons. Currently unavailable. Because it was written 3 years ago and the update has not arrived. I think since Flutter added HardwareKeyboard to itself, it was no longer needed.
Please write all the guesses you know and guess in the comment section.

Related

Keyboard not showing up properly on TextFormField when switching between "screen states"

I have a scenario that starts with a hidden input field. AppBar has two buttons. When the user clicks on one of then, I set state to change a variable's value and based on that value, I show the hidden input.
My problem is that this input has to have two different configurations, one for each button. If the user clicks on the left one, the input should receive focus but the keyboard should not open. The right one applies focus and shows the keyboard as well.
In this codepen i have an example of my problem. Codepen is not showing the keyboard when neither of the buttons are tapped, but thats a codepen's problem. If you run this code on vs code or on android studio, you will be able to test the code like below:
Tap the left button, input is shown with no keyboard. Then tap the left button again and then tap on the right one now. Input is shown with keyboard. Now tap the right button to hide the input and tap on the left once again. Now, instead of tapping the left button before taping on the right one, tap on the right button straightforward. No keyboard is shown.
Is there something wrong on my example or something that i can change in order to force the keyboard to show if I go straight from one button to another ?
I did a few tests with unfocus, trying to force it before applying focus again, but had no success.
codepen code below:
import 'package:flutter/material.dart';
class ScreenUsageState {
static int get defaultState => 0;
static int get firstState => 1;
static int get secondState => 2;
}
void main() {
runApp(
const MaterialApp(
debugShowCheckedModeBanner: false,
home: MyWidget(),
),
);
}
class MyWidget extends StatefulWidget {
const MyWidget({Key? key}) : super(key: key);
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
int _currentState = ScreenUsageState.defaultState;
late FocusNode focusNodeTextField;
#override
void initState() {
super.initState();
focusNodeTextField = FocusNode();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
icon: Icon(Icons.settings_remote_outlined,
color: _currentState == ScreenUsageState.secondState
? Colors.red
: Colors.white),
onPressed: () {
setState(
() {
if (_currentState == ScreenUsageState.secondState) {
_currentState = ScreenUsageState.defaultState;
} else {
_currentState = ScreenUsageState.secondState;
focusNodeTextField.requestFocus();
}
},
);
},
),
IconButton(
icon: Icon(Icons.dialpad_outlined,
color: _currentState == ScreenUsageState.firstState
? Colors.red
: Colors.white),
onPressed: () {
setState(() {
if (_currentState == ScreenUsageState.firstState) {
_currentState = ScreenUsageState.defaultState;
} else {
_currentState = ScreenUsageState.firstState;
focusNodeTextField.requestFocus();
}
});
}),
],
),
body: Center(
child: ConstrainedBox(
constraints: BoxConstraints.tight(const Size(200, 50)),
child: _currentState == ScreenUsageState.defaultState
? Container()
: TextFormField(
focusNode: focusNodeTextField,
autofocus: false,
keyboardType:
ScreenUsageState.firstState == _currentState
? null
: TextInputType.none,
),
),
),
);
}
}

flutter TextField use cause KeyboardListener alway show keyboard on event

using KeyboardListener to gain barcode from scanner, i can't resolve a incredibly stupid problem.
scanning first with keyboardListener work perfectly.
events are gained, barcode too, no virtual keyboard, perfect..
but if i use any Textfield, in the same screen or anywhere in the program, after that,
any event coming to keyboardListener show Virtual Keyboard, without any textfield or else in the screen.
it come to be a nightmare..
I WANT TO AVOID THE KEYBOARD SHOWING, without any input..
don't want to close keyboard, many way on stack overflow to do it.
Step to reproduce:
1:use Physical keyboard or HID to enter serialcode or else whith KeyboardListener
2: Tap on textfield, and write anything and valid text
3: normay, KeyboardListener regain control and get physical events, and the Keyboard show on each...and this is the problem..
youtube video to illustrate (52s)
strange thing. if you use square key to set app background and get foreground, problem disapear.. virtual keyboard dont show again on physical keyboard or HID use... until next textfield use..
import 'package:flutter/foundation.dart';
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);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'KbdListener with TextField'),
);
}}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
List<FocusNode> ListFocusNode = [FocusNode(), FocusNode()];
DateTime whenlastchar = DateTime.now();
List<String> scanned4 = [];
String _receivedtext = "Scanned text here..";
final TextEditingController _myTextControler =
TextEditingController(text: "");
#override
void initState() {
ListFocusNode.first.requestFocus();
super.initState();
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
KeyboardListener(
key: const Key('KeyboardListener-files'),
focusNode: ListFocusNode.first,
autofocus: true,
onKeyEvent: (event) async {
var difference = DateTime.now().difference(whenlastchar);
whenlastchar = DateTime.now();
if (event.character != null) {
if (difference.inMilliseconds > 1000) {
scanned4.clear();
}
scanned4.add(event.character.toString());
if ((event.character == "\n") ||
(event.character == " ") ||
(event.character == 0x09)) {
String tempo =
scanned4.reduce((first, second) => first + second);
scanned4.clear();
tempo = tempo.trim();
// update
setState(() {
_receivedtext = tempo;
});
}
}
},
child: Column(
children: <Widget>[
Text(
_receivedtext,
style: Theme.of(context).textTheme.headlineSmall,
),
],
),
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
TextField(
controller: _myTextControler,
autofocus: false,
focusNode: ListFocusNode.last,
keyboardType: TextInputType.text,
style: const TextStyle(
fontSize: 20,
color: Colors.black,
fontWeight: FontWeight.w400,
),
textInputAction: TextInputAction.done,
onSubmitted: (value) {
print("textfield value: '$value'");
setState(() {
_receivedtext = value;
});
_myTextControler.clear();
FocusScope.of(context)
.requestFocus(ListFocusNode.first);
},
),
Row(children: [
TextButton(
child: const Text("KeyboardListener Focus"),
onPressed: () {
setState(() {
FocusScope.of(context)
.requestFocus(ListFocusNode.first);
});
},
),
]),
],
),
),
],
),
),
);
}
}
ok, so, there is no flutter software solution at this time.
its a google keyboard bug or flutter bug.. or both.
seem that the google keyboard don't dismiss from textfield, TextControler, focusnode or else. or flutter don't destroy callback to google keyboard. don't know.
But, trying other app keyboard, and its magical. its working.. normaly.. fluently.. as expected.
to be honest and complete, perhaps this replacement keyboard don't implement a callback or else.
but he work, the app work, my customers can use it fluently..
without changing any part of my code or flutter code..
the cost? just say to customer. 'install and use this keyboard..'
Did you try using https://pub.dev/packages/flutter_barcode_listener
This Library allows barcode scanning and addresses many problem arising from keyboard listener

Flutter: Calling Function from another Class State

My app allows people to post text and switch between different pages on a navbar. On the users page, there is a button, when clicked, will show an overlay so the user can create a post. The overlay includes a back button that calls a function to close the overlay. I want to keep the navbar available at the bottom so user can back out of the post that way if they want to.
The problem is, when the user uses the navbar, the overlay does not close because the close overlay function is on the user page and the navbar page does not have access to it.
How do I give another class on another dart file access to a method or function? If you are able to answer, can you please use my code instead of another example to help me follow better? Thank you.
User Page File #1
class UserPage extends StatefulWidget {
#override
_UserPageState createState() => _UserPageState();
}
class _UserPageState extends State<UserPage> {
OverlayEntry? entry;
#override
Widget build(BuildContext context) {
return Scaffold(
ElevatedButton(
child: const Text('New Post'),
onPressed: showOverlay,
),
),
}
void showOverlay() {
(...)
}
void closeOverlay() {
entry?.remove();
entry = null;
}
}
Nav Bar File #2 (Need help with "OnTap")
class Nav extends StatefulWidget {
const Nav({Key? key}) : super(key: key);
#override
_NavState createState() => _NavState();
}
class _NavState extends State<Nav> {
int currentTab = 1; // makes the home page the default when loading up the app
#override
void initState() {
super.initState();
}
List<Widget> tabs = <Widget>[
const Other(),
const Home(),
const UserPage(),
];
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: tabs.elementAt(currentTab),
),
// BOTTOM NAVIGATION BAR
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentTab,
onTap: (value) {
setState(() => currentTab = value);
const _UserPageState().closeOverlay(); //HERE IS WHERE I NEED HELP WITH THE CODE
},
items: const [
BottomNavigationBarItem(
label: 'Other',
),
BottomNavigationBarItem(
label: 'Home',
),
BottomNavigationBarItem(
label: 'User Page',
),
],
),
);
}
}
You can try to make your _UserPageState public by removing - from it, and then call it UserPageState().closeOverlay();

How to Force the user to answer a question for open the app in Flutter?

I have a Flutter application and I want to add a page that appears when opening the application asking the user to answer a question such as how many countries in the world - the answer is already stored in the application, so that if the answer is correct, the answer is stored and the application opens and this page does not appear again,
But if the answer is wrong, the user remains On this page, he cannot open the application until he writes the correct answer
Any suggestions or examples that would be helpful?
Update: I have created the following verification page that checks if the entered text is equal to the stored text,I used flutter_secure_storage to store the text if it is true Now iwant to know how i can add the shared_preferences to my code?
class check extends StatefulWidget {
#override
_checkState createState() => _checkState();
}
class _checkState extends State<check> {
final formKey = GlobalKey<FormState>();
final verifierController = TextEditingController();
String storedvalue = '200';
#override
void initState() {
super.initState();
init();
}
Future init() async {
final realcode = await UserSecureStorage.getCodestored() ?? '';
setState(() {
this.verifierController.text = realcode;
});
}
Codecheck() async {
await UserSecureStorage.setUsername(verifierController.text);
if (storedvalue == verifierController.text) {
Navigator.of(context).pushReplacementNamed('/homeScreen');
}
else {
Navigator.of(context).pushReplacementNamed('/checkScreen');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(10),
child: Center(
child: Stack(
children: [
Align(
child: Text(
'how many countries are there in the world?',
.........
),
Align(
child: TextFormField(
.......
controller: verifierController,
)),
Align(
child: RaisedButton(
.........
onPressed: () async {
Codecheck();
},
..........
you would check the user's answer, if it's correct, you save a boolean in the shared preferences and then navigate to the app home page and every time you open the app you check on this boolean from the shared preferences, if it's true then don't show the question and open the home page directly, if not, then show the question again
In a very simple way, you can use the SharedPreferences plugin to store the answer to your question permanently, for example
You can store a "question" key that will have the value "how many countries are there in the world?" (optional).
You also store an "answer" key with the value "324" (the exact number of countries in the world)
Then you create an "answer_found" key which will be a boolean and will update if yes or no the user answers the question correctly.
Then when the application starts, you will first query the "answer_found" key to see if its value is True.
If this value is True, you do not display the questionnaire page, if it is false or null, you display the questionnaire page.
When the user will enter the answer, simply compare there his answer to the one contained in the "answer" key in the preferences. If it is correct, simply update the key "answer_found" to become true. In the opposite case do nothing (or what you want)
UPDATE :
As you asked, here is an extract of the code.
I made it as simple as possible (although it is a bit barbaric) so that you can understand the mechanism as well as possible and that you can adapt it as you want.
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await SharedPreferences.getInstance().then((preferences) async {
//Optional key (and can put want you want)
//Store the question in preferences
await preferences.setString('question', 'how many countries are there in the world?');
//Store the answer
await preferences.setInt('answer', 324);
});
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
bool? questionAnswered;
#override
void initState() {
super.initState();
_getQuestionState();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: home,
);
}
Widget get home {
if (questionAnswered == null) {
return const Scaffold(body: Center(child: CircularProgressIndicator()));
} else if (questionAnswered!) {
return const HomePage();
} else {
return const QuestionPage();
}
}
Future<void> _getQuestionState() async {
final preferences = await SharedPreferences.getInstance();
//obtaining the present value of 'answer_found' to know if the question has been answered (with a correct answer)
final isQuestionAnswered = preferences.getBool('answer_found') ??
false; //if the value is null, we set it to false (to avoid a NullException)
setState(() => questionAnswered = isQuestionAnswered);
}
}
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('Home Page'),
),
body: const Center(
child: Text('WELCOME'),
),
);
}
class QuestionPage extends StatefulWidget {
const QuestionPage({Key? key}) : super(key: key);
#override
State<QuestionPage> createState() => _QuestionPageState();
}
class _QuestionPageState extends State<QuestionPage> {
late final TextEditingController _answerTextController;
#override
void initState() {
super.initState();
_answerTextController = TextEditingController();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
const Text('how many countries are there in the world?'),
TextField(
controller: _answerTextController,
decoration: const InputDecoration(hintText: 'Enter answer'),
),
ElevatedButton(
onPressed: () async {
//Convert the user's response to be in an integer type (because we want to make a comparison with an integer)
//The user's response will be null, if the user has not entered an integer
final userAnswer = int.tryParse(_answerTextController.text);
if (userAnswer != null) {
final preferences = await SharedPreferences.getInstance();
final storedAnswer = preferences.getInt('answer')!;
if (userAnswer == storedAnswer) {
preferences.setBool('answer_found', true);
Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) => const HomePage()));
} else {
//Notify user that his answer was wrong or do some stuff (what you want)
}
} else {
//Do some stuff
}
},
child: const Text('Validate')),
],
),
),
);
}
}
I have never used the flutter_secure_storage plugin, but following the code snippet I made for you, I think you could readapt it to work with flutter_secure storage, the reasoning and logic is the same.
P.S: You don't have to use shared_preferences and flutter_secure_storage at the same time. You can simply use flutter_secure_storage which, unlike shared_preferences, offers you a secure storage space (as its name indicates) and you only have to implement the same logic.
I hope you will have the splash screen as the first screen to load in your application.
now, when the first screen will load.
make this first screen a stateful.
this will allow you to use the "initstate" method. and you know that the initstate method will be called first to execute the code.
initstate(){
/// in this case I am using "get_storage" to store the data in local.
GetStorage prefs = GetStorage("ApplicationName");
bool isAnswered = prefs.read("isAnsweredQuestion") ?? false;
if(isAnswered){
/// redirect to the other screen.
}else{
/// redirect to the screen where you have the questions
/// or open the dialog having questions.
}
super.initstate();
};
the initstate method will execute for the first time when the application loads and execute the splash screen and it will check for the local data store.
if the user is opening the application for the first time then the local data will be null and we have used the null-check operator to handle that.
If the user already answered the question then we will get "true" stored in the local. and we will redirect the user to the other screen in that case.

Get Selection of TextFormField in Flutter

My goal is to get the selected text of the TextFormField, but each time the button is pressed, the TextFormField loses its focus, and the print in the console shows only a selection between -1 and -1.
It did work a few weeks ago, did the behavior change in the latest release? I am on the stable Flutter channel. (Flutter Channel stable, 2.5.0)
This is my test example:
import 'package:flutter/material.dart';
class Play extends StatefulWidget {
const Play({Key? key}) : super(key: key);
#override
_PlayState createState() => _PlayState();
}
class _PlayState extends State<Play> {
TextEditingController _controller = TextEditingController();
FocusNode _node = FocusNode();
void _onPressed() {
TextSelection selection = _controller.selection;
print("selection.start: ${selection.start}");
print("selection.end: ${selection.end}");
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
focusNode: _node,
controller: _controller,
),
ElevatedButton(
onPressed: _onPressed,
child: Text("do something"),
),
],
),
);
}
}
Since it works fine on mobile, I'd say it's a bug in Flutter, but fear not! Welcome to the world of workarounds and temporary solutionsTM; replace your entire _onPressed method by this:
int start = -1;
int end = -1;
#override
void initState() {
super.initState();
_controller.addListener(() async {
TextSelection selection = _controller.selection;
if (selection.start > -1) start = selection.start;
if (selection.end > -1) end = selection.end;
});
}
void _onPressed() {
print(start);
print(end);
}
Let me know if something is not very clear and I'll be happy to edit the answer with further explanations.
DartPad example
The Flutter community would love you to file a bug report (if it doesn't exist already)! :D