I have a TextFormField in a flutter app and its Focus is important to the functionality of my screen. When the field has focus, I hide some widgets on the page so it looks good when the keyboard is open.
When I press the back button to exit the field, the keyboard is minimized, but the field still maintains focus. So the widgets I hid on the screen are still hidden. As soon as I tap out of focus, the widgets come back, but I know users will get confused at why the back button did not make the widget re-appear. I know I can release the focus by requesting a new focus:
FocusScope.of(context).requestFocus(FocusNode());
But, I don't know where I can invoke this release focus logic. I thought I might be able to create an EventChannel to subscribe to the native activity onBackButtonPressed() and send back button events down the channel, but it looks like an open issue to be able to override this function in a Flutter app.
I know there is a WillPopScope widget that allows you to capture the back button event if it will exit the current ModalRoute, but I'm not sure I can use this to solve my problem because the widget of interest is a TextFormField not a ModalRoute.
Is there a way I can subscribe to the keyboard closing instead?
When I got to the end of typing out my problem, I thought of the idea to subscribe to the keyboard closing event... sure enough there is a package for that! https://pub.dev/packages/keyboard_visibility
So, I can solve my problem by using the keyboard_visibility package
import 'package:keyboard_visibility/keyboard_visibility.dart';
// ...
final FocusNode roomIdInputFocusNode = FocusNode();
final roomIdHasFocus = BehaviorSubject<bool>();
// ...
roomIdHasFocus.sink.add(false);
roomIdInputFocusNode.addListener(() {
roomIdHasFocus.sink.add(roomIdInputFocusNode.hasFocus);
});
KeyboardVisibilityNotification().addNewListener(
onChange: (bool visible) {
if (!visible) {
roomIdHasFocus.sink.add(false);
} else if (visible && roomIdInputFocusNode.hasFocus) {
roomIdHasFocus.sink.add(true);
}
},
);
Unfortunately, this still keeps the focus on the TextFormField, but it is good enough for me because I'm able to unhide the widgets I hid away when the keyboard opened.
Related
I want to set the keyboard to be always opened in the chat page and should not be dismissable by the back button.
The WillPopScope() widget is only preventing the back button from navigating back to the previous page however is still dismissing the keyboard. I am getting the keyboard opened initially via by setting autofocus = true in the textformfield so the only issue I am facing is on preventing the dismissal of the keyboard.
I have looked upon multiple questions in Stack however could not find a suitable solution to this issue. I have been searching for the answer for over 2 months now and hope someone is able to assist me with this issue.
UPDATE:
I did the following change to my heightofdevice where I subtracted the MediaQuery.of(context).viewInsets.bottom and now the transition of the keyboard is better.
I realised the previous method of me trying to fix the keyboard and preventing it from being dismissed had too many fixes to be done on a native level using Java or Kotlin for Android. I initially wanted to fix the keyboard as the transition was poor but with the following method, the transition is better now but there are still room for improvement.
var heightStatusBar = MediaQuery.of(context).padding.top;
var bottom = MediaQuery.of(context).viewInsets.bottom;
widthofdevice = MediaQuery.of(context).size.width;
heightofdevice =(MediaQuery.of(context).size.height) -
heightStatusBar - bottom;
The problem is that when the keyboard is opened and the user press the Android Backbutton, the app doesn't change the proper Focus of the keyboard, that is, even if it closes the keyboard, the focus continue as True...
With this, the next tap on the textfield to open again the keyboard, the focus is true and the flutter retrieve the information that it already is open and doesn't do anything.
To fix this, you can wrap your tap of the TextField using a GestureDetector and use a combine Focus and viewInsets validation.
void _onFocus() {
if (_focusNode.hasFocus && MediaQuery.of(context).viewInsets.bottom == 0) {
_focusNode.unfocus();
Future.delayed(const Duration(microseconds: 1),
() => _focusNode.requestFocus());
} else {
_focusNode.requestFocus();
}
}
With this, if the user press the backbitten to close the keyboard and press on the TextField to open again, this validation will fail in MediaQuery.viewInsets and it will update the real status of Focus.
I have a page where the user needs to enter their email and then hit a button at the bottom to confirm. While confirming the email, there is a ProgressDialog overlay on the screen. When an invalid email is entered into the EditText, like just entering the letter "a", an error message appears below the EditText in the form of a TextView. I am trying to make this page more accessible.
My intended approach is to have TalkBack mode focus in on the error text when it appears. However, this isn't quite working. Instead, when the ProgressDialog overlay is dismissed, nothing on the screen is selected by Talkback, I notice that the "confirm" button still appears to be in a selected state, and I hear the app announce the app name again, as if the Activity is getting recreated (though it is not). Swiping right at this point will select the first view on the screen. It's worth noting that the confirm button does not appear to hold the "selected" state when talkback mode is not turned on, like it does when talkback mode is turned on.
Here's the code that I've tried:
private fun onServerResult() {
...
dismissDialogOverlay()
if (error) {
showError(errorMessage)
}
}
private fun showError(msg : String?) {
mTvEmailError.text = msg
mTvEmailError.visibility = View.VISIBLE
mTvEmailError.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED) //want focus now!
}
I have also tried mTvEmailError.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED) and also tried pairing each with accessibilityEvent with mTvEmailError.requestFocus(). No luck.
What I'd like to have happen is for mTvEmailError to be the selected view in talkback mode. Instead, nothing is selected after the button press. It's as if Talkback mode gets completely lost after clicking on the confirm button.
mTvEmailError.postDelayed({ mTvEmailError.requestFocus() }, 300)
//Explanation
requestFocus function need to be called in a separate thread,if you call directly it will be called in the main thread,in main thread someother process may occur,if you call requestFocus() function traffic may occur,so we need to call in a separate thread with 300 milliseconds delay.
I had the exact same issue as OP, word for word.
Solved with this:
editTextObject.performAccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK, null);
Now after the Dialog, the button that initiated the Dialog is unselected by Accessibility. Accessibility focus is on the EditText and TalkBack reads everything related to the EditText including the error message.
I need to listen to the back button to run a certain function but the usual platform.registerBackButtonAction() won't work when I've got an open keyboard.
It doesn't work because it's not a back button event when the keyboard is open, you'll need the Keyboard plugin and use the onKeyboardHide() subscribing to the event, so when you press the button to hide the keyboard a event is fired.
import { Keyboard } from '#ionic-native/keyboard'; // import it
construtor(public keyboard: Keyboard){
keybard.onKeyboardHide().subscribe(event =>{
// DO YOUR STUFF
});
}
Hope this helps.
In my case, keyboard events are not getting fired.
Need help on how to handle physical back button click when the keypad is open
Im trying to resolve a usability problem in my app, im using Xamarin Android but for this question i think we can ignore Xamarin.
I have a list where each ítem has a edittext like a counter for each ítem.
The user is capable of changing that value by clicking on + and - buttons or directly by writing in the edittext with the softKeyboard.
My problem shows when the user opens the keyboard, change the number, but do not click on done but instead just close the keyboard using the backbutton.
Im using something like this to get the counter change.
editCount.EditorAction += delegate (object sender, TextView.EditorActionEventArgs e) {
if (e.ActionId == Android.Views.InputMethods.ImeAction.Done)
{
Of course, thats not being called if the user simply close the keyboard instead of hitting done.
I know i could use TextChanged instead of EditorAction but it will be a real nightmare to make those changes to this app...
Just forcing the user to hit the done button to close the keyboard should be enought, much better if i can restore the previous value of the edittext in case they just close the keyboard.
How can i achieve something like this?
i have done application using sencha touch 2. Back page logic is maintaining through the routes in application. Back logic is working very nice in both ipad/android device. One issue is causing problem in android device when we clicks the back button in device not closing popup select field popup.In this page i have select field, when clicks the select field it will show select field popup with option cancel and done, i wont select any option directly i will press the back button in android device. it is not closing popup, but it is going to back previous page but select field popup remains opened not closing. i need to close bottom select field popup when clicks the back button in android device. Can any one tell me how to achieve this one. I have attached screen shot of select field popup for more reference.
Without seeing any code I'm guessing that you've either created an own popup or that you've set usePicker to false on the selectfield.
Why so? Wouldn't set usePicker: true, suffice for your solution?
Anyway, do deal with the problem at hand and with the information you've provided what I would have done is to set a id on your popup (or css class) that enables you to identify it and in any relevant route function hide them if they are visible
var popupElement = findYourPopup(); //Ext.getCmp("popup") || ParentEl.query("cssclass") etc.
if (popupElement && !popupElement.isHidden()) {
popupElement.hide();
}
Hope that this will help you!
You can also handle hardware back button like this:
if (Ext.os.is('Android')) {
document.addEventListener("backbutton", Ext.bind(onBackKeyDown, this), false);
function onBackKeyDown(eve) {
eve.preventDefault();
//do something
alert('back button pressed');
}
}