How to use StreamBuilder inside CustomScrollWidget? - flutter

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

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

App crashes when load a lot of data inside a gridview

Description
I'm using a gridview to show the products of a market and when a scroll the gridview nine times my app crashes.
The widget build of this screen return a SmartRefresher wrapped a GridView.
return SmartRefresher(
controller: controller.refreshController,
onLoading: controller.onLoading,
onRefresh: controller.onRefresh,
enablePullUp: true,
child: Observer(builder: (context) {
return GridView.builder(
padding: EdgeInsets.only(top: 10),
shrinkWrap: true,
cacheExtent: 100,
physics: ScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount:
MediaQuery.of(context).orientation == Orientation.portrait
? 2
: 3,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0,
childAspectRatio: 0.85,
),
itemCount:
controller.produtos == null ? 0 : controller.produtos.length,
itemBuilder: (context, index) {
Produto produto = controller.produtos[index];
//here I return my card
},
);
}),
);
Note: If I remove the SmartRefresher I can scroll the GridView with a lot of data.
Try to place the Observer above SmartRefresher, like this:
Observer(
builder: (context) {
return SmartRefresher(
controller: controller.refreshController,
onLoading: controller.onLoading,
onRefresh: controller.onRefresh,
enablePullUp: true,
child: GridView.builder(
padding: EdgeInsets.only(top: 10),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount:
MediaQuery.of(context).orientation == Orientation.portrait
? 2
: 3,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0,
childAspectRatio: 0.85,
),
itemCount:
controller.produtos == null ? 0 : controller.produtos.length,
itemBuilder: (context, index) {
Produto produto = controller.produtos[index];
},
),
);
},
);