I have a problem. Please help. I just started the program in a flutter. I want to display my array list(foto) to DropDownButton.
array list in firebase ,
final List<String> listCategorys = FirebaseFirestore.instance
.collection('shoppingList')
.doc('category');
String categoryName = 'fruit';
DropdownButton<String>(
focusColor: Colors.white,
value: categoryName,
style: const TextStyle(color: Colors.white),
iconEnabledColor: Colors.black,
items: listCategorys
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Padding(
padding: const EdgeInsets.only(
left: 42,
),
child: Text(
value,
style: const TextStyle(
color: Colors.black,
fontSize: 18,
),
),
),
);
}).toList(),
hint: const Text(
"Select categories",
style: TextStyle(
color: Colors.black,
fontSize: 24,
fontWeight: FontWeight.w500),
),
onChanged: (String? value) {
setState(() {
categoryName = value!;
});
},
),
when I create a rigid array final List<String> listCategorys = ['fruit', 'vegetables' ...] it works, but I won't get all data in firebase
Your Query should be
DocumentSnapshot snap = await FirebaseFirestore.instance
.collection('shoppingList')
.doc($yourDocumentID).get();
List<String> listOfCategory = List.from(snap.data['category']);
I am trying to create form.
I managed to create every widget in it, but every time I try to open TextFormField I get redirected back to my MainMenuScreen without any error.
I am using BLoC and routes. I think that issue might be related with using named routes.
Issue was not spotted before changing to named routes
MainMenuScreen fragment:
CategoryCard(
categoryName: 'Main dishes',
assetPath: 'assets/images/main_dish.png',
onPressed: () => Navigator.pushReplacement(context,
MaterialPageRoute(builder: (BuildContext context) {
return BlocProvider.value(
value: BlocProvider.of<RecipesBloc>(context)
..add(LoadRecipesEvent())
..category = 'main_dish',
child: RecipesScreen(),
);
})),
),
From MainMenuScreen I redirect to RecipesScreen
Fragment of RecipesScreen with redirect to RecipeCreateForm:
floatingActionButton: FloatingActionButton(
onPressed: () => Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (BuildContext context) {
return RecipeCreateForm();
}),
),
and then I redirect to RecipeCreateForm where I'm using TextFormFields.
Whenever I try to use TextFormField I get redirected back to MainMenuScreen.
class RecipeCreateForm extends StatefulWidget {
#override
_RecipeCreateFormState createState() => _RecipeCreateFormState();
}
class _RecipeCreateFormState extends State<RecipeCreateForm> {
final _recipeNameController = TextEditingController();
final _imageUrl = TextEditingController();
String? _difficultyValue;
late int _ingredientsQuantity;
late int _preparationStepsQuantity;
late List<Ingredient> _ingredientsValues;
late List<PreparationStep> _preparationStepsValues;
late double _preparationTime;
String? _portions;
#override
void initState() {
_ingredientsQuantity = 1;
_preparationStepsQuantity = 1;
_ingredientsValues = [];
_preparationStepsValues = [];
_preparationTime = 0;
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: true,
appBar: AppBar(
leading: IconButton(
onPressed: () {
Navigator.of(context).pop();
},
icon: Icon(
Icons.arrow_back,
color: Colors.white,
),
),
),
body: Scrollbar(
thickness: 10,
hoverThickness: 2,
child: SingleChildScrollView(
child: Container(
color: Colors.lightGreen.shade100,
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 15),
),
Text(
'Recipe name',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
TextFormField(
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontStyle: FontStyle.italic),
controller: _recipeNameController,
),
Padding(
padding: EdgeInsets.only(top: 15),
),
Text(
'Image',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
TextFormField(
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontStyle: FontStyle.italic),
controller: _imageUrl,
),
Padding(
padding: EdgeInsets.only(top: 15),
),
Text(
'Difficulty',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
DropdownButton(
hint: _difficultyValue == null
? Text(
'Select difficulty',
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontStyle: FontStyle.italic),
)
: Text(
_difficultyValue!,
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontStyle: FontStyle.italic),
),
isExpanded: true,
iconSize: 30.0,
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontStyle: FontStyle.italic),
items: ['Easy', 'Medium', 'Hard'].map(
(val) {
return DropdownMenuItem<String>(
value: val,
child: Text(val),
);
},
).toList(),
onChanged: (val) {
setState(
() {
_difficultyValue = val as String;
},
);
},
),
Padding(
padding: EdgeInsets.only(top: 15),
),
Text(
'Preparation time',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Slider(
value: _preparationTime,
onChanged: (newPreparationTime) {
setState(() => _preparationTime = newPreparationTime);
},
label: _preparationTime.toStringAsFixed(0),
min: 0,
max: 360,
divisions: 24,
),
Padding(
padding: EdgeInsets.only(top: 15),
),
Text(
'Ingredients',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
SizedBox(
height: 175,
child: Scrollbar(
child: ListView.builder(
itemCount: _ingredientsQuantity,
itemBuilder: (context, index) {
return _ingredientRow(index);
}),
),
),
Row(
children: [
IconButton(
icon: Icon(Icons.add),
onPressed: () async {
setState(() {
_ingredientsQuantity++;
});
}),
IconButton(
icon: Icon(Icons.delete),
onPressed: () async {
setState(() {
_ingredientsQuantity = 1;
_ingredientsValues.clear();
});
})
],
),
Padding(
padding: EdgeInsets.only(top: 15),
),
Text(
'Preparation steps',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
Scrollbar(
child: SizedBox(
height: 100,
child: ListView.builder(
shrinkWrap: true,
itemCount: _preparationStepsQuantity,
itemBuilder: (context, index) {
return _preparationStepRow(index);
}),
),
),
Row(
children: [
IconButton(
icon: Icon(Icons.add),
onPressed: () async {
setState(() {
_preparationStepsQuantity++;
});
}),
IconButton(
icon: Icon(Icons.delete),
onPressed: () async {
setState(() {
_preparationStepsQuantity = 1;
_preparationStepsValues.clear();
});
}),
],
),
Padding(
padding: EdgeInsets.only(top: 15),
),
Text(
'Portions',
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 20),
),
DropdownButton(
hint: _portions == null
? Text(
'Select number of portions',
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontStyle: FontStyle.italic),
)
: Text(
_portions!,
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontStyle: FontStyle.italic),
),
isExpanded: true,
iconSize: 30.0,
style: TextStyle(
color: Colors.black,
fontSize: 18,
fontStyle: FontStyle.italic),
items: ['1', '2', '3', '4', '5', '6', '7'].map(
(val) {
return DropdownMenuItem<String>(
value: val,
child: Text(val),
);
},
).toList(),
onChanged: (val) {
setState(
() {
_portions = val as String;
},
);
},
),
ElevatedButton(
onPressed: () {
BlocProvider.of<RecipesBloc>(context).add(
AddRecipeEvent(
Recipe(
name: _recipeNameController.text,
image:
'https://www.thespruceeats.com/thmb/dA8o8EZpjJyeocYZNpzfknoKh2s=/4351x3263/smart/filters:no_upscale()/baked-stuffed-potatoes-482217-hero-01-850f2d87fe80403f923e140dbf5f1bf3.jpg',
ingredients: _ingredientsValues,
difficulty: _difficultyValue,
preparationTime: _preparationTime,
preparationSteps: _preparationStepsValues,
type: BlocProvider.of<RecipesBloc>(context)
.category
.toString(),
portions: _portions,
),
),
);
Navigator.of(context).pop();
},
child: Text('Submit'),
),
],
),
),
),
),
);
}
_ingredientRow(int key) {
return IntrinsicHeight(
child: Row(
children: [
Padding(padding: EdgeInsets.only(left: 10)),
SizedBox(
width: 225,
child: TextFormField(
maxLength: 35,
onChanged: (val) {
setState(() {
_onIngredientUpdate(key,name: val);
});
},
),
),
VerticalDivider(
width: 20,
thickness: 1,
color: Colors.black,
indent: 30,
endIndent: 10,
),
SizedBox(
width: 55,
child: TextFormField(
maxLength: 7,
initialValue: '0',
onChanged: (val) {
setState(() {
_onIngredientUpdate(key, quantity: val);
});
},
),
),
Padding(padding: EdgeInsets.only(left: 10)),
DropdownButton(
hint: Text('pcs'),
items: ['pcs', 'ml', 'g'].map(
(val) {
return DropdownMenuItem<String>(
value: val,
child: Text(val),
);
},
).toList(),
onChanged: (val) {
setState(() {
_onIngredientUpdate(key,measurement: val.toString());
});
},
)
],
),
);
}
_onIngredientUpdate(int key, {String? name, String? measurement, String? quantity}) {
int foundKey = -1;
_ingredientsValues.forEach((element) {
if (element.id.contains(key.toString())) {
foundKey = key;
}
});
if (-1 != foundKey) {
_ingredientsValues.removeWhere((map) {
return map.id == foundKey.toString();
});
}
Map<String, dynamic> json = {'id': key, 'name': name, 'measurement': measurement, 'quantity':quantity};
_ingredientsValues.add(json as Ingredient);
}
_preparationStepRow(int key) {
return IntrinsicHeight(
child: Row(
children: [
Padding(padding: EdgeInsets.only(left: 10)),
SizedBox(
width: 225,
height: 50,
child: TextFormField(
maxLength: 35,
onChanged: (val) => {
_onPreparationUpdate(key,val)
},
),
),
],
),
);
}
_onPreparationUpdate(int key, String val) {
int foundKey = -1;
_preparationStepsValues.forEach((element) {
if (element.id.contains(key.toString())) {
foundKey = key;
}
});
if (-1 != foundKey) {
_preparationStepsValues.removeWhere((map) {
return map.id == foundKey.toString();
});
}
Map<String, dynamic> json = {'id': key, 'step': val};
_preparationStepsValues.add(json as PreparationStep);
}
}
Issue GIF:
EDIT:
Issue is not related with form. I have replaced whole form with only one field without any logic and issue remains.
It is probably related to named routes.
As I was thinking, issue was related with usage of named routes.
I managed to bypass this issue with using Future.delayed and pushNamedAndRemoveUntil
In main_menu_screen I have created method which I later used to redirect to categories.
void redirectToCategory(BuildContext context, String categoryName) {
Future.delayed(Duration.zero, () {
Navigator.pushNamedAndRemoveUntil(
context,
'/recipeScreen',
(_) => false,
arguments: BlocProvider.value(
value: BlocProvider.of<RecipesBloc>(context)
..add(LoadRecipesEvent())
..category = categoryName,
child: RecipesScreen(),
),
);
});
I want to show PopupMenu with dynamic data which will I got from API response. I tried with listview.builder inside PopupMenu child but it not works.
My code of showmenu()
void showMemberMenu() async {
await showMenu(
context: context,
position: RelativeRect.fromLTRB(200, 150, 100, 100),
items: [
PopupMenuItem(
value: 1,
child: Text(
"ROHIT",
style: TextStyle(
fontSize: 15.sp,
fontWeight: FontWeight.bold,
fontFamily: 'Roboto',
color: green3),
),
),
PopupMenuItem(
value: 2,
child: Text(
"REKHA",
style: TextStyle(
fontSize: 15.sp,
fontWeight: FontWeight.bold,
fontFamily: 'Roboto',
color: green3),
),
),
PopupMenuItem(
value: 3,
child: Text(
"DHRUV",
style: TextStyle(
fontSize: 15.sp,
fontWeight: FontWeight.bold,
fontFamily: 'Roboto',
color: green3),
),
),
],
elevation: 8.0,
).then((value) {
if (value != null) print(value);
});
}
Please help to get out from this.
You can use List.generate method to generate the dynamic length of the list using the existing list you get from the API response.
Below is an example of how you can achieve it.
void showMemberMenu() async {
final List<String> popList = ['ROHIT', 'REKHA', 'DHRUV'];
await showMenu(
context: context,
position: RelativeRect.fromLTRB(200, 150, 100, 100),
items: List.generate(
popList.length,
(index) => PopupMenuItem(
value: 1,
child: Text(
popList[index],
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.bold,
fontFamily: 'Roboto',
),
),
),
),
elevation: 8.0,
).then((value) {
if (value != null) print(value);
});
}
I've added final List<String> popList = ['ROHIT', 'REKHA', 'DHRUV'];
just for the testing purpose, and you can replace it with your list
you get from API response.
Try this:
call the api and put the value in PopupMenuItem
class PopupMenu {
PopupMenu({#required this.title, #required this.onTap});
final String title;
final VoidCallback onTap;
static PopupMenuButton<String> createPopup(List<PopupMenu> popupItems) {
return PopupMenuButton<String>(
onSelected: (value) {
popupItems.firstWhere((e) => e.title == value).onTap();
},
itemBuilder: (context) => popupItems
.map((item) => PopupMenuItem<String>(
value: item.title,
child: Text(
item.title,
),
))
.toList(),
);
}
}
you can try to do something like this:
https://dartpad.dev/?id=a3f9002a37cbacc2cfae46174cbd2eba
you can add any state management to replace the FutureBuilder but this is the logical approach.
I hope it is helpful.
is it possible to add widget input text field when I selected item 'other' dropdown in flutter?
this is for flutter mobile android
my code
List <String> klasifikasi = [
'Fatality',
'Lainnya'];
DropdownButton<String>(
focusColor:Colors.white,
value: _chosenValue,
//elevation: 5,
style: TextStyle(color: Colors.white),
iconEnabledColor:Colors.blue,
items: klasifikasi.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value,style:TextStyle(color:Colors.black),),
);
}).toList(),
hint:Text(
"Klasifikasi Insiden",
style: TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w400),
),
onChanged: (String value) {
setState(() {
_chosenValue = value;
if (_chosenValue == klasifikasi){
return Scaffold(
appBar: AppBar(),
body: Center(
child: Container(
color: Colors.grey,
child: _buildTextField(
labelText: 'Lainnya',
controller: _lainCtrl,
),
),
),
);
}
});
},
),
when I selected 'lainnya' showing textfield to input value
Maybe you can try this,
bool _showTextField = false;
Column(
children: [
DropdownButton<String>(
focusColor: Colors.white,
value: _chosenValue,
//elevation: 5,
style: TextStyle(color: Colors.white),
iconEnabledColor: Colors.blue,
items: klasifikasi.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(
value,
style: TextStyle(color: Colors.black),
),
);
}).toList(),
hint: Text(
"Klasifikasi Insiden",
style: TextStyle(
color: Colors.black,
fontSize: 14,
fontWeight: FontWeight.w400),
),
onChanged: (String value) {
setState(() {
_chosenValue = value;
if (_chosenValue == klasifikasi.last) {
_showTextField = true;
} else {
_showTextField = false;
}
});
}),
Visibility(
visible: _showTextField,
child: //Your textfield here,
),
],
);
i hope that will be help you ^_^
Put you dropdown button in a column,
bool addtextfield = false;
if (_chosenValue == klasifikasi){
setState((){
addtextfield = true;
});
}
addtextfield == true?
//Show ur input field
:Container(),
so I have a PopUpMenuButton which has some choices,
PopupMenuButton(
icon: Icon(
Icons.tune,
color: FortBuddyTheme.grey,
size: 24,
),
color: Colors.white,
onCanceled: () {},
onSelected: (int value) {
if (selectedChart == 0) {
setState(() {
playlist = 'p2';
name = 'Solo';
color = 0xff4af699;
});
} else if (selectedChart == 1) {
setState(() {
playlist = 'p10';
name = 'Duo';
color = 0xFFA29BFE;
});
} else if (selectedChart == 2) {
setState(() {
playlist = 'p9';
name = 'Squad';
color = 0xFF0984E3;
});
}
},
itemBuilder: (_) => [
PopupMenuItem(
child: Text(
'Show Solos Chart',
style: TextStyle(
fontFamily:
FortBuddyTheme.fontName,
fontSize: 16,
letterSpacing: 0.5,
height: 0.9,
color:
FortBuddyTheme.darkerText,
),
),
value: 0,
),
PopupMenuItem(
child: Text(
'Show Duos Chart',
style: TextStyle(
fontFamily:
FortBuddyTheme.fontName,
fontSize: 16,
letterSpacing: 0.5,
height: 0.9,
color:
FortBuddyTheme.darkerText,
),
),
value: 1,
),
PopupMenuItem(
child: Text(
'Show Squad Chart',
style: TextStyle(
fontFamily:
FortBuddyTheme.fontName,
fontSize: 16,
letterSpacing: 0.5,
height: 0.9,
color:
FortBuddyTheme.darkerText,
),
),
value: 2,
),
],
)),
It has a few if statements which check and set some variables, those variables are used by a widget, but even if the variables change under setState, the widget is not rebuilding.
(widget)
MainContainer(
animation: Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
parent: animationController,
curve:
Interval((1 / count) * 3, 1.0, curve: Curves.fastOutSlowIn))),
animationController: animationController,
title: name,
key: UniqueKey(),
child: LineChartSample1(
key: UniqueKey(),
playlist: playlist,
title: '$name' + 's',
color: color,
),
),
Also note, the widget is being added through a addAllListData() function which adds all the widgets to a list, that list later gets build by a builder method.
Change your if statements, use value instead of selectedChart. Like this:
if (value == 0)
instead of
if (selectedChart == 0)