So i have a few TextFormFields inside scrollable column and it works fine, until error text appears. Because it causes bottom overflow. The desired behavior is button that just jumps up and don’t cause overflow. I have tried to put textfield inside a container with fixed height but after error text appear it just shrinked textfield
Form _buildForm(BuildContext context, BoxConstraints constraints) {
print(constraints);
return Form(
key: _form,
child: SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minWidth: constraints.maxWidth, minHeight: constraints.maxHeight),
child: IntrinsicHeight(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 48, bottom: 32),
child: Text(
'Sign Up',
style: AppTheme.theme.textTheme.headline3
.copyWith(color: AppColors.black),
),
),
LabeledTextField(
margin: EdgeInsets.only(bottom: 16),
fieldName: 'Email',
decoration: InputDecoration(hintText: 'example#mail.com'),
textInputAction: TextInputAction.next,
onFieldSubmitted: (_) {
FocusScope.of(context).requestFocus(_userNameFocusNode);
},
onSaved: (val) => _authData["login"] = val,
validator: FormValidators.emailValidator,
autovalidateMode: AutovalidateMode.onUserInteraction,
),
LabeledTextField(
margin: EdgeInsets.only(bottom: 16),
fieldName: 'User Name',
decoration: InputDecoration(hintText: 'alexexample...'),
textInputAction: TextInputAction.next,
onFieldSubmitted: (_) {
FocusScope.of(context).requestFocus(_passwordFocusNode);
},
onSaved: (val) => _authData["username"] = val,
validator: FormValidators.isRequiredValidator,
autovalidateMode: AutovalidateMode.onUserInteraction,
),
LabeledTextField(
fieldName: 'Password',
focusNode: _passwordFocusNode,
obscureText: true,
decoration: InputDecoration(hintText: 'Type in...'),
onSaved: (val) => _authData["password"] = val,
validator: FormValidators.passwordValidator,
autovalidateMode: AutovalidateMode.onUserInteraction,
),
Spacer(),
AuthButtons(
mode: AuthMode.signup,
saveForm: _saveForm,
),
],
),
),
),
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
//resizeToAvoidBottomInset: false,
appBar: CustomAppBar(
preferredSize: Size.fromHeight(64),
title: 'title',
),
body: Padding(
padding: const EdgeInsets.only(left: 16, right: 16),
child: LayoutBuilder(
builder: (context, constraints) => _buildForm(context, constraints),
),
),
);
}
LabeledTextField is basically just a textFormField with Text widget before it
class _LabeledTextFieldState extends State<LabeledTextField> {
#override
Widget build(BuildContext context) {
return Container(
margin: widget.margin,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.fieldName,
style: AppTheme.theme.textTheme.caption
.copyWith(color: AppColors.darkGrey),
),
TextFormField(
initialValue: widget.initialValue,
textInputAction: widget.textInputAction,
focusNode: widget.focusNode,
onSaved: widget.onSaved,
onChanged: widget.onChanged,
validator: widget.validator,
onFieldSubmitted: widget.onFieldSubmitted,
autovalidateMode: widget.autovalidateMode,
obscureText: widget.obscureText,
decoration: widget.decoration,
),
],
),
);
}
}
You can try expanded instead of a container as a parent widget or use wrap as a parent widget on your text field.
Wrap your Column with SingleChildScrollView
class _LabeledTextFieldState extends State<LabeledTextField> {
#override
Widget build(BuildContext context) {
return Container(
margin: widget.margin,
child: SingleChildScrollView(
physics: BouncingScrollPhysics(), // u can ignore this if u want
child : Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
widget.fieldName,
style: AppTheme.theme.textTheme.caption
.copyWith(color: AppColors.darkGrey),
),
TextFormField(
initialValue: widget.initialValue,
textInputAction: widget.textInputAction,
focusNode: widget.focusNode,
onSaved: widget.onSaved,
onChanged: widget.onChanged,
validator: widget.validator,
onFieldSubmitted: widget.onFieldSubmitted,
autovalidateMode: widget.autovalidateMode,
obscureText: widget.obscureText,
decoration: widget.decoration,
),
],
),
),
);
}
}
Related
The many solutions that are already present for the same error do not seem to help in my case, thus the question.
I get a bunch of "RenderBox was not laid out" errors while my Scaffold body remains empty. The error occurs because of the Row widget in my Column widget. When removing the Row widget the error does not occur anymore.
I tried wrapping the Row widget in an Expanded as well as SizedBox or Flexible but none helped. I also tried the same with the parent Column widget even both at the same time with different combinations.
When looking at the Widget Details Tree in the Flutter Inspector the renderObject of the Padding at the top has size: MISSING but constraints are finite constraints: BoxConstraints(0.0<=w<=392.7, 0.0<=h<=413.8).
Same goes for the child Column widget with the following constraints: constraints: BoxConstraints(0.0.<=w<=340.7, 0.0<=h<=361.8).
The child Row widget then also has size: MISSING but constraints: BoxConstraints(0.0<=w<=340.7, 0.0<=h<=Infinity).
I thought that the child widgets inherit the constraints of the parent? And as I said wrapping the Row with Expanded, SizedBox or Flexbox did not help except I did it wrong.
I would be glad to get some answers and a solution. Thank you in advance!
The exact error messages can be found here: https://pastebin.com/n3RFbasT
My code:
class AddBalance extends StatefulWidget {
const AddBalance({Key? key}) : super(key: key);
#override
State<AddBalance> createState() => _AddBalanceState();
}
class _AddBalanceState extends State<AddBalance> {
final _formKey = GlobalKey<FormState>();
bool subtractSwitch = false;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 0,
title: const Text("Add Entry"),
),
body: Padding(
padding: const EdgeInsets.all(26.0),
child: Form(
autovalidateMode: AutovalidateMode.always,
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
autofocus: true,
maxLines: 1,
textInputAction: TextInputAction.next,
decoration: const InputDecoration(
labelText: "Name",
),
),
Row(
children: [
TextFormField(
textInputAction: TextInputAction.next,
keyboardType: TextInputType.number,
maxLength: 10,
maxLines: 1,
decoration: const InputDecoration(
labelText: "Amount",
),
),
const Text("Subtract"),
Switch(
value: subtractSwitch,
onChanged: (value) {
setState(() {
subtractSwitch = value;
});
}),
],
)
],
),
),
),
);
}
}
Just need to wrap your Row with any Widget that give you boundaries like Center, Align , SizedBox, etc.
In this case I used Align and ... don't forget to use Expanded over your TextFormField inside the Row.
Scaffold(
appBar: AppBar(
elevation: 0,
title: const Text("Add Entry"),
),
body: Padding(
padding: const EdgeInsets.all(26.0),
child: Form(
autovalidateMode: AutovalidateMode.always,
key: _formKey,
child: Column(
children: <Widget>[
TextFormField(
autofocus: true,
maxLines: 1,
textInputAction: TextInputAction.next,
decoration: const InputDecoration(
labelText: "Name",
),
),
Align(
child: Row(
children: [
Expanded(
child: TextFormField(
textInputAction: TextInputAction.next,
keyboardType: TextInputType.number,
maxLength: 10,
maxLines: 1,
decoration: const InputDecoration(
labelText: "Amount",
),
),
),
const Text("Subtract"),
Switch(
value: subtractSwitch,
onChanged: (value) {
setState(() {
subtractSwitch = value;
});
}),
],
),
)
],
),
),
),
)
I am new to flutter, I would like to align the text widget shown in the screen shot to the bottom of the row so it is on the same line as the text form field. I would like to move "Kilos" to the bottom of the row.
Here is the code:
return Row(
children: [
Flexible(
child: TextFormField(
decoration: InputDecoration(labelText: label),
keyboardType: _getKeyboardType(),
validator: (value) => _getFormValidator()(value),
onSaved: (value) { saveAction(formItem,value); },
onTap: () {print(TextInputType.emailAddress);},
),
),
Text('Kilos',style: TextStyle(backgroundColor: Colors.red),)
],
);
I have tried wrapping the Row and the Text widgets within an Align widget.
You can achieve this with Stack..
here is sample code.
Stack(
children: [
TextFormField(
decoration: const InputDecoration(
hintText: 'Enter a search term'
),
),
Positioned(
right: 0,
bottom: 0,
child: Text(
'Kilos',
style: TextStyle(backgroundColor: Colors.red),
))
],
),
You can try with intrinsic height and make your crossAlignment.end
IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Flexible(
child: Align(
alignment: Alignment.topLeft,
child: TextFormField(
decoration: InputDecoration(labelText: "label"),
/* keyboardType: _getKeyboardType(),
validator: (value) => _getFormValidator()(value),
onSaved: (value) {
saveAction(formItem, value);
},*/
onTap: () {
print(TextInputType.emailAddress);
},
),
),
),
Text(
'Kilos',
style: TextStyle(backgroundColor: Colors.red),
)
],
),
)
Output:
I'm making a login form in Flutter, I'm using a ListView that has a Container as a child and it's child is a Stack widget which has Positioned children. Because of the Stack, I have to have a bound height, so hence the Container widget, which I gave the height: height: MediaQuery.of(context).size.height * 1.2. If I remove the * 1.2 my Button and and Text widget don't show up, and when I click on the login with * 1.2, my validator pops up, red warning signs shows that your info is entered incorrectly, so I can't see the button anymore. Example in pictures:
This is with height: MediaQuery.of(context).size.height * 1.2
Then I try to login, validator pops, and now I can't see the button nor the text and link below the button:
The problem I am facing is, how do I layout this login form that can only be scrollable as far as it needs, so I don't have empty space after the Button, just to spread the form so it is visible, not get something like this if I increment the height of the Container?
Code:
ListView(
shrinkWrap: true,
children: [
Container(
height: MediaQuery.of(context).size.height * 1.6,
child: Stack(
children: [
Positioned(
width: MediaQuery.of(context).size.width,
top: MediaQuery.of(context).size.width * 0.1,
child: Image.asset(
'assests/images/loginform.png',
scale: 2.5,
height: 60,
width: 119,
),
),
Positioned(
width: MediaQuery.of(context).size.width,
top: MediaQuery.of(context).size.width * 0.35,
child: Padding(
padding: const EdgeInsets.only(left: 24.0, right: 25.0),
child: Column(
children: <Widget>[
Form(
key: _registrationFormKey,
child: Column(
children: [
TextFormField(
textAlign: TextAlign.start,
onChanged: (value) {
context.read<User>().name = value;
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Full name'),
validator: (thisValue) {
if (thisValue.isEmpty) {
return 'Please enter your full name';
}
return null;
},
),
SizedBox(
height: 24.0,
),
TextFormField(
keyboardType: TextInputType.phone,
textAlign: TextAlign.start,
onChanged: (value) {
context.read<User>().phoneNumber = value;
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Phone number'),
validator: _validateMobile,
),
SizedBox(
height: 24.0,
),
TextFormField(
keyboardType: TextInputType.streetAddress,
textAlign: TextAlign.start,
onChanged: (value) {
context.read<User>().address = value;
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Address'),
validator: (thisValue) {
if (thisValue.isEmpty) {
return 'Please enter your address';
}
return null;
},
),
SizedBox(
height: 24.0,
),
TextFormField(
keyboardType: TextInputType.text,
textAlign: TextAlign.start,
onChanged: (value) {
context.read<User>().companyName = value;
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Company name'),
validator: (thisValue) {
if (thisValue.isEmpty) {
return 'Please enter your company name';
}
return null;
},
),
SizedBox(
height: 24.0,
),
TextFormField(
keyboardType: TextInputType.text,
textAlign: TextAlign.start,
onChanged: (value) {
context.read<User>().website = value;
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Website name'),
validator: _validateWebsite),
SizedBox(
height: 24.0,
),
TextFormField(
keyboardType: TextInputType.emailAddress,
textAlign: TextAlign.start,
onChanged: (value) {
context.read<User>().email = value;
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'E-mail address'),
validator: _validateEmail,
),
SizedBox(
height: 24.0,
),
TextFormField(
keyboardType: TextInputType.text,
obscureText: _obscureText,
textAlign: TextAlign.start,
onChanged: (value) {
context.read<User>().password = value;
},
decoration: kTextFieldDecoration.copyWith(
hintText: 'Password',
suffixIcon: IconButton(
icon: const Icon(Icons.visibility_outlined),
onPressed: _togglePassVisibility,
),
),
validator: _validatePassword,
),
],
),
),
FormField<bool>(
// 1
initialValue: _agree,
builder: (FormFieldState<bool> state) {
// 2
return Column(
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Checkbox(
// 3
value: state.value,
onChanged: (bool val) => setState(() {
// 4
_agree = val;
// 5
state.didChange(val);
}),
),
const Text('I agree with'),
TextButton(
onPressed: () {},
child: Text('Terms and conditions'),
),
],
),
// 6
state.errorText == null
? Text("")
: Text(state.errorText,
style: TextStyle(color: Colors.red)),
],
);
},
// 7
validator: (val) => _validateTerms(_agree),
),
AlarmButtons(
buttonColour: Color(0xFF29ABE2),
buttonText: 'CREATE ACCOUNT',
buttonTextColour: Colors.white,
buttonBorderSide: Colors.white,
onButtonPress: () async {
if (_registrationFormKey.currentState.validate()) {
signUpToCognito(context);
Navigator.pushNamed(
context, ConfirmRegistrationScreen.id);
}
},
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Already have an account?'),
TextButton(
onPressed: () {
Navigator.pushNamed(context, LoginScreen.id);
},
child: Text('Log in'),
),
],
),
],
),
),
),
],
),
)
],
)
Thanks in advance for the help and suggestions!
there are a couple mistakes (or at least optimizations) in your code. I will go over them one by one:
If you have a small number of children that need to be scrolled, it is better to use a SingleChildScrollView with a Column instead of a list view. ListView builds its children lazily - that is, it only builds widgets that are visible on the screen. If you have only a handful of widgets with no complex animations, then you don't really need that. SingleChildScrollView is more flexible that a ListView.
SingleChildScrollView(
child: Column(
children: [...]
)
)
It seems that you want the image to be on the background with the form validation on top of it. For that, you used a Stack and a Column as children to a ListView. Instead, have the Stack as a parent, with both the ListView and the image as children to the Stack. Now this might produce an error, as the ListView might expand infinitely. A simple solution is to wrap it within a Positioned widget with all its sides set to zero.
Stack(
children: [
BackgroundImageWidget(),
Positioned(
top: 0, bottom: 0, left: 0, right: 0, // or width: screenWidth, height: screenHeight,
child: ListView(...),
)
]
)
Is it possible to push all content up when open keyboard? (not only textField area, whole page push up)
showModalBottomSheet(
context: context,
builder: (BuildContext context) {
return BottomSheet();
},
isScrollControlled: true);
BottomSheet class
class BottomSheet extends StatefulWidget {
#override
_BottomSheetState createState() => _BottomSheetState();
}
class _BottomSheetState extends State<BottomSheet> {
#override
Widget build(BuildContext context) {
return
SingleChildScrollView(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom),
child: Container(
child: Column(
children: <Widget>[...
I want to like this push-up,
But Current output is,
Simply putting reverse=true inside the SingleChildScrollView will suffice.
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: SingleChildScrollView(
reverse: true,
child: Container(
........
........
You can simply give the widget a bottom position of MediaQuery.of(context).viewInsets.bottom if you are using a stack.
In your case, set margin : to MediaQuery.of(context).viewInsets.bottom instead of padding.
Wrap your whole widget inside a container and provide that container padding like this, it will work.
child: Container(
padding: EdgeInsets.only(
top: 25.0,
bottom: MediaQuery.of(context).viewInsets.bottom,
left: 25.0,
right: 25.0,
),
child: Form(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField(
decoration: InputDecoration(
hintText: 'Email',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
keyboardType: TextInputType.emailAddress,
validator: (value) {
return _isEmailValid(value);
},
textInputAction: TextInputAction.next,
onSaved: (value) {},
controller: _emailController,
),
SizedBox(height: 10),
TextFormField(
obscureText: true,
decoration: InputDecoration(
hintText: 'Password',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
),
keyboardType: TextInputType.emailAddress,
validator: (value) {
return _isEmailValid(value);
},
textInputAction: TextInputAction.done,
onSaved: (value) {},
controller: _passwordController,
),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
TextButton(
onPressed: () {},
child: const Text('Forgot Password'),
),
],
),
ElevatedButton(
style: ElevatedButton.styleFrom(
side: const BorderSide(width: 1.3, color: Colors.white),
shadowColor: Colors.white,
),
onPressed: () {},
child: const Text('Login'),
),
TextButton(
onPressed: () {},
child: Text('Not have any Accoung? Sign Up'),
),
],
),
),
),
Set resizeToAvoidBottomInset property of the Scaffold as true.
showModalBottomSheet(
context: context,
isScrollControlled: true,
builder: (BuildContext context) {
return Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: //your code
);
}
)
This works for me. Maybe try moving your padding inside the container.
This question already has an answer here:
Combine SingleChildScrollView and PageView in Flutter
(1 answer)
Closed 3 years ago.
I added 2 Form widgets to a PageView. Each form has 6 TextFormField. When I tap on the last 2 TextFormField from either Form, the keyboard shows up over the fields and hides them to the user.
I tried using a SingleChildScrollView inside PageView, with no luck.
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomPadding: false,
body: PageView(
children: <Widget>[
_sampleForm(),
_sampleForm(),
],
),
)
}
_sampleForm(){
return Container(
margin: const EdgeInsets.fromLTRB(0, 0, 0, 10),
width: MediaQuery.of(context).size.width,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Form(
child: Column(
children: <Widget>[
TextFormField(...),
TextFormField(...),
TextFormField(...),
TextFormField(...),
TextFormField(...),
TextFormField(...),
],
),
),
],
),
),
);
}
What I need is to automatically scroll up when tapping the TextFormFields, and also be able to swipe a PageView horizontally,
try this approach. I have used a ListView instead of [SingleChildScrollView, Column].
Here the code.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: SampleScreen(),
);
}
}
class SampleScreen extends StatefulWidget {
#override
_SampleScreenState createState() => _SampleScreenState();
}
class _SampleScreenState extends State<SampleScreen> {
List<FocusNode> _focusNodes = List.generate(7, (_) => FocusNode());
#override
void dispose() {
_focusNodes.forEach((_) => _.dispose());
super.dispose();
}
setFocus(BuildContext context, {FocusNode focusNode}) {
FocusScope.of(context).requestFocus(focusNode ?? FocusNode());
}
_sampleForm1() {
return Form(
child: ListView(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 10),
children: <Widget>[
TextFormField(
focusNode: _focusNodes[0],
decoration: InputDecoration(labelText: "Flutter 1"),
onFieldSubmitted: (_) => setFocus(context, focusNode: _focusNodes[1]),
textInputAction: TextInputAction.next,
),
TextFormField(
focusNode: _focusNodes[1],
decoration: InputDecoration(labelText: "Flutter 2"),
onFieldSubmitted: (_) => setFocus(context, focusNode: _focusNodes[2]),
textInputAction: TextInputAction.next,
),
TextFormField(
focusNode: _focusNodes[2],
decoration: InputDecoration(labelText: "Flutter 3"),
onFieldSubmitted: (_) => setFocus(context, focusNode: _focusNodes[3]),
textInputAction: TextInputAction.next,
),
TextFormField(
focusNode: _focusNodes[3],
decoration: InputDecoration(labelText: "Flutter 4"),
onFieldSubmitted: (_) => setFocus(context, focusNode: _focusNodes[4]),
textInputAction: TextInputAction.next,
),
TextFormField(
focusNode: _focusNodes[4],
decoration: InputDecoration(labelText: "Flutter 5"),
onFieldSubmitted: (_) => setFocus(context, focusNode: _focusNodes[5]),
textInputAction: TextInputAction.next,
),
TextFormField(
focusNode: _focusNodes[5],
decoration: InputDecoration(labelText: "Flutter 6"),
onFieldSubmitted: (_) => setFocus(context, focusNode: _focusNodes[6]),
textInputAction: TextInputAction.next,
),
TextFormField(
focusNode: _focusNodes[6],
decoration: InputDecoration(labelText: "Flutter 7"),
onFieldSubmitted: (_) => setFocus(context),
textInputAction: TextInputAction.done,
),
],
),
);
}
_sampleForm2() {
return Form(
child: ListView(
padding: const EdgeInsets.fromLTRB(0, 0, 0, 10),
children: <Widget>[
TextFormField(decoration: InputDecoration(labelText: "Flutter 1")),
TextFormField(decoration: InputDecoration(labelText: "Flutter 2")),
TextFormField(decoration: InputDecoration(labelText: "Flutter 3")),
TextFormField(decoration: InputDecoration(labelText: "Flutter 4")),
TextFormField(decoration: InputDecoration(labelText: "Flutter 5")),
TextFormField(decoration: InputDecoration(labelText: "Flutter 6")),
TextFormField(decoration: InputDecoration(labelText: "Flutter 7")),
],
),
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Sample App"),
),
body: PageView(
children: <Widget>[
_sampleForm1(),
_sampleForm2(),
],
),
);
}
}
Hope it helps :)