How to create a DropMenuItem from Future list in flutter? - flutter

I am getting a data from local database using floor in flutter.
And I want to display the list of doctors from the db I am using DropDownButton to do this
This is my code
DropdownButton(
icon: const Icon(
Icons.arrow_drop_down,
),
hint: const Text('Select a doctor'),
items: FutureBuilder<List<Doctor>>(
future: findAllDoctor(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return snapshot.data
.map((doctor) => DropdownMenuItem(
child: Text(doctor.name),
value: doctor,
))
.toList();
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
}
), ,
onChanged: (val) {
print(val);
},
),
And this is how I get the data
Future<List<Doctor>> findAllDoctor() async {
return await database.doctorDao.findAllDoctor();
}
I am getting this error
The argument type 'FutureBuilder<List<Doctor>>' can't be assigned to the parameter type 'List<DropdownMenuItem<Object>>?'.
What I need here is to display the doctors as list in a form to choose from.

Your FutureBuilder was placed in wrong place, check my edited code:
FutureBuilder<List<Doctor>>(
builder: (context, snapshot) {
if (snapshot.hasData) {
return DropdownButton<String>(
icon: const Icon(
Icons.arrow_drop_down,
),
hint: const Text('Select a doctor'),
items: snapshot.data
?.map((doctor) => DropdownMenuItem(
child: Text(doctor.name),
value: doctor.name,
))
.toList(),
onChanged: (val) {
print(val);
},
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return const CircularProgressIndicator();
},
);

Related

I want to display document ids of collection in future builder but i got error

Below is the code in which i want to display documents ids from cloud firestore but i got error.
FutureBuilder<QuerySnapshot>(
future: db.collection('/Exam').get(),
builder:
(BuildContext context, AsyncSnapshot asyncSnapshot) {
if (asyncSnapshot.hasError)
return Text("Error: ${asyncSnapshot.error}");
if (!asyncSnapshot.hasData)
return const CircularProgressIndicator();
return DropdownButton<String>(
isExpanded: true,
items: asyncSnapshot.data!.docs
.map(
(snap) => DropdownMenuItem(
value: snap.id,
child: Text(
snap.id.toString(),
),
),
)
.toList(),
value: _selectedexam,
onChanged: (String? newValue) {
setState(() {
_selectedexam = newValue!;
_selectedsemester = null;
print(_selectedexam);
});
},
);
}),
Try adding the Generic type to the map<T>() method:
asyncSnapshot.data!.docs
.map<DropdownMenuItem<String>>(
(snap) => DropdownMenuItem<String>(
value: snap.id,
child: Text(
snap.id.toString(),
),
),
)
.toList(),

RangeError (RangeError (index): Invalid value: Valid value range is empty: 0) when attempting to access sub collection via stream

I am trying to essentially create 2 dropdown within flutter that are populated via firebase streams. The idea is that a user can choose a category and the second dropdown will then show relevant sub categories based on the selected category. What happens is that I keep getting the following. My subcategories collection is nested within my categories collection
RangeError (RangeError (index): Invalid value: Valid value range is empty: 0)
static Stream<QuerySnapshot> getCategories() {
return catsRef.orderBy('name', descending: false).snapshots();
}
static Stream<QuerySnapshot> getSubCategories(category) {
return catsRef
.doc(category)
.collection('sub-categories')
.orderBy('name', descending: false)
.snapshots();
}
StreamBuilder<QuerySnapshot>(
stream: DatabaseService.getCategories(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) return Container();
if (setDefaultCategory) {
category = snapshot.data.docs[0].get('name');
debugPrint('setDefault category: $category');
}
return DropdownButton(
isExpanded: false,
value: category,
items: snapshot.data.docs.map((value) {
return DropdownMenuItem(
value: value.get('name'),
child: Text('${value.get('name')}'),
);
}).toList(),
onChanged: (value) {
debugPrint('selected onchange: $value');
setState(
() {
debugPrint('category selected: $value');
category = value;
setDefaultCategory = false;
setDefaultMakeModel = true;
},
);
},
);
},
),
Container(
child: category != null
? StreamBuilder<QuerySnapshot>(
stream:
DatabaseService.getSubCategories(category),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
debugPrint(
'snapshot status: ${snapshot.error}');
return Container(
child: Text(
'snapshot empty category: $category sub-category: $subCategory'),
);
}
if (setDefaultMakeModel) {
subCategory =
snapshot.data.docs[0].get('name');
debugPrint('setDefault subCat: $subCategory');
}
return DropdownButton(
isExpanded: false,
value: subCategory,
items: snapshot.data.docs.map((value) {
debugPrint('subCat: ${value.get('name')}');
return DropdownMenuItem(
value: value.get('name'),
child: Text(
'${value.get('name')}',
overflow: TextOverflow.ellipsis,
),
);
}).toList(),
onChanged: (value) {
debugPrint('subCat selected: $value');
setState(
() {
// Selected value will be stored
subCategory = value;
// Default dropdown value won't be displayed anymore
setDefaultMakeModel = false;
},
);
},
);
},
)
: Container(
child:
Text('cat: $category subcat: $subCategory'),
),
),

FormBuilderDropdown broke inside futurebuilder

I put the FormBuilderDropdown inside FutureBuilder so when I press on it will rebuild infinitely and will not enter the FormBuilderDropdown if anyone can give me a proper solution
and this is my code
FutureBuilder(
future: widget.fetchBrands(yearValue),
builder:
(BuildContext context, AsyncSnapshot snapshot) {
var snap = snapshot.data;
if (snapshot.connectionState ==
ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
}
if (snapshot.hasError) {
if (yearValue == null) {
return Container(
child: Text(
"Select the manufacture year to Select Brand",
style: TextStyle(
color: Colors.red[800],
fontSize: 16,
),
overflow: TextOverflow.visible,
),
);
}
return Center(
child: new Text('Error'),
);
}
snap.forEach((key, brand) {
brands.add(brand['make']);
});
brands = brands.toSet().toList();
brands.sort();
return
FormBuilderDropdown(
key: ValueKey("Brands"),
name: "Brands",
items: brands
.map(
(e) => DropdownMenuItem<String>(
child: Text(e),
value: e,
),
)
.toList(),
validator: FormBuilderValidators.compose(
[
FormBuilderValidators.required(
context,
)
],
),
decoration: InputDecoration(
labelText: 'Brands',
labelStyle:
Theme.of(context).textTheme.display1,
),
hint: Text('Select Brand'),
allowClear: true,
onChanged: (value) {
setState(() {
brandValue = value;
carValue = null;
carsName.clear();
});
},
);
},
),
before the new version of FormBuilder, this works well
I do temp solution that after I get the data I remove the future and build a new one without future but this solution not good enough because it takes two tap to open the dropdown
future: widget.fetchBrands(yearValue),
This means, every time build is called, you will start over and fetch the brands. You only want to fetch them once, yo you should only call the method once.
You want this line in initState where it runs only once:
brandsFuture = widget.fetchBrands(yearValue);
and then use that one future in your build method:
future: brandsFuture,
obviously you need to declare brandsFuture as a field in your state class.

How to build a dropdown in flutter from a future list

I am trying to make a dropdown select input in flutter! I want to get the data from a future list ref.
I am not sure how to go about it, as this gives me error :
Here is the code:
dropdownButtonHideUnderline(FormFieldState<String> state) {
return FutureBuilder(
future: Global.payCountriesRef.getData(),
builder: (BuildContext context, AsyncSnapshot snap) {
if (snap.hasData) {
List<PayCountries> countries = snap.data;
return new DropdownButtonHideUnderline(
child: new DropdownButton<String>(
value: _sendForm.country,
isDense: true,
onChanged: (String newValue) {
setState(() {
_sendForm.country = newValue;
//_updateDropdownValue(newValue, model);
state.didChange(newValue);
});
},
items: List<String>.from(countries)
.map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList()
),
);
}
}
);
}
The code Below shows an example to populate a dropdown from a future list
new DropdownButtonHideUnderline(
child: new FutureBuilder<List<BranchItems>>(
future: new BranchService().fetchBranchItems(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return new Container();
} else if (snapshot.hasData) {
list.clear();
//listItemNames.clear();
dropDownItemsMap = new Map();
snapshot.data.forEach((branchItem) {
//listItemNames.add(branchItem.itemName);
int index = snapshot.data.indexOf(branchItem);
dropDownItemsMap[index] = branchItem;
list.add(new DropdownMenuItem(
child: new DropDownItem(
image: Image.network(branchItem.itemPicture),
text: branchItem.itemName),
value: index));
});
return DropdownButton(
items: list,
onChanged: (int selected) {
_selectedItem = list[selected].value;
setState(() {
selectedItemName =
dropDownItemsMap[_selectedItem].itemName;
});
},
hint: new Text(
selectedItemName,
style: new TextStyle(color: Colors.blue),
),
);
} else {
return CircularProgressIndicator();
}
},
),
),

Flutter:my DropdownButton value is not taken?

my working with DropdownButton and i facing a problem my value is not taken
is show me null
my code is below
SizedBox(
height: 60.0,
child: new StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection("Category").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return new Text("Please wait");
var length = snapshot.data.documents.length;
DocumentSnapshot ds = snapshot.data.documents[length - 1];
return new DropdownButton(
items: snapshot.data.documents.map((
DocumentSnapshot document) {
return DropdownMenuItem(
child: new Text(document.data["name"]));
}).toList(),
value: category,
onChanged: (value) {
print(value);
},
hint: new Text("Category"),
style: TextStyle(color: Colors.black),
);
}
),
),
You should read more about StatefulWidget, here you have documentation: https://flutter.io/tutorials/interactive/
To fix your issue just update your category variable and refresh the state.
UPDATE
looks like you forget the value for the item also.
SizedBox(
height: 60.0,
child: new StreamBuilder<QuerySnapshot>(
stream: Firestore.instance.collection("Category").snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData) return new Text("Please wait");
var length = snapshot.data.documents.length;
DocumentSnapshot ds = snapshot.data.documents[length - 1];
return new DropdownButton(
items: snapshot.data.documents.map((
DocumentSnapshot document) {
return DropdownMenuItem(
value: document.data["name"],
child: new Text(document.data["name"]));
}).toList(),
value: category,
onChanged: (value) {
print(value);
setState(() {
category = value;
});
},
hint: new Text("Category"),
style: TextStyle(color: Colors.black),
);
}
),
),