How to get data from Firebase and show them in grid view? - flutter

I have some data in firebase real time database. I want to show them in gridview and if I click on any item it will take me another page. How can I do it?
I am doing this code for printing values of my data:
void printFirebase(){
dbRef.once().then((DataSnapshot snapshot) {
print('pets : ${snapshot.value}');
});
}
Column(
children: <Widget> [
ElevatedButton(onPressed: printFirebase,
child: Text('click'),
)
],
),

You can use a StreamBuilder and return a GridView with Children through a stream
StreamBuilder(
stream: firebaseFirestore
.collection(widget.sportName)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(),
);
}
return GridView.count(
shrinkWrap: true,
physics: ClampingScrollPhysics(),
crossAxisCount: 5,
mainAxisSpacing: 10.0,
childAspectRatio: 1,
crossAxisSpacing: 10.0,
children: snapshot.data!.docs.map((document) {
return GestureDetector(
child:
dateSelector(document['date'], document.id),
);
}).toList(),
);
},
),

Related

How to use future builder in slivers in flutter

I want to display data from the api in the slivers using future builder with grid layouts. I tried but i could not do that.
Here is the code
slivers: [
SliverGrid(
delegate: SliverChildBuilderDelegate(
(context, index) {
return Container(
alignment: Alignment.center,
color: Colors.teal[100 * (index % 9)],
child: Text('grid item $index'),
);
},
childCount: 10,
),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 15,
crossAxisSpacing: 15,
childAspectRatio: 2.0,
),
)
],
I tried this way. but child count is the problem
SliverGrid(
delegate: SliverChildBuilderDelegate(
(context, index) {
return FutureBuilder(
future: getProducts(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Container(
child: Text(
'Hello',
),
);
},
);
},
childCount: 10,
),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 15,
crossAxisSpacing: 15,
childAspectRatio: 2.0,
),
)
get that future and build the delegate inside your FutureBuilder
FutureBuilder(
future:YourApi(),
builder:(_,snap){
if(snap.hasData){
//get your data , if its json, decode and make it into a class
List<T> data = snap.data .......
return ....(
slivers:[
SliverGrid(
......
,childCount: data.lenght
)
]
)
} else {}
}
)
You can directly use FutureBuilder inside slivers. just make sure the return widget is needed to be a sliver widget on every case.
body: CustomScrollView(
slivers: [
FutureBuilder<List<int>>(
/// create a future variable on state class
future: Future.delayed(Duration(seconds: 2))
.then((value) => List.generate(11, (i) => i)),
builder: (context, snapshot) {
if (snapshot.hasData) {
return SliverGrid(
delegate: SliverChildBuilderDelegate(
childCount: snapshot.data?.length,
(context, index) => Container(
child: Text("${snapshot.data?[index]}"),
),
),
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2));
}
return const SliverToBoxAdapter(
child: CircularProgressIndicator(),
);
},
)
],
),

How do I remove all the spaces between the card in gridview.builder

I need help regarding my gridview.builder, my gridview become like this, this happened because I want to filter to show only item that has the same id. I already try to put padding: EdgetInsets.zeroand SizedBox.shrink but it still does not work. How can I remove the blank card and only show the visible card? if I remove return Card()
Stack(
children: [
StreamBuilder(
stream: FirebaseFirestore.instance.collection('products').snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot){
if(snapshot.hasError){
return Text("Error: ${snapshot.error}");
}
if(snapshot.hasData){
return Column(
children: [
Flexible(
child: GridView.builder(
padding: EdgeInsets.zero,
itemCount: snapshot.data!.docs.length,
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 15,
crossAxisSpacing: 15,
childAspectRatio: 2/3,
),
itemBuilder: (context, index){
final DocumentSnapshot documentSnapshot =
snapshot.data!.docs[index];
DateTime dt = (documentSnapshot['endDateTime'] as Timestamp).toDate();
if(documentSnapshot['userid'] == widget.sellerId){
print(documentSnapshot['userid']);
return buildImageCard(
documentSnapshot['imageURL'][0], documentSnapshot['nameP'],
documentSnapshot['startPrice'], dt, documentSnapshot['id'],
documentSnapshot['categoryP']);
}
return Card();
}
)
),
],
);
}
return const Center(
child: CircularProgressIndicator(),
);
}
),
],
);
The solution would be to filter the list of docs above the widget, and use the filtered list for itemCount and the builder.
The cause of the issue is that each widget takes the aspect ratio that your specified (2/3) + the spacing regardless of the size of the child widget.
So it should be a little something like this
if(snapshot.hasData){
final listToUse = snapshot.data?.docs.where((documentSnapshot) => documentSnapshot['userid'] == widget.sellerId).toList() ?? [];
return Column(...

Flutter List view builder doesn't shrink when Keyboard appears

I'm creating a chat feature in flutter but noticed this behavior on IOS that doesnt shrink the list so you can see the last sent message. How can I have the listview builder shrink to show the last message when the keyboard appears?
Note: This issue doesn't happen on Android
Scaffold(
resizeToAvoidBottomInset: true,
body: Stack(
children: <Widget>[
StreamBuilder(
stream: _chats,
builder: (context, snapshot) {
if (!snapshot.hasData) return Container();
return snapshot.hasData
? GestureDetector(
onPanDown: (_) {
FocusScope.of(context).requestFocus(FocusNode());
},
child: ListView.builder(
shrinkWrap: true,
controller: _scrollController,
padding: EdgeInsets.only(top: 10, bottom: 100),
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index) {
return MessageWidget(
tripId: widget.docId,
uid: snapshot.data.docs[index].data()["uid"],
messageId: snapshot.data.docs[index].id,
message: snapshot.data.docs[index].data()["message"],
sender: snapshot.data.docs[index].data()["senderName"],
sentByMe: widget.uid ==
snapshot.data.docs[index].data()["uid"],
mediaFileUrl:
snapshot.data.docs[index].data()["mediaFileUrl"],
);
}),
)
: Container();
},
);
]
)
)
I think you can try the 'reverse' property from the ListView.builder.
Tell me if this example didn't fit your needs, can you share us your code ? (I didn't see why you use a Stack and what could be the issue around that).
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: <Widget>[
Expanded(
child: Stack(
children: <Widget>[
StreamBuilder<dynamic>(
builder: (context, dynamic snapshot) {
return GestureDetector(
onPanDown: (_) {
FocusScope.of(context).unfocus();
},
child: ListView.builder(
reverse: true,
shrinkWrap: true,
itemCount: 100,
padding: const EdgeInsets.only(top: 10, bottom: 10),
itemBuilder: (context, index) {
return ListTile(title: Text(index.toString()));
},
),
);
},
),
],
),
),
Container(
padding: const EdgeInsets.all(8),
color: Colors.black12,
child: const TextField(),
),
],
),
);
}
}

A RenderViewport expected a child of type RenderSliver but received a child of type RenderErrorBox

I'm showing the data I got from Firestore with SliverStaggeredGrid.countBuilder without any problem. I want to show my data with listview.builder.
My normal code is like this, I wanted to change it to be able to filter.
body: CustomScrollView(
slivers: [
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection(widget.title)
.orderBy("id", descending: true)
.snapshots(),
builder: (context, dataSnapshot) {
return !dataSnapshot.hasData
? SliverPadding(
sliver: SliverToBoxAdapter(
child: ColorLoader(
dotOneColor: Colors.white,
dotTwoColor: Colors.white,
dotThreeColor: Colors.white,
),
),
padding: EdgeInsets.all(0),
)
: SliverPadding(
sliver: SliverStaggeredGrid.countBuilder(
crossAxisCount: 1,
staggeredTileBuilder: (_) => StaggeredTile.fit(1),
itemBuilder: (context, index) {
DataModel model = DataModel.fromJson(
dataSnapshot.data!.docs[index].data()
as Map<String, dynamic>);
return sourceInfo(model, context);
},
itemCount: dataSnapshot.data!.docs.length),
padding: EdgeInsets.all(0),
);
},
),
],
),
When I use my code with listview.builder in sliver, it doesn't show any data on the screen.
body: CustomScrollView(
slivers: [
StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('posts')
.orderBy("id", descending: true)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> dataSnapshot) {
return !dataSnapshot.hasData
? SliverPadding(
sliver: SliverToBoxAdapter(
child: ColorLoader(
dotOneColor: Colors.white,
dotTwoColor: Colors.white,
dotThreeColor: Colors.white,
),
),
padding: EdgeInsets.all(0),
)
: ListView.builder(
itemCount: dataSnapshot.data!.docs.length,
itemBuilder: (context, index) {
DataModel model = DataModel.fromJson(
dataSnapshot.data!.docs[index].data()
as Map<String, dynamic>);
return sourceInfo(model, context);
},
);
},
),
],
),
What do I have to do in this situation?
Flutter has two protocols to draw widgets. Sliver and Box protocol. StreamBuilder originally is a StatefulWidget and it uses a Box protocol. Which is something does not work with CustomScrollView.
You can wrap your StreamBuilder with SliverToBoxAdapter to keep it as it is. Also remove the SliverPadding inside the StreamBuilder to have a box protocol widget.
body: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection('posts')
.orderBy("id", descending: true)
.snapshots(),
builder: (BuildContext context,
AsyncSnapshot<QuerySnapshot> dataSnapshot) {
return !dataSnapshot.hasData
? Padding(
child: ColorLoader(
dotOneColor: Colors.white,
dotTwoColor: Colors.white,
dotThreeColor: Colors.white,
),
padding: EdgeInsets.all(0),
)
: ListView.builder(
itemCount: dataSnapshot.data!.docs.length,
itemBuilder: (context, index) {
DataModel model = DataModel.fromJson(
dataSnapshot.data!.docs[index].data()
as Map<String, dynamic>);
return sourceInfo(model, context);
},
);
},
),
),
],
),

Having a different StreamBuilder for every tab in TabBarView

I am trying to create a home page that has 3 tabs: Friends, Groups and Events.
After finally managing to load events for the current user only, I realised ill have a hard time loading different data for each tab - as the StreamBuilder stream: will always be the same for all the tabs.
I need help finding a way to have a different StreamBuilder for each of my tabs.
Code below:
StreamBuilder(
stream: _streamer(),//Firestore.instance.collection("Events").snapshots(),
builder: (BuildContext context, AsyncSnapshot<List<dynamic>> snapshot) {
if (!snapshot.hasData) return const Text("Loading...");
return new SizedBox(
height: MediaQuery.of(context).size.height - 42 - MediaQuery.of(context).padding.bottom -AppBar().preferredSize.height - kToolbarHeight,
child: Column(
children: <Widget>[
Expanded(
child: TabBarView(
controller: _tabController,
children: <Widget>[
Container(
child: ListView.separated(
itemCount: snapshot.data.length,
itemBuilder: (context, index) =>
_buildListItem(context,
snapshot.data[index]),
separatorBuilder: (context, index) {
return Divider();
},
shrinkWrap: true,
),
),
Container(
child: ListView.separated(
itemCount: snapshot.data.length,
itemBuilder: (context, index) =>
_buildListItem(context,
snapshot.data[index]),
separatorBuilder: (context, index) {
return Divider();
},
shrinkWrap: true,
),
),
Container(
child: ListView.separated(
itemCount: snapshot.data.length,
itemBuilder: (context, index) =>
_buildListItem(context,
snapshot.data[index]),
separatorBuilder: (context, index) {
return Divider();
},
shrinkWrap: true,
),
),
],
),
)
],
));
},
)
This is how the home page looks
I assume you're loading all data required for all your tabs using that stream. What you could do is have distinct pairs of stream/StreamBuilder for each tab:
... // widgets leading up to the TabBarView
TabBarView(
controller: _controller,
children: <Widget>[
StreamBuilder(
stream: friendsStream,
builder: (BuildContext context, AsyncSnapshot<SomeData> snapshot) {
return FriendsWidget();
}
),
StreamBuilder(
stream: groupsStream,
builder: (BuildContext context, AsyncSnapshot<SomeData> snapshot) {
return GroupsWidget();
}
),
//... etc
]
)
You could also wrap ONLY the widgets that should be rebuilt with a StreamBuilder instead of wrapping the whole parent widget:
... // widgets leading up to the TabBarView
TabBarView(
controller: _controller,
children: <Widget>[
Column(
children: <Widget>[
SomeWidget(),
AnotherWidget(),
StreamBuilder(
stream: friendsStream,
builder: (BuildContext context, AsyncSnapshot<SomeData> snapshot) {
return FriendsWidget();
}
)
]
),
// ... etc
]
)
If you are compelled to load all your data from the database at once, you could use a Repository class to load and store all your data, and then, depending on your architecture, have streams built from that class.
As a side note, I highly recommend using a Bloc with BlocBuilder. Blocs are just far superior to other designs (in most cases). flutter_bloc is my favorite.