Problem with first item displayed
Closed this issue · 2 comments
Initially, the InfiniteList builder is called with indexes -1, 0 and 1, and the displayed item is the 0, naturally.
However, after using setState() inside the InfiniteList items, causing a rebuild, the list item being displayed first is not 0 anymore, it is always the last one, and this gets progressively worse if you traverse the scroll too much before tapping.
To reproduce: click on any date on the first month (should be december at the time of the writing).
You will see that the text at the top changes, and the scroll behaves as expected.
Now scroll further, to month 2, 3, etc, and click on a day. You will see that the scroll changes it offset weirdly, even thou the date computation is correct.
After clicking on Month 12, day 15:
After clicking on Month 1, day 8:
I don't understand why month 2 is being rendered first here, since month 1 is still my first item (index 0 if you will).
This is a bit complex to explain, but this code does exactly that:
class Test extends StatefulWidget {
@override
_TestState createState() => _TestState();
}
class _TestState extends State<Test> {
DateTime date = DateTime.now();
@override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Column(
children: <Widget>[
Container(
color: Colors.lightBlue,
child: Text(
date.toString(),
style: TextStyle(
fontSize: 30,
),
),
),
Container(
margin: EdgeInsets.symmetric(vertical: 30),
height: 300,
child: InfiniteList(
scrollDirection: Axis.horizontal,
direction: InfiniteListDirection.multi,
builder: (context, index) {
var month = DateTime(date.year, date.month + index, 1);
var monthNumDays =
DateTime(month.year, date.month + index, 0).day;
return InfiniteListItem(
initialHeaderBuild: true,
headerAlignment: HeaderAlignment.centerLeft,
headerBuilder: (context) {
return Container(
color: Colors.white,
child: Text(
"Month: ${month.month}",
style: TextStyle(
fontSize: 20,
),
),
);
},
contentBuilder: (context) {
return Container(
color: RandomColor().randomColor(),
child: Padding(
padding: const EdgeInsets.only(
left: 96, // Necessary to fix header overlay bug.
),
child: Row(
children: List.generate(monthNumDays, (itemIndex) {
return RaisedButton(
child: Text("${itemIndex + 1}"),
onPressed: () {
setState(() {
date = month.add(Duration(days: itemIndex));
});
},
);
}),
),
),
);
},
);
},
),
)
],
),
),
);
}
}
Will take a look at this
Could you explain the issue a little bit more? Not sure that I'm following. Issue is that scroll remains on same position even after you call setState?
TL;DR;
Set a random unique "key" on InfiniteList so that the scroll offset is reset everytime.
Ok, I finally figured it out.
The thing here is that the ScrollController saves it's scroll position in the PageStorage of the list it is attached to. So, what is happening, is that the list is rebuilt, but now the new month is at offset 0, but the scroll position is maintained, which throws it off.
What must be done here is reset the list scroll offset when it is rebuilt, in my case.
The way to do this here, is to set a different key to the list when it is created. This way the widget will not maintain the offset, and everything will work.
This question on SO discusses this well too: https://stackoverflow.com/questions/56364950/listview-doesnt-scroll-to-the-correct-offset-position-after-its-built-again#56365266