Closed sososdk closed 6 months ago
That is not unexpected. Even during initial layout the may scroll offset gets corrected in some cases. The offset estimation works differently in the lists. In 0.2.0 it is approximation based on existing items.
What is your use-case? Are all your list item fixed-extent? If so, you can provide extentEstimation
, i.e.
SuperSliverList(
extentEstimation(_,__) => 48,
)
which means that estimated item extent will match actual extent and there is no correction.
I think super_sliver_list
currently needs way to reliably jump to initial index with variable extent lists. That's a missing feature currently.
Note that you can get quite far by simply jumping to initial item in first post frame callback.
@override
void initState() {
super.initState();
_scrollController = ScrollController();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
_listControllers.last.jumpToItem(
index: 50, scrollController: _scrollController, alignment: 0.0);
});
}
This will show the list initially at the beginning, but only for one frame after which it immediately jump to requested item. The benefit here is that this works with variable extents, you don't need to know the height of your item and they don't need to be same.
jumpToItem
not work, when SuperSliverList
wrapped with MultiSliver
.
Example:
import 'package:flutter/material.dart';
import 'package:sliver_tools/sliver_tools.dart';
import 'package:super_sliver_list/super_sliver_list.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final scrollController = ScrollController(initialScrollOffset: 50 * 48);
final listController = ListController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: CustomScrollView(
controller: scrollController,
slivers: [
MultiSliver(
pushPinnedChildren: true,
children: [
SliverPinnedHeader(
child: Container(
height: 48,
color: Theme.of(context).colorScheme.inversePrimary,
child: const ListTile(title: Text('Group 1')),
),
),
SuperSliverList(
extentEstimation: (index, crossAxisExtent) => 48,
delegate: SliverChildBuilderDelegate(
(context, index) => SizedBox(
height: 48,
child: ListTile(title: Text('Group 1 Item $index')),
),
childCount: 30,
),
)
],
),
MultiSliver(
pushPinnedChildren: true,
children: [
SliverPinnedHeader(
child: Container(
height: 48,
color: Theme.of(context).colorScheme.inversePrimary,
child: const ListTile(title: Text('Group 2')),
),
),
SuperSliverList(
extentEstimation: (index, crossAxisExtent) => 48,
delegate: SliverChildBuilderDelegate(
(context, index) => SizedBox(
height: 48,
child: ListTile(title: Text('Group 2 Item $index')),
),
childCount: 30,
),
)
],
),
MultiSliver(
pushPinnedChildren: true,
children: [
SliverPinnedHeader(
child: Container(
height: 48,
color: Theme.of(context).colorScheme.inversePrimary,
child: const ListTile(title: Text('Group 3')),
),
),
SuperSliverList(
listController: listController,
extentEstimation: (index, crossAxisExtent) => 48,
delegate: SliverChildBuilderDelegate(
(context, index) => SizedBox(
height: 48,
child: ListTile(title: Text('Group 3 Item $index')),
),
childCount: 30,
),
)
],
),
// SuperSliverList(
// listController: listController,
// extentEstimation: (index, crossAxisExtent) => 48,
// delegate: SliverChildBuilderDelegate(
// (context, index) => SizedBox(
// height: 48,
// child: ListTile(title: Text('Group 2 Item $index')),
// ),
// childCount: 90,
// ),
// )
],
), //
floatingActionButton: FloatingActionButton(
onPressed: () => listController.jumpToItem(
index: 2,
scrollController: scrollController,
alignment: 0.0,
),
tooltip: 'Jump',
child: const Icon(Icons.gps_fixed),
),
);
}
}
MultiSliver
does not properly implement the childScrollOffset
method so it is not possible to have jumpToOffset
working with it. Second issue is that SliverGeometry
does not currently have any way of signalling child obstruction extent, needed for determining visible children in sliver.
If you want to have sticky headers, you can use this widget from example and place SuperSliverList
s directly in the CustomScrollView
(without MultiSliver
).
Is there a way to avoid rebound?
The rebound is there because you are showing last item with alignment = 0, which means beginning of viewport. If you want to show last item at the end of the list, you need to set alignment to 1, which will get rid of rebound.
Yes, you are right.
But when we jump to any item, we don’t know whether this item will cause a rebound.
Just like scroll_to_index does not cause a rebound.
That is a good point. I agree current behavior is counter-intuitive. Should be fixed by https://github.com/superlistapp/super_sliver_list/pull/38.
Overscroll should be fixed in 0.2.1 (just published).
Thank you so much!
Use initialScrollOffset:
super_sliver_list 0.0.8 works fine.
super_sliver_list 0.2.0 initialScrollOffset is incorrect.