How to use future builder in slivers in flutter - 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(),
);
},
)
],
),

Related

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(...

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

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(),
);
},
),

How to get data from the StreamBuilder into the GridView directly in flutter?

Hello I'm setting a profile page and I'm trying to get the data from Firestore, so I set up a StreamBuilder<List> and gave it the stream to get the data but the problem is how to I use that data to display it into my GridView?
My code:
StreamBuilder<List<UserModel>>(
stream: userProviderData.user,
builder: (context, snapshot) {
return GridView(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
primary: false,
shrinkWrap: true,
children: [
Text(
'How Do I get the FullName here?' ?? fullNameOnNullText,
style: Theme.of(context).textTheme.subtitle2.copyWith(fontWeight:
FontWeight.w600),
),
],
);
You can get the data from the StreamBuilder by using the .data property of the snapshot in the builder.
And since your data is a list, you can map that into a list of child widgets in the GridView.
See below:
StreamBuilder<List<UserModel>>(
stream: userProviderData.user,
builder: (context, snapshot) {
List<UserModel> userModelList = snapshot.data;
return GridView(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
primary: false,
shrinkWrap: true,
children: userModelList.map((UserModel userModel) =>
Text(
userModel.name,
style: Theme.of(context).textTheme.subtitle2.copyWith(fontWeight:
FontWeight.w600),)
.toList()
,
);
Before returning the widget, you should check first if the stream has finished loading.
After getting the data, the best way is to use List<Widget>.generate() for the child creation.
You can try this:
StreamBuilder<List<UserModel>>(
stream: userProviderData.user,
builder: (context, snapshot) {
if (!snapshot.hasData) { // Check the status here
return Center(child: CircularProgressIndicator());
}
return GridView(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 10,
mainAxisSpacing: 10,
),
primary: false,
shrinkWrap: true,
children: List<Widget>.generate(
snapshot.data.length, // same length as the data
(index) => Text(
snapshot.data[index].fullName, // Use the fullName property of each item
style: Theme.of(context)
.textTheme
.subtitle2
.copyWith(fontWeight: FontWeight.w600),
),
),
);
},
);

The getter was called on null

I am trying to display the list of hobbies of the user from firestore and I get the value from there as it gets print in console but not able to display it.
Error:-
The getter 'hobbies' was called on null.
Receiver: null
Tried calling: hobbies
Create account data is model class:-
Future<CreateAccountData> getUser() async {
final User user = auth.currentUser;
return _reference.doc(user.uid).get().then((m) => CreateAccountData.fromDocument(m));
}
getting data this way and it gets print also in console:-
List hobbies;
void initState() {
super.initState();
getUser().then((value) {
if (!mounted) return;
setState(() {
accountData = value;
hobbies = value.hobbies;
print("hobbies"+ hobbies.toString());
});
});
}
Error takes me to the line I commented:-
child:GridView.builder(
physics: BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: accountData.hobbies.length, // Error take me to hobbies.
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 2, childAspectRatio: 5, crossAxisSpacing: 5,),
itemBuilder: (BuildContext context, int index){
return Padding(
padding: EdgeInsets.fromLTRB(5, 5, 5, 5),
child: Text(accountData.hobbies[index]),
);
}
),
On suggestions updated to this(wrapped in future builder):-
child: FutureBuilder(
future: getUser(),
builder: (context, snapshot){
if(snapshot.connectionState == ConnectionState.waiting){
return Center(
child: CircularProgressIndicator(),
);
} if(snapshot.data.docs.isEmpty){
return Align(
alignment: FractionalOffset.centerLeft,
child: Text("Add what you love to do.....",textAlign: TextAlign.left,style: TextStyle(fontSize: 17),),
);
}
return GridView.builder(
physics: BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount: accountData?.hobbies?.length ?? 0,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 5,
crossAxisSpacing: 5,),
itemBuilder: (BuildContext context,
int index) {
return Padding(
padding: EdgeInsets.fromLTRB(5, 5, 5, 5),
child: Text(accountData.hobbies[index]),
);
}
);
},
),
Now getting NoSuchMethodError error:-
Class 'CreateAccountData' has no instance getter 'docs'.
Receiver: Instance of 'CreateAccountData'
Tried calling: docs
check hobbies is null
itemCount: accountData?.hobbies?.length ?? 0,
In flutter 2.0, we have null safety. We need to check if value is null or not using an ! mark after the variable. I will post the code in a few minutes since Im in school rn
Wrap GridView.builder in a FutureBuilder passing getUser() as the future.
child: FutureBuilder(
future: getUser(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting)
return CircularProgressIndicator();
if (snapshot.hasData)
return GridView.builder(
physics: BouncingScrollPhysics(),
scrollDirection: Axis.horizontal,
shrinkWrap: true,
itemCount:
snapshot.data.hobbies.length, // Error take me to hobbies.
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: 5,
crossAxisSpacing: 5,
),
itemBuilder: (BuildContext context, int index) {
//DocumentSnapshot interestList = snapshot.data.docs[index]['hobbies'];
return Padding(
padding: EdgeInsets.fromLTRB(5, 5, 5, 5),
child: Text(snapshot.data.hobbies[index]),
);
});
return Text('No data.');
},
),

How to use StreamBuilder inside CustomScrollWidget?

I'm trying to show some data from sqlite and everything is ok but I can't use StreamBuilder inside CustomScrollView it says:
A RenderViewport expected a child of type RenderSliver but received a child of type
RenderPositionedBox.
Then I wrapped StreamBuilder with SliverPadding (normally SliverPadding was wrapped by StreamBuilder) but this time it says:
A RenderSliverPadding expected a child of type RenderSliver but received a child of type
RenderPositionedBox.
I tried to use SliverToBoxAdapter and wrapped StreamBuilder with this but didn't solve my problem so How can I achieve this?
Here is last status of my code:
CustomScrollView(
slivers: <Widget>[
SliverAppBar(
centerTitle: true,
floating: false,
snap: false,
pinned: false,
expandedHeight: 0,
elevation: 3,
forceElevated: true,
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
brightness: Brightness.light,
title: Text(
'Your Lists',
style: GoogleFonts.openSans(
fontSize: 22, fontWeight: FontWeight.bold, color: Colors.black),
),
),
SliverPadding(
padding: EdgeInsets.fromLTRB(16, 16, 16, 74),
sliver: StreamBuilder<List<Category>>(
stream: bloc.getAll().asStream(),
builder: (context, AsyncSnapshot<List<Category>> snapshot) {
if (snapshot.hasData) {
return SliverGrid(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
if (index == snapshot.data.length) {
return ListAddCard();
}
return ListCard(
category: snapshot.data[index],
);
}, childCount: snapshot.data.length + 1),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 16,
crossAxisSpacing: 16,
childAspectRatio: 1.1),
);
}
return Center(child: CircularProgressIndicator());
}),
)
],
);
The solution to this is that you should wrap your entire CustomScrollView inside the Streambuilder rather than the other way round. I was facing the same issue till I used this strategy.
The problem here is that SliverPadding expects a RenderSliverPadding but you are giving it a RenderPositionedBox. What you could do instead is to remove SliverPadding and wrap it in SliverGrid instead of in StreamBuilder.
Try this:
StreamBuilder<List<Category>>(
stream: bloc.getAll().asStream(),
builder: (context, AsyncSnapshot<List<Category>> snapshot) {
if (snapshot.hasData) {
return SliverPadding(
padding: EdgeInsets.fromLTRB(16, 16, 16, 74),
sliver: SliverGrid(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
if (index == snapshot.data.length) {
return ListAddCard();
}
return ListCard(
category: snapshot.data[index],
);
}, childCount: snapshot.data.length + 1),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
mainAxisSpacing: 16,
crossAxisSpacing: 16,
childAspectRatio: 1.1),),
);
}
return Center(child: CircularProgressIndicator());
}),