Remove focus from TextField when the keyboard is dismissed - flutter

It seems that a TextField does not lose the focus when the keyboard is dismissed. That makes the cursor continually blink, and more seriously, when the main window gets focus again, for example, after showing a dialogue box, the keyboard pops up again. Can I make the TextField lose focus when the keyboard is dismissed?
return Scaffold(
body: Padding(
padding: EdgeInsets.all(16),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextField(decoration: InputDecoration(hintText: "Example"),),
RaisedButton(
child: Text("Test"), onPressed: () => onButtonClick(context))
],
),
)),
);

Try using this when clicking on the button:
FocusScope.of(context).unfocus();
and if you have a textEditingController (which I recommend using) you can call to clear the field
_textEditingController.clear();
but you have to create the controller first:
final _textEditingController = TextEditingController();
and when you create your TextField:
TextField(
decoration: InputDecoration(hintText: "Example"),
controller: _textEditingController,
),

Use TextEditingController:
First Initialise :
TextEditingController _textController = new TextEditingController();
code:
TextField(controller: _textController),

Related

TextFormField retrieves the focus and reassigns the initial value when the keyboard is closed

I find myself working with TextFormField hand in hand with DropdownButton. There is a strange process going on inside the view. I put the steps below:
To the TexFormField, I'm assigning an initial value: controller: _nameController..text = datumAdministrative.name.
I enter a new value to the TextFormField.
When the DropdownButton is deployed, the keyboard is closed the whole Widget is redrawn, which causes the TextFormField to recover its initial value and does not keep the new value entered.
Is there any way to avoid, that the TextFormField returns to its initial value when selecting DropdownButton? I would appreciate if you could help me with a post or feedback.
Code Scaffold:
return Scaffold(
resizeToAvoidBottomPadding: false,
resizeToAvoidBottomInset: true,
appBar: AppBar(
backgroundColor: ColorsTheme.primary,
leading: GestureDetector(
onTap: () {
Navigator.pushReplacementNamed(context, 'administrativeListData');
},
child: Container(
padding: EdgeInsets.all(16),
child: FaIcon(FontAwesomeIcons.arrowLeft)),
),
centerTitle: true,
title: Text(datumAdministrative.name +
' ' +
datumAdministrative.lastNameFather),
automaticallyImplyLeading: false),
body: Stack(
children: <Widget>[
ImageBackground(),
_sizeBoxHeight(),
Container(
child: Form(
key: formValidator.formKey,
child: ListView(
children: <Widget>[
_containerImageUser(context, datumAdministrative),
_sizeBoxHeight(),
CustomCardExpansionTile(
title: Constants.administrativeData,
icon: FontAwesomeIcons.addressBook,
widget: Container(
padding: EdgeInsets.all(15),
child: responsiveDataAdministrative(context)),
),
CustomCardExpansionTile(
title: Constants.addressInformationAdministrative,
icon: FontAwesomeIcons.houseUser,
widget: Container(
padding: EdgeInsets.all(15),
child: responsiveAddressInformationAdministrative(
context))),
CustomCardExpansionTile(
title: Constants.userDataSystem,
icon: FontAwesomeIcons.idCard,
widget: Container(
padding: EdgeInsets.all(15),
child: responsiveInformationSystemAdministrative(
context))),
],
),
),
),
],
),
);
Code TextFormField:
TextFormField(
controller: _nameController..text = datumAdministrative.name,
keyboardType: TextInputType.text,
textInputAction: null,
textCapitalization: TextCapitalization.sentences,
onChanged: (value) => formValidator.name = value,
validator: (value) {
if (formValidator.fieldValidText(value)) {
return null;
} else {
return Constants.nameAdministrativeMessage;
}
});
I see that you set the value for the text property of TextEditingController inside the build method. So, it will be invoked whenever the widget rebuilds, causing the value for the field back to the initial one.
The docs actually tells about it:
text property
Setting this will notify all the listeners of this
TextEditingController that they need to update (it calls
notifyListeners). For this reason, this value should only be set
between frames, e.g. in response to user actions, not during the
build, layout, or paint phases.
To fix it, you should remove the cascade notation here:
controller: _nameController..text = datumAdministrative.name,
Do it inside initState() instead:
#override
void initState() {
_nameController.text = datumAdministrative.name;
}

How to wait until keyboard is opened in flutter?

I need to scroll page when textfield is focused.
So I used scroll_to_index plugin(https://pub.dev/packages/scroll_to_index).
But It operate well only when keyboard is already opened.
If I tap the textfield when keyboard is not opened, it doesn't scroll the page.
I think it is because page is scrolled before the keyboard is opened.
In my opinion, the problem seems to arise because the code works this way.
1.tap a textfield (linked Focusnode has Focus)
2. It try to scroll the page to the target. But because now keyboard is not opened yet, target is already in view. So It looks like nothing happened.
3. Keyboard is opened, and hide the content.
auto_scroll.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
class MyCustomScrollView extends StatefulWidget {
#override
_MyCustomScrollViewState createState() => _MyCustomScrollViewState();
}
class _MyCustomScrollViewState extends State<MyCustomScrollView> {
AutoScrollController _autoScrollController = new AutoScrollController();
List<FocusNode> nodes = List<FocusNode>.generate(3, (index) => FocusNode());
Future _scrollToIndex(int index) async {
await _autoScrollController.scrollToIndex(index,
preferPosition: AutoScrollPosition.end);
}
#override
void initState() {
super.initState();
for(var i =0;i<3;i++) {
nodes[i].addListener(() {
if(nodes[i].hasFocus) _scrollToIndex(i);
});
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
body: CustomScrollView(
controller: _autoScrollController,
slivers: [
SliverToBoxAdapter(
child: Container(
child: Column(
children: [
SizedBox(height: 100),
CupertinoTextField(
focusNode: nodes[0],
onEditingComplete: (){
FocusScope.of(context).requestFocus(nodes[1]);
},
),
AutoScrollTag(
key: ValueKey(0),
controller: _autoScrollController,
index: 0,
child: Container(height: 300, color : Colors.green)
),
CupertinoTextField(
focusNode: nodes[1],
onEditingComplete: (){
FocusScope.of(context).requestFocus(nodes[2]);
},
),
AutoScrollTag(
key: ValueKey(1),
controller: _autoScrollController,
index: 1,
child: Container(
height: 300,
color : Colors.green,
child: Center(
child: Text("Here should be visible!!!!"),
),
)
),
CupertinoTextField(
focusNode: nodes[2],
),
AutoScrollTag(
key: ValueKey(2),
controller: _autoScrollController,
index: 2,
child: Container(height: 300, color : Colors.green)
),
],
),
)
)
],
),
);
}
}
This is the code. When I tap on the second textfield, it should scroll to show all the red containers below the second textfield, but it doesn't.
But when the keyboard is up, clicking on the second text field works as desired.
What I expected when I tap 2nd Textfield
What actually happened
I know that just giving focus to textfield is enought to scroll to the textfield. But in my application I have a Positioned bar just above the keyboard, so I have a situation where the Positioned bar covers the text field. This is why I use scroll_to_index.
So what I want to do is wait for the keyboard to come up, and then when the keyboard comes up, run the _scrollToIndex function. How can I wait until keyboard is opened? or there are any good solution for this problem? Thank you for your reading.
You can use flutter_keyboard_visibility. There are 2 ways of implementation: with provider and with listener.

Show element as soon as form is valid

I'm trying to show element as soon as my form becomes valid. According to flutter docs I need to use _formKey.currentState.validate() but in the docs it is used on button click, while I'm trying to use it to show/hide element.
Working code (as used in flutter tutorial)
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
autovalidate: true,
validator: validateName,
),
RaisedButton(
onPressed: () => _formKey.currentState.validate()
? print('valid')
: print('not valid'))
My code with (INVALID MEMER OF NULL: 'validate) error
return Form(
key: _formKey,
child: Column(
children: [
TextFormField(
autovalidate: true,
validator: validateName,
),
_formKey.currentState.validate()
? Container(child: Text('valid'))
: Container(child: Text('not valid'))
The best answer for you will be to use TextFormField onChanged, which is of type ValueChanged, which keeps track of the changes you make in your TextFormField
Advantage
You will be able to keep a track of the text changes, and show/hide content based upon that
Disadvantage
Each TextField should be maintaining the bool for the different TextField to keep a track of the items
It is like a workaround, so can use in this way. I am using a single TextFormField to show how it works, you can then work on ther TextFormField and get it up and running.
FINAL SOLUTION
bool isValid = false;
final TextEditingController _controller = new TextEditingController();
Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
decoration: InputDecoration(hintText: 'Enter your data'),
controller: _controller,
onChanged: (String val){
setState((){
if(val.length >= 4) isValid = true;
else isValid = false;
});
}
),
SizedBox(height: 20.0),
isValid ? Text('Valid String', textAlign: TextAlign.center) : Text('Name must be more than 3 character', textAlign: TextAlign.center)
]
)
SOLUTION: For various, you must check whether all the different bools are true or not, based upon that, you can show/hide the data.
RESULT
Let me know if is of any use to you :)

getSelectedText on inactive InputConnection flutter

I have this weird problem,When i try to enter value to textfield the keyboard comes up when i try to type the the keyboard goes away , i am also doing fill up Username,Place,mobile that i fetching from sharedprefrence and it's working,but when i try to enter age,height,weight keyboard comes up & goes with in seconds,this is the error/warning i am getting,there is no problem in flutter doctor,
W/IInputConnectionWrapper(23904): getSelectedText on inactive InputConnection
W/IInputConnectionWrapper(23904): getTextAfterCursor on inactive InputConnection
W/IInputConnectionWrapper( 8756): beginBatchEdit on inactive InputConnection
W/IInputConnectionWrapper( 8756): endBatchEdit on inactive InputConnection
Code & View
class RegistrationScreenState extends State<RegistrationScreen> {
TextEditingController mobile = TextEditingController();
TextEditingController Username = TextEditingController();
TextEditingController Place = TextEditingController();
TextEditingController age = TextEditingController();
TextEditingController height = TextEditingController();
TextEditingController weight = TextEditingController();
void initState() {
getDetails();
}
#override
Widget build(BuildContext context) {
Size size = MediaQuery
.of(context)
.size;
return Scaffold(
appBar: AppBar(
title: Text("Registration", style: TextStyle(color: Colors.black)),
backgroundColor: Colors.orange,
),
body: SingleChildScrollView(
child: Stack(
children: [
Column(
children: [
Container(
child: Image.asset('assets/images/gym.png',
height: 150,
width: double.infinity,
fit: BoxFit.fitWidth),
),
SizedBox(
height: 50,
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: Username,
decoration: InputDecoration(
prefixIcon: Icon(Icons.perm_identity),
),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: mobile,
decoration: InputDecoration(
prefixIcon: Icon(Icons.mobile_screen_share),
),
),
),
),
],
),
Padding(
padding: const EdgeInsets.all(8.0),
child: Align(
alignment: Alignment.centerLeft,
child: Text(
"User Information",
style: TextStyle(
fontSize: 15, fontWeight: FontWeight.bold),
)),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: Place,
decoration: InputDecoration(),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: age,
decoration: InputDecoration(
hintText: 'Age',
),
),
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: height,
decoration: InputDecoration(
hintText: 'Height(in cm)',
),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: weight,
decoration: InputDecoration(
hintText: 'Weight(in kg)',
),
),
),
),
],
),
],
),
],
),
),
);
}
// get & fill Shareprefrece data to textfield
getDetails() async {
Future user = SharedPrefrence().getUserMobile();
Future name = SharedPrefrence().getUserName();
Future place = SharedPrefrence().getUserPlace();
user.then((data) async {
var mobile_no = data;
setState(() {
if (mobile_no.isNotEmpty) {
mobile.text = mobile_no;
}
else
{
mobile.text = "";
}
});
});
name.then((data) async {
var user_name = data;
setState(() {
if (user_name.isNotEmpty) {
Username.text = user_name;
}
});
});
place.then((data) async {
var user_place = data;
setState(() {
if (user_place.isNotEmpty) {
Place.text = user_place;
}
});
});
}
}
I am not sure that if this will help you but I was also facing the same issue.
I was getting this error when I was using the TextFormField with in Form and the issue was that I created the global form key with in the build method.
class _AddTaskState extends State<AddTask> {
final _formKey = GlobalKey<FormState>(); // <-
String name;
String description;
#override
Widget build(BuildContext context) {
// first I was using that here.
return Scaffold(
In my case I had TextFeild in AnimatedSwitcher. The problem was i had key: UniqueKey() inside my widget that containes the TextFeild. So When ever i touched the text feid UniqueKey() gets called and then the widget tree gets regernated.
If you are using form validator, move your form key out of your build context. This should solve your issue.
Instead of -
class SingInScreen extends StatelessWidget {
final _sigInFormKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
Size size = getRelativeSize(context);
Use this-
`final _sigInFormKey = GlobalKey<FormState>();
class SingInScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
Size size = getRelativeSize(context);`
Known issue, tracked here,it's still there. There is no explanation on what's causing it.
I did a little bit of digging. It looks like Android is generating these warnings because we are holding the InputConnection incorrectly in the Engine's TextInputPlugin. I haven't really figured out what we're doing wrong, though.
source
I was also getting the same issue when tapping on the TextField.
Use auto focus = true, and issue will be resolved.
TextField( autofocus: true, );
I "forced" the formatting, in my case for example my form controller was an integer so I did it :
TextFormField(
controller: _formTextControllers.mycontroller,
keyboardType: TextInputType.number,
decoration: InputDecoration(
labelText: 'label',
),
inputFormatters: [
**FilteringTextInputFormatter.digitsOnly,**
MyInputFormatter(),
],
),
Try using the textfield methods like onChanged, onSubmitted,...
Also try using focus nodes.
Use
FocusScope.of(context).requestFocus(FocusNode());
I've been scratching my head over the same issue.
The thing that resolved it for me was removing the media query from within the build method. I guess calling the keyboard messes with this if its running inside the build method and funny things happen.
Size size = MediaQuery
.of(context)
.size;
Try doing without it, or passing it into the widget as a static property above the build method
So okay I have found a solution for this in my use case.
My keyboard used to disappear as soon as it appeared on my screen. This not the bug in the framework, let us understand what happens in the background.
When the keyboard pops up, the screen needs to rebuild its components accordingly, so it forces a rebuild. Therefore the entire widgets are re-rendered including the TextFormField or TextField, which closes the Keyboard.
To solve this problem, we have to find a way to uniquely identify our widget which is rebuilding, and stop flutter from recreating that widget.
This can be done by using keys, first find the parent widget under which our TextFormField/TextField falls, and provide a unique key to it. Remember NOT to initialize the key on the key parameter of the widget. Instead declare it outside the build method and then use it. This makes sure that the unique does not change every time the build method is called.
I hope that this helps.
Thank You!
In my case, I had this problem while my array of TextFormFields were children of DataCells of a DataTable which this DataTable was itself child of a ExpansionPanel widget.
The problem got fixed when I replaced the parent ExpansionPanelList with a Column and removed the ExpansionPanel widget.
I thought that explaining my own case might help somebody with similar issues.
In my case, I was using a global navigation key for navigation. when I was on an inner page the moment I touch Textfeild or TextFormFeild it pop to the navigation initial root, the problem that cause this problem was I declared the Navigation module inside a Stateless widget. When I changed it to stateful the issue is solved.

Flutter Dismissible causing unnecesary redraw when pushed navigated on top

I'm making an app with Flutter, and I found a weird behaviour that I got to reproduce in a small demo: https://pastebin.com/ZJd2fnHK
The objective is to have a TextField that when in focus should select the whole text. I achieved this with a FocusNode.
Everything was working fine, until I set a Dismissible widget to remove items from a list. For some reason (probably drawing ones) the whole page has a rebuild and the focusnode state is lost.
It's very interesting since the issue only happens when the widget is within a page that has being pushed by the navigator. The same Widget works fine in the root Page.
The Widget is the following:
Widget build(BuildContext context) {
textControllerA.text = "Hello";
textControllerB.text = "World";
return Column(children: [
Dismissible(
key: Key("potatoes"),
onDismissed: (direction) {},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
child: TextField(
focusNode: focusNodeA,
controller: textControllerA,
),
padding: EdgeInsets.all(20),
),
],
),
),
Padding(
child: TextField(
focusNode: focusNodeB,
controller: textControllerB,
),
padding: EdgeInsets.all(20),
),
]);
}
Maybe I should try to store the focus' state in state, but that'd have some overhead. Or maybe (possibly) I'm doing something very wrong, in which case I would love to have some guidance.
Cheers
You should use StatefulWidget and call setState
onDismissed: (direccion) {
setState(() {
...
});