We have a gridview.builder showing thumbnails which on tap lead to videos in a gridview. We have a streamcontroller that should trigger as you hit the bottom of the page, load a block (12 thumbnails a time). We wanted to make The whole page scroll. But now the streamcontroller doesn't trigger. If I remove the Neverscrollable physics from the gridview.builder it will trigger.
So in short, I want to be able to scroll the entire page, profile information and then through the ever loading video thumbnails. I am stuck as to why this isn't working. How do I make the streamcontroller trigger with the entire page able to scroll? Help appreciated.
SingleChildScrollView(
controller: _scroller,
physics: ScrollPhysics(),
child: Container(
height: MediaQuery.of(context).size.height,
child: StreamBuilder<List<Post>>(
stream: _streamController,
builder: (context, snapshot) {
return Column(
children: <Widget>[
_buildProfileInfo(_profileUser),
Divider(),
Flexible(
child: GridView.builder(
controller: _scrollController,
shrinkWrap: true,
physics: NeverScrollableScrollPhysics(),
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 1.0,
mainAxisSpacing: 2.0,
crossAxisSpacing: 2.0,
),
itemCount: _posts.length + 1,
itemBuilder: (BuildContext context, int index) {
if (index == _posts.length) {
if (!_hasMorePosts) {
return SizedBox.shrink();
} else {
return Center(
child: Container(
child: SpinKitThreeBounce(
color: HexColor(hexColorMmRed),
size: 30,
),
));
}
} else {
Post post = _posts[index];
return _buildTilePost(post);
}
},
),
)
],
);
}),
),
);
Container(
height: MediaQuery.of(context).size.height,
child: StreamBuilder<List<Post>>(
stream: _streamController,
builder: (context, snapshot) {
return SingleChildScrollView(
controller: _scroller,
child: Column(
children: <Widget>[
_buildProfileInfo(_profileUser),
Divider(),
GridView.builder(
shrinkWrap: true,
primary: false,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: 1.0,
mainAxisSpacing: 2.0,
crossAxisSpacing: 2.0,
),
itemCount: _posts.length + 1,
itemBuilder: (BuildContext context, int index) {
if (index == _posts.length) {
if (!_hasMorePosts) {
return SizedBox.shrink();
} else {
return Center(
child: Container(
child: SpinKitThreeBounce(
color: HexColor(hexColorMmRed),
size: 30,
),
));
}
} else {
Post post = _posts[index];
return _buildTilePost(post);
}
},
)
],
),
);
}),
);
Answer found here
Related
I'm trying to get the 2 images next to each other instead of beneath each other but I have no clue how to go about something like this, I tried something with an I variable to influence the index but that did not really work out. normally I could do this but with the index, I have no clue how to go about this.
code:
Center(
child: Column(
children: [
_items.isNotEmpty
? Expanded(
child: ListView.builder(
itemCount: _items.length,
itemBuilder: (context, index) {
return Column(
children: [
RawMaterialButton(
child: Image(
image: NetworkImage(_items[index]["portrait"]),
height: 200,
width: 140,
),
onPressed: () {
Navigator.push(context, _AnimatedNavigation(SpecificCharacterRoute(
_items[index]["name"], _items[index]["name"], _items[index]["shop_background"], _items[index]["overview"],
_items[index]["difficulty"], "images/dbdsurvivorlogo.png")
)
);
},
),
],
);
},
),
)
: Container()
],
),
),
Here's an image of what I mean
Here's an image of what I have
You can use GridView.count( with crossAxisCount: 2,
GridView.count(
crossAxisCount: 2,
children: [...],
);
Or with .builder
GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: ,
itemBuilder: (context, index) {...},
);
You can use Gridview for 2 in a row. If you want to change from 2 to 3 in row then you can change crossAxisCount.
GridView.builder(
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200,
crossAxisCount: 2,
childAspectRatio: 3 / 2,
crossAxisSpacing: 20,
mainAxisSpacing: 20),
itemCount: _items.length,
itemBuilder: (BuildContext ctx, index) {
return Column(
children: [
RawMaterialButton(
child: Image(
image: NetworkImage(_items[index]["portrait"]),
height: 200,
width: 140,
),
onPressed: () {
Navigator.push(context, _AnimatedNavigation(SpecificCharacterRoute(
_items[index]["name"], _items[index]["name"], _items[index]["shop_background"], _items[index]["overview"],
_items[index]["difficulty"], "images/dbdsurvivorlogo.png")
)
);
},
),
],
);
}),
I am trying to create a container and write no data found in case the data coming from the API is null.
Although I deleted the link of the API, the container does not appear.
body: Obx(
() => fGames2SecScreenLoad.isLoading.value
? const Center(
child: LoadingWidget(),
)
: GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: (itemWidth / itemHeight),
crossAxisCount: 1,
),
scrollDirection: Axis.vertical,
itemCount: freeGameTypeController.freeGames.length,
itemBuilder: (BuildContext context, int index) {
var freeGames = freeGameTypeController.freeGames[index];
if(freeGamesController.freeGamesList.isEmpty){
return Container(chid: Text("No data found");}
else{
return Padding(
padding: EdgeInsets.symmetric(
horizontal: 7.w,
vertical: 8.h,
),
child: GestureDetector(
onTap: () {
Get.to(
const FreeGamesIndex(),
transition: Transition.native,
duration: const Duration(milliseconds: 200),
arguments: freeGames,
);
},
child: FreeGamesListCard(
fImage: freeGames.image,
fTitle: freeGames.title,
fPlatforms: freeGames.platforms,
fWorth: freeGames.worth,
),
),
);
}}),
),
Try this:
body: Obx(
() => fGames2SecScreenLoad.isLoading.value
? const Center(
child: LoadingWidget(),
)
: Builder(
builder: (_){
if(freeGamesController.freeGamesList.isEmpty){
return Container(chid: Text("No data found");
}
return GridView.builder(
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
childAspectRatio: (itemWidth / itemHeight),
crossAxisCount: 1,
),
scrollDirection: Axis.vertical,
itemCount: freeGameTypeController.freeGames.length,
itemBuilder: (BuildContext context, int index) {
var freeGames = freeGameTypeController.freeGames[index];
return Padding(
padding: EdgeInsets.symmetric(
horizontal: 7.w,
vertical: 8.h,
),
child: GestureDetector(
onTap: () {
Get.to(
const FreeGamesIndex(),
transition: Transition.native,
duration: const Duration(milliseconds: 200),
arguments: freeGames,
);
},
child: FreeGamesListCard(
fImage: freeGames.image,
fTitle: freeGames.title,
fPlatforms: freeGames.platforms,
fWorth: freeGames.worth,
),
),
);
}),
),
}),
),
I'm building my first Flutter app and I have some performance issues.
I have a screen with a CustomScrollView, to especially get a custom AppBar, with a Hero image. Next, I have some fields/filters and then I have my GridView.builder because I can have between 25 to 400 elements in this list, and I want to build them only when the user is scrolling.
But after checking, I saw that all of my elements (400) are built immediately, and makes the app very laggy.
Can you please tell me what I've done wrong?
EDIT: You can test it via this Pastebin: Just simply copy-paste it into a new flutter project https://pastebin.com/xhd9CdUp
Basically, this is the structure of my code:
CustomScrollView
SliverAppBar
SliverPadding
SliverChildListDelegate
SearchField
SizedBox
ValueListenableBuilder // hive package
GridView.builder
CardWithMenuWidget(index)
And here is my code:
#override
Widget build(BuildContext context) {
return Scaffold(
extendBodyBehindAppBar: true,
body: SafeArea(
child: LayoutBuilder(builder: (context, constraints) {
return CustomScrollView(
slivers: [
SliverAppBar(
elevation: 0,
backgroundColor: Colors.transparent,
expandedHeight: 300,
pinned: true,
flexibleSpace: FlexibleSpaceBar(
titlePadding: EdgeInsets.symmetric(vertical: 4),
title: ConstrainedBox(
constraints:
BoxConstraints(maxWidth: constraints.maxWidth * 0.5),
child: Hero(
tag: "extension-logo-${extension.uuid}",
child: CachedImage(imageUrl: extension.logoMediumUrl!),
),
),
background: Align(
alignment: Alignment.topCenter,
child: Hero(
tag: "extension-spotlight-${extension.uuid}",
child: CachedImage(
imageUrl: extension.spotlightMediumUrl!,
fit: BoxFit.fitWidth,
height: 240,
width: double.infinity,
alignment: Alignment.topCenter,
),
),
),
),
),
SliverPadding(
padding: EdgeInsets.symmetric(horizontal: 16.0),
sliver: SliverList(
delegate: SliverChildListDelegate(
[
SearchField(
controller: this._searchController,
labelText: "Search into ${extension.name}",
cancelText: 'Cancel',
),
SizedBox(height: 32),
// Dynamic item list
ValueListenableBuilder(
valueListenable:
ExtensionBox.box.listenable(keys: [_uuid]),
builder: (context, Box<Extension> box, child) {
var cardUuidList = box.get(_uuid)!.cards?.keys;
return _buildCardListGrid(cardUuidList);
},
),
FakeBottomBarPadding(),
],
),
),
),
],
);
}),
),
);
}
Widget _buildCardListGrid(cardUuidList) {
return LoadingContainer(
isLoading: _isSyncing,
child: cardUuidList == null
? Center(
child: Text('No data.'),
)
: GridView.builder(
addAutomaticKeepAlives: true,
shrinkWrap: true,
physics: ScrollPhysics(),
itemCount: cardUuidList.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 16.0,
crossAxisSpacing: 16.0,
childAspectRatio: CardModel.ratio,
),
itemBuilder: (context, cardIndex) {
String cardUuid = cardUuidList.elementAt(cardIndex);
return CardWithMenuWidget(
uuid: cardUuid,
);
},
),
);
}
Thank you!
My solution, based on my pastebin code:
CustomScrollView(
SliverAppBar(),
...
ValueListenableBuilder(
valueListenable: _list,
builder: (context, listBuilder, child) {
return SliverGrid(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 16.0,
crossAxisSpacing: 16.0,
childAspectRatio: 635 / 889,
),
delegate: SliverChildBuilderDelegate(
(c, i) {
return ItemWidget(listBuilder[i]);
},
childCount: _list.value.length,
),
);
},
),
)
I want to rend ListView.builder and GridView.builder on one page i tried this code but working
difficulty in rendring these two builders both will display vertical list and scrollable.
Scaffold(
appBar: AppBar(title: Text("Title")),
body: Column(
children: <Widget>[
ListView.builder(
primary: false,
shrinkWrap: true,
itemBuilder: (context, position) {
return ListTile(
title: Text(choices[position]),
);
},
itemCount: choices.length,
),
GridView.builder(
physics: ScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: (3 / 2),
),
itemCount: personas.length,
itemBuilder: (BuildContext context, int index) {
return Container(
child: Card(
child: Center(
child: Text(personas[index]?.fullName),
),
),
);
}),
],
),
);
Wrap them in containers and give them proper height or width.
Scaffold(
appBar: AppBar(title: Text("Title")),
body: Column(
children: <Widget>[
Container(
height:200,
child: ListView.builder(
primary: false,
shrinkWrap: true,
itemBuilder: (context, position) {
return ListTile(
title: Text(choices[position]),
);
},
itemCount: choices.length,
),
),
Container(
height:200,
child: GridView.builder(
physics: ScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: (3 / 2),
),
itemCount: personas.length,
itemBuilder: (BuildContext context, int index) {
return Container(
child: Card(
child: Center(
child: Text(personas[index]?.fullName),
),
),
);
}),
)
],
),
);
Wrap Column with SingleChildScrollView which will resolve the rendering issue. Add
physics: ScrollPhysics(), which will allow ListView and GridView to scroll vertically
Scaffold(
appBar: AppBar(title: Text("Title")),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
ListView.builder(
shrinkWrap: true,
physics: ScrollPhysics(),
itemBuilder: (context, position) {
return ListTile(
title: Text('List Item'),
);
},
itemCount: 20,
),
GridView.builder(
physics: ScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: (3 / 2),
),
itemCount: 15,
itemBuilder: (BuildContext context, int index) {
return Container(
child: Card(
child: Center(
child: Text('Grid Item'),
),
),
);
}),
],
),
),
);
Output:
i found the solution this can done by using slivers
Scaffold(
appBar: AppBar(title: Text(appBarTitle)),
body: CustomScrollView(
slivers: <Widget>[
SliverList(
delegate: SliverChildBuilderDelegate((context, index) {
return ListTile(
title: Text(title(choices[index])),
selected: isSame(choices[index], selected),
onTap: () {
onSelected(choices[index]);
},
);
}, childCount: choices.length),
),
SliverToBoxAdapter(
child: GridView.builder(
physics: ScrollPhysics(),
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
childAspectRatio: (3 / 2),
),
itemCount: personas?.length ?? 0,
itemBuilder: (BuildContext context, int index) {
return Container(
child: Card(
child: Center(
child: Text(personas[index]?.fullName),
),
),
);
}),
),
],
));
I have a SliverToBoxAdapter containing a ListView of FilterChips. I want to add a title at the beginning of ListView but I'm always getting a Horizontal viewport was given unbounded width error.
Here's my code:
SliverToBoxAdapter(
child: Container(
height: 70,
child: ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: _expectedAnswers.length,
separatorBuilder: (context, index) {
return SizedBox(height: 7, width: 7);
},
itemBuilder: (context, index) {
return FilterChip(
label: Text('Hello');
// rest of FilterChip params here
);
},
),
),
);
This code as is works OK. But when I'm trying to add a title in front of the list, I'm getting the error above. Here's my attempt:
SliverToBoxAdapter(
child: Container(
height: 70,
child: Row(
children: <Widget>[
Text('Title'),
ListView.separated(
scrollDirection: Axis.horizontal,
itemCount: _expectedAnswers.length,
separatorBuilder: (context, index) {
return SizedBox(height: 7, width: 7);
},
itemBuilder: (context, index) {
return FilterChip(
label: Text('Hello');
// rest of FilterChip params here
);
},
),
],
),