How do I get the current sliver item that is visible/scrolled inside a Flutter CustomScrollView?
My CustomScrollView has multiple SliverGrid and SliverToBoxAdapter widgets inside. I would like to know which `SliverGrid' is currently visible (scrolled to).
child: CustomScrollView(
slivers: <Widget>[
SliverToBoxAdapter(),
SliverGrid(),
SliverToBoxAdapter(),
SliverGrid(),
...
],
)
I would also like to know the scroll position inside the currently visible SliverGrid.
you can try this to get the current visible widget, inview_notifier_list:
import 'package:flutter/material.dart';
import 'package:inview_notifier_list/inview_notifier_list.dart';
import 'box.dart';
class CSVExample extends StatelessWidget {
final ScrollController? scrollController;
final IsInViewPortCondition? inViewPortCondition;
const CSVExample({Key? key, this.scrollController, this.inViewPortCondition})
: super(key: key);
#override
Widget build(BuildContext context) {
final IsInViewPortCondition condition = inViewPortCondition ??
(double deltaTop, double deltaBottom, double vpHeight) {
return deltaTop < (0.5 * vpHeight) && deltaBottom > (0.5 * vpHeight);
};
return Stack(
fit: StackFit.expand,
children: <Widget>[
InViewNotifierCustomScrollView(
initialInViewIds: ['grid3', 'grid4', 'grid5'],
isInViewPortCondition: condition,
contextCacheCount: 35,
controller: scrollController,
slivers: <Widget>[
SliverPadding(
padding: EdgeInsets.only(top: 50.0, bottom: 50.0),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 1.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Box(
id: 'grid$index',
height: 200.0,
);
},
childCount: 20,
),
),
),
SliverPadding(
padding: EdgeInsets.only(top: 50.0, bottom: 50.0),
sliver: SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
width: double.infinity,
height: 300.0,
color: Colors.blueGrey,
alignment: Alignment.center,
margin: EdgeInsets.symmetric(vertical: 50.0),
child: Box(id: 'list$index'),
);
},
childCount: 3,
),
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 1.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Box(
id: '2nd-grid$index',
height: 200.0,
);
},
childCount: 10,
),
),
],
),
IgnorePointer(
ignoring: true,
child: Align(
alignment: Alignment.center,
child: Container(
// height: 200.0,
// color: Colors.redAccent.withOpacity(0.2),
height: 1.0,
color: Colors.redAccent,
),
),
),
],
);
}
}
Related
I am trying to implement a search function in GridView, which is on SimpleDialog. When I perform a search, the GridView is not updating. But when I scroll through GridView, It updates.
Below is my code
Widget _showParentCategory() {
return SimpleDialog(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: TextField(
onChanged: (value) {
finalList = myProducts
.where((product) => product["name"]
.toString()
.toLowerCase()
.contains(value.toLowerCase()))
.toList();
setState(() {
finalList;
});
},
decoration: const InputDecoration(suffixIcon: Icon(Icons.close)),
),
),
const SizedBox(
height: 20,
),
SizedBox(
height: MediaQuery.of(context).size.height * 0.7,
width: MediaQuery.of(context).size.width,
child: Container(
color: Colors.white,
child: GridView.builder(
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 5.0,
mainAxisSpacing: 5.0),
itemCount: finalList.length,
itemBuilder: (BuildContext context, index) {
return Column(
children: [
const CircleAvatar(
backgroundColor: Colors.red,
radius: 20.0,
),
Text(finalList[index]["name"])
],
);
},
),
),
)
],
);
}
I tried to print my finalist which is getting updated but it seems setState is not working.
How can I redraw/ refresh GridView?
I have a gridView(3 items in a row) with dynamic number of items. I want gridView item should be at bottom means if there are 3 items then one row at bottom of screen, if there are 6 items then two row at bottom. So a dynamic padding at top depending on item count and screen size. My code structure is like below
class className extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Stack(
children: [
ImageFiltered(
//code here
),
Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
color: Colors.black38,
),
GridView.builder(
primary: false,
reverse: false,
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 30,
),
itemBuilder: (context, index) {
return InkWell(
onTap: () {
//code
},
child: CustomWidget(
title: toolkitStore.getLabel(toolKit),
icon: ImageIcon(
AssetImage(
'assets/images/abcd.png'),
size: 48,
color: kWhiteColor,
),
),
);
},
itemCount: getCount().length,
gridDelegate:
SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
),
],
),
),
);
}
}
Now I have this
but I want this
Thanks in advance.
**Try this one: **
import 'package:flutter/material.dart';
class ClassName extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Stack(
children: [
Container(height: 100,width: 200,color: Colors.green,),//<----Your any widget
Align(
alignment: Alignment.bottomCenter,
child: GridView.builder(
primary: false,
reverse: true,
padding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 30,
),
itemBuilder: (context, index) {
return InkWell(
onTap: () {
//code
},
child: const Icon(Icons.person,size: 50,color: Colors.green,),
);
},
itemCount: 5,
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
),
),
Container(height: 100,width: 100,color: Colors.red,),//<----Your any widget
],
),
),
);
}
}
Wrap your GridView.builder with Align widget provide alignment: Alignment.bottomCenter,
Align(
alignment: Alignment.bottomCenter,
child: GridView.builder(
primary: false,
reverse: false,
While using Stack use positional widget(Positioned, Align,...) to place children on UI.
Thank you, everyone.
I solved my problem by using Positioned widget and setting shrinkWrap: true, my code structure is
new Positioned(
left: 0.0,
bottom: 0.0,
width: MediaQuery.of(context).size.width,
child: GridView.builder(
shrinkWrap: true, //this is most important
))
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 create 2 list type data
first linear and other grid type list
now requirement is that both data list height will be dynamic means it should be expand when , new object is added.
and sorting scrollable along with all 4 widgets(sort, list, sort, grid).
like below image:
i have tried but height is static ,
i have used expanded but not giving result as i'm expecting.
code:
Container(
padding: EdgeInsets.all(10),
child: Container(
child: Column(
children: [
sort(context),//sort widget
createForlderView(context), //dynamic list widget
sort(context), //sort widget
_createGridView()// dynamic grid list widget
],
),
),
);
Widget sort(BuildContext context){
return Container(
// color: Colors.red,
height: 25,
width: MediaQuery.of(context).size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Folder"),
Container(
// color:Colors.green,
width: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Sort"),
// SizedBox(width:5),
InkWell(onTap: () {}, child: Icon(Icons.sort))
],
),
),
],
),
);
}
// list view:
Widget createForlderView(BuildContext context) {
final _width = MediaQuery.of(context).size.width;
final _height = MediaQuery.of(context).size.height;
return Expanded(
child: Container(
height: _height / 1.2,
child: ListView.builder(
// padding: ,
itemCount: directoryItems.length,
itemBuilder: (context, index) {
return Card(
child: ListTile(
leading: Container(
height: 30,
width: 30,
child: Icon(Icons.folder, color: Colors.brown),
),
title: Text(directoryItems[index]),
subtitle: Text("15 items"),
trailing:
IconButton(icon: Icon(Icons.more_vert), onPressed: () {}),
));
}),
),
);
}
// grid view:
Widget _createGridView() {
var mSize = MediaQuery.of(context).size;
/*24 is for notification bar on Android*/
final double itemHeight = (mSize.height - kToolbarHeight) / 2;
final double itemWidth = mSize.width / 2;
int gridItemCount =
Provider.of<DocumentProvider>(context).allDocuments.length;
return Expanded(
child: Container(
height: 100,
child: GridView.count(
key: animatedListKey,
scrollDirection: Axis.vertical, //default
reverse: false,
crossAxisCount: 2,
crossAxisSpacing: 4.0,
mainAxisSpacing: 4.0,
childAspectRatio: (itemWidth / itemHeight),
children: List.generate(gridItemCount, (index) {
return Center(
child: SelectCard(
index: index,
itemHeight: itemHeight,
itemWidth: itemWidth,
deletefun: () {
Navigator.pop(context);
DeleteDialog(
index: index,
dateTime:
Provider.of<DocumentProvider>(context, listen: false)
.allDocuments[index]
.dateTime);
},
),
);
}),
),
),
);
}
output screen:
I have found my solution,
used CustomScrollView along with it's slivers: SliverToBoxAdapter (for single widget),SliverFixedExtentList (for linear list) , and SliverGrid (for Grid list).
import 'package:flutter/material.dart';
class ExpandableList extends StatefulWidget {
final List<FolderModel> listData;
final List<FilesModel> gridListData;
const ExpandableList({Key key, this.listData, this.gridListData}) : super(key: key);
#override
_ExpandableListState createState() => _ExpandableListState();
}
class _ExpandableListState extends State<ExpandableList> {
#override
Widget build(BuildContext context) {
return Scaffold(
body: CustomScrollView(
slivers: <Widget>[
buildAppBar(),
sorting(title: "folders", tralingTitle: "sort", onTap: (){}),
expandableListBuilder(),
sorting(title: "files", tralingTitle: "sort",onTap: (){}),
expandableGridList(),
],
)
);
}
buildAppBar(){
return SliverAppBar(
title: Text("Multi Expandable list example"),
centerTitle: true,
pinned: true,
);
}
expandableListBuilder(){
return SliverFixedExtentList(
itemExtent: 75.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Card(
child: ListTile(
leading: Container(
height: 30,
width: 30,
child: Icon(Icons.folder, color: Colors.brown),
),
title: Text(widget.listData[index].title),
subtitle: Text(widget.listData[index].subtitle),
trailing:
IconButton(icon: Icon(Icons.more_vert), onPressed: () {}),
));
},
childCount: widget.listData.length
),
);
}
expandableGridList(){
return SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Card(
elevation: 8,
child: Container(
alignment: Alignment.center,
height: 100,
width: 100,
child: Text("${widget.gridListData[index].title}${(index+1).toString()}"),
),
);
},
childCount: widget.gridListData.length,
),
);
}
sorting({String title,String tralingTitle, void Function() onTap}){
return SliverToBoxAdapter(
child: Container(
padding: const EdgeInsets.all(10),
height: 50,
width: MediaQuery.of(context).size.width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(title),
Container(
// color:Colors.green,
width: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(tralingTitle),
// SizedBox(width:5),
InkWell(
onTap:onTap,
child: Icon(Icons.sort))
],
),
),
],
),
),
);
}
}
result:
Has anyone used Gradient in SliverAppBar? I can do it in FlexibleSpace when it's expanded, but when it's collapsed it gets a solid color. Is it possible to treat this?
Wrap a new Container Widget in FlexibleSpaceBar, then add BoxDecoration, LinearGradient under the container.
Unless I'm missing something, this should do what you're asking.
Before it's scrolled it looks like this:
And after scrolling like this:
import 'package:flutter/material.dart';
void main() => runApp(GradientAppBar());
class GradientAppBar extends StatefulWidget {
#override
_GradientAppBarState createState() => _GradientAppBarState();
}
class _GradientAppBarState extends State<GradientAppBar> {
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: CustomScrollView(
slivers: <Widget>[
SliverAppBar(
pinned: true,
expandedHeight: 100,
flexibleSpace: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Colors.black,
Colors.white,
],
),
),
),
title: Text("This app bar has a gradient!"),
),
SliverList(
delegate: SliverChildListDelegate(
[
Container(
color: Colors.blue,
height: 500,
),
Divider(),
Container(
color: Colors.black12,
height: 500,
),
Divider(),
Container(
color: Colors.lightBlue,
height: 500,
),
Divider(),
Container(
color: Colors.lightGreen,
height: 500,
),
Divider(),
],
),
),
],
),
),
);
}
}
show this from Flutter documention:
CustomScrollView(
slivers: <Widget>[
const SliverAppBar(
pinned: true,
expandedHeight: 250.0,
flexibleSpace: FlexibleSpaceBar(
title: Text('Demo'),
),
),
SliverGrid(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 200.0,
mainAxisSpacing: 10.0,
crossAxisSpacing: 10.0,
childAspectRatio: 4.0,
),
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.teal[100 * (index % 9)],
child: Text('grid item $index'),
);
},
childCount: 20,
),
),
SliverFixedExtentList(
itemExtent: 50.0,
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return Container(
alignment: Alignment.center,
color: Colors.lightBlue[100 * (index % 9)],
child: Text('list item $index'),
);
},
),
),
],
)