I have TextField purchaseCommentField() within the structure as below:
#override
Widget build(BuildContext context)
{
return GestureDetector(
onTap: CommonUtils.endEditing(context),
child: Container(
width: _width,
color: Colors.white,
child: SingleChildScrollView(
child: Column(
children: <Widget>[
....
...
Visibility(
visible: _additionalInfo != null,
child: purchaseCommentField()
),
rowSpacer(16.0),
actionButton(context)
],
)
)
),
);
}
Widget purchaseCommentField()
{
return TextField(
controller: _purchaseCommentController,
minLines: 1,
maxLines: null,
keyboardType: TextInputType.multiline,
style: new TextStyle(fontSize: 14.0),
decoration: new InputDecoration(
labelText: 'Additional Info',
border: OutlineInputBorder(),
),
);
}
When I focus textfield, I got unwanted scrollbar at right inside the field:
This issue occured when I set fontSize:14.0. When I remove that or set fontSize to 16.0 then there is no scrollbar at right:
Is there a way to remove that scrollbar in textfield ?
try using TextFormField,
TextFormField(
controller: textController,
validator: (value) {
if (value.trim().isEmpty) {
return _reportTypeModel.language.msgEnterDesc;
}
return null;
},
style: new TextStyle(fontSize: 14.0),
maxLength: 500,
decoration: InputDecoration(
labelText: _reportTypeModel.language.description,
),
minLines: 4,
maxLines: 6,
keyboardType: TextInputType.multiline,
textInputAction: TextInputAction.next,
),
Output:
There's an open issue on github regarding this.
For now you can fix this by creating a custom ScrollBehavior and overriding buildScrollbar method:
class CustomScrollBehavior extends ScrollBehavior {
const CustomScrollBehavior();
#override
Widget buildScrollbar(context, child, details) {
return child;
}
}
And then wrap your TextField with ScrollConfiguration widget:
return ScrollConfiguration(
behavior: const CustomScrollConfiguration(),
child: TextField(
// Your text-field params
),
);
This works for TextFormField too.
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;
});
}),
],
),
)
],
),
),
),
)
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,
),
],
),
),
);
}
}
The title essentially sums up the issue I'm having. In the code below you can see that I have a text field controller assigned to the URL input. I'm wanting the effect to be that when the user finishes entering a URL the image is automatically updated. In my case, the image never updates after the field value is changed. However, the image does change once a hot reload is performed. Only then is the desired effect achieved. What's going on here? I would assume that on every change to the Text controllers value that the entire form would be rebuilt(which I do see when I enable rainbow flag in dev tools) except for the image widget. Why is this happening?
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class EditProductPage extends StatefulWidget {
static const routeName = '/edit-product-page';
#override
_EditProductPageState createState() => _EditProductPageState();
}
class _EditProductPageState extends State<EditProductPage> {
final _urlTextController = TextEditingController();
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Edit Product'),
),
body: Card(
elevation: 5,
child: Padding(
padding: const EdgeInsets.all(15),
child: Form(
child: ListView(
children: [
TextFormField(
decoration: InputDecoration(labelText: 'Title'),
textInputAction: TextInputAction.next,
),
TextFormField(
decoration: InputDecoration(labelText: 'Price'),
keyboardType: TextInputType.number,
textInputAction: TextInputAction.next,
),
TextFormField(
decoration: InputDecoration(labelText: 'Description'),
maxLines: 3,
keyboardType: TextInputType.multiline,
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: <Widget>[
Container(
width: 100,
height: 100,
margin: EdgeInsets.only(
top: 8,
right: 10,
),
decoration: BoxDecoration(
border: Border.all(
width: 1,
color: Colors.grey,
),
),
child: _urlTextController.text.isEmpty
? Text('Enter a URL')
: FittedBox(
child: Image.network(
_urlTextController.text,
fit: BoxFit.cover,
key: ValueKey(_urlTextController.text),
),
),
),
Expanded(
child: TextFormField(
decoration: InputDecoration(labelText: 'Image URL'),
keyboardType: TextInputType.url,
textInputAction: TextInputAction.done,
controller: _urlTextController,
),
),
],
),
],
),
),
),
),
);
}
}
You need to change state every time user inputs something to rebuild form with new values
#override
void initState() {
_urlTextController.addListener(onUrlChanged);
super.initState();
}
void onUrlChanged() {
setState(() {});
}
#override
void dispose() {
_urlTextController.removeListener(onUrlChanged);
super.dispose();
}
Add onChanged callback which will trigger after adding anything inside TextFormField
Expanded(
child: TextFormField(
decoration: InputDecoration(labelText: 'Image URL'),
keyboardType: TextInputType.url,
textInputAction: TextInputAction.done,
controller: _urlTextController,
onChanged: (value) {
if (value.contains('.png')) { // Add ||(or) condition like this for other extension
setState(() {});
}
},
),
),
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);
});
},
),
]
)
);
I have a widget, that has an image element and and expanded listview of form elements that as I fill out and scroll the data disappears when it scrolls behind the image. It is not throwing any errors when I debug and it happens on any field that scrolls behind the image at the top of the widget. Any ideas?
#override
Widget build(BuildContext context) {
var _children = <Widget>[
new Center(
child: new Text(widget.prov.fname + widget.prov.lname,
style: new TextStyle(fontSize: 20.0, fontWeight: FontWeight.bold),
),
),
new Center(
child: new Container(
padding: new EdgeInsets.only(
left: 125.0, right: 125.0, bottom: 50.0),
child: new Image.network('http://$baseurl:8080/getimage/'+widget.prov.pic.assetName),
)
),
new Form(
key: _formKey,
autovalidate: _autovalidate,
onWillPop: _warnUserAboutInvalidData,
child: new Expanded(
child: new ListView(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
children: <Widget>[
new TextFormField(
decoration: const InputDecoration(
icon: const Icon(Icons.person),
hintText: 'First Name?',
labelText: 'First Name *',
),
onSaved: (String value) { referral.fname = value; },
validator: _validateName,
),
new TextFormField(
decoration: const InputDecoration(
icon: const Icon(Icons.person),
hintText: 'Last Name?',
labelText: 'Last Name *',
),
onSaved: (String value) { referral.lname = value; },
validator: _validateName,
),
new TextFormField(
decoration: const InputDecoration(
icon: const Icon(Icons.phone),
hintText: 'How to contact?',
labelText: 'Phone Number *',
prefixText: '+1'
),
keyboardType: TextInputType.phone,
onSaved: (String value) { referral.contact = value; },
validator: _validatePhoneNumber,
// TextInputFormatters are applied in sequence.
inputFormatters: <TextInputFormatter> [
WhitelistingTextInputFormatter.digitsOnly,
// Fit the validating format.
_phoneNumberFormatter,
],
),
new TextFormField(
decoration: const InputDecoration(
hintText: 'Tell us about patient',
helperText: 'It does not have to be detailed yet',
labelText: 'Referral Details',
),
maxLines: 5,
),
new _DateTimePicker(
labelText: 'DOB',
selectedDate: _fromDate,
selectDate: (DateTime date) {
setState(() {
referral.dob = date;
});
},
),
new InputDecorator(
decoration: const InputDecoration(
labelText: 'Type of Appointment',
hintText: 'Choose an Appointment Type',
),
isEmpty: _typeAppt == null,
child: new DropdownButton<String>(
value: _typeAppt,
isDense: true,
onChanged: (String newValue) {
setState(() {
_typeAppt = newValue;
});
},
items: _allTypeAppt.map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
),
),
],
)
)
),
/*new RefreshIndicator(
child: new ListView.builder(
itemBuilder: _itemBuilder,
itemCount: listcount,
),
onRefresh: _onRefresh,
),*/
];
return new Scaffold(
appBar: new AppBar(title: new Text("My Provider")),
body: new Column(
children: _children,
),
);
}
This is because you are using ListView to render your children. ListView only renders the visible children (has recycling nature). Instead, use Column with SingleChildScrollView.
SingleChildScrollView(child:Column(children:yourFormChildren));
I think the problem is that you don't save the values which were put into the TextFields (to a state for example).
From your code I assume you are using the ListView.builder() to set up your ListView. This method, as stated in the documentation, renders only the children, which are in view. Once you scroll a child out of view, it is removed from the ListView and only added again, once you scroll it into view. Because the TextField is removed, the value is removed as well.
To permanently have the value saved, I would advice to use TextFields and save the input to state in the onChanged() method of the TextField, or make use of TextEditingController.
Using TextEditorController it's quite simple:
For "First Name" add==>
TextEditingController nameController = new TextEditingController();
and
`new TextFormField(
decoration: const InputDecoration(
icon: const Icon(Icons.person),
hintText: 'First Name?',
labelText: 'First Name *',
),
validator: _validateName,
controller: nameController,
),`
In your onSubmit() ==>
print(nameController.text)
if you have TextFormField that you auto generate by listView.builder()
you can do this solution
List<TextEditingController> generalController=[];
#override
void initState() {
super.initState();
for(int i=0;i<100;i++){
generalController.add(TextEditingController(text: ""));
}}
first define list that contain list of TextEditingController
and after that initialize it with default value inside initState
and inside every item in listview.builder()
use controller with index
like that
ListView.builder(itemBuilder: (context,int index){
CustomTextFromField(
textEditingController: generalController[index],
hint: AppLocalizations.of(context)!.translate("subject_name"),
texInputType: TextInputType.text,
),
},
itemCount: state.subjectList.length,
I had the same issue, I was using ListView and the values go missing when i was scroll, Just as someone above mentioned(Suman Maharjan) i tried using SingleChildScrollView with Column and issue is resolved now.