Keyboard Crushing TextFields - flutter

I'm learning to code in Flutter and and redoing one of my Android/iOS apps. This is roughly what the UI looks like so far:
When I tap a field to open the keyboard, it smashes all of the fields together.
I'm trying to decide what the best way of handling this is. The only way I can think of to fix it is to somehow not have all of the fields and the button pushed up when the keyboard opens. But if I do that, then the fields lower on the screen will not be visible when data is being entered into them. Is there a good way to handle this?
This is the code for this view:
class _CarbCalcState extends State<CarbCalc> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Carb Calculator'),
backgroundColor: Colors.purple.shade700,
leading: IconButton(
icon: Icon(Icons.settings),
onPressed: () {},
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.library_books),
onPressed: () {},
)
],
),
body: Container(
margin: EdgeInsets.only(top: 40.0, left: 20.0, right: 20.0),
child: Column(
children: <Widget>[
NutrientValue(
nutrientName: 'Protein',
onChanged: (value) {
print('Protein Changed');
},
),
NutrientValue(
nutrientName: 'Fat',
onChanged: (value) {},
),
NutrientValue(
nutrientName: 'Fiber',
onChanged: (value) {},
),
NutrientValue(
nutrientName: 'Moisture',
onChanged: (value) {},
),
NutrientValue(
nutrientName: 'Ash',
onChanged: (value) {},
),
SizedBox(
height: 30.0,
),
ButtonTheme(
minWidth: double.infinity,
height: 50,
child: FlatButton(
onPressed: () {},
color: Colors.blue.shade700,
child: Text(
'Calculate Carbs',
style: TextStyle(
fontSize: 20,
),
),
),
),
],
),
),
);
}
}
class NutrientValue extends StatelessWidget {
NutrientValue({#required this.nutrientName, #required this.onChanged});
final String nutrientName;
final Function onChanged;
#override
Widget build(BuildContext context) {
return Expanded(
child: Row(
children: <Widget>[
Container(
width: 90,
child: Text(
nutrientName,
style: TextStyle(fontSize: 18.0),
),
),
Expanded(
child: TextField(
keyboardType: TextInputType.numberWithOptions(
signed: false,
decimal: true,
),
style: TextStyle(
color: Colors.black,
fontSize: 18.0,
),
onChanged: this.onChanged,
decoration: InputDecoration(
hintText: 'Enter $nutrientName',
hintStyle: TextStyle(
color: Colors.grey,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(15.0),
),
borderSide: BorderSide.none,
),
filled: true,
fillColor: Colors.white,
),
),
),
],
));
}
}

You are having a Column with several Expanded inside which take care that the space is evenly distributed. When the space shrinks by the keyboard, they move together. Basically it is a good idea to put input fields into a Scrollview because of the Keyboard. You can use SingleChildScrollView with your Column but you need to replace the Expanded widgets with a Container for example or just use the plain TextFields.

Related

Space bar key doesn't work on textfields ( Flutter Web )

In our application, SOMETIMES space input doesn't work in textFields (f.e. adding a new message in a chat), but after refreshing the page it works again. This situation is causing unstable environment for our users and thus a lot of complaints. Is there any official documentation, or solution by flutter for this "sometimes space button cannot be used" problem. What we notices that, when this problem occurs, fonts are different than it supposed to be, like it's not loaded. It is not browser specific because problem occurs at Chrome & Edge.
The real problem is that the bug is not easy to detect because it happens only few times so i don't know how to detect and solve it.
In particular i noted that it happens only in production ( i never spotted it during development) .
Flutter doctor:
This is an example widget that have the bug
Widget build(BuildContext context) {
return CustomAlertDialog(
title: const Text(
"Aggiungi un messaggio",
style: TextStyle(fontSize: 16),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text(
"Annulla",
style: TextStyle(color: Colors.grey.shade700),
)),
TextButton(
onPressed: () {
},
child: const Text("Invia"))
],
content: Container(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
margin: const EdgeInsets.only(top: 10.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(16)),
border: Border.all(
color: Colors.green,
width: 2,
),
),
width: 800,
child: TextField(
minLines: 8,
maxLines: 8,
autofocus: false,
controller: _chatController,
//keyboardType: TextInputType.multiline,
onChanged: (text) {
setState(() {
textValue = text;
});
},
decoration: const InputDecoration(
contentPadding: EdgeInsets.all(8),
hintText: "Scrivi qui il tuo messaggio",
border: InputBorder.none,
),
),
),
SizedBox(
height: 10,
),
CheckboxListTile(
value: _hiddenMessage,
onChanged: (value) {
setState(() {
_hiddenMessage = value!;
});
},
title: const Text("Nascondi"),
)
],
),
),
));
}
CustomAlertDialog:
class _CustomAlertDialogState extends State<CustomAlertDialog> {
#override
Widget build(BuildContext context) {
return AlertDialog(
insetPadding: widget.insetPadding ??
EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0),
title: widget.title ??
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: const [
Icon(
Icons.cancel,
color: Colors.red,
size: 30,
),
SizedBox(width: 5),
Text('Errore'),
],
),
elevation: widget.elevation ?? 24.0,
actions: widget.actions ??
[
TextButton(
child: const Text("Ok"),
onPressed: () {
Navigator.of(context).pop();
},
)
],
content: widget.content ??
const Text(
"Errore di connessione al server",
style: TextStyle(fontSize: 20),
),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(16),
),
);
}
}
Thank you for the help!
Widget build(BuildContext context) {
return MaterialApp(
shortcuts: {
LogicalKeySet(LogicalKeyboardKey.space): ActivateIntent(),
},
title: 'title of your app',
//....
);
}

SingleChildScrollView is not scrolling with Stack Widget Flutter

Here when I type inside text fields Keyboard covers the Login button. So I need to scroll down to the Login button when typing. I tried wrapping LayoutBuilder with SingleChildScrollView and tried using Positioned widget inside Stack but nothing solved my issue. And I set physics to AlwaysScrollableScrollPhysics() inside SingleChildScrollView but it also didn't solve the problem. I can't figure out what I've done wrong. I would be grateful if anyone can help me with this issue
Here's my code
Material(
child: SingleChildScrollView(
child: Stack(
overflow: Overflow.clip,
children: <Widget>[
Image.asset(
'assets/login-screen-img.jpg'
),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 220.0, 16.0, 0),
child: Card(
child: Padding(
padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 24.0),
child: Form(
//associating global key with the form(It keeps track of the form)
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Email', style: TextStyle(fontSize: 14.0, color: Colors.grey),),
TextFormField( // email field
cursorColor: Colors.brown[500],
decoration: InputDecoration(
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.brown[500])
),
//hintText: 'Enter your Email'
),
// validation
validator: (email) => email.isEmpty ? 'Enter the email' : null,
onChanged: (emailInput) {
setState(() {
email = emailInput;
});
},
),
SizedBox(height: 16.0),
Text('Password', style: TextStyle(fontSize: 14.0, color: Colors.grey),),
TextFormField( // password field
cursorColor: Colors.brown[500],
decoration: InputDecoration(
//hintText: 'Enter your Password'
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.brown[500])
)
),
// validation
validator: (password) => password.length < 6 ? 'Password must be more than 6 characters' : null,
obscureText: true, // hide when type
onChanged: (passwordInput) {
setState(() {
password = passwordInput;
});
},
),
SizedBox(height: 48.0,),
Center(
child: RaisedButton( // login button
child: Text('LOG IN', style: TextStyle(fontSize: 16.0, color: Colors.white),),
color: Colors.brown[500],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25)
),
padding: EdgeInsets.fromLTRB(66.0, 16.0, 66.0, 16.0),
onPressed: () async {
if(_formKey.currentState.validate()) {
// show loading screen
setState(() {
loading = true;
});
dynamic result = await _auth.signInWithEmailAndPassword(email, password);
if(result == null) {
// stop showing loading screen/widget
setState(() {
loading = false;
});
// show an error message
Fluttertoast.showToast(
msg: 'Could not sign in!',
toastLength: Toast.LENGTH_SHORT,
);
}
}
},
),
),
SizedBox(height: 24.0),
Center(child: Text('Don\'t have and account ?' )),
SizedBox(height: 16.0,),
Center(
child: FlatButton( // sign up button
child: Text('SIGN UP', style: TextStyle(fontSize: 16.0, color: Colors.brown[500] )),
onPressed: (){
Navigator.push(context, MaterialPageRoute(
builder: (context) => SignUp()
));
},
),
)
],
),
),
),
),
)
],
),
),
);
Screenshot of my UI
Here I found that the issue is with the height of the stack. As #sajithlakmal mentioned in the comments, height of the stack is small and there is nothing to scroll. But in my case, I don't want to make an extra height than the screen height because this is just a login screen. I could easily solve the issue by replacing Material widget with Scaffold. inside the body of the Scaffold gives the required height when typing and able to scroll down.
Here's the working code.
Scaffold(
body: SingleChildScrollView(
child: Stack(
overflow: Overflow.visible,
children: <Widget>[
Image.asset(
'assets/login-screen-img.jpg',
alignment: Alignment.topCenter,
),
Padding(
padding: const EdgeInsets.fromLTRB(16.0, 220.0, 16.0, 0),
child: Card(
child: Padding(
padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 24.0),
child: Form(
//associating global key with the form(It keeps track of the form)
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text('Email', style: TextStyle(fontSize: 14.0, color: Colors.grey),),
TextFormField( // email field
cursorColor: Colors.brown[500],
decoration: InputDecoration(
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.brown[500])
),
//hintText: 'Enter your Email'
),
// validation
validator: (email) => email.isEmpty ? 'Enter the email' : null,
onChanged: (emailInput) {
setState(() {
email = emailInput;
});
},
),
SizedBox(height: 16.0),
Text('Password', style: TextStyle(fontSize: 14.0, color: Colors.grey),),
TextFormField( // password field
cursorColor: Colors.brown[500],
decoration: InputDecoration(
//hintText: 'Enter your Password'
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.brown[500])
)
),
// validation
validator: (password) => password.length < 6 ? 'Password must be more than 6 characters' : null,
obscureText: true, // hide when type
onChanged: (passwordInput) {
setState(() {
password = passwordInput;
});
},
),
SizedBox(height: 48.0,),
Center(
child: RaisedButton( // login button
child: Text('LOG IN', style: TextStyle(fontSize: 16.0, color: Colors.white),),
color: Colors.brown[500],
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(25)
),
padding: EdgeInsets.fromLTRB(66.0, 16.0, 66.0, 16.0),
onPressed: () async {
if(_formKey.currentState.validate()) { // check validation
// show loading screen
setState(() {
loading = true;
});
dynamic result = await _auth.signInWithEmailAndPassword(email, password);
if(result == null) {
// stop showing loading screen/widget
setState(() {
loading = false;
});
// show an error message
Fluttertoast.showToast(
msg: 'Could not sign in!',
toastLength: Toast.LENGTH_SHORT,
);
}
}
},
),
),
SizedBox(height: 24.0),
Center(child: Text('Don\'t have and account ?' )),
SizedBox(height: 16.0,),
Center(
child: FlatButton( // sign up button
child: Text('SIGN UP', style: TextStyle(fontSize: 16.0, color: Colors.brown[500] )),
onPressed: (){
Navigator.push(context, MaterialPageRoute(
builder: (context) => SignUp()
));
},
),
)
],
),
),
),
),
)
],
),
),
);

Can't seem to save the value of a TextField for future use

On my login screen : user is asked to type in login and password. I save these value in two variables : email and password. Then the user must tap a button to actually log in.
The problem is that for some reason (that I really can't figure out...) email and password are always empty when user hits the button....
Here's the full code :
class SeConnecterScreen extends StatelessWidget {
static const String id = 'se_connecter_screen';
#override
Widget build(BuildContext context) {
var uD = Provider.of<UdProvider>(context);
String email = '';
String passWord = '';
final Connectivity _connectivity = Connectivity();
var scaffold = Scaffold(
extendBody: true,
extendBodyBehindAppBar: true,
backgroundColor: Colors.indigo[900],
body: Column(
children: [
Expanded(
flex: 4,
child: Container(
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('images/start2.jpg'), fit: BoxFit.cover),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(50),
),
),
height: MediaQuery.of(context).size.height / 4,
),
),
Expanded(
flex: 6,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Container(
height: MediaQuery.of(context).size.height -
(MediaQuery.of(context).size.height / 4),
padding: EdgeInsets.all(8.0),
decoration: BoxDecoration(
color: Colors.white,
border: Border.all(
width: 1.0,
color: Colors.white,
),
borderRadius: BorderRadius.circular(10.0),
),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: [
Text(
'WORD CHAMPIONS',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.indigo[900],
fontSize: 28.0,
fontWeight: FontWeight.bold),
),
SizedBox(
height: 50,
),
TextField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
focusColor: Colors.white,
border: OutlineInputBorder(),
labelText: 'Tape ton adresse email'),
onChanged: (value) {
email = value;
print(email);
},
),
SizedBox(
height: 25.0,
),
TextField(
obscureText: true,
keyboardType: TextInputType.name,
decoration: InputDecoration(
focusColor: Colors.white,
border: OutlineInputBorder(),
labelText: 'Tape ton mot de passe'),
onChanged: (value) {
passWord = value;
print(passWord);
},
),
SizedBox(
height: 35.0,
),
ElevatedButton.icon(
label: Text('Se connecter'),
icon: Icon(Icons.library_add_check_rounded),
style: ElevatedButton.styleFrom(
minimumSize: Size(30, 45),
primary: Colors.green[800],
onPrimary: Colors.white,
),
onPressed: () async {
print('Email : $email');
print('passWord : $passWord');
if (await _connectivity.checkConnectivity() ==
ConnectivityResult.none) {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text(
'Oups!',
style: TextStyle(fontSize: 22.0),
),
content: Text(
'Il faut te connecter à Internet !',
style: TextStyle(fontSize: 18.0),
),
actions: [
TextButton(
onPressed: () {
Navigator.pushReplacementNamed(
context, StartingScreen.id);
},
child: Text(
'OK',
style: TextStyle(fontSize: 18.0),
),
),
],
),
);
} else {
String accountVerif =
await uD.checkAccountId(email, passWord);
if (accountVerif == '') {
DialogBuilder(context).showLoadingIndicator(
text: 'Téléchargement des bases',
color: Colors.black45);
await uD.downLoadUserInfo(email);
DialogBuilder(context).hideOpenDialog();
Navigator.pushReplacementNamed(
context, ProfileScreen.id);
} else {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
title: Text(
'Oups!',
style: TextStyle(fontSize: 22.0),
),
content: Text(
'Ce compte n\'existe pas, ou le mot de passe est incorrect.',
style: TextStyle(fontSize: 18.0),
),
actions: [
TextButton(
onPressed: () {
Navigator.pushReplacementNamed(
context, StartingScreen.id);
},
child: Text(
'OK',
style: TextStyle(fontSize: 18.0),
),
),
],
),
);
}
}
}),
],
),
),
),
),
)),
],
),
);
return scaffold;
}
}
Please Try to define you password and email variables outside build method. It may solve issue.
See it works for me, May be you should do
stop execution
run 'flutter clean'
run 'flutter pub get'
execute it
Define the variables just below the override instead of below the build method
Add the textediting controller to it
so that your problem will be solved

How can I make the leading flutter date icon clickable and selectable widget?

Below is my flutter code and I would like to make the leading calendar icon launch a calendar widget for date selection in the last textfield just before the raised button as marked below in code.
** widget starts inside stateful class**
#override
Widget build(BuildContext context) {
var db = DBHelper();
return Scaffold(
appBar: AppBar(
title: Text('Add'),
),
body: Container(
padding: EdgeInsets.only(top: 30, left: 50, right: 50),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
TextField(
keyboardType: TextInputType.text,
decoration: InputDecoration(
icon: Icon(Icons.bookmark), hintText: 'nickname'),
controller: nameController,
),
const SizedBox(height: 15),
TextField(
decoration: InputDecoration(
icon: Icon(Icons.date_range), hintText: 'date created'),
controller: otherController,
),
const SizedBox(height: 50),
RaisedButton(
onPressed: () {
db.saveAssets(Asset(
name: nameController.text,
other: otherController.text));
Navigator.push(
context,
MaterialPageRoute(builder: (context) => MyAssetsList()),
);
},
padding: EdgeInsets.only(left: 18, right: 18),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
textColor: Colors.white,
color: Colors.lightGreen,
splashColor: Colors.lightGreenAccent,
child: const Text(
'Save',
style: TextStyle(fontSize: 20),
),
),
],
),
),
),
);
}
}
One more solution.
If you want only the calendar icon to be tapable (while still having similar design to the one you've provided):
Row(
children: <Widget>[
IconButton(
icon: Icon(Icons.date_range),
padding: EdgeInsets.zero,
onPressed: () {
print('Yay!');
},
),
Expanded(
child: TextField(
decoration: InputDecoration(
hintText: 'Date Created',
),
readOnly: true, // Or wrap the input with AbsorbPointer if you do not want the field to get highlighted by taping on it
),
),
],
)
If you want to let the user input the date by typing - just remove the readOnly attribute from the input.

How to reduce the width of button and put it in center

I have just started learning flutter, I have a screen that contains a form, I'd like to know how to reduce the width of button and put it in center.
This the code:
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Ajouter note'),
centerTitle: true,
backgroundColor: Colors.pink,
),
body: Container(
padding: EdgeInsets.all(10.0),
child: Form(
key: _formKey,
child: ListView(
children: <Widget>[
TextFormField(
decoration: InputDecoration(
hintText: 'Saisir un titre',
),
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
SizedBox(
height: 10.0,
),
TextFormField(
minLines: 10,
maxLines: 10,
decoration: InputDecoration(
hintText: 'Saisir une déscription',
),
// The validator receives the text that the user has entered.
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
SizedBox(
height: 10.0,
),
RaisedButton.icon(
onPressed: () {
if (_formKey.currentState.validate()) {
Scaffold.of(context).showSnackBar(
SnackBar(content: Text('Processing Data')));
}
},
label: Text(
'Enregistrer',
style: TextStyle(
fontSize: 16.0,
color: Colors.white,
),
),
icon: Icon(
Icons.save,
color: Colors.white,
),
color: Colors.pink,
),
],
),
),
),
);
}
One way could be wrapping the RaisedButton in a Padding.
You can apply padding only horizontal axis.
Padding(
padding: const EdgeInsets.symmetric(horizontal: 40),
child: // your RaisedButton
}
There are a lot of ways of doing it. One option is to limit the size of your button like this:
Center(
child: SizedBox(
width: 100,
child: RaisedButton(
...
),
),
)