Pulling data from QuerySnapshot in flutter - flutter

I need to populate a DropdownButton from a document in Firestore. I can retrieve the data. When I look into the snapshot.data I see 2 records which is what I expect to see. In the code below, everything works fine as long as I comment out the code snippet as you can see.
Container(
child: StreamBuilder(
//stream: _firestoreService.getAgency(),
stream: _db.collection('agency').snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Center(
child: CircularProgressIndicator(),
);
} else {
//var length = snapshot.data.docs.length;
//print('length: ' + length);
return new DropdownButton<String>(
hint: new Text("Select Agency"),
value: _currentAgency,
/* <<<< The code below is where I am having problems
//onChanged: changedDropDownState,
**items: snapshot.data.docs.map((Map map) {
return new DropdownMenuItem<String>(
value: map["name"].toString(),
child: new Text(
map["name"],
),
);
}).toList(),**
*/
);
}
;
}),
),
When I uncomment the code and run the app I get this error:
======== Exception caught by widgets library =======================================================
The following _TypeError was thrown building StreamBuilder<QuerySnapshot>(dirty, state: _StreamBuilderBaseState<QuerySnapshot, AsyncSnapshot<QuerySnapshot>>#d9273):
type '(Map<dynamic, dynamic>) => DropdownMenuItem<String>' is not a subtype of type '(QueryDocumentSnapshot) => dynamic' of 'f'
What I want to accomplish is to populate the value: attribute with the document ID but I don't see it in snapshot.data. The other thing I want to do is populate child: attribute with some of the values from the snapshot.data.
How do I do this?

Your issue is here
items: snapshot.data.docs.map(( ISSUE HERE===>Map map) {
return new DropdownMenuItem<String>(
value: map["name"].toString(),
child: new Text(
map["name"],
),
);
}).toList(),
The value for docs is QueryDocumentSnapshot and not type Map that is why you got the error message.
Instead, change it to this.
items: snapshot.data.docs.map((documentSnapshot) {
return new DropdownMenuItem<String>(
value: documentSnapshot["name"].toString(),
child: new Text(
documentSnapshot["name"],
),
);
}).toList(),

I found a post that says to use .forEach instead of map (Snapshot code inside StreamBuilder does not get executed when receiving data from Firebase FireCloud in Flutter).
This gets rid of the error but the DropdownButton is static and won't drop down or let me click on it. Here is my new code for the DropdownButton:
return new DropdownButton<String>(
hint: new Text("Select Agency"),
value: _currentAgency,
//onChanged: changedDropDownState,
items: snapshot.data.docs.forEach((document) {
return new DropdownMenuItem<String>(
value: document.data()['name'],
child: new Text(document.data()['name']),
);
}),
);
The data is there and I can see it when I debug. I want to get the documentId as well but I don't see that in the debugger.
How do I get the DropdownButton to be active and how do I add the documentId to the value: attribute?

Related

error flutter _CastError (Null check operator used on a null value) using sqlite database

i have trouble with my flutter database , i'm using sqlite for database and when i want to run it i have error said that _CastError (Null check operator used on a null value). i already use (!)in my code but still the same ,can anybody help me
my code in dbhelbper
Future<List<DistribusiModel>> getAll() async {
final data = await _database!.query(namaTabel);
List<DistribusiModel> result =
data.map((e) => DistribusiModel.fromJson(e)).toList();
return result;
}
my code from main.dart
FutureBuilder<List<DistribusiModel>>(
future: databasedistribusi!.getAll(),
builder: (context, snapshot) {
print('Hasil: ' + snapshot.data!.toString());
return ListTile(
title: Text('Algoritma dan Pemrograman I'),
trailing: Wrap(
children: [
Text(
'3',
style: TextStyle(color: Colors.black),
),
SizedBox(
width: 20,
),
Text(
'A',
style: TextStyle(color: Colors.black),
),
SizedBox(
width: 20,
),
Text('LULUS'),
],
)
please help me , i need to finish it immediately
sorry for my bad english
i hope anyone can help me whit this problem
Firstly make sure the databasedistribusi is not null. Beware of using ! directly without checking null.
and return empty list on null case
Future<List<DistribusiModel>> getAll() async {
if(_database==null) return [];
final data = await _database.query(namaTabel);
List<DistribusiModel> result =
data.map((e) => DistribusiModel.fromJson(e)).toList();
return result;
}
And for the FutureBuilder
FutureBuilder<List<DistribusiModel>>(
future: databasedistribusi?.getAll(),
builder: (context, snapshot) {
if(snapshot.hasError) return Text("got Error");
else if(snapshot.hasData){
if(snapshot.data.isEmpty) return Text("EmptyData");
else return ListView(....);
}
return CircularProgressIndicator(); //default
}
More about using FutureBuilder and understanding-null-safety

Flutter DropDownButton using FutureBuilder value is not updating after selecting values from DropdownItems

I have been trying to code this app using Flutter and I want to make a Dropdown that displays the values received from a JSON response via provider. The response is successful from the service and correctly fetches the data. Dropdown is wrapped in a FutureBuilder, the information can be displayed without problems in the Dropdown, the problem is generated when I want to select an element, it is not updated, for this reason it is not reflected.
My code:
List<Datum> listDatumEnterprise;
Datum _selectEnterprise;
return FutureBuilder(
future: getInformationAdministrative.enterpriseDataGet(),
builder: (BuildContext context, AsyncSnapshot<List<Datum>> snapshot) {
if (snapshot.hasData) {
listDatumEnterprise = snapshot.data;
return CustomDropDown<Datum>(
title: 'Selecciona empresa',
value: _selectEnterprise,
onChanged: (Datum datum) {
setState(() {
_selectEnterprise = datum;
print(_selectEnterprise.id);
});
},
dropdownMenuItemList: listDatumEnterprise?.map((Datum item) {
return new DropdownMenuItem<Datum>(
child: Text(item.alias),
value: item,
);
})?.toList() ??
[],
);
} else {
return CircularProgressIndicator();
}
The problem is that you are initializing _selectEnterprise inside your build , calling setState() will call build and will re-initiate the value again to empty, move Datum _selectEnterprise outside your build. Also be sure to have distinct values for the DropdownMenuItem.
I have wrapped my DropdownButton with StatefulBuilder. This is the only way I could change the data of DropdownButton without recalling FutureBuilder.
Demo code:
StatefulBuilder(builder: (context, setState) {
return DropdownButton<String>(
borderRadius: BorderRadius.circular(
10,
),
hint: Text(
"Your Hint",
),
value: selectedValue, //your selected value
isExpanded: true,
items: your_list.map((String value) { //your list here
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (value) {
setState(() => selectedValue = value); //your selected value
},
);
}),
Learn more from: StatefulBuilder

DropdownButtonFormField not getting rebuilt

I'm trying to update the selected value programmatically.
I've used various method, including Consumer etc., and have made sure that the value is updated and the widget is called when the value changes, however, DropdownButtonFormField never got rebuilt with the latest value.
Currently I'm wrapping the DropdownButtonFormField in a StreamBuilder, which supposedly, should get rebuild whenever there's a new event sent through the stream. This is my code:
Declaration
final StreamController<String> _raceStreamController = new StreamController<String>();
DropdownButtonFormField
return
StreamBuilder<String>(
stream: _raceStreamController.stream,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
return new DropdownButtonFormField<String>(
value: snapshot.data,
hint: new Text(hint,
textAlign: TextAlign.center),
isExpanded: true,
items: items.map((String value) {
return new DropdownMenuItem<String>(
child: new Text(value),
value: value
);
}).toList(),
validator: (value) => value == null ? 'field required' : null,
onChanged: (value) {} // no use for now,
);
});
Push data
onFocusChange: (focus) async {
if (!focus) {
try{
await userBloc.searchUser(controller.text.toUpperCase());
_raceStreamController.sink.add(userBloc.user.race);
} catch(e) {
if (e.toString() == ERROR_UNAUTHORISED)
navigateToRoot(context);
}
}
}
I've tried to remove as much redundant code as possible.
Thank you.
In Flutter version 1.17.2, that DropdownButtonFormField bug was fixed, so be sure to upgrade.
Github issue: https://github.com/flutter/flutter/issues/56898
Fixed in version 1.17.2: https://github.com/flutter/flutter/wiki/Hotfixes-to-the-Stable-Channel#1172-may-28-2020

I am getting error while using stream builder in flutter

i am making a mobile app using flutter. And i am using stream builder for this screen. I am not getting the point where i am wrong in the code. Can you please help me in this. I am sharing code and screenshot for this particular row which is causing problem
var timeSelected = 'Click here';
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
'Time Slot:',
style: TextStyle(color: Colors.white),
),
Spacer(),
GestureDetector(
onTap: () {
_asyncInputDialog(context);
//_displayDialog();
},
child: StreamBuilder(stream: cartManager.getTimeSlotSelected,
initialData: timeSelected,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData){
timeShow(snapshot,);
}
else if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
return Center(
child: Container(
child: Text('Select time slot'),
),
);
},)
),
],
),
This alert dialog will show when i click on the text of row:
_asyncInputDialog(
BuildContext context,
) {
return showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Center(child: Text('Available Time Slot')),
content: TEAlertDialogContent(),
actions: <Widget>[
new FlatButton(
child: new Text('CANCEL'),
onPressed: () {
Navigator.of(context).pop();
},
)
],
);
});
}
When i got the value from showdialog i will store the value in streamcontroller that is present in CartManager.
static StreamController<Timeslot> timeSlotController = BehaviorSubject();
timeSlotSelected(Timeslot time){
timeSlotController.sink.add(time);
}
get getTimeSlotSelected{
return timeSlotController.stream;
}
And we call the above method in stream property of streamcontroller and get the snapshot. This is the method which was called when our snapshot has data:
Widget timeShow(AsyncSnapshot<Timeslot> snapshot ) {
timeSelected = '${snapshot.data.firstTimeSlot}-${snapshot.data.secondTimeSlot}';
timeslotid = snapshot.data.id.toString();
return Text(timeSelected);
}
But i am getting error: type 'BehaviorSubject' is not a subtype of type 'Stream'
Please let me know where i am wrong. I had also shared a screen shot of screen showing this error too.
As your error states, you are trying to pass a type Timeslot to a Stream builder expecting a stream of type String. You must check which one is correct (String or Timeslot) and use the same type on both sides.
Apparently, your problem is in the timeSelected variable. Where is it defined? If this is a String, the Stream builder will infer that your stream is of type String, which is not true. You must set this variable as a Timeslot, since this is your stream type.
Also, you have an error in your code. You have to return a widget to be rendered if snapshot has data. Check the code below:
StreamBuilder(stream: cartManager.getTimeSlotSelected,
initialData: timeSelected,
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData){
return timeShow(snapshot,);
}
else if (snapshot.hasError) {
return Text(snapshot.error.toString());
}
return Center(
child: Container(
child: Text('Select time slot'),
),
);
},)

type 'List<dynamic>' is not a subtype of type 'List<DropdownMenuItem<String>>'

Im working on a flutter project where i pass an array of objects (List> array) to the stream-builder from my bloc. If i print the object it prints nicely, but when i try to map them out in the DropdownMenuItem, it throws me the mentioned error. Hence, if i create a dummy array in the same format within the class and access it i do not get the error. not sure what am I missing here, code as bellow.
StreamBuilder(
stream: _bLoc.getJsonArray,
builder: (context, snapshot) {
return snapshot.hasData
? new Container(
width: 150,
color: Theme.of(context).primaryColor,
child: new DropdownButton<String>(
items: snapshot.data.map((value) =>
new DropdownMenuItem<String>(
value: value["distance"],
child: new Text(value["distance"]),
)
).toList(),
onChanged: (_) {},
),
)
: Container();
}),
my json structure as bellow.
[
{"distance": "12km","price": "200LKR",},
{"distance": "2km","price": "100LKR",},
{"distance": "132km","price": "340LKR",}
]
This is how you must use map as list build. You have to precize the type you want to return. Especially you can do something like this
StreamBuilder(
stream: _bLoc.getJsonArray,
builder: (context, snapshot) {
return snapshot.hasData
? new Container(
width: 150,
color: Theme.of(context).primaryColor,
child: new DropdownButton<String>(
items: snapshot.data.map<DropdownMenuItem<String>>((value) =>
new DropdownMenuItem<String>(
value: value["distance"],
child: new Text(value["distance"]),
)
).toList(),
onChanged: (_) {},
),
)
: Container();
}),
PS You can catch some errors here when trying to get selected DropdownMenuItem. consider using custom generated list instead of mapping
As error tells you are casting concrete type to dynamic. You may want to use .map<TYPE>() to cast to String. See the end of this thread - https://github.com/flutter/flutter/issues/18979