Good morning, I am trying to align the DropDown's arrow icon with the hint text but I can't understand why the render engine positions it so far below.
I tried to set the InputDecoration's contentPadding property to EdgeInsets.zero and it seemed to work. However, it ruined the DropDown's focusBorder. Furthermore, I didn't understand what element the padding is attached to.
import 'package:flutter/material.dart';
class Example extends StatefulWidget {
const Example({Key? key}) : super(key: key);
#override
State<Example> createState() => _ExampleState();
}
class _ExampleState extends State<Example> {
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border.all(
color: Colors.black87,
),
),
width: 135.0,
height: 55.0,
child: _buildField(),
);
}
Widget _buildField() {
return DropdownButtonFormField<String>(
icon: const Icon(
Icons.expand_more,
size: 30.0,
color: Color(0xFF2E2E2E),
),
hint: const Text(
'Status',
style: TextStyle(
fontSize: 18.0,
fontWeight: FontWeight.w500,
color: Color(0xFFA6A6A6),
),
),
decoration: const InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.transparent,
width: 2.5,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFFFF7321),
width: 2.5,
),
),
),
items: ['Item 1', 'Item 2', 'Item 3', 'Item 4']
.map<DropdownMenuItem<String>>(
(String item) => DropdownMenuItem(
value: item,
child: Text(item),
),
)
.toList(),
onChanged: (String? value) => print(value),
);
}
}
DropdownButton is a material component as seen on material.io.
This means that Flutter has every single styling hard-coded into the default styling like its defined on this website. It appears that by forcing this widget into a certain height:
return SizedBox(
height: 55.0,
child: Stack(...
you break the prebuilt styling of this widget. I have tested it with a height 50 to demonstrate this:
Here, you can see why setting the height is a bad idea on a Material Component.
Enough talking, what is the solution: Remove the height from SizedBox and any styling that is given by the material component:
return DropdownButtonFormField<String>(
icon: const Icon(
Icons.expand_more,
//size: 30.0,
color: Color(0xFF2E2E2E),
),
hint: const Text(
'Status',
style: TextStyle(
//fontSize: 18.0,
//fontWeight: FontWeight.w500,
color: Color(0xFFA6A6A6),
),
),
Now I know this will break your UI, but this is how Flutter works. Either you completely adopt the Material Design, or you will have a really bad time.
Try this example code I wrote for you.
My code:
DropdownButtonHideUnderline(
child: DropdownButtonFormField<String>(
decoration: const InputDecoration(
border: OutlineInputBorder(),
),
icon: const Icon(
Icons.expand_circle_down, // Custom icon goes here.
),
iconSize: 30, // Icon height goes here.
hint: const Text('Choose item'),
value: _dropDownValue,
items: _dropDownList
.map(
(label) => DropdownMenuItem(
value: label,
child: Text(
label.toString(),
),
),
)
.toList(),
onChanged: (value) {
_dropDownValue = (value ?? _dropDownList[0]);
setState(() {});
},
),
),
Related
basically the problem is clear :
here is my code :
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class AddTaskScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
late String newTaskTitle;
return Container(
color: Color(0xFF757575),
child: Container(
padding: EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.limeAccent[400],
borderRadius: BorderRadius.only(
topLeft: Radius.circular(60),
topRight: Radius.circular(60),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Text(
'Add Task',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 30, color: Colors.brown[900]),
),
SizedBox(
height: 12,
),
TextField(
onChanged: (newText) {
newTaskTitle = newText;
},
autocorrect: false,
decoration: InputDecoration(
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.brown[800]!,
width: 2,
),
borderRadius: BorderRadius.circular(30),
),
hintText: 'Type Your Task ...',
labelStyle: TextStyle(
color: Colors.green[900],
),
helperStyle: TextStyle(
color: Colors.brown[900],
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(60),
borderSide: BorderSide(
color: Colors.brown[900]!,
width: 2,
),
),
),
),
SizedBox(
height: 12,
),
ElevatedButton(
onPressed: () {
// Provider.of<TaskData>(context, listen: false)
// .addTask(newTaskTitle);
// Navigator.pop(context);
print(newTaskTitle);
// Provider.of<TaskData>(context, listen: false)
// .addTask(newTaskTitle);
// Navigator.pop(context);
},
child: Text(
'Add',
style: TextStyle(color: Colors.brown[900]),
),
style: ButtonStyle(
backgroundColor: MaterialStateColor.resolveWith(
(states) => Colors.lightGreen),
elevation: MaterialStateProperty.resolveWith((states) => 6),
shadowColor:
MaterialStateColor.resolveWith((states) => Colors.green),
minimumSize: MaterialStateProperty.resolveWith(
(states) => Size.square(40.67)),
),
),
],
),
),
);
}
}
please help me ...at this stage I just want to print the value that the user enters in the text field in the console ...but it gives me error :
LateInitializationError: Local 'newTaskTitle' has not been initialized.
i have also changed it to a stateful widget to check if it works with setstate but it didnt ..
i also made it nullable like this => String? newTaskTitle; and Ofcourse it made to change a lot of things but at last the null value was passed ...there is a problem whithin the onChange callback of the textfield that doesnt assign the new value of user input to the variable i have created ...
How can I fixe this problem ?
change
late String newTaskTitle;
to
ValueNotifier<String> newTaskTitle = ValueNotifier("");
and put ElevatedButton inside ValueListenableBuilder
like this
ValueListenableBuilder<String>(
listenable : newTaskTitle,
builder : (ctx,taskt,_){
return ElevatedButton(
onPressed : taskt.isEmpty ? null : (){
// put you logic
}
.....
);
}
);
change onChanged of textField like this
onChanged: (newText) {
newTaskTitle.value = newText;
},
This has been very confusing to me since the TextField wrapped in a SizedBox only, works perfectly; but it appears to be a simple image (it can't be tapped, nor focused) when I wrap that same piece of code in a Transform.translate widget.
Also, if I change the Transform.translate to a Positioned widget, the TextField works perfectly, but I'd like to understand why this is happening, because I am required to use Transform.translate over Positioned for this special project.
class Login extends StatelessWidget {
Login({
Key key,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xfff4f6fa),
body: Stack(
children: <Widget>[
SizedBox(
width: 302.0,
height: 60.0,
child: TextField(
decoration: InputDecoration(
hintText: 'Correo electrónico *',
prefixIcon: Icon(Icons.send),
border: OutlineInputBorder(
borderSide:
BorderSide(width: 1.0, color: const Color(0xffe7e7e7)),
borderRadius: BorderRadius.circular(4.0),
),
),
style: TextStyle(
fontFamily: 'Nunito',
fontSize: 14,
color: const Color(0xff777777),
height: 1.4285714285714286,
),
textAlign: TextAlign.left,
onChanged: (String value) async {
},
onSubmitted: (String value) async {
},
),
),
// Positioned(
Transform.translate(
offset: Offset(36.0, 317.8),
// left: 36,
// top: 317.8,
child:
// Adobe XD layer: 'input:mail' (component)
SizedBox(
width: 302.0,
height: 60.0,
child: TextField(
decoration: InputDecoration(
hintText: 'Correo electrónico *',
prefixIcon: Icon(Icons.send),
border: OutlineInputBorder(
borderSide:
BorderSide(width: 1.0, color: const Color(0xffe7e7e7)),
borderRadius: BorderRadius.circular(4.0),
),
),
style: TextStyle(
fontFamily: 'Nunito',
fontSize: 14,
color: const Color(0xff777777),
height: 1.4285714285714286,
),
textAlign: TextAlign.left,
onChanged: (String value) async {
},
onSubmitted: (String value) async {
},
),
),
),
],
),
);
}
}
As mentioned before, both inputs show on the screen (one at the center and one at the top) only the TextField wrapped with a SizeBox (first element of the Stack, at the top of the sreen)can be tapped, nothing happens when the one at the center of the screen (second element of the stack, located there because of the translation) is tapped.
Try debugging and see the layout grid lines and correct boundaries of widget using Flutter inspector.
That should give you a better idea at why is it behaving this way.
Seems like when You use the Transform widget, it doesnt register the hit as it merely moves the boundaries and not the tap area.
Try wrapping the Transform widget with a GestureDetector and see.
You can also follow a similar issue on GitHub:
https://github.com/flutter/flutter/issues/27587
where the text is meant to be rendered from
import 'package:flutter/material.dart';
class Icons1 extends StatefulWidget {
const Icons1({
Key key,
}) : super(key: key);
#override
_Icons1State createState() => _Icons1State();
}
class _Icons1State extends State<Icons1> {
List<Icon> icons = [
Icon(Icons.account_balance, color: Colors.black),
Icon(Icons.account_balance_wallet, color: Colors.black),
Icon(Icons.add_shopping_cart, color: Colors.black),
Icon(Icons.assessment, color: Colors.black),
//
Icon(Icons.assignment, color: Colors.black),
Icon(Icons.beach_access, color: Colors.black),
Icon(Icons.attach_file, color: Colors.black),
Icon(Icons.attach_money, color: Colors.black),
//
Icon(Icons.business, color: Colors.black),
Icon(Icons.business_center, color: Colors.black),
Icon(Icons.credit_card, color: Colors.black),
Icon(Icons.device_hub, color: Colors.black),
//
Icon(Icons.golf_course, color: Colors.black),
Icon(Icons.local_gas_station, color: Colors.black),
Icon(Icons.local_grocery_store, color: Colors.black),
Icon(Icons.import_contacts, color: Colors.black),
//
Icon(Icons.insert_chart, color: Colors.black),
Icon(Icons.label_important, color: Colors.black),
Icon(Icons.kitchen, color: Colors.black),
Icon(Icons.local_bar, color: Colors.black),
//
Icon(Icons.ac_unit, color: Colors.black),
Icon(Icons.account_circle, color: Colors.black),
Icon(Icons.add_alert, color: Colors.black),
Icon(Icons.add_to_photos, color: Colors.black),
Icon(Icons.adjust, color: Colors.black),
Icon(Icons.airplanemode_active, color: Colors.black),
Icon(Icons.airport_shuttle, color: Colors.black),
Icon(Icons.bubble_chart, color: Colors.black),
//
Icon(Icons.directions_bus, color: Colors.black),
Icon(Icons.email, color: Colors.black),
Icon(Icons.radio, color: Colors.black),
Icon(Icons.audiotrack, color: Colors.black),
];
#override
Widget build(BuildContext context) {
return GridView.count(
mainAxisSpacing: 2,
crossAxisSpacing: 2,
physics: BouncingScrollPhysics(),
crossAxisCount: 4,
children: icons
.map(
(iconData) => GestureDetector(
child: iconData,
onTap: () {},
),
)
.toList(),
);
}
}
//were the icon is meant to be displayed
class DisplayingIcon extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircleAvatar(
// child:
//chosen icon(),
)
],
);
}
}
when I click the icon in the grid View in Icons1 script I would like it to show up in the Circle Avatar in the DisplayingIcon script
I have tried to wrap each individual icon in the list with a GestureDetector with an onTap which I made the icon equal to a local variable.
which didn't work.
please help.I am new to flutter
It depends on where you want to place your chosen Image.
Here is an example when you have the CircleAvatar in the same column as the GridView. Then you can simply save the selected Icon in a member variable of the state:
void main() {
runApp(MaterialApp(
title: 'NiklasLehnfeld',
home: Scaffold(body: Icons1())));
}
class Icons1 extends StatefulWidget {
const Icons1({
Key key,
}) : super(key: key);
#override
_Icons1State createState() => _Icons1State();
}
class _Icons1State extends State<Icons1> {
List<Icon> icons = [
Icon(Icons.account_balance, color: Colors.black),
Icon(Icons.account_balance_wallet, color: Colors.black),
Icon(Icons.add_shopping_cart, color: Colors.black),
Icon(Icons.assessment, color: Colors.black),
//
Icon(Icons.assignment, color: Colors.black),
Icon(Icons.beach_access, color: Colors.black),
Icon(Icons.attach_file, color: Colors.black),
Icon(Icons.attach_money, color: Colors.black),
//
Icon(Icons.business, color: Colors.black),
Icon(Icons.business_center, color: Colors.black),
Icon(Icons.credit_card, color: Colors.black),
Icon(Icons.device_hub, color: Colors.black),
//
Icon(Icons.golf_course, color: Colors.black),
Icon(Icons.local_gas_station, color: Colors.black),
Icon(Icons.local_grocery_store, color: Colors.black),
Icon(Icons.import_contacts, color: Colors.black),
//
Icon(Icons.insert_chart, color: Colors.black),
Icon(Icons.label_important, color: Colors.black),
Icon(Icons.kitchen, color: Colors.black),
Icon(Icons.local_bar, color: Colors.black),
//
Icon(Icons.ac_unit, color: Colors.black),
Icon(Icons.account_circle, color: Colors.black),
Icon(Icons.add_alert, color: Colors.black),
Icon(Icons.add_to_photos, color: Colors.black),
Icon(Icons.adjust, color: Colors.black),
Icon(Icons.airplanemode_active, color: Colors.black),
Icon(Icons.airport_shuttle, color: Colors.black),
Icon(Icons.bubble_chart, color: Colors.black),
//
Icon(Icons.directions_bus, color: Colors.black),
Icon(Icons.email, color: Colors.black),
Icon(Icons.radio, color: Colors.black),
Icon(Icons.audiotrack, color: Colors.black),
];
Icon _chosenIcon;
#override
Widget build(BuildContext context) {
return Column(
children: [
Expanded(child: DisplayingIcon(_chosenIcon)),
Expanded(
flex: 10,
child: GridView.count(
mainAxisSpacing: 2,
crossAxisSpacing: 2,
physics: BouncingScrollPhysics(),
crossAxisCount: 4,
children: icons
.map(
(iconData) => GestureDetector(
child: iconData,
onTap: () => setState(() => this._chosenIcon = iconData),
),
)
.toList(),
),
),
],
);
}
}
class DisplayingIcon extends StatelessWidget {
final Icon icon;
DisplayingIcon(this.icon);
#override
Widget build(BuildContext context) {
return CircleAvatar(child: this.icon);
}
}
If sth is unclear - Feel free to ask.
I'm learning to code in Flutter and and redoing one of my Android/iOS apps. This is roughly what the UI looks like so far:
When I tap a field to open the keyboard, it smashes all of the fields together.
I'm trying to decide what the best way of handling this is. The only way I can think of to fix it is to somehow not have all of the fields and the button pushed up when the keyboard opens. But if I do that, then the fields lower on the screen will not be visible when data is being entered into them. Is there a good way to handle this?
This is the code for this view:
class _CarbCalcState extends State<CarbCalc> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Carb Calculator'),
backgroundColor: Colors.purple.shade700,
leading: IconButton(
icon: Icon(Icons.settings),
onPressed: () {},
),
actions: <Widget>[
IconButton(
icon: Icon(Icons.library_books),
onPressed: () {},
)
],
),
body: Container(
margin: EdgeInsets.only(top: 40.0, left: 20.0, right: 20.0),
child: Column(
children: <Widget>[
NutrientValue(
nutrientName: 'Protein',
onChanged: (value) {
print('Protein Changed');
},
),
NutrientValue(
nutrientName: 'Fat',
onChanged: (value) {},
),
NutrientValue(
nutrientName: 'Fiber',
onChanged: (value) {},
),
NutrientValue(
nutrientName: 'Moisture',
onChanged: (value) {},
),
NutrientValue(
nutrientName: 'Ash',
onChanged: (value) {},
),
SizedBox(
height: 30.0,
),
ButtonTheme(
minWidth: double.infinity,
height: 50,
child: FlatButton(
onPressed: () {},
color: Colors.blue.shade700,
child: Text(
'Calculate Carbs',
style: TextStyle(
fontSize: 20,
),
),
),
),
],
),
),
);
}
}
class NutrientValue extends StatelessWidget {
NutrientValue({#required this.nutrientName, #required this.onChanged});
final String nutrientName;
final Function onChanged;
#override
Widget build(BuildContext context) {
return Expanded(
child: Row(
children: <Widget>[
Container(
width: 90,
child: Text(
nutrientName,
style: TextStyle(fontSize: 18.0),
),
),
Expanded(
child: TextField(
keyboardType: TextInputType.numberWithOptions(
signed: false,
decimal: true,
),
style: TextStyle(
color: Colors.black,
fontSize: 18.0,
),
onChanged: this.onChanged,
decoration: InputDecoration(
hintText: 'Enter $nutrientName',
hintStyle: TextStyle(
color: Colors.grey,
),
border: OutlineInputBorder(
borderRadius: BorderRadius.all(
Radius.circular(15.0),
),
borderSide: BorderSide.none,
),
filled: true,
fillColor: Colors.white,
),
),
),
],
));
}
}
You are having a Column with several Expanded inside which take care that the space is evenly distributed. When the space shrinks by the keyboard, they move together. Basically it is a good idea to put input fields into a Scrollview because of the Keyboard. You can use SingleChildScrollView with your Column but you need to replace the Expanded widgets with a Container for example or just use the plain TextFields.
In Flutter, I am trying to change the color of the DropdownButton's icon (the down arrow icon) to white color.
I tried using the style property with no help. The text color became white but the icon is still the default grey.
DropdownButton(
style: TextStyle(color: Colors.white, decorationColor:
Colors.white),
items: this.items,
value: null,
hint: Text(SaveOptions[_saveOption], style: TextStyle(color:
Colors.white)),
onChanged: (selectedOption) {
setState(() {
_saveOption = selectedOption;
});
})
How do I change the color of the arrow icon to white?
You can use the fields iconEnabledColor and iconDisabledColor in the following manner:
final myDropDownMenu = DropdownButton<String>(
iconEnabledColor: Colors.white,
iconDisabledColor: Colors.white,
value: myInitialValue,
// The rest of your code
);
Since the DropdownButton gets the color from the nearest Theme, you have two options.
The first one is by changing the brightness of the application theme.
And the other is by wrapping your dropdown button with a new Theme with dark brightness.
Theme(
data: Theme.of(context).copyWith(brightness: Brightness.dark),
child: DropdownButton(
style: TextStyle(color: Colors.white, decorationColor: Colors.white),
items: this.items,
value: null,
hint: Text(SaveOptions[_saveOption], style: TextStyle(color: Colors.white)),
onChanged: (selectedOption) {
setState(() {
_saveOption = selectedOption;
});
},
),
)
This is a bit of a hack but it gives you complete control over how the drop down looks collapsed, in short make value: null, hint: null, iconsize: null, make a stack that has 2 containers with same sizing: 1 that displays your collapsed dropdown and 1 that detects gestures 'expand'.
class MyDropdownFilled extends StatefulWidget {
final List<String> dropDownValues;
const MyDropdownFilled({Key key, #required this.dropDownValues})
: super(key: key);
List<DropdownMenuItem<String>> getDropDownMenuItems() {
return dropDownValues
.map((itemString) =>
DropdownMenuItem(child: Text(itemString), value: itemString))
.toList();
}
#override
_MyDropdownFilledState createState() => _MyDropdownFilledState();
}
class _MyDropdownFilledState extends State<MyDropdownFilled> {
String _activeDropdown;
#override
initState() {
super.initState();
_activeDropdown = widget.dropDownValues[0];
}
#override
Widget build(BuildContext context) {
return Stack(
children: [
Container(
width: double.infinity,
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: primaryColor.shade600,
borderRadius: BorderRadius.all(Radius.circular(2))),
child:
Row(mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [
Text(_activeDropdown, style: Theme.of(context).textTheme.caption),
Icon(Icons.arrow_drop_down, size: 30, color: Colors.white),
]),
),
Container(
width: double.infinity,
padding: EdgeInsets.all(10.0),
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.all(Radius.circular(2))),
child: DropdownButtonHideUnderline(
child: DropdownButton<String>(
value: null,
isDense: true,
iconSize: 0,
hint: null,
onChanged: (String newValue) {
setState(() {
_activeDropdown = newValue;
});
},
items: widget.dropDownValues.map((String value) {
return DropdownMenuItem(
value: value,
child: Text(value),
);
}).toList(),
)),
)
],
);
}
}
Currently the arrow color is hardcoded for DropdownButton:
Color get _downArrowColor {
// These colors are not defined in the Material Design spec.
if (_enabled) {
if (Theme.of(context).brightness == Brightness.light) {
return Colors.grey.shade700;
} else {
return Colors.white70;
}
} else {
if (Theme.of(context).brightness == Brightness.light) {
return Colors.grey.shade400;
} else {
return Colors.white10;
}
}
}
You can create your own widget to customize this property.
It seems Flutter should have a way to do this, but I don't think it's currently possible. What I did to handle this was to set the "value" to null, "iconSize" to 0, and have the "hint" dynamically generated based on what is selected. Doing this lets you have complete control over hint widget.
DropdownButton<int>(
value: null,
iconSize: 0,
hint: Row(
children: <Widget>[
Text(_selected,
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w700,
),
),
Padding(
padding: EdgeInsets.only(left: 5),
child: Icon(
FontAwesomeIcons.caretDown,
color: Colors.white,
size: 20,
),
),
],
),
items: dateRanges.map((Map<String, dynamic> value) {
return DropdownMenuItem<int>(
value: value['type'],
child: Text(
value['name'],
style: TextStyle(
color: Colors.grey[800],
fontWeight: FontWeight.w700,
),
),
);
}).toList(),
onChanged: (type) => _onDateRangeTypeChanged(type),
)
Hope this helps.
Go to DropdownButton class
Edit this code
if (!DropdownButtonHideUnderline.at(context)) {
final double bottom = widget.isDense ? 0.0 : 8.0;
result = Stack(
children: <Widget>[
result,
Positioned(
left: 0.0,
right: 0.0,
bottom: bottom,
child: Container(
height: 1.0,
decoration: const BoxDecoration(
border: Border(bottom: BorderSide(color: Color(0xFFBDBDBD), width: 0.0))
),
),
),
],
);
}
to this
if (!DropdownButtonHideUnderline.at(context)) {
final double bottom = widget.isDense ? 0.0 : 8.0;
result = Stack(
children: <Widget>[
result,
Positioned(
left: 0.0,
right: 0.0,
bottom: bottom,
child: Container(
height: 1.0,
decoration: const BoxDecoration(
border: Border(bottom: BorderSide(color: Colors.red
("Here any color you want")
, width: 0.0))
),
),
),
],
);
}
Wrap your widget around a new Theme that has the values you want set, given that you can go to the source code and see which colors it's using off the theme.