I am pretty new to flutter and am practicing using drop down button. So I made a basic app which allows you to create new string values and store then on the cloud and then populate the dropdownbutton with the values. I want to add a functionality that we can edit the member of the dropdropmenu item by long pressing it.
This is my drop down menu button
StreamBuilder<QuerySnapshot>(
stream: _fireStore.collection("items").orderBy("value").snapshots(),
builder: (context, snapshots) {
if (!snapshots.hasData) {
CircularProgressIndicator();
}
return DropdownButton(
items: itemList,
value: dropDownValue,
onChanged: (newValue) {
setState(() {
dropDownValue = newValue;
});
},
);
},
),
This is the drop down menu item
List<DropdownMenuItem> itemList = [];
String dropDownValue;
void getList() async {
await for (var snapshot in _fireStore.collection("items").snapshots()) {
itemList.clear();
for (var message in snapshot.documents) {
itemList.add(DropdownMenuItem(
value: message.data["value"].toString(),
child: Text(
message.data["value"],
),
));
}
}
}
I couldn't find any other solution regarding this.
Wrap your items each in a GestureDetector and pass a function to the LongPress() callback to update the cloud data.
Related
I'm building a todo app in which CRUD operations can be carried out. With the help of Sqlflite todo items will be stored/retrieved from the database. When i try to delete todos, it works just as fine except the last item in the list view remains even after deleting it from the database. But, once the app restarts the last item gets removed. Tried setState() manually to refresh the page but nothings works. Help me sort this out and thanks in adavance.
//Holds the todos and will be passed to ListView.builder
List<Todo> listOfValues;
//Holds the indexes of selected items to highlight the items in the ListView
List<int> indexes = [];
//Holds the ids of selected items to perform CRUD
List<int> selectedItems = [];
//This is where the todos are retrieved from the database.
//Following DatabaseModel is a database helper class
DatabaseModel data = DatabaseModel();
data.queryingData().then((value) {
setState(() {
if (value.isNotEmpty) {
listOfValues = value;
}
});
});
Following block builds/returns the body of the main page of the app which is a ListView contains todos.
Widget content() {
return SafeArea(
child: Container(
child: ListView.builder(
itemBuilder: (context, index) {
return Card(
color: (indexes.contains(index))
? Colors.blue.withOpacity(0.5)
: Colors.transparent,
child: ListTile(
title: Text(listOfValues[index].todo),
subtitle: Text('Planned on ${listOfValues[index].date}'),
trailing: Text(listOfValues[index].time),
onLongPress: () {
setState(() {
lonPressEnabled = true;
if (!indexes.contains(index)) {
indexes.add(index);
selectedItems.add(listOfValues[index].id);
}
});
},
onTap: () {
setState(() {
if (indexes.isNotEmpty && lonPressEnabled) {
if (indexes.contains(index)) {
indexes.remove(index);
selectedItems.remove(listOfValues[index].id);
} else {
indexes.add(index);
selectedItems.add(listOfValues[index].id);
}
}
});
},
),
);
},
itemCount: listOfValues.length,
),
),
);}
Following block is used to delete items from database
ElevatedButton(
onPressed: () {
lonPressEnabled = false;
DatabaseModel model = DatabaseModel();
for (int i = 0; i < selectedItems.length; i++) {
model.deletingRecord(selectedItems[i]);
//selectedItems holds the ids of each todos
}
selectedItems = [];
indexes = [];
setState(() {
print();
});
},
child: const Icon(Icons.delete));
Following block deletes todo from the database
deletingRecord(int ids) async {
Database db = await openingDB;
db.rawDelete('delete from ToDos where id=$ids');}
this is firebase pic i want these keys in drop down listI only know how to print key on debug console(code given) but I cannot map them with drop down button
void fall() async {
final ref = FirebaseDatabase.instance.ref();
final snapshot = await ref.child("/Exam/fall 2022").get();
if (snapshot.exists) {
print(snapshot.children.length);
final examvalue = snapshot.children.forEach((DataSnapshot dataSnapshot) {
print(dataSnapshot.key);
});
} else {
print('No data available.');
}
}
Something like this should do the trick:
FutureBuilder(
future: ref.child("/Exam/fall 2022").get(),
builder: (context, AsyncSnapshot<DataSnapshot> asyncSnapshot) {
if (asyncSnapshot.hasError) return Text("Error: ${asyncSnapshot.error}");
if (!asyncSnapshot.hasData) return const CircularProgressIndicator();
return DropdownButton<String>(
items: asyncSnapshot.data!.children.map((snap) => DropdownMenuItem(value: snap.key, child: Text(snap.key))).toList(),
onChanged: (String? newValue) {
...
},
);
}
),
This code is modified from something I needed myself yesterday, but I didn't try to run this exact code. So if you get any syntax or other errors while running this code, please search for the error message first - as it's likely been covered before.
Following on from Unhandled Exception: type '_DropdownRouteResult<int>' is not a subtype of type 'int?' when returning value from Navigator.pop() as I still haven't resolved the issue.
I have a DropdownFormField which I am dynamically populating from a db via a Provider. I would like to add a DropdownMenuItem which, when selected, pushes a new route (for inserting a new record into the db).
The route returns the id of the newly-inserted db record when popped, and I would like to set the new value as the value of the DropdownFormField.
Implementing the new item with a TextButton child and pushing in the buttons' onPressed results in expected push/pop behaviour, but is styled inconsistently from the "normal" items, and does not close the dropdown (which makes sense as the button is pressed, but the DropdownMenuItem is not tapped). Tapping outside the dropdown after popping reveals that the dropdown's value is updated correctly.
DropdownMenuItem<int>(child: TextButton(
onPressed: () async {
final int newValue = await Navigator.push(context, AddNewTeaProducerRoute());
setState(() {
_selectedValue = newValue;
});
},
child: Text('Add New Manufacturer')));
Implementing the new item with a Text child and pushing in the DropdownMenuItem's onTap (which seems like the correct approach) results in an immediate attempt to return the value, disrespecting the asynchronous nature of the onTap and resulting in the exception from my previous question. Breakpoint debugging without specifying the type of newValue shows that it is immediately assigned the Future/_DropdownRouteResult<int>, rather than awaiting its returned int.
DropdownMenuItem<int>(
onTap: () async {
final int newValue = await Navigator.push(context, AddNewTeaProducerRoute());
setState(() {
_selectedValue = newValue;
});
},
child: const Text('Add New Manufacturer'));
I have no idea why await is being respected in TextButton.onPressed but not in DropdownMenuItem.onTap
I don't know if it's the right way, since it relies on null as a placeholder value and I can't see how you'd easily scale it beyond a single DropdownMenuItem with special behaviour (as unlikely as it seems that you'd want to) but after reading this for the third time I finally grokked a solution - return null as the value, and perform navigation/assignment in the DropdownButtonFormField's onChanged
final brokenAddNewTeaProducerButton = DropdownMenuItem<int>(
value: null,
child: const Text('Add New Manufacturer'));
return DropdownButtonFormField<int?>(
value: _selectedValue,
items: [brokenAddNewTeaProducerButton] + teaProducerListItems,
onChanged: (value) async {
if (value == null) {
final newTeaProducerId = await Navigator.push(context, AddNewTeaProducerRoute());
setState(() {
_selectedValue = newTeaProducerId;
});
} else {
setState(() {
_selectedValue = value;
});
}
},
hint: Text('Select a manufacturer'),
);
}
**You can try this statfulBuilder**
StatefulBuilder(builder: (context,state){
return DropdownMenuItem<int>(child: TextButton(
onPressed: () async {
var newValue = await Navigator.push(context,
AddNewTeaProducerRoute());
state(() {
_selectedValue = newValue;
});
},
child: Text('Add New Manufacturer')));
}),
This is my drop down list code
String dropdownValue = "a";
#override
Widget build(BuildContext context) {
return DropdownButton<String>(
value: dropdownValue
),
onChanged: (String? newValue) {
setState(() {
dropdownValue = newValue!;
});
},
items: <String>[
'a','b','c'
].map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
);
}
}
I want to use the selected value here
uploadDatatoFirebase() async {
*firebase connection code*
await FirebaseFirestore.instance
.collection(**selected value in drop down**) // the value from the box goes here
}
I can't seem to find a solution please help i want to create a database depending on the select box value any other techniques will also be welcomed
https://pub.dev/packages/get_it This is the package I use so I can can access a single instance of a class anywhere in the app, it is very popular and very well maintained. You could do this
setState(() {
dropdownValue = newValue!;
GetIt.I.get<ControllerWhatever>().selectedValueInDropDown = newValue!;
})
Just register the 'global controller instance' just as described in the get_it page and you will be able to get the value from any attribute you want anywhere in your app. It will make you life a lot easier
this the constructor I made
class User {
String strength1;
String strength2;
String strength3;
String strength4;
String strength5;
User({this.strength1, this.strength2, this.strength3,this.strength4,this.strength5});
Map<String, dynamic>toMap(){
var map = <String, dynamic>{
'strength1' : strength1,
'strength2' : strength2,
'strength3' : strength3,
'strength4' : strength4,
'strength5' : strength5,
};
return map;
}
User.fromMap(Map <String, dynamic>map){
strength1 = map['strength1'];
strength2 = map['strength2'];
strength3 = map['strength3'];
strength4 = map['strength4'];
strength5 = map['strength5'];
}
}
and then the loop should be
List<Widget> createRadioListUsers() {
List<Widget>widgets = [];
for (User user in users) {
widgets.add(
RadioListTile(
value: user,
groupValue: selectedUser,
title: Text(user.strengths),
onChanged: (currentUser) {
print('Current User ${currentUser.strengths}');
setSelectedUser(currentUser);
},
selected: selectedUser == user,
activeColor: Colors.green,
);
}
return widgets;
}
I learned this loop from a tutorial and tried changing the type of constructor but apparently the values for the radio button won't work since in the tutorial the values were already given but for the program I'm making I have to get the user input from another page to this page and show them in a radio list tile
Here is a solution using StreamBuilder:
First, let's declare the controller, the stream and getter
final BehaviorSubject<User> _radioController = BehaviorSubject<User>();
Stream<User> get radioStream => _radioController.stream;
Function(User) get updateRadio => _radioController.sink.add;
Then, put the builder in your widget
StreamBuilder(
stream: radioStream,
initialData: null,
builder: (BuildContext context, AsyncSnapshot<User> snapshot) {
for (User user in users) {
widgets.add(
RadioListTile(
value: user,
groupValue: snapshot.data, // The value will be updated when you select an other radio
title: Text(user.strengths),
onChanged: updateRadio, // Update the groupValue
selected: selectedUser == user,
activeColor: Colors.green,
);
},
// Return your widgets, for example Column(children: widgets),
},
),
...