-
Notifications
You must be signed in to change notification settings - Fork 28.5k
Return index of the first visible item in ListView #19941
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
You can add a key to each ListView of item, and then traverse the collection of key to know which is displayed. |
Could you please add a code snippet how to find the only visible items? Thank you for your response. I appreciate it. |
Any update on this? How to get only items displayed on the screen? |
I hope they provide a code of it |
My solution here: Visible Items of ListView Demo |
The work for #10595 might provide a solution for this issue as well. |
are there news about this point? For example I want to change TextStyle of the first visible item and apply the actions of a FloatingActionBottom over that element |
Probably also related is #12319 |
@debuggerx01 RectGetter solution has bad performance and is a bit buggy ( if you wrap a TextField inside it, keyboard will disappear automatic and some more bugs ). |
Is there any update in this issue or any work around? |
Following example is used inside our app, you can use the pseudo code to get your visible widget state in any position. I assume we wanna get the position of the first row. just wrap your children with MetaData MetaData(
// [this] can be a custom class, so you can locate it using the class type
// assume this is a statefull class named DefaultRow and its state called DefaultRowState
metaData: this,
behavior: HitTestBehavior.translucent,
child: row//a row inside the ListView
) so the widget in the concept will become ListView(
children: [
MetaData(),
MetaData(),
MetaData(),
MetaData(),
MetaData()
]
) when we need to fetch the first visible row inside the viewport of ListView, just call as following: final box = listViewState?.context?.findRenderObject() as RenderBox;
if (box != null) {
// adjust it according to your list padding,
// e.g. you have 20's points for your top padding,
// just send Offset(0, 20) into [localToGlobal] or maybe 25 just in case of any other border.
final offset = box.localToGlobal(Offset(0, 0));
final targetState = getTargetState<DefaultRowState>(offset);
print('offset: $offset, name: $targetState');
}
T getTargetState<T>(Offset globalPosition) {
final HitTestResult result = new HitTestResult();
WidgetsBinding.instance.hitTest(result, globalPosition);
// Look for the RenderBoxes that corresponds to the hit target (the hit target
// widgets build RenderMetaData boxes for us for this purpose).
for (HitTestEntry entry in result.path) {
if (entry.target is RenderMetaData) {
final renderMetaData = entry.target as RenderMetaData;
if (renderMetaData.metaData is T)
return renderMetaData.metaData as T;
}
}
return null;
} |
@jerrywell Thanks ! It works! And some additional info: If you running getTargetState on some callback, it must wrap with Future.microtask ( I don't know why, can somebody tell me?) class _VideoListState extends State<VideoList> {
@override
void initState() {
super.initState();
}
ListView _listView;
@override
Widget build(BuildContext context) {
_listView = ListView.builder(
itemBuilder: (context,index) {
var item = widget.list[index];
return _getChild(item,);
},
itemCount: widget.list.length,
);
var notificationListener = NotificationListener(
onNotification: (noti){
if (noti is ScrollStartNotification) {
// stop playing
}else if(noti is ScrollEndNotification){
// resume playing
print("end");
Future.microtask((){
VideoInfo info = getMeta(0, 10);
print("scrolling to ${info.title}");
});
}
},
child: _listView,
);
return notificationListener;
}
T getMeta<T>(double x,double y){
var renderBox = context.findRenderObject() as RenderBox;
var offset = renderBox.localToGlobal(Offset(x,y));
HitTestResult result = HitTestResult();
WidgetsBinding.instance.hitTest(result, offset);
for(var i in result.path){
if(i.target is RenderMetaData){
var d = i.target as RenderMetaData;
if(d.metaData is T) {
return d.metaData as T;
}
}
}
return null;
}
Widget _getChild(VideoInfo info){
return MetaData(
metaData: info,
child: VideoCard(info:info),
);
}
}
class VideoCard extends StatefulWidget{
final VideoInfo info;
const VideoCard({Key key, this.info}) : super(key: key);
@override
VideoCardState createState() => VideoCardState();
}
class VideoCardState extends State<VideoCard> {
@override
Widget build(BuildContext context) {
return Container(child: Text("hello world ${widget.info.title}"));
}
}
//-------------data---------------
class VideoInfo {
final String title;
VideoInfo(this.title);
} |
Try adding a ScrollController to the list, then add a listener to it as shown below. class _NumberListState extends State<NumberList> {
ScrollController scrollController;
// use this one if the listItem's height is known
// or width in case of a horizontal list
void scrollListenerWithItemHeight() {
int itemHeight = 60; // including padding above and below the list item
double scrollOffset = scrollController.offset;
int firstVisibleItemIndex = scrollOffset < itemHeight
? 0
: ((scrollOffset - itemHeight) / itemHeight).ceil();
print(firstVisibleItemIndex);
}
// use this if total item count is known
void scrollListenerWithItemCount() {
int itemCount = 100;
double scrollOffset = scrollController.position.pixels;
double viewportHeight = scrollController.position.viewportDimension;
double scrollRange = scrollController.position.maxScrollExtent -
scrollController.position.minScrollExtent;
int firstVisibleItemIndex =
(scrollOffset / (scrollRange + viewportHeight) * itemCount).floor();
print(firstVisibleItemIndex);
}
@override
void initState() {
super.initState();
scrollController = ScrollController();
scrollController.addListener(scrollListenerWithItemHeight);
}
@override
void dispose() {
super.dispose();
scrollController.removeListener(scrollListenerWithItemHeight);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.blueGrey[200],
appBar: AppBar(
backgroundColor: Colors.blueGrey[500],
),
body: ListView.builder(
itemCount: 100,
controller: scrollController,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(5.0),
child: Container(
height: 50,
decoration: BoxDecoration(
color: Colors.white,
),
child: Center(child: Text('$index'))),
);
}));
}
} |
this way is limit to the item height is be known, for flexible height, this is not work. |
SOLUTION IS HERE!!! TAKE IT! https://github.com/google/flutter.widgets |
I have searched a lot for something similar in Flutter, like a list that would tell you where is which element, if it is visible etc., but I've only found a few results. |
https://pub.dev/packages/inview_notifier_list. Has anyone tried this package? It builds a ListView and notifies when the widgets are visible on the viewport. |
1 similar comment
https://pub.dev/packages/inview_notifier_list. Has anyone tried this package? It builds a ListView and notifies when the widgets are visible on the viewport. |
I made a library: https://pub.dev/packages/widgets_visibility_provider It is very simple and flexible to use |
@scofieldpeng - Here, we are able to get firstVisibleItemIndex. But can you please help me out with getting lastVisibleItemIndex ? Help will be appreciated. |
Your answer save my time, thanks.
|
U SAVED MY LIFE !!!!!!! THANK YOU ! |
thanks, solved my problem! |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
Any update on this? |
In the current implementation of ListView, there is no easy way to find which item is the first one in a visible part of the screen. There could be many use cases if we know the index of the first visible item. For instance, playing audio of the first visible item, etc.
Could you please add this feature to the ListView? Thank you.
The text was updated successfully, but these errors were encountered: