Loading data from Firebase into a DropdownButton in Flutter - firebase

I'm new in Flutter with Firebase and I'm trying to load some arrays stored in Firebase into a DropdownButton.
This piece of code works when a I call it from a button. It returns a list of drinks that I can print on the screen:
Future<List<String>> get drinks async {
QuerySnapshot docs = await _constantes.getDocuments();
List<String> res = List();
List<Map<String, dynamic>> datos = List();
for (var d in docs.documents) {
datos.add(d.data);
}
for (var d in datos[0]['drinks'].toList()) {
res.add(d.toString());
}
return res;
}
But my problem is that I'd like to load this list into a DropdownButton, so the user could choose one of the drinks when the app shows him the form :
DropdownButtonFormField(
hint: Text('Choose a drink'),
value: _currentDrink ?? 'Water',
items: _db.drinks.then((drinks) {
List<DropdownMenuItem> datos = List();
for (var d in drinks) {
datos.add(DropdownMenuItem(
value: d,
child: Text(d),
));
}
return datos;
}),
onChanged: (val) => setState(() => _currentDrink = val),
),
But it doesn't work because the result is a Future.
How could I fix it?
Thanks.

Wrap it in a StreamBuilder. Instead of querying using _constantes.getDocuments() return the stream from _constantes.snapshots() assuming _constantes is your firebase collection:
StreamBuilder<List<DocumentSnapshot>>(
stream: _drinkStream,
builder: (context, snapshot) {
return snapshot.hasData
? DropdownButton(
onChanged: (value) {},
items: [
for (var child in snapshot.data)
DropdownMenuItem(
child: Text(
child.data['name'],
),
value: child,
),
],
)
: Container();
},
)

Assign an empty list [] to dropdown until drinks are fetched and when fetched we will assign drinks list.Drinks list will get items after our future is completed.You need to call this future in initState of your method so when page is opened it fetches drinks and then assigns it to the drinks list.Dropdown will remain empty until drinks are fetched.
(Incase you want to show progressindicator on dropdown until drinks are fetched wrap dropdown in a future builder)
List<String> drinks = List();
Future<List<String>> get drinks async {
QuerySnapshot docs = await _constantes.getDocuments();
List<String> res = List();
List<Map<String, dynamic>> datos = List();
for (var d in docs.documents) {
datos.add(d.data);
}
for (var d in datos[0]['drinks'].toList()) {
res.add(d.toString());
}
setState(() {
drinks = res;
});
return res;
}
DropdownButtonFormField(
hint: Text('Choose a drink'),
value: _currentDrink ?? 'Water',
items: drinks == null? []: drinks.map((drink) {
return DropdownMenuItem<String>(
child: Text(drink),
value: drink,
);
}).toList(),
onChanged: (val) => setState(() => _currentDrink = val),
),

you can build drop down widget after you get data from your firebase and if you want to make show water before data is load or not data found then following solutions could be best for you.
in method comment part is for your case and un comment part is for demo.
class DeleteWidget extends StatefulWidget {
const DeleteWidget({Key key}) : super(key: key);
#override
_DeleteWidgetState createState() => _DeleteWidgetState();
}
class _DeleteWidgetState extends State<DeleteWidget> {
String initdata = 'water';
List<String> _getdata = List();
#override
void initState() {
super.initState();
getdatafromAPI();
}
void getdatafromAPI() async {
/*
_db.drinks.then((drinks){
setState((){
_getdata.addAll(drinks);
initdata = _getdata[0];
});
});
*/
await Future.delayed(Duration(seconds: 1));
setState(() {
_getdata.addAll(['coffee', 'tea', 'greentea']);
initdata = _getdata[0];
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('test app'),
),
body: Container(
child: Center(
child: DropdownButton(
items: _getdata.length > 0
? _getdata.map((e) {
return DropdownMenuItem<String>(
child: Text(e.toString()),
value: e.toString(),
);
}).toList()
: [
DropdownMenuItem<String>(
child: Text("water"),
value: 'water',
)
],
value: initdata,
onChanged: (value) {
setState(() {
initdata = value;
});
},
),
),
),
);
}
}

Related

Flutter Firebase Get data from docs and convert map to List?

I want to fetch data from Firebase and show it.
No data is visible.
There is a problem with the getProduct function. How can I do it?
Please kind answer.
====Build result =========
===== Model ===
class Product {
String id;
String name;
String category;
String image;
list images;
String desc;
String price;
Timestamp createdAt;
Timestamp updatedAt;
Product();
Product.fromMap(Map<String, dynamic> data) {
id = data['id'];
name = data['name'];
category = data['category'];
image = data['image'];
images = data['images'];
desc = data['desc'];
price = data['price'];
createdAt = data['createdAt'];
updatedAt = data['updatedAt'];
}
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'category': category,
'images': images,
'image': image,
'desc': desc,
'price': price,
'createdAt': createdAt,
'updatedAt': updatedAt
};
}
}
========= Product Notifier Lib =============
class ProductNotifier with ChangeNotifier {
List<Product> _productList = [];
Product _currentProduct;
UnmodifiableListView<Product> get productList =>
UnmodifiableListView(_productList);
Product get currentProduct => _currentProduct;
set productList(List<Product> productList) {
_productList = productList;
notifyListeners();
}
set currentProduct(Product product) {
_currentProduct = product;
notifyListeners();
}
addProduct(Product product) {
_productList.insert(0, product);
notifyListeners();
}
deleteProduct(Product product) {
_productList.removeWhere((_product) => _product.id == product.id);
notifyListeners();
}
}
========= Get Products API ===============
There seems to be a problem with the code below. What should I do?
getProducts(ProductNotifier productNotifier) async {
QuerySnapshot snapshot = await FirebaseFirestore.instance
.collection('Products')
.orderBy("createdAt", descending: true)
.get();
List<Product> _productList = [];
snapshot.docs.forEach((document) {
Product product = Product.fromMap(
document.data());
_productList.add(product);
});
productNotifier.productList = _productList;
}
========= Product Feed Page =============
class ProductFeed extends StatefulWidget {
#override
_ProductState createState() => _ProductState();
}
class _ProductState extends State<ProductFeed> {
#override
void initState() {
ProductNotifier productNotifier =
Provider.of<ProductNotifier>(context, listen: false);
getProducts(productNotifier);
super.initState();
}
#override
Widget build(BuildContext context) {
AuthNotifier authNotifier = Provider.of<AuthNotifier>(context);
ProductNotifier productNotifier = Provider.of<ProductNotifier>(context);
Future<void> _refreshList() async {
getProducts(productNotifier);
}
return Scaffold(
appBar: AppBar(
title: Text('Product Feed'
),
actions: <Widget>[
// action button
FlatButton(
onPressed: () => signOut(authNotifier),
child: Text(
"Logout",
style: TextStyle(fontSize: 20, color: Colors.white),
),
),
],
),
body: new RefreshIndicator(
child: ListView.separated(
itemBuilder: (BuildContext context, int index) {
return ListTile(
leading: Image.network(
productNotifier.productList[index].image != null
? productNotifier.productList[index].image
: 'https://www.testingxperts.com/wp-content/uploads/2019/02/placeholder-img.jpg',
width: 120,
fit: BoxFit.fitWidth,
),
title: Text(productNotifier.productList[index].name),
subtitle: Text(productNotifier.productList[index].category),
onTap: () {
productNotifier.currentProduct =
productNotifier.productList[index];
Navigator.of(context)
.push(MaterialPageRoute(builder: (BuildContext context) {
return Text('Product Detail'); //FoodDetail();
}));
},
);
},
itemCount: productNotifier.productList.length,
separatorBuilder: (BuildContext context, int index) {
return Divider(
color: Colors.black,
);
},
),
onRefresh: _refreshList,
),
);
}
}

Store documents from firestore in list

I am trying to retrieve documents from collection called "Org" and store it in list to display in the dropdownmenu, but the list returns [Instance of 'QueryDocumentSnapshot', Instance of 'QueryDocumentSnapshot'] and thus i get following error:
The method 'map' was called on null.
Receiver: null
Tried calling: map(Closure: (dynamic) => DropdownMenuItem<dynamic>)
This is the code i implemented:
void organisation() async {
await Firebase.initializeApp();
QuerySnapshot querySnapshot = await FirebaseFirestore.instance.collection("Org").get();
final List<DocumentSnapshot> list = querySnapshot.docs;
print(list);
list.forEach((data) => print(data));
}
#override
initState() {
Firebase.initializeApp();
organisation();
super.initState();
}
This is how i implemented dropdownmenu:
DropdownButtonFormField(
validator: (value) => value == "Select" ? 'Field required' : null,
value: _selectorg,
onChanged: (val) => setState(() => _selectorg = val),
items: list.map(
(item) {
return DropdownMenuItem(
child: Text('$item'),
value: item,
);
},
).toList(),
hint: Text(
"SELECT ORGANISATION",
style: TextStyle(color: Colors.white)),
),
Try this,
#override
initState() {
super.initState();
Firebase.initializeApp();
organisation();
}
And,
Declare your list outside the function
final List<DocumentSnapshot> list;
void organisation() async {
await Firebase.initializeApp();
QuerySnapshot querySnapshot = await FirebaseFirestore.instance.collection("Org").get();
list = querySnapshot.docs;
print(list);
list.forEach((data) => print(data));
}
Update
StreamBuilder(
stream: FirebaseFirestore.instance.collection("Org").get();
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshots) {
if (snapshots.connectionState == ConnectionState.active &&
snapshots.hasData) {
print(snapshots.data);
return ListView.builder(
itemCount: snapshots.docs.length,
itemBuilder: (BuildContext context, int index) {
DocumentSnapshot doc = snapshots.data[index];
Map data= doc.data;//This is your data
return Text(
data.toString(),
);
},
);
} else {
return Center(child: CircularProgressIndicator());
}
},
),

Populating dropdown menu with data from sqlite database in flutter

I am working on a list app and want to load the categories of tasks of the home page in the dropdown menu button.
I have used SQflite to save the categories in a table.
When I tap the Dropdown Menu, the values seem not to be loaded on the menu. It's just an empty list.
Here is the code:
class _HomeState extends State<Home> {
TodoService _todoService;
var _selectedValue;
var _categories = List<DropdownMenuItem>();
List<Todo>_todoList=List<Todo>();
final GlobalKey<ScaffoldState> _globalKey=GlobalKey<ScaffoldState>();
#override
initState(){
super.initState();
getAllTodos();
}
_loadCategories() async {
var _categoryService = CategoryService();
var categories = await _categoryService.readCategory();
categories.forEach((category) {
setState(() {
_categories.add(DropdownMenuItem(
child: Text(category['name']),
value: category['name'],
));
});
});
}
getAllTodos()async{
_todoService=TodoService();
_todoList=List<Todo>();
var todos= await _todoService.readTodo();
todos.forEach((todo){
setState(() {
var model=Todo();
model.id=todo['id'];
model.title=todo['title'];
model.dueDate=todo['dueDate'];
model.category=todo['category'];
model.isFinished=todo['isFinished'];
_todoList.add(model);
});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
key: _globalKey,
appBar: AppBar(
actions: <Widget>[
DropdownButton(
value: _selectedValue,
items: _categories,
onChanged: (value) {
setState(() {
_selectedValue = value;
_loadCategories();
});
},
),
Have you checked the service calls?
Maybe, the SQFlite database is not populated with the values you are trying to retrieve.
Try with a dummy set of values first, like:
items: <String>['One', 'Two', 'Free', 'Four']
.map<DropdownMenuItem<String>>((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
If the above works, make sure you populate the table by calling an insert query via the service call.
Or you can use the SQFlite's build-in support for doing a raw insert. This means that you can use a SQL string using rawInsert().
You forgot to map the List<Object> in the items of DropdownButton.
DropdownButton _dropdownButton(List<Object> categoriesList) {
return DropdownButton(
value: _selectedValue,
onChanged: (dynamic newValue) {
setState(() {
_selectedValue = newValue;
});
},
items: categoriesList
.map<DropdownMenuItem<Object>>((Object value) {
return DropdownMenuItem<Object>(
value: value,
child: Text(value.getTxt()), // Replace this with getter for the value you want to display
);
}).toList(),
);
}

The getter 'iterator' was called on null. Flutter & FireBase

i have a problem and dont know how to solve... I want to get data through a stream from Firebase
i have UserData in FireBase and now want to get in another script by using a Stream of this UserData(Cutom Class) but the stream is throwing a error. I already proofed the basic lines, if i am using an iterator on null. But i think i dont. There has to be something wrong with the provider. Here is the error :
════════ Exception caught by provider ══════════════════════════════════════════════════════════════
The following assertion was thrown:
An exception was throw by _MapStream<DocumentSnapshot, UserData> listened by
StreamProvider<UserData>, but no `catchError` was provided.
Exception:
NoSuchMethodError: The getter 'iterator' was called on null.
Receiver: null
Tried calling: iterator
════════════════════════════════════════════════════════════════════════════════════════════════════
This is the basic stream:
final String uid;
DatabaseService({this.uid});
final CollectionReference userCollection = Firestore.instance.collection("user");
Stream<UserData> get userData{
if(userCollection.document(uid).snapshots() != null){
return userCollection.document(uid).snapshots().map(_userDataFromSnapshot);
}
else{
return null;
}
}
UserData _userDataFromSnapshot(DocumentSnapshot snapshot){
List<Map<String, dynamic>> daysMaps = List<Map<String, dynamic>>.from(snapshot.data["days"]);
List<Day> days = [];
//List<dynamic> _daysMaps = snapshot.data["days"];
if(daysMaps.length > 1){
days = daysMaps.map((day) => Day.fromMap(day)).toList();
}
else{
days.add(Day.fromMap(daysMaps[0]));
}
Map<String,dynamic> todayMap = Map<String,dynamic>.from(snapshot.data["today"]);
Day today = Day.fromMap(todayMap);
return UserData(
uid: uid,
name: snapshot.data["name"],
days: days,
today: today,
);
}
and this is where i make the StreamProvider (the user stream above is another one):
class Wrapper extends StatelessWidget {
#override
Widget build(BuildContext context) {
final user = Provider.of<User>(context);
if(user == null){
return Authenticate();
}
else{
return StreamProvider<UserData>.value(
value: DatabaseService(uid: user.uid).userData,
child: Home()
);
}
}
}
i dont know if in here is the error but this is the Home Widget:
class Home extends StatefulWidget {
#override
_HomeState createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _currentIndex = 1;
#override
Widget build(BuildContext context) {
//getting other streams
final userdata = Provider.of<UserData>(context);
final user = Provider.of<User>(context);
print(userdata);
final AuthService _auth = AuthService();
//_auth.signOut();
List<dynamic> tabs = [
//TrainingTab
Center(child: Text("Coming Soon")),
//HomeTab
Padding(
padding: const EdgeInsets.all(10.0),
child: Column(
children: <Widget>[
DayStats(),
DayOverview(),
],
),
),
//
Center(child: FloatingActionButton(
onPressed: (){
DatabaseService(uid: user.uid).updateUserData([], Day(
burnedCalories: 300,
targetCalories: 0,
foodCalories: 0,
date: DateTime(DateTime.now().year,DateTime.now().month,DateTime.now().day,0,0,0,0,0)));
},
))
];
return userdata != null ? Scaffold(
backgroundColor: Color.fromRGBO(193, 214, 233, 1),
appBar: AppBar(
title: Text("MyApp"),
centerTitle: true,
elevation: 0.0,
actions: <Widget>[
FlatButton.icon(
onPressed: () async {
await _auth.signOut();
Navigator.pushReplacementNamed(context, "/Wrapper");
},
icon: Icon(Icons.person),
label: Text("logout")
)
],
),
body: tabs[_currentIndex],
bottomNavigationBar: BottomNavigationBar(
backgroundColor: Colors.white,
currentIndex: _currentIndex,
items: [
BottomNavigationBarItem(
icon: Icon(Icons.fitness_center),
title: Text("Workout")
),
BottomNavigationBarItem(
icon: Icon(Icons.home),
title: Text("Home")
),
BottomNavigationBarItem(
icon: Icon(Icons.fastfood),
title: Text("Ernährung")
)
],
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
),
) : Loading();
}
}
In my case i forgot to check Whether item in a list is null or not. Below code help me to recover from same error.
if (filterRestaurantList[index].category != null) {
for (var category
in filterRestaurantList[index].category) {
if (category.categoryDetail != null) {
categoryList.add(category.categoryDetail.categoryName);
}
}
I used List.from, without checking for null.

flutter firebase database and ListView builder issue

I suppose to show each item of my shopList in ListView, but I can't find out the reason why it keep showing the same record. Please help to solve this issue.
Here with the code, get data from firebase database.
databaseReference.once().then((DataSnapshot snapshot) {
Map<dynamic, dynamic> getMap = snapshot.value;
getMap.forEach((k, v) {
Map<dynamic, dynamic> f = v;
shop.key = k;
shop.shopName = f["ShopName"];
shop.tel = f["ShopTel"];
shop.address = f["ShopAddress"];
shop.thumbnail = f["Thumbnail"];
debugPrint(k);
shopList.add(shop);
debugPrint(shopList[shopList.length-1].shopName);
});
});
DebugPrint result:
flutter: -LLySYDHHHx9WtCvKPrO
flutter: shop1111
flutter: -LLyXwR0nnAcx6_H4cYy
flutter: shop2222
Here with the database:
Here with the code for ListView:
child: Flexible(
child: new ListView.builder(
itemCount: shopList.length,
itemBuilder: (context, index) {
return new Card(
color: Colors.red,
//elevation: 2.0,
child: new ListTile(
title: new Text("Name: ${shopList[index].key}"),
),
);
}),
),
the result of simulator:
Try this.
That's what I would do
Shop.dart
class Shop {
String key;
String name;
String address;
String phone;
String thumbnail;
Shop(this.name,this.address,this.phone,this.thumbnail);
Shop.fromSnapshot(DataSnapshot snapshot)
: key = snapshot.key,
name = snapshot.value["name"],
address= snapshot.value["address"],
phone= snapshot.value["phone"],
thumbnail= snapshot.value["thumbnail"];
toJson() {
return {
"name": name,
"address": address,
"phone": phone,
"thumbnail": thumbnail,
};
}
}
main.dart
List<Shop> itemsShop = List();
Shop itemShop;
DatabaseReference itemRefShop;
#override
void initState() {
super.initState();
itemShop = Shop("", "", "", "");
final FirebaseDatabase database = FirebaseDatabase.instance;
itemRefShop = database.reference().child('Shop');
itemRefShop.onChildAdded.listen(_onEntryAddedShop);
itemRefShop.onChildChanged.listen(_onEntryChangedShop);
}
_onEntryAddedShop(Event event) {
setState(() {
itemsShop.add(Shop.fromSnapshot(event.snapshot));
});
}
_onEntryChangedShop(Event event) {
var old = itemsShop.singleWhere((entry) {
return entry.key == event.snapshot.key;
});
setState(() {
itemsShop[Shop.indexOf(old)] = Shop.fromSnapshot(event.snapshot);
});
}
#override
Widget build(BuildContext context) {
return new Container(
child: new Column(
children: <Widget>[
new Flexible(
child: new FirebaseAnimatedList(
query: itemRefShop,
itemBuilder:(_, DataSnapshot snapshot, Animation<double> animation, int index){
return new ListTile(
title: new Text(snapshot.value['name']),
subtitle: new Text(itemsShop[index].address),
);
}
),
]
),
);
}
}

Resources