How can we make this kind of list behavior?
As input parameters we have
- index of the element to be fixed (can be found in advance if necessary)
- list of items
I can imagine a way to fix the element on top, although the way is depressing:
final indexPinnedItem = 23;
final listBefore = [...all elements up to 23];
final listAfter = [...all elements after 23];
return CustomScrollView(
slivers: [
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return listBefore; // elements with index from 0 to 22 inclusive
}),
),
SliverPinnedHeader(
child: // element with index 23,
),
SliverList(
delegate: SliverChildBuilderDelegate(
(BuildContext context, int index) {
return listAfter; // elements with index from 24 to
}),
),
],
);
Note that the height of the element can be different. Size of fixed element and element in "free float" are the same (in fact, they are the same widget. I don’t need it to start changing size, shape, or anything else as soon as an item becomes fixed).
How can this behavior be achieved?
Update:
The whole sacred point is that I want to see the current item selected in the big list, which does not fit completely on the screen.
-> Suppose that our selected item has the sequence number 23. When we go to the screen, we only see items 1 through 4. And therefore, the 23rd element must be secured from below (Fig. 1).
-> Now we scroll down to 23 items and it is automatically detached and visible anywhere in the list (Fig. 2).
-> But as soon as an item is out of view, it’s automatically re-locked (at the bottom or top, depending on where we’re scrolling at the moment) (Fig. 3, 4).
4
Answers
Thanks colleagues, solution found based on suggestions from @AJ- и @magesh magi
I used the split list to set
SliverPinnedHeader
from the sliver_tools package in between. This is how I achieved fixing the element at the top.To pin the element at the bottom, I used
Column
and visibility state tracking with the visibility_detector package.Thus, the item can be anchored dynamically at the top or bottom, depending on its position in the list and current visibility.
The code for our list looks like this:
(I used hooks to shorten the code and make it easier to understand.)
A widget to pin at the bottom of the list:
And the card widget itself (be sure to wrap in
Material
):I purposely changed the color of the widget when it starts attaching to the bottom edge. Just to show the animation, which can be anything.
Performance: There is nothing wrong with performance because:
scrollController
and other things (conditionally, because we use the package visibility_detector)You can run the example and find the full code in the repository here: github.com/PackRuble/dynamic_pin_list
Here is the edited solution after you provided more informations about what you are trying to achieve.
For the
Pinned
tile, I used the same package as you, for simplicity sake, but thepinned
effect is easily achievable by just wrappingCustomScrollView
in aStack
widget and then playing a bit with aPositioned
widget to create a top sticky tile.The logic is to create a
Class
, that has aisPinned
property, andmapping
all yourlist elements
to such a class.With this step, you can now track each widget’s state.
You’ll now create a couple of utility
methods
that will loop over your list, and set / unset the pinned state.In the following working example, I implemented the
top pinned
and thebottom pinned
feature, and it works by using aNotificationListener
and each time you set as pinned an item, it will save the scroll position on they
axis, and thanks to that, with theNotificationListener
, you can determine if the pinned item should be displayed at the top or at the bottomWhen you set a widget as
pinned
, it will only get pinned when it’s not in the view, to detect the visibility, I’m using a really good and supported package,visibility_detector
, maintained by the Google team.This is the custom
Class
that will be mapped to your list:This is your
view
, that displays all the UI:and finally this is the
ListTile
custom widget, that I extracted in a separateclass
:For any question feel free to ask!
It seems to me that what you are describing is best achieved with a Stack and an Align widget. That is, wrap your ListView (or equivalent widget) in a Stack, then place a copy of the selected item in an Align and put that Align into the Stack after the ListView. Something like this:
The result, if executed properly, should be a an item that is rendered above the list with whatever alignment you prefer. The list will scroll up and down behind the item and the item would always be visible. Make sure to use
shrinkWrap: true
or equivalent because otherwise height will be unbounded.Align: https://api.flutter.dev/flutter/widgets/Align-class.html
Stack: https://api.flutter.dev/flutter/widgets/Stack-class.html
This may be work around for how you want. u can change to builders.
I just calculate to show and hide top pinned widget by using height of widget in controller of scroll position. same way u can calculate for bottom.