i am getting values from server to dropdown which are inserted previous from static list of dropdown values, but i need to use dropdown when value from server is 'Pending' to update specific record, below my code.
List<String> approvalList = ['Pending', 'Approve', 'Discard'];
String dropdownValue="Pending";
Container(
height: MediaQuery.of(context).size.height*0.3,
width: MediaQuery.of(context).size.width,
child:StreamBuilder<List<ApprovalModel>>(
stream: bloc.approvalsStream,
initialData: [],
builder: (context, snapshot) {
return ListView.builder(
itemCount: snapshot.data!.length,
itemBuilder: (context,i){
return snapshot.connectionState==ConnectionState.waiting?Lottie.asset(
'assets/lottieloading.json',
width: 70,
height: 70,
fit: BoxFit.fill,
):ListTile(
title: Text(snapshot.data![i].approverName),
trailing: StatefulBuilder(
builder: (BuildContext context, StateSetter setState) {
return DropdownButton<String>(
value: snapshot.data![i].status==0?'Pending':
snapshot.data![i].status==1?'Approve':
'Discard',
items: approvalList.map((String val) {
return DropdownMenuItem<String>(
value: val,
child: new Text(val),
);
}).toList(),
hint: Text(selectedValue),
onChanged: (val) {
setState(() {
dropdownValue = val!;
});
});
}
),
);
});
}
)
,
),
As You see i am setting value from server it is working fine, but when the value is pending i want to use the dropdown to update record in database.
At onChanged when you update your dropdownValue , also call the method you are using to update record in database.
Related
I am currently attempting to make a user search list within an alert dialog, which will query users from the project's database based on the user's search input. I am doing this in Android Studio, using Flutter's native language (Dart) and Firebase Cloud Firestore. I have the search bar itself working, but for some reason, whenever I try to actually get the results from the database, my code will access the stream for the Streambuilder being used, but will never touch the actual builder, skipping it entirely. What exactly am I doing wrong here?
The function responsible for creating the alert dialog:
Future createAlertDialog(BuildContext context){
String userToSearch = '';
bool showUsers = false;
return showDialog(context: context, builder: (context){
return AlertDialog(
title: const Text("Search for a user:"),
content: StatefulBuilder(
builder: (context, setState) => Container(
child: CupertinoSearchTextField(
onChanged: (value) => {
setState(() {
showUsers = true;
}),
showUsers
? Expanded(
child: StreamBuilder(
stream: FireStoreMethods().searchUsers(value),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.waiting) {
return const Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.connectionState ==
ConnectionState.none) {
return const Center(child: Text("Internet error"));
}
if (snapshot.hasError) {
return const Center(
child: Text("Something went wrong."),
);
}
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.docs.length,
itemBuilder: (context, index) {
return ListTile(
onTap: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => ProfileScreen(
uid: snapshot.data!.docs[index]['uid'],
),
),
),
leading: CircleAvatar(
backgroundImage: NetworkImage(
snapshot.data!.docs[index]['photoUrl'],
),
radius: 16,
),
title: Text(
snapshot.data!.docs[index]['username'],
),
);
},
);
},
),
)
: const Expanded(child: Text("error"))
}
),
),
)
);
});
}
Function responsible for querying the database:
Stream searchUsers(String userInput){
String? currentUserID = FirebaseAuth.instance.currentUser?.uid;
//String? valueFromFirebase = '';
Stream s = FirebaseFirestore.instance.collection('users').where('username', isGreaterThanOrEqualTo: userInput).orderBy('username', descending: false).snapshots();
return s;
}
To be clear, I expected this code to create a list of users from the database, under the search bar in the alert dialog, containing the users that match the current input. I tried debugging, changing the positioning of certain lines of code, and comparing and contrasting my code to code I found all over the internet. The actual result that I received was the ability to use the search bar and have the input saved properly, but literally nothing happens after pressing enter. No list is rendered, no error is thrown, and the program continues like nothing happened.
You need to place StreamBuilder inside widget tree to make it visible. Currently having inside onChanged which is just callback method for textFiled.
Future createAlertDialog(BuildContext context) {
String userToSearch = '';
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("Search for a user:"),
content: StatefulBuilder(
builder: (context, setState) => Column(
children: [
CupertinoSearchTextField(
onChanged: (value) {
setState(() {
userToSearch = value;
});
},
),
userToSearch.isNotEmpty
? Expanded(
child: StreamBuilder(
stream: FireStoreMethods().searchUsers(userToSearch),
...........
),
)
: Text("Empty")
],
),
),
);
});
Hi in my flutter app have FutureBuilder that return listview, my list listview create some button for update the hive table. when I click the first time on one of buttons everything is run smoothly, but when I click on same button again my hive key turn to null and program show my this error: "type 'Null' is not a subtype of type 'int' "
I write print all over my code but still I do not get it why the key turn null from the second time.
How can I Correct this? please help my.
my Futurebuilder body is:
FutureBuilder<List>(
future: controller.showTaskList(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return SizedBox(
height: Get.height,
child: const Center(
child: CircularProgressIndicator(),
),
);
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List data = snapshot.data ?? [];
return ListView.separated(
scrollDirection: Axis.vertical,
physics:
const BouncingScrollPhysics(),
shrinkWrap: true,
itemCount: data.length,
itemBuilder: (context, index) {
// controller.taskIconCheckList
// .clear();
for (int i = 0;
i < data.length;
i++) {
if (data[i].status == true) {
controller.taskIconCheckList
.add(true.obs);
} else {
controller.taskIconCheckList
.add(false.obs);
}
}
return ListTile(
leading: Obx(
() => PageTransitionSwitcher(
transitionBuilder: (
child,
primaryAnimation,
secondaryAnimation,
) {
return SharedAxisTransition(
animation:
primaryAnimation,
secondaryAnimation:
secondaryAnimation,
transitionType:
SharedAxisTransitionType
.horizontal,
fillColor:
Colors.transparent,
child: child,
);
},
duration: const Duration(
milliseconds: 800),
child: controller
.taskIconCheckList[
index]
.value
? SizedBox(
child: IconButton(
icon: const Icon(
Icons
.check_circle_rounded,
color: Colors
.lightGreenAccent,
),
onPressed: () {
controller
.functionTaskIconCheckList(
index,
);
print('طول دیتا');
print(data.length.toString());
print('مقدار ایندکس');
print(index.toString());
print('مقدار کلید');
print(data[index].key.toString());
print(data[index].taskText.toString());
controller
.updateStatusTask(
index,
data[index]
.key); // here when i first click // return key currectly, but after that show null and updatestatusetask not run and show error.
},
),
)
: IconButton(
onPressed: () {
controller
.functionTaskIconCheckList(
index,
);
print('طول دیتا');
print(data.length.toString());
print('مقدار ایندکس');
print(index.toString());
print('مقدار کلید');
print(data[index].key.toString());
print(data[index].taskText.toString());
controller
.updateStatusTask(
index,
data[index]
.key); // here when i first click // return key currectly, but after that show null and updatestatusetask not run and show error.
},
icon: const Icon(
Icons
.radio_button_unchecked_outlined,
color: Colors.red,
),
),
),
),
title: Text(data[index].taskText,
style: normalTextForCategory),
subtitle: Text(
data[index]
.date
.toString()
.substring(0, 10),
textDirection:
TextDirection.ltr,
textAlign: TextAlign.right,
style: normalTextForSubtitle,
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () {
myDefaultDialog(
'هشدار',
'آیا از حذف این گزینه اطمینان دارید؟',
'بله',
'خیر',
() {
Get.back();
mySnakeBar(
'',
'گزینه مورد نظر با موفقیت حذف شد.',
Icons
.warning_amber_rounded,
Colors.yellow);
},
);
},
icon: const Icon(
Icons.delete),
color: Colors.redAccent,
),
IconButton(
onPressed: () {
Get.offNamed(
Routs.editTaskScreen,
arguments: 'edit');
},
icon: const Icon(
Icons.edit_calendar,
color:
Colors.yellowAccent,
),
),
],
),
);
},
separatorBuilder:
(BuildContext context,
int index) {
return const Divider(
height: 2,
color: Colors.white70,
);
},
);
}
}
},
),
this is my functionTaskIconCheckList form controller:
functionTaskIconCheckList(int index) {
taskIconCheckList[index].value = !taskIconCheckList[index].value;}
and this the updatestatusetask function
updateStatusTask(int index,int taskKey) async {
print('در تابع آپدیت ایندکس هست: ${index.toString()}');
print('در تابع آپدیت کی هست: ${taskKey.toString()}');
var taskBox = await Hive.openBox('task');
var filterTask = taskBox.values.where((task) => task.key == taskKey).toList();
Task task = Task(
filterTask[0].taskText,
filterTask[0].date,
taskIconCheckList[index].value,
filterTask[0].deleteStatus,
null,
null,
filterTask[0].taskCatId,
filterTask[0].userId);
await taskBox.put(taskKey, task);}
and this is my showtasklist function:
Future<List> showTaskList() async {
SharedPreferences sharedPreferences = await SharedPreferences.getInstance();
var taskBox = await Hive.openBox('task');
var filterTask = taskBox.values
.where((task) => task.userId == sharedPreferences.getInt('key'))
.toList();
return filterTask;}
this is my model:
#HiveType(typeId: 2)
class Task extends HiveObject{
#HiveField(0)
String taskText;
#HiveField(1)
DateTime date;
#HiveField(2)
bool status;
#HiveField(3)
bool deleteStatus;
#HiveField(4)
int taskCatId;
#HiveField(5)
int userId;
#HiveField(6)
User? user;
#HiveField(7)
TaskCat? taskCat;
Task(this.taskText, this.date, this.status, this.deleteStatus, this.user,
this.taskCat, this.taskCatId, this.userId);
}
One possible solution would be to wait for the Future function to finish and then load the list. If it tries to load the list early before finishing up the Future function, it might presume the value to be null.
Hope this helps.
Still I do not know what is cause this problem, But I found an alternative temporary solution. I create temporary Int list. then just before the return listTile in the futureBuilder body, I write the Loop and save all of the key in that list. finally instead of pass the "data[index].key." I pass my key from that temporary Int list. so everything work fine now
this is my part of code change from before, but still I want know main solution.
return ListView.separated(
scrollDirection: Axis.vertical,
physics:
const BouncingScrollPhysics(),
shrinkWrap: true,
itemCount: data.length,
itemBuilder: (context, index) {
// controller.taskIconCheckList
// .clear();
for (int i = 0;
i < data.length;
i++) {
Get.find<
HomeScreenController>()
.taskKey.add(data[i].key);
if (data[i].status == true) {
Get.find<
HomeScreenController>()
.taskIconCheckList
.add(true.obs);
} else {
Get.find<
HomeScreenController>()
.taskIconCheckList
.add(false.obs);
}
}
return ListTile(
In my application user can have multiple home and multiple rooms for each home. On top of my application I have dropdown box which im trying to set default value to selectedHome by user. Below that dropdown box I am showing the rooms in the home selected by user. In firebase I have rooms collection under each home. I'm getting the selected home data from firebase too. Also to show the rooms in selected home i need to query by home name. I have two FutureBuilder as you can see code below. One of them to get the selectedHome data from firebase and other for the getting the rooms in that home from firebase. As I said before to get the rooms in selected home I need to query by name of the home so I have a parameter which is the value of dropdownbox. In my code the problem is getting the rooms part is working before I get the selectedHome data from firebase and assign it to dropdown value. In this case I'm getting "Null check operator used on a null value".
Basicly the question is how can i assign value from future to variable before screen gets build.
Here you can see the code for getting selected home data from firebase;
Future<String> selectedHome() async {
return await database.selectedHome();
}
Future<String> selectedHome() async {
DocumentSnapshot docS =
await firestore.collection("users").doc(auth.currentUser()).get();
String selectedHome = (docS.data() as Map)["selectedHome"];
return selectedHome;
}
Here you can see the code for getting room data based on selectedHome from firebase;
Future<List<Map>> deviceAndRoomInfo() async {
return database.numberOfRooms(_dropdownValue!);
}
Future<List<Map>> numberOfRooms(String selectedHome) async {
List<Map> prodsList = [];
final snapshot = await firestore
.collection("users")
.doc(auth.currentUser())
.collection("homes")
.doc(selectedHome)
.collection("rooms")
.get();
List listOfRooms = snapshot.docs;
for (int a = 1; a <= listOfRooms.length; a++) {
var productsInRoom = await firestore
.collection("users")
.doc(auth.currentUser())
.collection("homes")
.doc(selectedHome)
.collection("rooms")
.doc(listOfRooms[a - 1]["roomName"])
.collection("products")
.get();
List prodList = productsInRoom.docs
.map((e) => DeviceModel.fromMap(e.data()))
.toList();
Map qq = {
"roomName": listOfRooms[a - 1]["roomName"],
"deviceInfo": prodList
};
prodsList.add(qq);
}
return prodsList;
}
Here you can see the code for screen contains 2 future builder that i told;
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:shelly_ess_production/constants.dart';
import 'package:shelly_ess_production/helper_widgets/loading_widget.dart';
import 'package:shelly_ess_production/screens/home_screen/components/circle_room_data.dart';
import 'package:shelly_ess_production/screens/home_screen/components/device_in_room_card.dart';
import 'package:shelly_ess_production/screens/home_screen/provider/home_screen_provider.dart';
import 'package:shelly_ess_production/screens/models/device_model.dart';
import 'package:shelly_ess_production/size_config.dart';
class Body extends StatefulWidget {
const Body({Key? key}) : super(key: key);
#override
State<Body> createState() => _BodyState();
}
class _BodyState extends State<Body> {
#override
Widget build(BuildContext context) {
var providerHelper =
Provider.of<HomeScreenProvider>(context, listen: false);
return SafeArea(
child: Padding(
padding:
EdgeInsets.symmetric(horizontal: getProportionateScreenWidth(0.07)),
child: SingleChildScrollView(
child: Column(
children: [
SizedBox(
height: getProportionateScreenHeight(0.02),
),
Consumer<HomeScreenProvider>(builder: (context, data, child) {
return FutureBuilder<List<String>>(
future: data.getHomesAndSelected(),
builder: (context, snapshot) {
if (snapshot.hasData) {
data.setDropDownValue = snapshot.data![0];
return DropdownButtonHideUnderline(
child: DropdownButton(
iconEnabledColor: kPrimaryColor,
iconDisabledColor: kPrimaryColor,
style: TextStyle(
color: kPrimaryColor,
fontSize: getProportionateScreenHeight(0.05)),
menuMaxHeight: getProportionateScreenHeight(0.4),
borderRadius: BorderRadius.circular(15),
key: UniqueKey(),
value: data.dropdownValue,
isExpanded: true,
icon: const Icon(Icons.arrow_downward),
onChanged: (String? newValue) async {
data.setDropDownValue = newValue;
await data.changeSelectedHome();
},
items: snapshot.data!
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
alignment: Alignment.center,
value: value,
child: Text(value),
);
}).toList(),
),
);
} else {
return Transform.scale(
scale: 0.5,
child: const Center(
child: CircularProgressIndicator(),
),
);
}
});
}),
SizedBox(
height: getProportionateScreenHeight(0.02),
),
SizedBox(
height: getProportionateScreenHeight(0.14),
child: ListView.builder(
shrinkWrap: true,
scrollDirection: Axis.horizontal,
itemCount: 5,
itemBuilder: (context, index) {
return CircleRoomData(
title: "Oda Sayısı",
icon: Icons.meeting_room,
content: "8",
);
}),
),
Consumer<HomeScreenProvider>(builder: (context, data, snapshot) {
return FutureBuilder<List<Map>>(
future: data.deviceAndRoomInfo(data.dropdownValue!),
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
physics: const NeverScrollableScrollPhysics(),
itemCount: snapshot.data!.length,
shrinkWrap: true,
itemBuilder: (context, index) {
return Column(
children: [
Divider(
thickness:
getProportionateScreenHeight(0.002),
),
Text(
snapshot.data![index]["roomName"],
style: TextStyle(
fontWeight: FontWeight.bold,
color: kSecondaryColor,
fontSize:
getProportionateScreenHeight(0.03)),
),
SizedBox(
height: getProportionateScreenHeight(0.01),
),
Text(
"${(snapshot.data![index]["deviceInfo"] as List).length.toString()} Cihaz",
style:
const TextStyle(color: kSecondaryColor),
),
SizedBox(
height: getProportionateScreenHeight(0.02),
),
GridView.builder(
shrinkWrap: true,
physics:
const NeverScrollableScrollPhysics(),
itemCount: (snapshot.data![index]
["deviceInfo"] as List)
.length,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
),
itemBuilder: (context, indexx) {
print(index);
return DeviceInRoom(
icon: Icons.light,
productName: ((snapshot.data![index]
["deviceInfo"]
as List)[indexx] as DeviceModel)
.deviceName,
);
})
],
);
});
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
});
}
)
],
)),
),
);
}
}
Am not certain where your error is coming from, but from what I see it maybe as a result of one of your functions returning null and a rendering of your content happens before the data is received.
You could try one of these:
You could declare the return type of your feature as being nullable for example you are expecting a value of type int:
Future<int?> xyz(){
......
return .....;
}
Now because your return type is nullable you wont have an issues as long as the receiving variable is also nullable.
Alternatively:
Future<int?> xyz(){
......
return ..... ?? 10 /*some default value*/;
}
because you know you result could be null you could also provide an optional default value incase your Future call returns a null value.
I created a map called "records", the keys of this map are taken from the user when presed on 'save' botton, and the values are from the time counter that I have in my code.
But the problem is when I create a ListView.builder to export this map indexes to cards, it gave me Null values in each card index !!!
How can I show the real value instead of Null ?!!
Here is my code:
var _item;
List listCount = [];
Map<String, dynamic> records = {};
String name;
createAlertDialog(buildContext, context) {
TextEditingController controller;
return showDialog(
context: context,
// barrierDismissible: false,
builder: (context) {
return AlertDialog(
title: Text(
'Type record name',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18.0),
),
content: TextField(
controller: controller,
onChanged: (value) {
name = value;
}),
actions: [
MaterialButton(
elevation: 5.0,
child: Text('Save'),
onPressed: () {
listCount.add(_item);
print(_item);
records[name] = _item;
print(records);
Navigator.pop(context);
},
),
MaterialButton(
elevation: 5.0,
child: Text('Cancel'),
onPressed: () {
Navigator.pop(context);
},
),
],
);
},
);
}
The variable _item is taking it's value from another site, see this:
StreamBuilder<int>(
stream: _stopWatchTimer2.rawTime,
initialData: 0,
builder: (context, snap) {
final value = snap.data;
final displayTime = StopWatchTimer.getDisplayTime(
value,
hours: _isHours2);
_item = displayTime;
return Padding(
padding: EdgeInsets.all(5.0),
child: Text(displayTime,
style: TextStyle(
fontSize: 30.0, color: Colors.white)),
);
},
),
And here where I create the ListView.builder:
ListView.builder(
scrollDirection: Axis.vertical,
shrinkWrap: true,
itemCount: records.length,
itemBuilder: (context, index) {
return MyCard(
colour: Colors.cyanAccent,
maker: Container(
width: 250.0,
height: 75.0,
child: Text(
'${records[index]}',
style: TextStyle(fontSize: 25.0),
textAlign: TextAlign.center,
),
),
);
},
),
The image in the link is a screen shot from my app.
Image
Looking at the _item variable which is initially null. You are not assigning any value to it. Please check your code. You are assigning a null value to your records because _item has not been given any value.
onChanged: (value){
_item = value;
}
you are not giving any value to the _item variable, I'm assuming you wanted to assign the value in the onChanged event like this:
onChanged: (value) {
_item = value;
}
or maybe you wanted to add the name to the list instead?
If I am understanding correctly, you have stored the item values into the records based on name variable in Save button onPressed event. But you are getting the record through the ListView index values. So, there is no record found in the records collection based on that index. So it returns null value.
Provide the index to store the item in the Save button instead of name. Or check the record based on name inside the ListView builder instead of index.
I'm new with Flutter. Currently I am trying to do the CRUD. But then I got some error to delete the data by ID. I did manage to do the delete operation but then it will delete the latest inserted data instead, not the data that onTap. Here I attach my source code.
String docId;
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('taks');
DocumentSnapshot ds;
return new StreamBuilder(
stream: users.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return new Text('Loading...');
return new ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
ds = snapshot.data.docs[index];
// children: snapshot.data.docs.map((document) {
return new ListTile(
title: new Text(ds['task']),
subtitle: Wrap(
children: <Widget>[
Text("Priority: " + ds['priority']),
Text(" | Status: " + ds['status']),
],
),
onTap: (){
docId = ds.id;
print(docId);
},
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
IconButton(
icon: Icon(
Icons.update_rounded,
size: 20.0,
color: Colors.brown[900],
),
onPressed: () {
Navigator.push(context, MaterialPageRoute(
builder: (context) => UpdateScreen(docId)));
}
),
IconButton(
icon: Icon(
Icons.delete_outline,
size: 20.0,
color: Colors.brown[900],
),
onPressed: () async {
try {
FirebaseFirestore.instance
.collection("taks")
.doc(docId)
.delete()
.then((_) {
print("success!");
});
}
catch (e) {
print("ERROR DURING DELETE");
}
// _onDeleteItemPressed(index);
},
),
],
),
// subtitle: new Text(document['priority']),
);
});
// );
},
);
So, I tried to print the docId on which row that been selected. I tap all the data but it will only read the latest data id only.
So can anyone help me to sort out this problem on how to delete the data that been selected only, not always delete the latest data? Thank you in advanced
I'm sure I understand what exactly it is you want to delete, but your function tells Firebase to delete the entire document with the ID you are passing.
You also are defining `String docId' to your whole widget and using it for all your ListView.Builder items.
Try this:
ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
ds = snapshot.data.docs[index];
String docIdTobeDeleted= ds.id;
// children: snapshot.data.docs.map((document) {
return new ListTile(
title: new Text(ds['task']),
subtitle: Wrap(
children: <Widget>[
Text("Priority: " + ds['priority']),
Text(" | Status: " + ds['status']),
],
),
onTap: (){
//you won't be needing this anymore, instead you can type:
print(docIdTobeDeleted);
//docId = ds.id;
//print(docId);
},
and for firebase below, use this:
onPressed: () async {
try {
FirebaseFirestore.instance
.collection("taks")
.doc(docIdTobeDeleted)
.delete()
.then((_) {
print("success!");
});
}
catch (e) {
print("ERROR DURING DELETE");
}
It should work.
your Listtile onTap will set the docID to the selected tileID.. and the deleteIconButton will delete the id of docID.. so if you tap on the first ListTile and tap on any of the deleteIconButton.. It will delete the first ListTile
You can use the direct ds.id instead of docID in the deleteIconButton
IconButton(
icon: Icon(
Icons.delete_outline,
size: 20.0,
color: Colors.brown[900],
),
onPressed: () async {
try {
FirebaseFirestore.instance
.collection("taks")
.doc(ds.Id)
.delete()
.then((_) {
print("success!");
});
}
catch (e) {
print("ERROR DURING DELETE");
}
// _onDeleteItemPressed(index);
},
),