Flutter Cursor Appearing in two fields at the same time - flutter

I have problem with flutter textformfield. Like I said in title my app show cursor in two textformfield. It appear when I tap previous textfield. I'm using real phone for test, not tried in emulator. Thanks for any help.
Here image from my app:
enter image description here
Form(
key: _formKey,
child: Column(
children: <Widget>[
SizedBox(
height: 20.0,
),
TextFormField(
decoration: InputDecoration(labelText: 'Email'),
focusNode: _emailFocus,
onFieldSubmitted: (term) {
_passwordFocus.nextFocus();
_fieldFocusChange(
context, _emailFocus, _passwordFocus);
},
textInputAction: TextInputAction.next,
validator: (value) =>
value.isEmpty ? 'Enter an email' : null,
onChanged: (value) {
setState(() => email = value);
},
),
SizedBox(
height: 20.0,
),
TextFormField(
decoration: InputDecoration(labelText: 'Password'),
validator: (value) => value.length < 6
? 'Enter an password 6+ chars long'
: null,
obscureText: true,
focusNode: _passwordFocus,
onFieldSubmitted: (term) {
_passwordFocus.unfocus();
_submitForm();
},
textInputAction: TextInputAction.done,
onChanged: (value) {
setState(() => password = value);
},
),
SizedBox(
height: 20.0,
),
RaisedButton(
color: Theme.of(context).primaryColor,
child: Text('Sign in'),
onPressed: () {
_submitForm();
},
),
SizedBox(height: 12.0),
Text(error,
style:
TextStyle(color: Colors.red, fontSize: 14.0)),
],
)),

I found solution from this question:
Multiple cursor in form's TextFormField flutter
It is same as mine. We did same mistake. When I declare FocusNode global it solve problem.

Related

Flutter TextFormField cursor is focused but not accepted input text

After clear text in TextFormField below code:
Widget _textInputNumber({controller, name, enable, autofocus, required}) {
return TextFormField(
autofocus: autofocus,
keyboardType: TextInputType.number,
enabled: enable,
controller: controller,
decoration: InputDecoration(
filled: true,
fillColor: Colors.white,
suffix: InkWell(
onTap: () {
controller.text = '';
setState(() {});
},
child: const Icon(
Icons.clear,
),
),
),
validator: required
? (value) => value!.isEmpty ? 'Required!' : null
: null,
onSaved: (value) => name = value,
);
}
Cursor is focused but not accepted input text I need again click on focused input to enter new text.
Any solution?
you can solve issue by calling this code FocusScope.of(context).requestFocus(FocusNode())
Here's the full code:
Widget _textInputNumber({controller, name, enable, autofocus, required}) {
return TextFormField(
autofocus: autofocus,
keyboardType: TextInputType.number,
enabled: enable,
controller: controller,
decoration: InputDecoration(
filled: true,
fillColor: Colors.white,
suffix: InkWell(
onTap: () {
controller.text = '';
setState(() {});
FocusScope.of(context).requestFocus(FocusNode());
},
child: const Icon(
Icons.clear,
),
),
),
validator: required
? (value) => value!.isEmpty ? 'Required!' : null
: null,
onSaved: (value) => name = value,
);
}

the values not remain in the Text Form Field when I click continue, when I return to the page the value is empty Note that I used Page View

I'm trying to make Sign Up pages so I used Page View like this page view controller.nextPage(....,...) page view controller.prev page(....,...) Boom Value in the Text Form Field is gone. And this thing happens on all pages when I move to a new page in the page view and return to the page. The information that the user filled in is gone from the fields and has become empty! why?
Form(
key: EmailKeys.formKey,
child: Column(
children: [
const Text(
"Create an account, It's free",
textAlign: TextAlign.center,
style: TextStyle(fontSize: 16, color: Colors.grey),
),
const SizedBox(
height: 40,
),
TextFormField(
onFieldSubmitted: (val) {
EmailKeys.formKey.currentState!.setState(() {
email = val.trim();
});
EmailKeys.formKey.currentState!.save();
},
textInputAction: TextInputAction.done,
inputFormatters: [LengthLimitingTextInputFormatter(100)],
obscureText: false,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: "Email Address",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
),
labelStyle: TextStyle(color: Colors.grey[400]),
floatingLabelStyle:
const TextStyle(color: Colors.blueGrey, fontSize: 18),
),
validator: (value) {
if (value!.isEmpty) {
return 'Email address is required';
} else if (!value.contains("#")) {
return "Email address should contain ' # ' symbol";
}
},
onChanged: (value) {
email = value.trimLeft();
},
onSaved: (val) {
setState(() {
email = val!;
});
print(email);
},
controller: _emailCtrl,
),
],
),
),
try to pass email to TextFormField as value

Show a loading spinner when sending OTP using Firebase

I want to show a loading spinner whenever user clicks on "SEND OTP" button. I have created a variable _IsLoading which I am setting to true just prior to calling the verifyphone function and then setting it to false afterwards. It seems like it does not have any effect whatsoever on the UI. How can I make this work?
If I replace the verifyPhone with the following line of code, then I do see the spinner :
await new Future.delayed(new Duration(milliseconds: 1500));
I have disabled recaptcha, so my app does not navigate to the recaptcha verification page. But still the spinner does not work.
#override
Widget build(BuildContext context) {
return _isLoading
? Center(child: Loading()):Scaffold(
appBar: AppBar(
title: Text("OTP Verification"),
),
body:SizedBox(
width: double.infinity,
child: Padding(
padding:
EdgeInsets.symmetric(horizontal: getProportionateScreenWidth(20)),
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(height: SizeConfig.screenHeight * 0.05),
Text(
"OTP Verification",
style: headingStyle,
),
Text("Press Send OTP to get a 6 digit code via SMS and enter below.."),
// buildTimer(),
//OtpForm(),
Form(
child: Column(
children: [
SizedBox(height: SizeConfig.screenHeight * 0.15),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: getProportionateScreenWidth(40),
child: TextFormField(
autofocus: true,
maxLength: 1,
obscureText: true,
style: TextStyle(fontSize: 24),
keyboardType: TextInputType.number,
textAlign: TextAlign.center,
decoration: otpInputDecoration,
onChanged: (value) {
nextField(value, pin2FocusNode);
},
),
),
SizedBox(
width: getProportionateScreenWidth(40),
child: TextFormField(
focusNode: pin2FocusNode,
maxLength: 1,
obscureText: true,
style: TextStyle(fontSize: 24),
keyboardType: TextInputType.number,
textAlign: TextAlign.center,
decoration: otpInputDecoration,
onChanged: (value) => nextField(value, pin3FocusNode),
),
),
SizedBox(
width: getProportionateScreenWidth(40),
child: TextFormField(
focusNode: pin3FocusNode,
maxLength: 1,
obscureText: true,
style: TextStyle(fontSize: 24),
keyboardType: TextInputType.number,
textAlign: TextAlign.center,
decoration: otpInputDecoration,
onChanged: (value) => nextField(value, pin4FocusNode),
),
),
SizedBox(
width: getProportionateScreenWidth(40),
child: TextFormField(
maxLength: 1,
focusNode: pin4FocusNode,
obscureText: true,
style: TextStyle(fontSize: 24),
keyboardType: TextInputType.number,
textAlign: TextAlign.center,
decoration: otpInputDecoration,
onChanged: (value) => nextField(value, pin5FocusNode),
),
),
SizedBox(
width: getProportionateScreenWidth(40),
child: TextFormField(
autofocus: true,
focusNode: pin5FocusNode,
obscureText: true,
maxLength: 1,
style: TextStyle(fontSize: 24),
keyboardType: TextInputType.number,
textAlign: TextAlign.center,
decoration: otpInputDecoration,
onChanged: (value) {
nextField(value, pin6FocusNode);
},
),
),
SizedBox(
width: getProportionateScreenWidth(40),
child: TextFormField(
autofocus: true,
focusNode: pin6FocusNode,
maxLength: 1,
obscureText: true,
style: TextStyle(fontSize: 24),
keyboardType: TextInputType.number,
textAlign: TextAlign.center,
decoration: otpInputDecoration,
onChanged: (value) {
if (value.length == 1) {
pin6FocusNode.unfocus();
// Then you need to check is the code is correct or not
}
}
),
),
],
),
SizedBox(height: SizeConfig.screenHeight * 0.15),
DefaultButton(
text: _isLoading?"Sending OTP...":"Send OTP",
press: () async {
setState(() {
_isLoading = true;
});
verifyPhoneNumber();
setState(() {
_isLoading = false;
});
},
),
SizedBox(),
DefaultButton(
text: "Continue",
press: () async {
},
)
],
),
),
SizedBox(height: SizeConfig.screenHeight * 0.1),
GestureDetector(
onTap: () {
// OTP code resend
},
child: Text(
"Resend OTP Code",
style: TextStyle(decoration: TextDecoration.underline),
),
)
],
),
),
),
));
}
I found a solution. You can register a callback in the verifyPhoneNumber function by using then
Right before calling verifyPhoneNumber function, add this:
setState(() {
_isLoading = true;
});
Then in the function's call back, add change _isLoading to false.
await _auth.verifyPhoneNumber(
phoneNumber: '+11345670033',
timeout: const Duration(seconds: 5),
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
codeAutoRetrievalTimeout:
codeAutoRetrievalTimeout).then((value) {
setState(() {
_isLoading = false;
});
});

DropdownButton selection calls the onValidate functions of other fields in Flutter

Not sure what I'm missing. When selecting the value of drop-down, form onVaidate() fired which hence my other fields are showing the error. How can I stop it? Here is the code
Widget build(BuildContext context) {
// return Scaffold(
// appBar: AppBar(title: Text("Registration")),
// body: Center(child: Text(widget.user.displayName)),
// );
FirebaseUser user = widget.user;
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text("Registration"),
),
body: SafeArea(
top: false,
bottom: false,
child: Form(
key: _formKey,
autovalidate: true,
child: ListView(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
children: <Widget>[
TextFormField(
validator: (value) => value.isEmpty ? 'Name is Required' : null,
decoration: const InputDecoration(
icon: const Icon(Icons.person),
hintText: 'Enter your first and last name',
labelText: 'Name',
),
),
TextFormField(
decoration: const InputDecoration(
icon: const Icon(Icons.phone),
hintText: 'Enter a phone number',
labelText: 'Phone',
),
initialValue: user.phoneNumber,
enabled: user.phoneNumber == null,
keyboardType: TextInputType.phone,
validator: (value) => value.isEmpty ? 'Phone number is Required' : null
// inputFormatters: [
// WhitelistingTextInputFormatter.digitsOnly,
// ],
),
TextFormField(
decoration: const InputDecoration(
icon: const Icon(Icons.email),
hintText: 'Enter a email address',
labelText: 'Email',
),
initialValue: user.email,
enabled: user.email == null,
validator: (value) => value.isEmpty ? 'Email is Required' : null,
keyboardType: TextInputType.emailAddress,
),
TextFormField(
decoration: const InputDecoration(
icon: const Icon(Icons.remove_red_eye),
hintText: 'Enter the Password',
labelText: 'Password',
),
keyboardType: TextInputType.text,
obscureText: true,
validator: (value) => value.isEmpty ? 'Password is Required' : null
),
FormField(
builder: (FormFieldState state) {
return InputDecorator(
decoration: InputDecoration(
icon: const Icon(Icons.card_membership),
labelText: 'ID Type',
),
isEmpty: _profile.govId == null,
child: DropdownButtonHideUnderline(
child: DropdownButton(
value: _profile.govId,
isDense: true,
onChanged: (String newValue) {
setState(() {
_profile.govId = newValue;
state.didChange(newValue);
});
},
items: _govtIds.map((String value) {
return DropdownMenuItem(
value: value,
child: Text(value),
);
}).toList(),
),
),
);
},
),
TextFormField(
decoration: const InputDecoration(
icon: const Icon(Icons.confirmation_number),
hintText: 'Enter your Governmenr ID number',
labelText: 'ID Number',
),
keyboardType: TextInputType.datetime,
validator: (value) => value.isEmpty ? 'ID Number is Required' : null
),
FormField(
builder: (FormFieldState state) {
return InputDecorator(
decoration: InputDecoration(
icon: const Icon(Icons.business),
labelText: 'Block Info',
),
isEmpty: _profile.block == null,
child:
// Column(children: [RadioListTile(title: Text("A")),RadioListTile(title: Text("B"))]),
// Radio(
// value: 0,
// groupValue: _blocks,
// onChanged: (value){}),
DropdownButtonHideUnderline(
child:
DropdownButton(
value: _profile.block,
isDense: true,
onChanged: (String newValue) {
setState(() {
_profile.block = newValue;
state.didChange(newValue);
});
},
items: _blocks.map((String value) {
return DropdownMenuItem(
value: value,
child: Text(value),
);
}).toList(),
),
),
);
},
),
TextFormField(
decoration: const InputDecoration(
icon: const Icon(Icons.home),
hintText: 'Enter your Flat number',
labelText: 'Flat number',
),
inputFormatters: [LengthLimitingTextInputFormatter(3)],
validator: (value) {
if (value.isEmpty) {
return 'Flat number is Required';
} else if (_profile.isValidHouseNumber() == false) {
return 'Invalid flat number';
} else {
return null;
}
},
keyboardType: TextInputType.number,
onChanged:(value) {
_profile.houseNo = value;
},
),
Padding(
padding: EdgeInsets.fromLTRB(38.0, 30.0, 0.0, 0.0),
child: SizedBox(
height: 50.0,
child: FlatButton(
// elevation: 5.0,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0)),
color: Colors.green,
child: Text('Submit',
style: TextStyle(fontSize: 20.0, color: Colors.white)),
onPressed: _validateAndSubmit,
),
))
],
))),
);
}
Call a method to return null in the validate fucntion all the other fields. That will clear the validation. It's a fix, but doesn't solve the problem.
There seems to be nothing wrong with the code above, can you add the code for the other fields too?
EDIT:
The reason all the other fields validate is because of the autovalidate: true property of the parent Form widget. Remove it and wrap each TextFormField with a Form with different keys.
For example, your TextFormField should look as follows:
Form(
key: _formKey[0],
child: TextFormField(
validator: (value) => value.isEmpty ? 'Name is Required' : null,
decoration: const InputDecoration(
icon: const Icon(Icons.person),
hintText: 'Enter your first and last name',
labelText: 'Name',
),
),
),
Wrap it with a Form, _formKey is declared as
List<GlobalObjectKey<FormState>> _formKey = new List(number_of_keys);
Call the respective setState like so:
_formKey[position].currentState.setState((){});
And don't forget to remove the parent Form widget.

How to SingleChildScrollView scroll to TextField focus in dialog

I have a problem when show an alert dialog. My AlertDialog too tall (many TextFields). When I click any below TextFields, keyboard makes dialog short and SingleChildScrollView (I wrap my content in it) does not scroll to the TextField which has focus. However, when I type a character on that textfield, SingleChildScrollView works fine, scrolls to that TextField automatically. So, how do SingleChildScrollView scrolls to a TextField when user click in the first time (has focus).
p/s: I tried add a listener into FocusNode, but it does not work.
For example: I will click 'Fullname' field (picture 1) and the keyboard makes dialog short and I can not see the 'Fullname' field (picture 2). When I type a character, the scrollview scroll to that correctly (picture 3).
#override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(
'Dialog Title',
textAlign: TextAlign.center,
),
titleTextStyle: TextStyle(
fontSize: 16.0,
color: Theme.of(context).textTheme.title.color,
fontWeight: FontWeight.w800,
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('Cancel'.toUpperCase()),
),
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('OK'.toUpperCase()),
),
],
content: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
TextField(
focusNode: _nodePhone,
maxLength: 10,
keyboardType: TextInputType.phone,
decoration: InputDecoration(
labelText: 'Phone number',
),
textInputAction: TextInputAction.next,
onEditingComplete: () {
FocusScope.of(context).requestFocus(_nodeEmail);
},
),
TextField(
focusNode: _nodeEmail,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: 'Email',
),
textInputAction: TextInputAction.next,
onEditingComplete: () {
FocusScope.of(context).requestFocus(_nodeFullname);
},
),
TextField(
focusNode: _nodeFullname,
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: 'Fullname',
),
textInputAction: TextInputAction.next,
onEditingComplete: () {
FocusScope.of(context).requestFocus(_nodePassword);
},
),
TextField(
focusNode: _nodePassword,
obscureText: true,
keyboardType: TextInputType.text,
decoration: InputDecoration(
labelText: 'Password',
),
textInputAction: TextInputAction.done,
),
],
),
),
);
}
I also have this problem, I manage to solve it by give a controller to SingleChildScrollView then when onTap on TextField. Await for few millisecond for dialog to collapse then scroll to the end of dialog
Here is some sample code
//declare first
ScrollController _scrollController = ScrollController();
//your field code
SingleChildScrollView(
// give a controller
controller: _scrollController,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: < Widget > [
TextField(
focusNode: _nodePhone,
maxLength: 10,
keyboardType: TextInputType.phone,
decoration: InputDecoration(
labelText: 'Phone number',
),
textInputAction: TextInputAction.next,
onEditingComplete: () {
FocusScope.of(context).requestFocus(_nodeEmail);
},
),
//suppose this is your very last textfield
TextField(
focusNode: _nodeEmail,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: 'Email',
),
textInputAction: TextInputAction.next,
onEditingComplete: () {
FocusScope.of(context).requestFocus(_nodeFullname);
},
onTap: () {
Future.delayed(Duration(milliseconds: 500), () {
_scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: Duration(milliseconds: 200),
curve: Curves.ease);
});
},
),
]
)
);