Flutter textfield disable prefix icon on text changed - flutter

I have a text field widget that has an prefix icon . I want the prefix icon to be hidden when the text field is changed
my code:
TextField(
controller: messageInputController,
onChanged: (value){
messageInputChanged();
},
decoration: InputDecoration(
counterText: '',
prefixIcon: !showPrefixIcon ? Container() : Padding(
padding: const EdgeInsets.all(0),
child: IconButton(
onPressed: () {
imagePickerBottomSheet();
},
iconSize: 40,
color: Skin.gray,
icon: SvgPicture.asset(
'assets/svg/ic-image.svg',
height: 22,
color: Skin.gray,
)),
),
),
),
void messageInputChanged() {
if(messageInputController.text.isEmpty){
showPrefixIcon = true;
}else {
showPrefixIcon = false;
}
setState(() {});
}
But when the set state is called and the icon is hidden, the contents of the text field are also messed up

The InputDecorator accepts a Widget? for the prefixIcon, however, it does not work with a Container(). That's strange.
If I replace your
prefixIcon: !showPrefixIcon ? Container() : Padding(...)
with
prefixIcon: !showPrefixIcon ? SizedBox(height: 0.0, width: 0.0) : Padding(...)
it works.
I honestly don't know why.
updated proposal:
Use:
prefixIcon: !showPrefixIcon ? null : Padding(...)
And I just assume that you could then reasonably argue that this is ugly. Then, I fear, you have to change the way of doing it to something like this:
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Container(
height: 70.0,
decoration: BoxDecoration(
border: Border.all(
color: Colors.blue,
),
borderRadius: const BorderRadius.all(Radius.circular(10))),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
AnimatedSwitcher(
duration: const Duration(milliseconds: 600),
transitionBuilder: (child, animation) => SizeTransition(
sizeFactor: animation,
axis: Axis.horizontal,
child: child),
child: showPrefixIcon
? const Padding(
padding: EdgeInsets.all(8.0),
child: Icon(Icons.abc,
color: Colors.blue, size: 46),
)
: Container()),
Expanded(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: messageInputController,
decoration: const InputDecoration(
border: InputBorder.none,
hintText: 'enter text',
),
onChanged: (value) {
messageInputChanged();
},
),
),
),
],
),
),
),
),
);
}
}

Related

Bottom overflowed by 11 pixels

I'm having bottom overflowed by pixels flutter when showing keyboard, i tried SingleChildSCrollView and still couldn't find the solution for it. my aim to make the Get.defaultDialog scrollable.
here my code :
class AddCard extends StatelessWidget {
final homeCtrl = Get.find<HomeController>();
AddCard({super.key});
#override
Widget build(BuildContext context) {
final icons = getIcons();
var squareWidth = Get.width - 12.0.wp;
return Container(
width: squareWidth / 2,
height: squareWidth / 2,
margin: EdgeInsets.all(3.0.wp),
child: InkWell(
onTap: () async {
await Get.defaultDialog(
titlePadding: EdgeInsets.symmetric(vertical: 5.0.wp),
radius: 5,
title: 'Task Type',
content: Form(
key: homeCtrl.formKey,
child: Column(
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 3.0.wp),
child: TextFormField(
controller: homeCtrl.editCtrl,
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'title',
),
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Please enter your task title';
}
return null;
},
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 5.0.wp),
child: Wrap(
spacing: 2.0.wp,
children: icons
.map((e) => Obx(() {
final index = icons.indexOf(e);
return ChoiceChip(
selectedColor: Colors.grey[200],
pressElevation: 0,
backgroundColor: Colors.white,
label: e,
selected: homeCtrl.chipIndex.value == index,
onSelected: (bool selected) {
homeCtrl.chipIndex.value =
selected ? index : 0;
},
);
}))
.toList(),
),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
minimumSize: const Size(150, 40),
),
onPressed: () {
if (homeCtrl.formKey.currentState!.validate()) {
int icon =
icons[homeCtrl.chipIndex.value].icon!.codePoint;
String color =
icons[homeCtrl.chipIndex.value].color!.toHex();
var task = Task(
title: homeCtrl.editCtrl.text,
icon: icon,
color: color,
);
}
},
child: const Text("Confirm"),
),
],
),
));
},
child: DottedBorder(
color: Colors.grey[400]!,
dashPattern: const [8, 4],
child: Center(
child: Icon(
Icons.add,
size: 10.0.wp,
color: Colors.grey,
),
)),
),
);
}
}
The widget that makes the error is the Get.defaultDialog().
There are two ways:
You can use the resizeToAvoidBottomInset property on the Scaffold widget.
You can use ListView instead Column:
onTap: () async {
await Get.defaultDialog(
radius: 5,
titlePadding: EdgeInsets.symmetric(vertical: 5.0),
title: Text('Task Type'),
content: SizedBox(
height: 500,//your height
width: 300, //your width
child:
Form(
child: ListView(
children: [
Padding(
padding: EdgeInsets.symmetric(horizontal: 3.0),
child: TextFormField(
decoration: const InputDecoration(
border: OutlineInputBorder(),
labelText: 'title',
),
),
),
Padding(
padding: EdgeInsets.symmetric(vertical: 5.0),
child: Wrap(
spacing: 2.0,
children: List.generate(//replace with your content
100,
(index) => Container(
height: 20,
width: 50,
padding: EdgeInsets.all(20),
color: Colors.red,
))),
),
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
minimumSize: const Size(150, 40),
),
onPressed: () {},
child: const Text("Confirm"),
),
],
),
),
),
),
);
It`s important to give your dialog a fixed height and width, in this defined area it's possible to make a scrollable widget work.
If your aim is to make the dialog scrollable, Use ListView with defined height.
Further for your SizedBox to work as expected in case of any overplexes use the Flexible widget
Try the code structure:
GetDialog
|_Flexible
|_SizedBox 👈Define proper height and width here
|_ListView
I can't really understand your question well because you only posted part of the codes, but try wrapping your Scaffold body with SingleChildScrollView.
maybe you're using the SingleChildScrollView at a wrong place.

Flutter: Create custom dialog as StatefulWidget?

I'm working on an app that will get only 2 inputs from the user, maybe 3 in the future. So I use showDialog with 2 inputfield and 2 buttons. Very simple. Looks pretty much like this:
enter image description here
It shows up perfectly in the main screen if it's being called as a function. But I would like to use that same dialog in another screen in the app and just change the text on one of the buttons. Since the dialog code it's long and I don't want to repeat it, I want to put it in a new StatefulWidget (to retrieve the information from the input fields and clear them afterwards). Now the problem is that I can't find a way to return the dialog from an external widget. I don't know if it's actually impossible because of the whole async Widget incompatibility or I'm just too stupid to figure this out.
This is the code for my dialog (as a function):
_dialog() async {
await showDialog<String>(
context: context,
child: ButtonBarTheme(
data: ButtonBarThemeData(alignment: MainAxisAlignment.center),
child: AlertDialog(
contentPadding: const EdgeInsets.all(20.0),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: EdgeInsets.only(bottom: 15),
child: new TextField(
// onSubmitted: next,
autofocus: true,
controller: _title,
decoration: InputDecoration(
contentPadding: EdgeInsets.only(bottom: 2),
labelText: 'Title',
hintText: 'Example: New Smartphone',
),
),
),
TextField(
autofocus: true,
controller: _codigo,
decoration: InputDecoration(
contentPadding: EdgeInsets.zero,
labelText: 'Code',
hintText: 'Example: EC20008347607',
),
),
],
),
actions: [
Row(
children: [
Container(
padding: EdgeInsets.only(right: 35, bottom: 5),
child: FlatButton(
color: Colors.cyan[600],
child: const Text('CANCEL'),
onPressed: () {
Navigator.pop(context);
}),
),
Container(
padding: EdgeInsets.only(left: 15, bottom: 5),
child: FlatButton(
color: Colors.cyan[600],
child: const Text('ADD'),
onPressed: () {
setState(() {
_title.text = '';
_code.text = '';
});
Navigator.pop(context);
},
),
),
],
),
],
),
),
);
}
Thanks in advance for any help. I'm a beginner.
You can put it in another dart file that accepts any property values that aren't static, like this:
CustomAlertDialog({String leftButtonText, String rightButtonText}) async {
return AlertDialog(
contentPadding: const EdgeInsets.all(20.0),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
padding: EdgeInsets.only(bottom: 15),
child: new TextField(
// onSubmitted: next,
autofocus: true,
controller: _title,
decoration: InputDecoration(
contentPadding: EdgeInsets.only(bottom: 2),
labelText: 'Title',
hintText: 'Example: New Smartphone',
),
),
),
TextField(
autofocus: true,
controller: _codigo,
decoration: InputDecoration(
contentPadding: EdgeInsets.zero,
labelText: 'Code',
hintText: 'Example: EC20008347607',
),
),
],
),
actions: [
Row(
children: [
Container(
padding: EdgeInsets.only(right: 35, bottom: 5),
child: FlatButton(
color: Colors.cyan[600],
child: const Text('$leftButtonText'),
onPressed: () {
Navigator.pop(context);
}),
),
Container(
padding: EdgeInsets.only(left: 15, bottom: 5),
child: FlatButton(
color: Colors.cyan[600],
child: const Text('$rightButtonText'),
onPressed: () {
setState(() {
_title.text = '';
_code.text = '';
});
Navigator.pop(context);
},
),
),
],
),
],
),
),
);
}
I think we have to build our widget from the ground up as there is no dialog box that exposes the state. Hope this helps:
https://api.flutter.dev/flutter/widgets/StatefulBuilder-class.html
Try my code snippet
**
MaterialButton(
color: Colors.blue,
height: 50,
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(10)),
),
textColor: Colors.white,
child: Text('Show Info Dialog',),
onPressed: () {
SimpleDialogs.showinfoDialog(context: context, title: "Something insert here");
},

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.

Validator error message changes TextFormField's height

When the error message shows up, it reduces the height of the TextFormField. If I understood correctly, that's because the height of the error message is taking into account in the height specified.
Here's a screen before :
and after :
Tried to put conterText: ' ' to the BoxDecoration (as I've seen on another topic) but it didn't help.
An idea ?
EDIT : OMG completly forgot to put the code, here it is :
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
height: 40.0,
child: _createTextFormField(loginEmailController, Icons.alternate_email, "Email Adress", false, TextInputType.emailAddress),
),
Container(
height: 40.0,
child: _createTextFormField(loginPasswordController, Icons.lock, "Password", true, TextInputType.text),
),
SizedBox(
width: double.infinity,
child: loginButton
)
],
),
);
}
Widget _createTextFormField(TextEditingController controller, IconData icon, String hintText, bool obscureText, TextInputType inputType){
return TextFormField(
keyboardType: inputType,
controller: controller,
obscureText: obscureText,
/* style: TextStyle(
fontSize: 15.0,
), */
decoration: InputDecoration(
/* contentPadding:
EdgeInsets.symmetric(vertical: 5.0, horizontal: 8.0), */
border: OutlineInputBorder(borderRadius: BorderRadius.circular(5.0)),
icon: Icon(
icon,
color: Colors.black,
size: 22.0,
),
//hintText: hintText,
labelText: hintText,
),
validator: (value) {
if (value.isEmpty) {
return 'Enter some text';
}
return null;
},
);
}
In your Code - you need to comment out the 40 height given to each container.
Container(
// height: 40.0,
child: _createTextFormField(
loginEmailController,
Icons.alternate_email,
"Email Adress",
false,
TextInputType.emailAddress),
),
Container(
// height: 40.0,
child: _createTextFormField(loginPasswordController, Icons.lock,
"Password", true, TextInputType.text),
),
and then in your - TextFormField in InputDecoration, you can alter these value as per your liking.
contentPadding:
EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
Above solutions did not work for me however I have figured out a very simple solution to avoid the above issue
TextFormField(
decoration: InputDecoration(
**errorStyle: const TextStyle(fontSize: 0.01),**
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(borderRadius),
borderSide: const BorderSide(
color: AppColor.neonRed,
width: LayoutConstants.dimen_1,
style: BorderStyle.solid,
),
),
);
Catch in the above solution is that we are setting the size of the error message to 0.01 so as a result it don't show up.
Additionally we can have custom border for the error.
Note : Setting the Text size to 0 is not working as it don't consider the text size and textFormField widget gets shrinked.
The problem is that we are not able to see your code so it might be challenging to assist you but I will do everything from scratch. You can firstly create the authentication class in one dart file
class AuthBloc{
StreamController _passController = new StreamController();
Stream get passStream => _passController.stream;
bool isValid(String pass){
_passController.sink.add("");
if(pass == null || pass.length < 6){
_passController.sink.addError("Password is too short");
return false;
}
else{
return true;
}
}
void dispose(){
_passController.close();
}
}
And then insert the following code in another dart file...
class LoginPage extends StatefulWidget{
#override
_LoginPageState createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage>{
AuthBloc authBloc = new AuthBloc();
#override
void dispose(){
authBloc.dispose();
}
#override
Widget build(BuildContext context){
return Scaffold(
body: Container(
padding: EdgeInsets.fromLTRB(30, 0, 30, 0),
constraints: BoxConstraints.expand(),
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(0, 40, 0, 20),
child: StreamBuilder(
stream: authBloc.passStream,
builder: (context, snapshot) => TextField(
controller: _passController,
style: TextStyle(fontSize: 18, color: Colors.black),
decoration: InputDecoration(
errorText: snapshot.hasError ? snapshot.error:null,
labelText: "Password",
prefixIcon: Container(
width: 50,
child: Icon(Icons.lock),
),
border: OutlineInputBorder(
borderSide: BorderSide(color: Color(0xffCED802), width: 1),
borderRadius: BorderRadius.all(Radius.circular(6))
)
),
),
)
),
Padding(
padding: const EdgeInsets.fromLTRB(0, 30, 0, 40),
child: SizedBox(
width: double.infinity,
height: 52,
child: RaisedButton(
onPressed: _onLoginClicked,
child: Text(
"Login",
style: TextStyle(fontSize: 18, color: Colors.white),
),
color: Color(0xff327708),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(6))
),
),
),
),
]
)
)
}
_onLoginClicked(){
var isValid = authBloc.isValid(_passController.text);
if(isValid){
//insert your action
}
}
}
I hope it works :)
Instead of using a fixed height container to wrap the textFormField, You can try to put a space in the helper text so it will keep the height of the field constant while only displaying when there is an error.
return TextFormField(
// ...
decoration: InputDecoration(
// ...
helperText: " ",
helperStyle: <Your errorStyle>,
)
According to Flutter Doc :
To create a field whose height is fixed regardless of whether or not an error is displayed, either wrap the TextFormField in a fixed height parent like SizedBox, or set the InputDecoration.helperText parameter to a space.
The problem with content padding is that you cant decrease the size of the field to UI requirement with an emphasize on decrease but how ever the second answer helped me come with a solution for my perticular problem, so am sharing that
StreamBuilder(
stream: viewModel.outEmailError,
builder: (context, snap) {
return Container(
width: MediaQuery.of(context).size.width*.7,
height: (snap.hasData)?55:35,
child: AccountTextFormField(
"E-mail",
textInputType: TextInputType.emailAddress,
focusNode: viewModel.emailFocus,
controller: viewModel.emailController,
errorText: snap.data,
textCapitalization: TextCapitalization.none,
onFieldSubmitted: (_) {
nextFocus(viewModel.emailFocus,
viewModel.passwordFocus, context);
},
),
);
}),

Row with multiple TextFields and a DropDown where one TextField should be be bigger, and all should have same height

I have a Row that has 3 fields in it: 2 TextFields, 1 DropdownButtonHideUnderline wrapped in a Container. I'm trying to ensure that the first TextField takes up about 50-60% and the other two fields share the remaining space. I also want the fields to have the same height. So, something like this:
This is the code I have:
#override
Widget build(BuildContext context) {
return Container(
color: Theme.of(context).accentColor,
child: Padding(
padding: const EdgeInsets.fromLTRB(5.0, 10.0, 5.0, 10.0),
child: Row(children: <Widget>[
Expanded(
child: Container(
padding: EdgeInsets.only(right: 5.0),
child: TypeAheadField(
textFieldConfiguration: TextFieldConfiguration(
autofocus: true,
controller: widget.ingredientController,
style: DefaultTextStyle.of(context)
.style
.copyWith(fontStyle: FontStyle.italic),
decoration: InputDecoration(
border: InputBorder.none,
filled: true,
fillColor: Colors.white.withOpacity(1),
hintText: 'Ingredient',
suffixIcon: GestureDetector(
onTap: widget.addFunction,
child: Icon(
Icons.add,
color: Colors.grey,
)))),
suggestionsCallback: (pattern) async {
return await _findIngredients(pattern);
},
//If not items are found, return an empty container.
noItemsFoundBuilder: (context) {
return Container(height: 0, width: 0);
},
itemBuilder: (context, suggestion) {
return ListTile(
title: Text(suggestion.name),
);
},
onSuggestionSelected: (Ingredient suggestion) {
widget.ingredientController.text = suggestion.name;
},
))),
Expanded(
child: TextField(
maxLines: 1,
controller: widget.quantityController,
keyboardType: TextInputType.text,
autofocus: false,
decoration: InputDecoration(
border: InputBorder.none,
filled: true,
fillColor: Colors.white.withOpacity(1),
hintText: 'Qty',
))),
Expanded(flex: 1, child: UnitDropdown()),
])));
}
What I'm left with is this:
I've tried setting the flex factor on the Expanded to different things, but that just results in an overflow on the right side. I've also not found a way to force all of the widgets to have the same height.
Try this one,
Row(children : <Widget>[
Expanded(
Container(child: TextField1())
),
Expanded(
Row(children : <Widget>[
Expanded(child: TextFiled2()),
Expanded(child: DropDown())
]);
)
]);
Or else, You can use MediaQuery to get the exact size of the screen.
Example:
Width : MediaQuery.of(context).size.width * 0.5;
Width : MediaQuery.of(context).size.width * 0.25;
Width : MediaQuery.of(context).size.width * 0.25;
Here you have, the key is to encapsulate the TextField, Dropdown, or whatever component you have in a Container, and define the actual size from the Container, tweaking the predefined internal Paddings of each widget (if you have the chance, sometimes is not available).
double itemsHeight = 30;
Widget getTextField({String hint = 'Ingredients', Widget suffix}) {
// use Container to define the size of the child,
// and reset the original inner paddings!
return Container(
height: itemsHeight,
child: TextField(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.zero,
borderSide: BorderSide(color: Colors.white, width: 1)),
hintText: hint,
contentPadding: EdgeInsets.all(
0), // change each value, and set 0 remainding ones.
suffixIcon: suffix,
),
expands: false,
maxLines: 1,
controller: TextEditingController(),
),
);
}
return Scaffold(
body: Container(
color: Colors.green.withOpacity(.2),
margin: EdgeInsets.symmetric(vertical: 50, horizontal: 20),
child: Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Flexible(
flex: 2,
child: getTextField(
hint: 'Ingredients',
suffix: Icon(
Icons.add,
size:
18, // option 1: reduce the size of the icon, and avoid the padding issues..
)),
),
Flexible(
flex: 1,
child: getTextField(
hint: 'Qty',
// option2: trick to match the expanded height of the icon on the previous field
// make an icon transparent :)
suffix: Icon(
Icons.account_box,
color: Colors.transparent,
)),
),
Flexible(
flex: 1,
child: Container(
// use this to match the Flex size..., is like using Expanded.
width: double.infinity,
// container defines the BoxConstrains of the children
decoration: BoxDecoration(
color: Colors.white24,
border: Border.all(color: Colors.white, width: 1),
),
height: itemsHeight,
child: DropdownButton(
hint: Text("Unit"),
onChanged: (i) {},
underline: Container(),
items: List.generate(5, (i) {
return DropdownMenuItem(child: Text("item $i"));
})),
),
),
],
),
),
);
screenshot of result: