I want to use the result of my DropdownButton to change some text displayed, how would I achieve this?
I have attempted to use a textControlEditor, but I can't make this work with Text.
I want the text to display the advice as the text, which shoiuld be based on value
This is my DropdownButton :
child: Container(
width: 300,
padding: EdgeInsets.symmetric(vertical: 5, horizontal: 15),
decoration: BoxDecoration(
border: Border.all(color: Color(0xff202124), width: 3.0),
color: Colors.white,
borderRadius: BorderRadius.circular(30)),
child: DropdownButton<String>(
onChanged: (value){
setState(() {_selectedColor = value;});
if (value == 'basic') advice = 'Have a great day!';
else if (value == 'Tired') advice = 'Have a rest today!';
else if (value == 'Happy') advice = 'Try to get lots of work done today!';
else if (value == 'Sad') advice = 'Take some time for yourself today';
else if (value == 'Bored') advice = 'Go on a walk and then get back to working';
else if (value == 'Productive') advice = 'Use that productivity';
else advice = 'Good luck today!';
print(advice);
//update text here
},
value: _selectedColor,
underline: Container(),
hint: Center(
child: Text('How have you been feeling today?',
style: TextStyle(color: Color(0xffA9A9A9)),)),
icon: Icon(
Icons.arrow_downward,
color: Color(0xffA9A9A9),
),
isExpanded: true,
items: _emotion
.map((e) => DropdownMenuItem(
child: Container(
alignment: Alignment.centerLeft,
child: Text(
e,
style: TextStyle(fontSize: 18),
),
),
value: e,
)).toList(),
selectedItemBuilder: (BuildContext context) => _emotion
.map((e) => Center(
child: Text(
e,
style: const TextStyle(
fontSize: 18,
color: Color(0xffA9A9A9),
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold),
),
))
.toList(),
),
)),
and here is where I want the text to change:
Expanded(
child: Container(
alignment: Alignment.center,
padding: EdgeInsets.all(20),
width: 300,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(30.0),
color: Color(color),
),
child: Center(
child: Text(
advice,
style: TextStyle(fontFamily: 'Arial', fontSize: 18, color: Colors.white, height: 1, ),
textAlign: TextAlign.center,
)
),
),
),
If the whole code is required I am more than happy to share that as well, thank you!
Okay so here you have to create a function which returns String value. This string value will be the value which you select from the drop-down button.
Here's an example:
String getAdvice() {
if (selectedDropDownValue == 'DefaultValue') {
return 'Default Text';
} else {
return '$selectedDropDownValue';
}
}
And then call this function inside the Text Widget where you want the selected advice to appear. Like this:
child: Center:
child: Text(
getAdvice(), //This is the updated line from your code
style: TextStyle(fontFamily: 'Arial', fontSize: 18, color: Colors.white, height: 1, ),
textAlign: TextAlign.center,
),
),
Hope that answers your question. Feel free to clear up any confusions.
Related
I'm having a little challenge displaying selected file image in a bottomSheet form. The image simply doesn't display immediately until I tap any field in the form, then the image shows. I'm not sure what's causing this if it is because the form is outside scaffold or what? I want the image to display as soon as it is picked.
Below is what the form widget looks like.
//Form is outside scaffold
Widget bottomSheetSeller() {
return Form(
key: formKey,
onChanged: () => setState(() => value = value),
child: Container(
height: 400,
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0),
child: SingleChildScrollView(
child: Column(
children: [
Container(
height: 4.0,
width: 50.0,
decoration: BoxDecoration(
color: globalDefault,
borderRadius: BorderRadius.circular(50.0),
),
alignment: Alignment.center,
),
const SizedBox(height: 45.0),
Container(
child: Text(
'Seller Form',
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: globalHeading,
),
textAlign: TextAlign.center,
),
alignment: Alignment.topLeft,
),
const SizedBox(height: 25.0),
FormWithValidation(
initialValue: value,
hint: 'First Name',
keyboardType: TextInputType.text,
validate: (value) {
if (value!.isEmpty) {
return "fill this field";
} else if (value.length <= 2) {
return "too short!";
} else {
return null;
}
},
onSaved: (value) => setState(() => firstName = value!)),
const SizedBox(height: 10.0),
FormWithValidation(
initialValue: value,
hint: 'Last Name',
keyboardType: TextInputType.text,
validate: (value) {
if (value!.isEmpty) {
return "fill this field";
} else if (value.length <= 2) {
return "too short!";
} else {
return null;
}
},
onSaved: (value) => setState(() => lastName = value!)),
const SizedBox(height: 15.0),
//File upload
Container(
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
yourLogo.isEmpty
? TextButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(
const EdgeInsets.all(15)),
side: MaterialStateProperty.all(
BorderSide(
width: 1,
color: globalInfoColor,
style: BorderStyle.solid),
),
shape: MaterialStateProperty.all(
RoundedRectangleBorder(
borderRadius: BorderRadius.circular(0),
),
),
),
onPressed: () async {
selectImage(ImageSource.gallery);
},
child: Unicon(
Unicons.uniPlus,
size: bigHeader,
color: globalInfoColor,
),
)
: setPicture(),
const SizedBox(width: 5.0),
const Text('Company logo'),
],
),
),
Padding(
padding: const EdgeInsets.only(top: 10.0),
child: Text(
'Allowed image format: jpg, jpeg, png',
style: TextStyle(
color: globalInfoColor,
fontStyle: FontStyle.italic,
),
),
),
const SizedBox(height: 30.0),
ButtonWidget(),
//some fields are not added to avoid too much code. If there is a need for more information I will gladly provide them.
the images below describe the situation:
Image 1: Image is already selected but appears not to be
Image 2: I tapped a field in the form and the image is now seen
Wrap your Form with StatefulBuilder and use its setState to update on bottom Sheet UI.
return StatefulBuilder(builder: (context, setState) {
return Form(child:
},)
I'm using a TextFormField to filter an array of lists using GridView to display the Items. Inside my TextFormField there I used onChanged: onSearchTextChanged, which filters through the array response from the Rest API I created which is in json. The filter works fine but I'm looking for a way around showing no result when the user inputs a filter that is not available inside the filter. Below is my code:
TextFormField is as below:
TextFormField(
controller: controller,
keyboardType: TextInputType.text,
onChanged: onSearchTextChanged,
decoration: InputDecoration(
border: InputBorder.none,
contentPadding: EdgeInsets.fromLTRB(20.0, 15.0, 20.0, 15.0),
prefixText: ' ',
hintText: "Search",
hintStyle: TextStyle(
color: Colors.grey,fontWeight: FontWeight.normal,
fontSize: 15,
fontFamily:
'Montserrat'),
prefixIcon: Container(
child:Icon(
Icons.search,
color: Colors.grey,
size: 20,
)),
suffixIcon: backClear==true?InkWell(
onTap: () {
controller.clear();
onSearchTextChanged(
'');
},
child: Container(
child:Icon(
Icons.backspace,
color: Colors.grey,
size: 12,
))): Container(
child:Icon(
Icons.view_list_rounded,
color: Colors.grey,
size: 15,
)),
labelStyle: new TextStyle(color: Colors.grey),
),
)
onSearchTextChanged is as below:
onSearchTextChanged(String text) async {
_searchResult.clear();
if (text.isEmpty) {
setState(() {
backClear=false;
});
return;
}
content.forEach((userDetail) {
if (userDetail.username.toLowerCase().contains(text) || userDetail.username.toString().contains(text) ||
userDetail.username.toUpperCase().contains(text)||userDetail.fullname.toLowerCase().contains(text) || userDetail.fullname.toString().contains(text) ||
userDetail.fullname.toUpperCase().contains(text)||userDetail.phone.toLowerCase().contains(text) || userDetail.phone.toString().contains(text) ||
userDetail.phone.toUpperCase().contains(text)||userDetail.email.toLowerCase().contains(text) || userDetail.email.toString().contains(text) ||
userDetail.email.toUpperCase().contains(text) ) {
_searchResult.add(userDetail);
}
});
setState(() {
backClear=true;
});
}
For instance if John is not part of the return array list for username, fullname then on the screen it should show a message that says not found.
My gridview is as below:
Container(
child: _searchResult.length != 0 ||
controller.text.isNotEmpty
?new GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: _searchResult == null ? 0 : _searchResult.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:3),
itemBuilder: (BuildContext context, int index){
return Card(
elevation: 1,
semanticContainer: true,
child:InkWell(
onTap: () async {
},
child: Container(
padding: EdgeInsets.all(10),
child:Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Flexible(
child: Column(
children: <Widget>[
Stack(
alignment: Alignment.bottomCenter,
children: [
CircleAvatar(
radius: 30,
backgroundColor: Colors.white,
child: _searchResult[index].profile_img!=null?Container(
padding: EdgeInsets.all(0),
child:CircleAvatar(
radius: 40,
backgroundImage:NetworkImage(_searchResult[index].profile_img
,),
backgroundColor: Colors.white,
//
)):Container(
padding: EdgeInsets.all(0),
child:CircleAvatar(
radius: 40,
backgroundImage:AssetImage('assets/person_icon.png'),
backgroundColor: Colors.white,
//
)),
),
],),
Flexible(
child: Center(child: Text(_searchResult[index].username==null?"AppName":_searchResult[index].username,style: TextStyle(
color: colorBlack,
fontWeight: FontWeight.normal,
fontSize: 10,
fontFamily: 'Montserrat',
),))),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 15,right: 15,top: 1,bottom: 1),
decoration: BoxDecoration(
color: colorBlue,borderRadius: BorderRadius.circular(3)),
child: Text( "Follow",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 10,
fontFamily: 'Montserrat',
),
),
),)
],
),
),
]))));
;})
:GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: content == null ? 0 : content.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount:3),
itemBuilder: (BuildContext context, int index){
return Card(
elevation: 1,
semanticContainer: true,
child:InkWell(
onTap: () async {
},
child: Container(
padding: EdgeInsets.all(10),
child:Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: <Widget>[
Flexible(
child: Column(
children: <Widget>[
Stack(
alignment: Alignment.bottomCenter,
children: [
CircleAvatar(
radius: 30,
backgroundColor: Colors.white,
child: content[index].profile_img!=null?Container(
padding: EdgeInsets.all(0),
child:CircleAvatar(
radius: 40,
backgroundImage:NetworkImage(content[index].profile_img
,),
backgroundColor: Colors.white,
//
)):Container(
padding: EdgeInsets.all(0),
child:CircleAvatar(
radius: 40,
backgroundImage:AssetImage('assets/person_icon.png'),
backgroundColor: Colors.white,
//
)),
),
],),
Flexible(
child: Center(child: Text(content[index].username==null?"AppName":content[index].username,style: TextStyle(
color: colorBlack,
fontWeight: FontWeight.normal,
fontSize: 10,
fontFamily: 'Montserrat',
),))),
Flexible(
child: Container(
padding: EdgeInsets.only(left: 15,right: 15,top: 1,bottom: 1),
decoration: BoxDecoration(
color: colorBlue,borderRadius: BorderRadius.circular(3)),
child: Text( "Follow",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 10,
fontFamily: 'Montserrat',
),
),
),)
],
),
),
]))));}))
I'm not sure on where you actually display results in your sample code, but the logic you could implement is the following.
After the forEach you should check if some result is actually found, and in case it wasn't, set a showNotFoundText to true so that in your view you can display the message.
onSearchTextChanged(String text) async {
_searchResult.clear();
if (text.isEmpty) {
setState(() {
backClear=false;
showNotFoundText = false;
});
return;
}
content.forEach((userDetail) {
if (userDetail.username.toLowerCase().contains(text) || userDetail.username.toString().contains(text) ||
userDetail.username.toUpperCase().contains(text)||userDetail.fullname.toLowerCase().contains(text) || userDetail.fullname.toString().contains(text) ||
userDetail.fullname.toUpperCase().contains(text)||userDetail.phone.toLowerCase().contains(text) || userDetail.phone.toString().contains(text) ||
userDetail.phone.toUpperCase().contains(text)||userDetail.email.toLowerCase().contains(text) || userDetail.email.toString().contains(text) ||
userDetail.email.toUpperCase().contains(text) ) {
_searchResult.add(userDetail);
}
});
if (_searchResult.isEmpty) {
//Do something
setState(() {
showNotFoundText = true;
});
}
setState(() {
backClear=true;
});
}
Add something like this in your view
showNotFoundText ? Text("No results found") : Container() //Empty container instead
Since you didn't include the code for the results display, I made it this way, but you may also consider using this logic directly in your view:
_searchResult.isEmpty ? Text("No results found") : DisplayResultsWidget()
With the suggestion of Dani3le_ to use add below if statement inside my onSearchTextChanged and I declare my bool showNotFoundText = false; above my code
if (_searchResult.isEmpty) {
//Do something
setState(() {
showNotFoundText = true;
});
}
and above my Gridview I added the below to show the No result message find below the code:
showNotFoundText==true ? Text("No result found!",style: TextStyle(fontFamily: 'Montserrat',color: Colors.grey, fontSize: 13, fontWeight: FontWeight.bold),) : Container(),
But onPress of backspace the No result found still shows even if the input is available on the search list so I add showNotFoundText = false; to setState() so that every time the backspace is pressd it set the state of showNotFoundText back to false as shown below:
setState(() {
showNotFoundText = false;
});
Hello this is my flutter widget SearchableDropdown, and it is wrapped using a container just for highlighting the border for understanding how much space the widget have been taken.
I want to reduce the padding/space of the SearchableDropdown widget around the text, and make the UI like this. How to do that?
new Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
new Text(
'You are here',
style: GoogleFonts.poppins(
color: HexColor("#B8B8B8"), fontSize: 10),
textAlign: TextAlign.start,
),
new Container(
decoration: new BoxDecoration(
border: Border.all(color: Colors.red),
),
child: _searchableDropDown(context, providerData),
)
],
),
Widget _searchableDropDown(BuildContext context, HomePageProviderData data) {
return new SearchableDropdown.single(
hint: new Text(
data.currentCity,
style: GoogleFonts.poppins(
textStyle: TextStyle(
color: Color(0xFF373539),
fontSize: 16,
fontWeight: FontWeight.w600),
),
),
underline: SizedBox(),
displayClearIcon: false,
items: data.listCity.map((item) {
//
}).toList(),
onChanged: (String value) {
//
},
);
}
try use wrapping your dropdown inside SizedBox() like this :
SizedBox(
height: 20,
child: _searchableDropDown(context, providerData),
);
result:
I have a horizontal list of Categories; e.g. CARS and below it there is a dropdown menu fetch list of models per every CAR selected in the list. So my issue is after i select eg Toyota and the dropdown menu is filled with models eg - Camry, Vits, Caldina, Hilux, That is fine... but is i select one eg Camry and in the list of CARS i select another CAR eg HONDA.. the UI breaks.. can anyone tell me how i can efficiently reuse the same dropdown and just be changing the arraylist it fetchs from even when one item was selectd
I did something similar like this:
For car brand:
int selectedBrandItem;
int selectedModelItem;
int selectedByYearItem;
var listTemp;
Container(
width: _size.width,
height: _size.height* 0.08,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
border: Border.all(
color: Theme.greyColor[600]
),
),
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal:10.0),
child: DropdownButton(
hint: Text(allTranslations.text('brand')),
value: selectedBrandItem,
onChanged: (value){
setState(() {
selectedBrandItem = value;
selectedModelItem = null;
selectedByYearItem = null;
});
},
items: (state.response.brandList == null || state.response.brandList == [])?listTemp:state.response.brandList.map((e) =>
DropdownMenuItem(
value: e.id,
child: Text(
e.brandName,
style: TextStyle(
color: Theme.GreyColor,
fontWeight: FontWeight.w400,
fontSize: 16.0
),
)
)
).toList()??[],
isExpanded: true,
icon: Icon(Icons.keyboard_arrow_down, color: Theme.greyColor[500],),
underline: Container(color: Colors.transparent,),
),
),
);
For car model:
Container(
width: _size.width,
height: _size.height* 0.08,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
border: Border.all(
color: Theme.greyColor[600]
),
),
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal:10.0),
child: DropdownButton(
hint: Text(allTranslations.text('model')),
value: selectedModelItem,
onChanged: (value){
setState(() {
selectedModelItem = value;
selectedByYearItem = null;
});
},
items: state.response.modelList.map((e) =>
DropdownMenuItem(
value: e.id,
child: Text(
e.modelName,
style: TextStyle(
color: Theme.GreyColor,
fontWeight: FontWeight.w400,
fontSize: 16.0
),
)
)
).toList()??[],
isExpanded: true,
icon: Icon(Icons.keyboard_arrow_down, color: Theme.greyColor[500],),
underline: Container(color: Colors.transparent,),
),
),
);
For car year:
Container(
width: _size.width,
height: _size.height* 0.08,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(16.0),
border: Border.all(
color: Theme.greyColor[600]
),
),
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal:10.0),
child: DropdownButton(
hint: Text(allTranslations.text('year')),
value: selectedByYearItem,
onChanged: (value){
setState(() {
selectedByYearItem = value;
});
},
items: (state.response.yearList == null || state.response.yearList == [] )?listTemp:state.response.yearList .map((e) =>
DropdownMenuItem(
value: e.id,
child: Text(
e.year,
style: TextStyle(
color: Theme.GreyColor,
fontWeight: FontWeight.w400,
fontSize: 16.0
),
)
)
).toList()??[],
isExpanded: true,
icon: Icon(Icons.keyboard_arrow_down, color: Theme.greyColor[500],),
underline: Container(color: Colors.transparent,),
),
),
);
I am trying to build my own drop down button with separated and condensed menu items, like the image below:
here's the code I have tried so far, I got the drop down width to match the container but the items can't be customized so far and always the height starts from above the button and is not occupying the width of the container:
body: Container(
margin: EdgeInsets.symmetric(horizontal: 10.0, vertical: 5.0),
child: Container(
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
border: Border.all(color: Colors.brown, width: 1.0)),
padding: EdgeInsets.fromLTRB(10.0, 5.0, 10.0, 5.0),
child: DropdownButtonHideUnderline(
child: ButtonTheme(
alignedDropdown: true,
child: DropdownButton(
isExpanded: true,
isDense: true,
value: selection,
icon: Icon(
Icons.arrow_drop_down,
color: Colors.brown,
),
iconSize: 40,
underline: Container(
height: 1,
color: Colors.transparent,
),
onChanged: (String val) => setState(() => selection = val),
items: settingsOptions.map((option) {
return DropdownMenuItem(
value: option,
child: Text(option),
);
}).toList(),
),
),
)
),
),
This is the output from the code:
How do I customize the items width, height and add dividers like in the first image? Thanks
This is an example modify as you like !
DropdownButton(
isExpanded: true,
isDense: true,
value: selection,
icon: Icon(
Icons.arrow_drop_down,
color: Colors.brown,
),
iconSize: 40,
underline: Container(
height: 1,
color: Colors.transparent,
),
onChanged: (String val) => setState(() => selection = val),
items: sampleList.map((option) {
return DropdownMenuItem(
value: option,
child: Container(
width:double.infinity,
alignment:Alignment.centerLeft,
padding: const EdgeInsets.fromLTRB(0,8.0,0,6.0),
child:Text(option),
decoration:BoxDecoration(
border:Border(top:BorderSide(color:Colors.grey,width:1))
)
),
);
}).toList(),
selectedItemBuilder:(con){
return sampleList.map((m){
return Text(m,);
}).toList();
}
)
I came across a flutter Library called dropdown_below in pub.dev. It has additional methods that can allow you customize dropdownMenuItem to your preferred layout.
DropdownBelow(
value: category,
// isDense: true,
itemWidth: MediaQuery.of(context).size.width * 0.92,
itemTextstyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Colors.black),
boxTextstyle: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w400,
color: Colors.grey.shade600),
boxPadding: EdgeInsets.fromLTRB(13, 12, 0, 12),
boxWidth: MediaQuery.of(context).size.width,
boxHeight: 60,
hint: Text('choose video'),
items: video.data.value.videos
.map((e) => DropdownMenuItem(
onTap: () => e.title,
value: e.title ?? category,
child: Container(
width: double.infinity,
decoration: BoxDecoration(),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
e.title ?? '$category',
overflow: TextOverflow.ellipsis,
),
),
),
))
.toList(),
onChanged: (video) {
context.read(videoProvider).cateroryOnChange(video);
},
),
Library Link: https://pub.dev/packages/dropdown_below
The problem with the DropdownButton is that the menu will open randomly based on the selected index and other things. Also, You can't edit its code by just trying to pass offset as the logic code of the paint work based on that and trying to hardcode the selectedItemOffset to a value won't work perfectly fine.
So use DropDownButton2 , which is built just over this .
Package: DropDownButton2
Credits: Ahmed Elsayed
For reference: Refer this link