Can't figure out how to properly style DropdownButton - flutter

I'm kinda new to Flutter and I'm building an app for a college project, but I'm having problems with this widget.
DropdownButton input value in white color
DropdownButton input value in black color
This is my DropdownButton code, it appears with the Hint in white color, but when I select an item the value in the button appears as black. If I change the DropdownButton color to white, then when the popup appears the background-color is white and so the font-color. This way I can't see the items, because they're the same color as the background.
class DropdownWidget extends StatelessWidget {
final IconData icon;
final IconData arrowIcon;
final String hint;
final List items;
final Stream stream;
final Function onChanged;
DropdownWidget({this.icon, this.arrowIcon, this.hint, this.items, this.stream, this.onChanged});
#override
Widget build(BuildContext context) {
return StreamBuilder(
stream: stream,
builder: (context, snapshot) {
print("Snapshot data -> ${snapshot.data}");
return InputDecorator(
child: DropdownButton(
icon: Icon( arrowIcon, color: Colors.white,),
hint: Text( hint, style: TextStyle(color: Colors.white),),
items: items.map((value) {
print("Valor do item $value");
return DropdownMenuItem(
value: value,
child: Text(value.runtimeType == int ? value.toString() : value, style: TextStyle(color: Colors.black),),
);
}).toList(),
onChanged: onChanged,
value: snapshot.data,
isExpanded: true,
style: TextStyle(
// color: Colors.black,
color: Theme.of(context).textSelectionColor,
fontSize: 18.0,
),
underline: Container(),
isDense: true,
),
decoration: InputDecoration(
icon: icon == null ? null : Icon(icon, color: Colors.white,),
hintText: hint,
hintStyle: TextStyle(color: Colors.white),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Theme.of(context).primaryColor)
),
contentPadding: EdgeInsets.only(
left: 5,
right: 0,
bottom: 24,
top: 30
),
errorText: snapshot.hasError ? snapshot.error : null,
),
);
}
);
}
}
What could I do to solve this? Is there a way to make the popup's background-color darker or just the value inside the button in a different color from the item's color?

You have to wrap your DropDownButton in a Theme. Example code:
Theme(
data: ThemeData(canvasColor: Colors.black), //this is where the magic happens
child: DropdownButton<String>(
value: dropDownValue,
onChanged: (String newValue) {
setState(() {
dropDownValue = newValue;
});
},

For those, who have also smashed against the brutal reality finding a way to add a dropdown with Flutter.
As mentioned by #asterisk12 adding canvasColor to the Theme is the way to change the background for the dropdown list.
My answer is for the rest of you still battling with OTHER styling issues
I am leaving here an example of how I managed to achieve (almost) what I needed:
list appears below the button
button is rectangular
there is a hint text
list is the same width as button
For it to work you will need a dropdown_button2 dependency (https://pub.dev/packages/dropdown_button2/install)
class DropDownButton extends StatefulWidget {
final List<String> options;
const DropDownButton({Key? key, required this.options}) : super(key: key);
#override
State<DropDownButton> createState() => _DropDownButtonState();
}
class _DropDownButtonState extends State<DropDownButton> {
String? selectedValue;
#override
Widget build(BuildContext context) {
return SizedBox(
height: 70,
child: Container(
margin: const EdgeInsets.fromLTRB(2, 5, 2, 5),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(3),
),
child: DropdownButtonHideUnderline(
child: DropdownButton2<String>(
dropdownElevation: 0,
hint: const Text(
'Select Item',
),
icon: const Icon(
Icons.arrow_downward,
),
iconSize: 30,
isExpanded: true,
iconEnabledColor: Colors.teal,
buttonPadding: EdgeInsets.all(12),
value: selectedValue,
dropdownMaxHeight: 150,
scrollbarAlwaysShow: true,
items: widget.options
.map((e) => DropdownMenuItem(value: e, child: Text(e)))
.toList(),
offset: const Offset(0, 3),
onChanged: (value) {
setState(() {
selectedValue = value;
});
}),
),
),
);
}
}
Details:
remove the shadow from the list: dropdownElevation: 0
add a custom icon to DropdownButton2:
icon: const Icon(
Icons.arrow_downward,),
iconSize: 30,
Making the list scrollbar(you see it only when all elements do not fit in the dropdown, you can make dropdownMaxHeight smaller to see the difference).
scrollbarAlwaysShow: true,
Last but not least change the position of the list:
you can go wild :D and make some weird position
offset: const Offset(-20, -3),
or you can go not that wild and keep it as in my example so, that there is no space between button and the list
offset: const Offset(0, 3),
Hope I could help someone as desperate as I have recently been and save a bit of time for you.

Related

Flutter dropdown with getX not working properly on some devices

I'm using getx for state management,here's the video of the problem
https://drive.google.com/file/d/1tm2M46pkXVnGuf4vyh9rNs2HY2TdtBD8/view?usp=sharing
here is my code
class ActivitiesController extends GetxController {
late List<String> statusList = ["All", "Approved", "Unapproved"];
var selectedStatus = "Approved".obs;
}
#override
ActivitiesController get controller => Get.put(ActivitiesController());
in view:
const RequiredText(text: "Status"),
const SizedBox(height: Constants.defaultPadding / 2),
Obx(
() => GlobalDropDownContainer(
hintText: "All",
items: controller.statusList.toList(),
onChange: (value) {
controller.selectedStatus(value);
},
selectedValue: controller.selectedStatus.value,
)),
Here is the "GlobalDropDownContainer" code
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class GlobalDropDownContainer extends StatelessWidget {
final String hintText;
final List<String> items;
final double? width;
final Color? isNotValid;
final Function(String?) onChange;
final String? selectedValue;
const GlobalDropDownContainer({
Key? key,
required this.hintText,
required this.items,
this.width,
this.isNotValid,
required this.onChange,
this.selectedValue,
}) : super(key: key);
#override
Widget build(BuildContext context) {
Size size = Get.size;
return Container(
width: width != null ? (size.width * width!) : Get.width,
padding: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
border: Border.all(
width: 1,
color: isNotValid ??
Theme.of(context).colorScheme.onSurface.withOpacity(0.5),
),
color:Theme.of(context).colorScheme.background,
borderRadius: BorderRadius.circular(5)),
child: DropdownButton<String>(
dropdownColor: Theme.of(context).colorScheme.background,
value: selectedValue != null && selectedValue!.isNotEmpty
? selectedValue
: null,
isExpanded: true,
underline: const SizedBox(),
hint: Text(
hintText,
style: const TextStyle(
color: Color(0xFF666666),
),
),
style: TextStyle(
color: Theme.of(context).colorScheme.onBackground,
),
items: items.map((String value) {
return DropdownMenuItem<String>(
value: value != null && value.isNotEmpty ? value : null,
child: Text(
value,
style: TextStyle(
color: Theme.of(context).colorScheme.onBackground),
),
);
}).toList(),
onChanged: onChange,
));
}
}
I searched for a day and i didn't find anything, i tried debug the code but it gives no warning or error. Can Anyone help me?
Try this one
Make an instance of a controller, which i presumed u have done already.
final controller = Get.put(yourgetxcontrollername());
The Widget code
Obx(
() => DropdownButton<String>(
isExpanded: true,
value: controller.selectedStatus.value,
icon: const Icon(Icons.arrow_drop_down),
iconSize: 24,
elevation: 16,
style: const TextStyle(
color: Colors.blue,
fontSize: 14,
),
onChanged: (value) {
controller.selectedStatus(
value,
);
},
items: controller.statusList.map<DropdownMenuItem<String>>(
(String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: const TextStyle(
color: Colors.black,
),
),
);
},
).toList(),
),
)

How to control text overflow in Flutter when using a Text widget in a dropdown?

I'm using the next piece of code to render an item for a Dropdown component on Flutter web:
Widget _getColorInfo(int color, String text) {
return Row(
children: [
Container(
width: 10,
height: 10,
decoration: BoxDecoration(
color: Color(color),
borderRadius: BorderRadius.circular(5),
),
),
const SizedBox(width: 5),
Text(
text,
style: TextStyle(
color: Color(color),
overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.bold,
fontSize: ShapeStyle.standardFontSize,
),
),
],
);
}
Now the problem is that if I do not use Flex or Expanded the widget will not recognize the overflow:
Overflow on the selected item
And the dropdown is working:
Overflow on the expanded panel
Now, if I use Flex the overflow is now recognized by the main component:
Flexible(
child: Text(
text,
style: TextStyle(
color: Color(color),
overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.bold,
fontSize: ShapeStyle.standardFontSize,
),
),
),
Overlfow handled on the selected item
But now the dropdown is not working since it detects the 'size' property is missing:
Error on the expanded panel of the dropdown
Here is the error:
Image of the error
The problem is that I can't use the size of the screen to determine how much width is the component taking since all the other components are being expanded dynamically, so it will break the UI symmetry. Is there any other way to control the overflow when using a dropdown or something to differentiate the dropdown panel from the selected item so I can apply different strategies like using a fixed width for the dropdown panel and a Flex for the selected item?
I have already tried using Flex, Expanded, try catch, Wrap, ConstrainedBox, third party packages... nothing solved the problem.
UPDATE
Here is the full code of the component:
class ColorSelect extends StatelessWidget {
final AgendaFormHandler form;
final int? defaultColor;
final Map<String, int> elements = {
'Team 1': 0xFFFEC000,
'Team 2': 0xFF65CBFD,
'Team 3': 0xFFF2B085,
'Team 4': 0xFF01AE51,
'USO': 0xFF3764F7,
'Financiero': 0xFF702FA0,
'Procesos': 0xFF8FA9DA,
'TI': 0xFFFF7C81,
'Alterno 1': 0xFF00F3EB,
'Alterno 2': 0xFFA46B6B,
'Alterno 3': 0xFFDCBFB2,
'Alterno 4': 0xFFEF5CEF,
'Alterno 5': 0xFFC5D89F,
};
ColorSelect({super.key, required this.form, this.defaultColor});
#override
Widget build(BuildContext context) {
return SimpleSelectInput<int>(
title: 'Color',
customEmptyText: 'Ninguno',
activeOption: form.getValue<int?>('color') ?? defaultColor ?? 0xFFFEC000,
onOptionSelected: (color) {
form.updateValue('color', color);
},
options: List<SelectItem<int>>.from(elements.keys.map((key) {
return SelectItem(
value: elements[key]!,
text: key,
label: _getColorInfo(elements[key]!, key),
);
})),
);
}
Widget _getColorInfo(int color, String text) {
return Row(
children: [
Container(
width: 10,
height: 10,
decoration: BoxDecoration(
color: Color(color),
borderRadius: BorderRadius.circular(5),
),
),
const SizedBox(width: 5),
Flexible(
fit: FlexFit.tight,
child: Text(
text,
style: TextStyle(
color: Color(color),
overflow: TextOverflow.ellipsis,
fontWeight: FontWeight.bold,
fontSize: ShapeStyle.standardFontSize,
),
),
),
],
);
}
}
Here is the code of the SimpleSelectInput
/// A class to create a new option entry for the [SimpleSelectInput]
/// component with [T] as the datatype for the [value] and [String]
/// for the [label]
class SelectItem<T> {
final T value;
/// This text is only to be able to compare the content of the
/// input when applying any search filter. So, the same you put
/// in the [label] property is the one you should put here.
final String text;
final Widget? label;
SelectItem({
required this.value,
this.label,
required this.text,
});
}
/// A simple input with a bottom border and a blue title with
/// [T] as the value datatype for the dropdown options.
class SimpleSelectInput<T> extends StatelessWidget {
/// The options to be displayed in the dropdown
final List<SelectItem<T>> options;
/// The item to be rendered as the default selected item
final T activeOption;
final Function(T?) onOptionSelected;
final String Function(T?)? validator;
final String? title;
/// The text to be displayed when no option is selected
final String? customEmptyText;
final bool shouldDecorate;
final bool showSearchBox;
const SimpleSelectInput({
super.key,
required this.options,
required this.activeOption,
required this.onOptionSelected,
this.title,
this.validator,
this.shouldDecorate = true,
this.showSearchBox = false,
this.customEmptyText = '-- Seleccione --',
});
#override
Widget build(BuildContext context) {
if (activeOption == null) {
return Container();
}
// If there is no opportunity it will raise an error.
// TODO: check if it's ok to raise the BadState error.
late final SelectItem<T>? selectedOption;
try {
selectedOption = options.firstWhere(
(SelectItem<T> option) {
return option.value == activeOption;
},
);
} catch (_) {
selectedOption = null;
}
return DropdownSearch<SelectItem<T>>(
items: options,
selectedItem: selectedOption,
filterFn: (item, filter) {
return item.text.toLowerCase().contains(filter.toLowerCase());
},
dropdownButtonProps: const DropdownButtonProps(
focusColor: ColorStyle.blue,
color: Colors.black,
icon: Icon(
Icons.arrow_drop_down,
size: ShapeStyle.standardFontSize * 1.4,
),
),
dropdownBuilder: (context, value) {
return value?.label ??
Text(
customEmptyText ?? '-- Seleccione --',
style: const TextStyle(
fontSize: ShapeStyle.standardFontSize,
),
);
},
validator: (selected) {
return validator?.call(selected?.value);
},
popupProps: PopupPropsMultiSelection.menu(
showSearchBox: showSearchBox,
showSelectedItems: true,
itemBuilder: _itemBuilder,
searchFieldProps: const TextFieldProps(
scrollPadding: EdgeInsets.zero,
style: TextStyle(
color: ColorStyle.blue,
fontSize: ShapeStyle.standardFontSize,
),
),
),
compareFn: (i, s) => i.value == s.value,
dropdownDecoratorProps: DropDownDecoratorProps(
dropdownSearchDecoration: InputDecoration(
fillColor: Colors.black,
border: shouldDecorate ? null : InputBorder.none,
labelStyle: const TextStyle(
color: ColorStyle.blue,
fontSize: ShapeStyle.standardFontSize * 1.4,
),
label: title != null
? Text(
title!,
style: const TextStyle(
fontSize: ShapeStyle.standardFontSize * 1.4,
),
)
: null,
),
),
onChanged: (x) => onOptionSelected(x?.value),
);
}
Widget _itemBuilder(
BuildContext context, SelectItem<T> item, bool isSelected) {
final size = MediaQuery.of(context).size;
return Container(
width: size.width,
padding: const EdgeInsets.symmetric(
vertical: 12.0,
horizontal: 8.0,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
item.label ??
Text(
item.text,
style: const TextStyle(
color: Colors.black,
fontSize: ShapeStyle.standardFontSize,
),
),
// a check icon
if (isSelected)
const Icon(
Icons.check,
color: Colors.green,
size: ShapeStyle.standardIconSize,
),
],
),
);
}
}

Deleting specific item out of ListView with Bloc

I have a page that consists of a ListView, which contains TextFormFields. The user can add or remove items from that ListView.
I use the bloc pattern, and bind the number of Items and their content inside the ListView to a list saved in the bloc state. When I want to remove the items, I remove the corresponding text from this list and yield the new state. However, this will always remove the last item, instead of the item that's supposed to be removed. While debugging, I can clearly see that the Item I want removed is in fact removed from the state's list. Still, the ListView removes the last item instead.
I've read that using keys solves this problem and it does. However, if I use keys there is a new problem.
Now, the TextFormField will go out of focus every time a character is written. I guess this is to do with the fact that the ListView is redrawing its items everytime a character is typed, and somehow having a key makes the focus behave differently.
Any ideas how to solve this?
The page code (The ListView is at the bottom):
class GiveBeneftis extends StatelessWidget {
#override
Widget build(BuildContext context) {
var bloc = BlocProvider.of<CreateChallengeBloc>(context);
return BlocBuilder<CreateChallengeBloc, CreateChallengeState>(
builder: (context, state) {
return CreatePageTemplate(
progress: state.progressOfCreation,
buttonBar: NavigationButtons(
onPressPrevious: () {
bloc.add(ProgressOfCreationChanged(nav_direction: -1));
Navigator.of(context).pop();
},
onPressNext: () {
bloc.add(ProgressOfCreationChanged(nav_direction: 1));
Navigator.of(context).pushNamed("create_challenge/add_pictures");
},
previous: 'Details',
next: 'Picture',
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Text(
'List the benefits of you Challenge',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 28, fontWeight: FontWeight.bold),
),
SizedBox(height: 30),
Text(
'Optionally: Make a list of physical and mental benefits the participants can expect. ',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.grey,
fontSize: 14,
fontWeight: FontWeight.w400),
),
SizedBox(height: 50),
Container(
margin: EdgeInsets.all(8.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: Colors.yellow[600]),
child: FlatButton(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
onPressed: () => bloc.add(ChallengeBenefitAdded()),
child: Text('Add a benefit',
style: TextStyle(
color: Colors.white, fontWeight: FontWeight.bold)),
),
),
Expanded(
child: new ListView.builder(
itemCount: state.benefits.length,
itemBuilder: (BuildContext context, int i) {
final item = state.benefits[i];
return Padding(
padding: EdgeInsets.symmetric(horizontal: 25),
child: TextFieldTile(
//key: UniqueKey(),
labelText: 'Benefit ${i + 1}',
validator: null,
initialText: state.benefits[i],
onTextChanged: (value) => bloc.add(
ChallengeBenefitChanged(
number: i, text: value)),
onCancelIconClicked: () {
bloc.add(ChallengeBenefitRemoved(number: i));
},
));
})),
],
),
);
});
}
}
The Code of the TextfieldTile:
class TextFieldTile extends StatelessWidget {
final Function onTextChanged;
final Function onCancelIconClicked;
final Function validator;
final String labelText;
final String initialText;
const TextFieldTile(
{Key key,
this.onTextChanged,
this.onCancelIconClicked,
this.labelText,
this.initialText,
this.validator})
: super(key: key);
#override
Widget build(BuildContext context) {
return Stack(children: <Widget>[
TextFormField(
textCapitalization: TextCapitalization.sentences,
initialValue: initialText,
validator: validator,
onChanged: onTextChanged,
maxLines: null,
decoration: InputDecoration(
labelText: labelText,
)),
Align(
alignment: Alignment.topRight,
child: IconButton(
icon: Icon(Icons.cancel), onPressed: onCancelIconClicked),
),
]);
}
}
The relevant portion of the Bloc:
if (event is ChallengeBenefitAdded) {
var newBenefitsList = List<String>.from(state.benefits);
newBenefitsList.add("");
yield state.copyWith(benefits: newBenefitsList);
}
else if (event is ChallengeBenefitChanged) {
var newBenefitsList = List<String>.from(state.benefits);
newBenefitsList[event.number] = event.text;
yield state.copyWith(benefits: newBenefitsList);
}
else if (event is ChallengeBenefitRemoved) {
var newBenefitsList = List<String>.from(state.benefits);
newBenefitsList.removeAt(event.number);
yield state.copyWith(benefits: newBenefitsList);
}
I can think of two things you can do here.
Create a different bloc for processing the changes in the text field, that will avoid having to actually update the state of the entire list if no needed.
Have a conditional to avoid rebuilding the list when your bloc change to a state that is relevant only to the keyboard actions.
Example:
BlocBuilder<CreateChallengeBloc, CreateChallengeState>(
buildWhen: (previousState, currentState) {
return (currentState is YourNonKeyboardStates);
}
...
);

How to set dynamic height for dropdown popup in flutter

I am new to flutter development. I am using the dropdown button of my application. When opening the drop-down menu, the text is getting cut in the popup dialog. Below I attached a screenshot with coding. Please guide me in fixing this issue.
DropdownButtonHideUnderline(
child: new DropdownButton(
isExpanded: true,
value: dropDownValue,
isDense: true,
//icon: Icon(Icons.keyboard_arrow_down, color: Colors.white,),
onChanged: (String newValue) {
setState(() {
dropDownValue = newValue;
state.didChange(newValue);
});
},
items: dropDownList.map((String value) {
return new DropdownMenuItem(
value: value,
child: new SizedBox(
width: MediaQuery.of(context).size.width / 1.4,
child: new Text(value,
softWrap: true,
style: TextStyle(color: Colors.white, fontSize: 18.0),),)
);
}).toList(),
),
),
);
Copying the DropdownMenuItem class as someone else suggested will not be enough as DropdownButton requires items to be of type List<DropdownMenuItem<T>>.
I have created the following widget which should help with your issue:
import 'package:flutter/material.dart';
/// Looks like a DropdownButton but has a few differences:
///
/// 1. Can be opened by a single tap even if the keyboard is showing (this might be a bug of the DropdownButton)
///
/// 2. The width of the overlay can be different than the width of the child
///
/// 3. The current selection is highlighted in the overlay
class CustomDropdown<T> extends PopupMenuButton<T> {
CustomDropdown({
Key key,
#required PopupMenuItemBuilder<T> itemBuilder,
#required T selectedValue,
PopupMenuItemSelected<T> onSelected,
PopupMenuCanceled onCanceled,
String tooltip,
double elevation = 8.0,
EdgeInsetsGeometry padding = const EdgeInsets.all(8.0),
Icon icon,
Offset offset = Offset.zero,
Widget child,
String placeholder = "Please select",
}) : super(
key: key,
itemBuilder: itemBuilder,
initialValue: selectedValue,
onSelected: onSelected,
onCanceled: onCanceled,
tooltip: tooltip,
elevation: elevation,
padding: padding,
icon: icon,
offset: offset,
child: child == null ? null : Stack(
children: <Widget>[
Builder(
builder: (BuildContext context) => Container(
height: 48,
alignment: AlignmentDirectional.centerStart,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
DefaultTextStyle(
style: selectedValue!= null ? Theme.of(context).textTheme.subhead
: Theme.of(context).textTheme.subhead.copyWith(color:
Theme.of(context).hintColor),
child: Expanded(child: selectedValue== null ? Text(placeholder) : child),
),
IconTheme(
data: IconThemeData(
color: Theme.of(context).brightness == Brightness.light
? Colors.grey.shade700 : Colors.white70,
),
child: const Icon(Icons.arrow_drop_down),
),
],
),
),
),
Positioned(
left: 0.0,
right: 0.0,
bottom: 8,
child: Container(
height: 1,
decoration: const BoxDecoration(
border: Border(bottom: BorderSide(color: Color(0xFFBDBDBD), width: 0.0)),
),
),
),
],
),
);
}
It actually extends PopupMenuButton as you can see, but I've made it look the same as the DropdownButton.
itemBuilder needs to return List<PopupMenuEntry<T>>, with each entry usually being a PopupMenuItem to which you can provide any child widget.
selectedValue is the currently selected value, which will be highlighted in the overlay. If it is null, a Text widget with the placeholder string is shown. If it is not null, the child widget is shown.
You should be able to disable the highlight by modifying this class to either call super() with an initialValue of null, or even better add a boolean to the constructor to control this from the outside.
The height of DropdownMenuItem is hardcoded to _kMenuItemHeight:
https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/material/dropdown.dart#L486
The only thing you can do is copy this entire file and adjust to your needs.

how to make clear button appears when text is enter in TextFormField in flutter

I have a form where i want the clear button to appear on the right side of the textfield only when user start entering data and disappear if user delete all the data he input in the textfield. currently, i was able to add the clear button but it is there always.
see my code below
this is the code for my textiput
import 'package:flutter/material.dart';
import 'package:finsec/utils/hex_color.dart';
class CustomTextField extends StatelessWidget {
CustomTextField({
this.textInputType,
this.textController ,
this.errorMessage,
this.labelText,
});
TextInputType textInputType;
TextEditingController textController;
String errorMessage, labelText;
#override
Widget build(BuildContext context) {
bool isError = false;
return Container(
child: TextFormField(
keyboardType: textInputType,
style: Theme
.of(context)
.textTheme
.title,
controller: textController,
validator: (String value) {
if (value.isEmpty) {
return errorMessage;
}
},
decoration: InputDecoration(
suffixIcon: IconButton(
onPressed: (){
textController.clear();
},
icon: Icon(
Icons.clear,
color: Colors.grey,
),
),
labelStyle: TextStyle(
color: Colors.grey,
fontSize: 16.0
),
contentPadding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0), //size of textfield
errorStyle: TextStyle(
color: Colors.red,
fontSize: 15.0
),
border: OutlineInputBorder(
borderSide: BorderSide(width:5.0),
borderRadius: BorderRadius.circular(5.0)
)
)
),
);
}
}
here is my code for the form
import 'package:flutter/material.dart';
import 'package:finsec/widget/row_text_input.dart';
import 'package:finsec/widget/text_form_field.dart';
import 'package:finsec/widget/save_button.dart';
import 'package:finsec/utils/strings.dart';
import 'package:finsec/utils/dimens.dart';
import 'package:finsec/utils/colors.dart';
import 'package:finsec/widget/column_text_input.dart';
void main() {
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Simple Interest Calculator App',
home: ThirdFragment(),
theme: ThemeData(
brightness: Brightness.dark,
primaryColor: Colors.indigo,
accentColor: Colors.indigoAccent),
));
}
class ThirdFragment extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return _ThirdFragmentState();
}
}
class _ThirdFragmentState extends State<ThirdFragment> {
var _formKey = GlobalKey<FormState>();
var _currencies = ['Rupees', 'Dollars', 'Pounds'];
final double _minimumPadding = 5.0;
var _currentItemSelected = '';
#override
void initState() {
super.initState();
_currentItemSelected = _currencies[0];
// principalController.addListener(onChange);
}
TextEditingController amountController = TextEditingController();
TextEditingController frequencyController = TextEditingController();
TextEditingController datePaidController = TextEditingController();
TextEditingController categoryController = TextEditingController();
TextEditingController depositToController = TextEditingController();
TextEditingController descriptionController = TextEditingController();
var displayResult = '';
#override
Widget build(BuildContext context) {
TextStyle textStyle = Theme.of(context).textTheme.title;
return Scaffold(
appBar: AppBar(
title: Text('Simple Interest Calculator'),
),
body: Form(
key: _formKey,
onChanged: ,
child: SingleChildScrollView(
child: Column (children: [
Padding(
padding: EdgeInsets.only(top: 10.0, bottom: 5.0, left: 15.0, right: 15.0),
child: CustomTextField(textInputType:TextInputType.number,
textController: amountController,
errorMessage:'Enter Income Amount',
labelText:'Income Amount for testing'),
),
RowTextInput(inputName: 'Frequency:',
textInputType: TextInputType.number,
textController: frequencyController,
errorMessage: 'Choose Income Frequency',
labelText: 'Income Amount for testing'
),
RowTextInput(inputName: 'Date Paid:',
textInputType: TextInputType.number,
textController: datePaidController,
errorMessage: 'Pick Income Payment Date',
labelText: 'Income Amount for testing'
),
RowTextInput(inputName: 'Category:',
textInputType: TextInputType.number,
textController: categoryController,
errorMessage: 'Enter Income Category',
labelText: 'Income Amount for testing'
),
RowTextInput(inputName: 'Deposit To:',
textInputType: TextInputType.number,
textController: depositToController,
errorMessage: 'Choose Bank Acct Where Income Is Deposited',
labelText: 'Income Amount for testing'
),
RowTextInput(inputName: 'Description:',
textInputType: TextInputType.number,
textController: descriptionController,
errorMessage: 'Please enter principal amount',
labelText: 'Income Amount for testing'
),
SizedBox(height: 20),
//saveButton()
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
MaterialButton(
height: margin_40dp,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(margin_5dp)),
minWidth: (MediaQuery.of(context).size.width * .9) / 2,
color: Theme.of(context).primaryColor,
textColor: white,
child: new Text(save),
onPressed: () => {
setState(() {
if (_formKey.currentState.validate()) {
// amountController.text.isEmpty ? amountController.text='Value require' : amountController.text='';
//this.displayResult = _calculateTotalReturns();
}
})
},
splashColor: blueGrey,
),
MaterialButton(
height: margin_40dp,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(margin_5dp)),
minWidth: (MediaQuery.of(context).size.width * .9) / 2,
color: Theme.of(context).primaryColor,
textColor: white,
child: new Text(save_and_continue),
onPressed: () => {},
splashColor: blueGrey,
)
])
]
),
),
),
);
}
}
import 'package:flutter/material.dart';
import 'package:finsec/widget/text_form_field.dart';
class RowTextInput extends StatelessWidget {
RowTextInput({
this.inputName,
this.textInputType,
this.textController ,
this.errorMessage,
this.labelText,
// this.hint,
// this.height,
// this.padding,
// this.headerRadius,
});
TextInputType textInputType;
TextEditingController textController;
String inputName, errorMessage, labelText;
#override
Widget build(BuildContext context) {
return Padding(
padding: EdgeInsets.only(
top: 5.0, bottom: 5.0, left: 15.0, right: 15.0),
child: Row(children: [
Expanded(
child: Text(this.inputName, maxLines: 1,)
),
Expanded(
flex: 3,
child: CustomTextField(textInputType:TextInputType.number,
textController: this.textController,
errorMessage: this.errorMessage
),
),
]),
);
}
}
i am expecting the clear (x button) to disappear when textfield is empty and appear when user type or select a value from dropdown etc. can someone help? thanks in advance
You can make use of Dart's conditional expression to check if textfield is empty then don't show X button else show it. For ex, the textController is used to retrieve value of textfield. You can check if the value retrieved is greater than 0 then show X button, else show empty container().
textController.text.length > 0 ? IconButton(icon: Icon(Icons.clear), onPressed: () {} : Container()
Note: You will need to adjust above line w.r.t your code as applicable.
Hope this helps and resolves your issue.