i want to replace the card with for loop. here's the body for my screen where i am showing the output
body: FutureBuilder<List>
(
future: db.getAllRecords("EMPLOYEE"),
initialData: List(),
builder: (context,snapshot){
return snapshot.hasData
? ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (_, int position){
final item =snapshot.data[position];
return Card(
child:ListTile(
title: Text(
snapshot.data[position].row[1]
),
),
);
},
)
: Center(
child:CircularProgressIndicator() ,
);
Actually, the way you wrote it is the standard way of implementation in Flutter. You could call the default ListView constructor instead of ListView.builder one and then call the for loop but that's not the best practice.
List<Widget> _buildListItems(BuildContext context, List<...> list) {
final output = List<Widget>();
for (var item in list) {
output.add(
Card(
child: ListTile(
title: Text(item.row[1]),
),
),
);
}
return output;
}
//... back to your build(context) body
body: FutureBuilder<List>(
future: db.getAllRecords("EMPLOYEE"),
builder: (context, snapshot) {
return snapshot.hasData
? ListView(
children: [
..._buildListItems(context, snapshot.data),
],
)
: Center(
child: CircularProgressIndicator(),
);
}
)
Related
I am looking to retrieve data stored in Firebase Realtime database and display it in a new page in a lisview, how can I achieve that. So far I can retrieve and print it out in a console terminal.
My code is below:
class BarcodesResultPreviewWidget extends StatelessWidget {
FirebaseDatabase.instance.reference().child('ScannedResults');
body: Column(
children: <Widget>[
previewView,
//printing scanned results
Expanded(
child: ListView.builder(
itemBuilder: (context, position) {
return BarcodeItemWidget(preview.barcodeItems[position]);
},
itemCount: preview.barcodeItems.length,
),
),
FlatButton(
color: Colors.grey,
child: Text('Save',),
onPressed: () {
databaseRef.push().set({
'ScannedItem': preview.barcodeItems
.map((barCodeItem) => barCodeItem.toJson())
.toString(),
});
},
),
To fetch the data into a new page and build listview, try something like this:
return Scaffold(
body: FutureBuilder(
future: databaseRef.once(),
// future: FirebaseDatabase.instance
// .reference()
// .child("ScannedResults")
// .once(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
return new Text('Loading....');
if (snapshot.hasError) return new Text('Error: ${snapshot.error}');
List scannedItemsValues = [];
snapshot.data.value.forEach(
(_, values) => scannedItemsValues.add(values["ScannedItem"]));
print(scannedItemsValues);
return ListView.builder(
itemCount: scannedItemsValues.length,
itemBuilder: (BuildContext context, int index) {
// build your listView here
print(scannedItemsValues[index]);
return Text(scannedItemsValues[index]);
},
);
},
),
);
I have tried both the codes below: One with StreamBuilder.ListView and other StreamBuilder.ListView.builder. I couldn't get a result. It shows
The method 'data' was called on null. Receiver: null Tried calling:
data()
I am not able to get how QuerySnapshots work
class FeedStream extends StatelessWidget {
FeedStream();
Map document;
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection('users').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView.builder(
itemCount: 2,
itemBuilder: (context, index) {
return _buildList(context, snapshot.data.docs[index]);
},
);
},
),
);
}
Widget _buildList(BuildContext context, DocumentSnapshot document) {
return ListTile(
title: Text(document.data()['displayName']),
subtitle: Text(document.data()['bio']),
);
}
}
class FeedStream extends StatelessWidget {
FeedStream();
Map document;
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder(
stream: FirebaseFirestore.instance.collection('users').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return ListView(
children: snapshot.data.docs.map((document) {
return Center(
child: Container(
height: MediaQuery.of(context).size.height / 6,
child:
Text('Name:' + document.data()['displayName'].toString()),
),
);
}).toList(),
);
},
),
);
}
}
Can someone fix this please?
You're saying that there are two documents:
return ListView.builder(
itemCount: 2, // HERE
itemBuilder: (context, index) {
return _buildList(context, snapshot.data.docs[index]);
},
);
But that's not always the case, and when there are fewer than 2 documents you get the error that you're seeing.
The solution is to return the item count that corresponds to the number of documents in the query snapshot:
return ListView.builder(
itemCount:, snapshot.data.docs.length,
itemBuilder: (context, index) {
return _buildList(context, snapshot.data.docs[index]);
},
);
StreamBuilder have a property called initialData. You can try giving it an empty list.
Example:
StreamBuilder<T>(
initialData: [],
stream: _myStream,
builder: _myBuilder,
);
my this code is showing me error that querysnapshot is not a subtype of a list. can you edit my code and tell me how to make this error free.
buildProfilePosts() {
if (_isLoading) {
return Center(
child: Text(
"Loading...",
style: TextStyle(fontFamily: "QuickSand"),
),
);
}
return StreamBuilder(
stream: postsRef.document(widget.profileId).collection('userPosts').orderBy('timestamp', descending: true).snapshots(),
builder: (context, snapshot) {
return Column(
children: snapshot.data,
);
}
);
}
children is a property inside the widget Column, it takes a list of widgets. You should do the following:
child: Column(children: <Widget>[
StreamBuilder(
stream: postsRef.document(widget.profileId).collection('userPosts').orderBy('timestamp', descending: true).snapshots(),,
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.documents.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
contentPadding: EdgeInsets.all(8.0),
title:
Text(snapshot.data.documents[index].data["name"]),
);
});
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
}
return CircularProgressIndicator();
},
),
]),
Assuming you have a name field in your document.
Try
Column(
children: <Widget>[
...List.generate(
snapshot.data.length,
(index) {
return Container(
child: Text(snapshot.data[index].yourobject),
);
},
),
],
),
I want to update my listview which is created in futurebuilder, I want to update this on slider change
FlutterSlider(
values: [150],
min: 0,
max: 150,
onDragging: (handlerIndex, lowerValue, upperValue) {
radius = upperValue;
users.clear();
fut = _gdb.getUsersInRadius(radius);
},
),
and code of futurebuilder and list view
Expanded(
child: FutureBuilder(
future: fut,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if(!snapshot.hasData && snapshot.connectionState == ConnectionState.done){
return Center(
child: Text("No users"));
}else if(snapshot.connectionState == ConnectionState.waiting){
return SpinKitWave(color: Theme.of(context).primaryColor);
}
return StreamBuilder(
stream: snapshot.data,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if(!snapshot.hasData){
return Center(
child: SpinKitWave(
color: Theme.of(context).primaryColor,
));
}else{
print(snapshot.data.length);
users.addAll(snapshot.data);
return ListView.builder(
controller: _scrollController ,
itemCount: users.length,
itemBuilder: (BuildContext ctx, int index){
return GestureDetector(
onTap: (){
Navigator.pushNamed(context, '/userWidget', arguments: users[index]);
},
child: Card(
child: Column(
children: [
Text(users[index].data['username']),
FutureBuilder(
future: FirebaseStorage.instance.ref().child('profilepics').child('${users[index].documentID}.jpg').getDownloadURL(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if(!snapshot.hasData){
return CircleAvatar();
}
return CircleAvatar(backgroundImage: NetworkImage(snapshot.data) ,) ;//.network(snapshot.data)
},
),
Text(users[index].data['city'] == null ? "No city" : users[index].data['city'] )
],
),
),
);
});
}
},
);
},
),
)
When user will change value of slider new users nearby will be loaded to listview and old will be deleted
Users are downloaded from Firestore using Geoflutterfire,
just call setState when doing fut = _gdb.getUsersInRadius(radius);
About the slider part
FlutterSlider(
values: [sliderValue], // initialize it with 150
min: 0,
max: 150,
onDragging: (handlerIndex, lowerValue, upperValue) {
radius = upperValue;
users.clear();
setState(){(){
sliderValue = upperValue; // update it like this.
fut = _gdb.getUsersInRadius(radius);
}}
},
),
I have a SliverGrid that populates with the result of a StreamBuilder from Firestore.
Right now I have other screens where I can filter the results by category, but that means a request to Firebase everytime the user pick a category, because I make the filter in the query.
So Im thinking if there is any way I can filter the results "locally" in the app, instead of making another call to the server, since all the info is already loaded.
My question is, if there any way to add a filter in the "SliverGrid" to show only the results that meet the criteria?
This is the part of my code with the Stream and the SliverGrid:
return Scaffold(
body: StreamBuilder(
stream: Firestore.instance.collection('COLLECTION')
.orderBy('updated_at', descending: true)
.where('status', isEqualTo : 'published')
.snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator()
);
}
return CustomScrollView(
slivers: [
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.3,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
return InkWell(
child: CustomWidget();
);
},
childCount: snapshot.data.documents.length,
),
),
],
);
}),
);
Ok, the solution was easier than I thought.
I added a DropdownButton that updates the state dropdownValue onChanged.
Also, I replaced the query in the stream with a variable that have a filter depending of the dropdownValue value.
Here is the code of the SliverGrid with the filter:
String dropdownValue = 'all';
#override
Widget build(BuildContext context) {
var menuItems = {
'1': 'option 1',
'2': 'option 2',
'3': 'option 3'
};
var firestoreQuery;
if (dropdownValue == 'all'){
firestoreQuery = (
Firestore.instance.collection('COLLECTION')
.orderBy('updated_at', descending: true)
.where('status', isEqualTo : 'published')
.snapshots()
);
}else{
firestoreQuery = (
Firestore.instance.collection('COLLECTION')
.orderBy('updated_at', descending: true)
.where('fielt_to_filter', isEqualTo : dropdownValue)
.where('status', isEqualTo : 'published')
.snapshots()
);
}
return Scaffold(
body: StreamBuilder(
stream: firestoreQuery,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator()
);
}
return CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: <Widget>[ DropdownButton(
value: dropdownValue,
items: menuItems.entries
.map<DropdownMenuItem<String>>(
(MapEntry<String, String> e) => DropdownMenuItem<String>(
value: e.key,
child: Text(e.value),
))
.toList(),
onChanged: (String newValue) {
setState(() {
dropdownValue = newValue;
});
},
),
]
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 1.3,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
return InkWell(
child: CustomWidget();
);
},
childCount: snapshot.data.documents.length,
),
),
],
);
}),
);
}