google / flutter.widgets

https://pub.dev/packages/flutter_widgets
BSD 3-Clause "New" or "Revised" License
1.38k stars 486 forks source link

ScrollablePositionedList: Use ScrollablePositionedList inside a CustomScrollVIew #32

Open andreyeurope opened 5 years ago

andreyeurope commented 5 years ago

It would be awesome to be able to use ScrollablePositionedList inside a CustomScrollView. I am currently working on a MVP that uses Slivers for reading some content, and by using ScrollablePositionedList with another library, I would be able to save the position inside the content when the user has left the page on any device.

tarobins commented 5 years ago

Doing something as general as what you request is going to be difficult. If you can tell me more, even in a email robinsontom at google, I might be able to suggest something.

Sorry for the delay.

andreyeurope commented 5 years ago

Thanks for answering, Tom. I would like to keep the conversation here, as it might help other people as well. So I am working on a feature (no deadline) which will allow the users of my future app to save the location of an article (an article being in fact an HTML document parsed as a list of widgets) without knowing the size of the device and keep that location in sync on all devices own by the user (via Firestore). This is the main reason why I would like to know the index of the first visible element of the list.

tarobins commented 4 years ago

Do you need to have the list appearing alongside other slivers within the CustomScrollView? If not, then I think CustomScrollView will already do what you want.

There's an example here https://github.com/google/flutter.widgets/blob/6779a6512be4686b9d801367efb473128f331be4/example/scrollable_positioned_list/lib/scrollable_positioned_list_example.dart#L120 that figures out what the first visible item in a list is.

If you have a list plus other types of slivers in a CustomScrollView, ScrollablePositionedList does not handle that case and doing so will be a bunch more work, the scale of which I'm not certain of at the moment.

If this isn't making sense, or I'm misunderstanding what you are looking for, some sample code of what you're trying to do might help.

andreyeurope commented 4 years ago

You are right indeed. I need the list to appear alongside other slivers. For now, I only have a SliverAppBar and the SliverList, but in the future there will be other slivers as well.

The problem is that I tried thinking of different approaches, but all entail having some mechanism that tells me what index does the topmost visible element have and another mechanism for scrolling (or initializing the controller) to a specific index.

tarobins commented 4 years ago

So, I doubt I'll get ScrollablePositionedList to work within a CustomScrollView. I think supporting a SliverAppBar (or a similar custom widget) at the top of a ScrollablePositionedList might be doable. Are there other use cases you've come across so far; other types of slivers you want to use along side the ScrollablePositionedList?

andreyeurope commented 4 years ago

No, not in my case at least. The UI is basically a navigation bar and a list of widgets, which are the SliverChildDelegate and SliverAppBar widgets. It would be nice to still be able to choose the behavior of the app bar, like floating, pinned and snap,

psyanite commented 4 years ago

I totally need this. I'm currently putting a ScrollablePositionedList inside a Column, not sure if I can do ListView.

I also want to do ScrollablePositionedList inside a ScrollablePositionedList.

tarobins commented 4 years ago

Does a ListView inside a ListView work?

psyanite commented 4 years ago

I think NestedListView inside a ListView could work. I ended up using my original structure which was CustomScrollView with SliverList with ListView and then something like.

because I couldn't get Scrollable.ensureVisible to work correctly I don't know why 😭

Page Widget, where _scrollie is my ScrollController

void scrollToOffset(double offset) {
    var y = offset + _scrollie.position.pixels;
    _scrollie.animateTo(
      y,
      duration: Duration(milliseconds: 250),
      curve: Curves.ease,
    );
}

Card Widget

RenderBox box = context.findRenderObject(); 
var position = box.globalToLocal(Offset.zero); // Gives you the position of the card relative to the current scroll position
scrollToOffset(position.dy);
Ahmadre commented 4 years ago

Hi @tarobins,

I need this feature in production. Now I am scrolling the old fashioned way: CustomScrollView with pixel jumping. Not really helpfull. And we have to use CustomScrollView because we need the parallax effect inside the flexibeBar of SliverAppBar + a simple SliverList. When we have now for example an: SliverPositionedList. It would be awesome 💪🏼. Then you would just pass inside SliverPositionedList the itemScrollController and we would break up the work into just one simple Sliver Component :)

Hope this will come one day ❤️

Thx.

Peng-Qian commented 4 years ago

Hi @tarobins,

I need this feature in production. Now I am scrolling the old fashioned way: CustomScrollView with pixel jumping. Not really helpfull. And we have to use CustomScrollView because we need the parallax effect inside the flexibeBar of SliverAppBar + a simple SliverList. When we have now for example an: SliverPositionedList. It would be awesome 💪🏼. Then you would just pass inside SliverPositionedList the itemScrollController and we would break up the work into just one simple Sliver Component :)

Hope this will come one day ❤️

Thx.

Totally Agree !!!

If there are SliverPositionedList and SliverPositionedGird that can fast jump or animate to the item position or PositionedCustomScrollView that can jump or animate to sliver widget start position, it will really help!

And as the scroll view is more and more complicated now and in the future, the NestedScrollView and CustomScrollView may be more commonly used than ListView and GridView.

FernandoUFS commented 4 years ago

Hi guys, I found this package on StackOverflow, it works with CustomScrollView. https://stackoverflow.com/a/61709995/2888623 https://pub.dev/packages/scroll_to_index

hassanfaour commented 4 years ago

This is a not a problem in ScrollablePositionedList but in the SliverFillRemaining. If you set up physics in ScrollablePositionedList to NeverScrollableScrollPhysics() it works perfectly fine, until you realize the end of the list have been cut off depending the your device's screen height. I feel I am very close to solving it. if you set the SliverFillRemaining to has scrollable body. you'll get the error: LayoutBuilder does not support returning intrinsic dimensions. if you rap it with a container the extra height will be a problem and the the position of the first and last items will all be messed up. I think the easiest way is for the flutter team to create a bool option in SliverFillRemaining to assume the device's screen has an infinite height, which will allow our list to appear in full. I think the error is more in SliverFillRemaining rather than ScrollablePositionedList. I think this stack kind of addressed part of the problem.

hassanfaour commented 4 years ago

If someone has found a solution please let us know it's very important for my app. I would like to add that there might be another sliver widget that can solve it for me instead of ScrollablePositionedList, however I since my list is dynamically built I am not sure about the height before building.

hassanfaour commented 4 years ago

I believe the last piece of the puzzle is to figure out how to edit the scrollExtent and paintExtent SliverGeometry for the SliverFillRemaining. If there is a way to customize it. It would be great if anyone can figure it out

hassanfaour commented 4 years ago

@Piinks & @bkonyi , since you both worked on it(https://github.com/flutter/flutter/pull/47379) can you please help us. Thank you

bkonyi commented 4 years ago

@hassanfaour sorry, but I'm not familiar with flutter/flutter#47379.

hassanfaour commented 4 years ago

I just realized my analysis was wrong Increasing the max extend and scroll extend ruined the scroll position reader. If someone is interested in seeing what I did. I used this instead of sliver fill remaining:

class _SliverFillRemainingWithScrollable extends SingleChildRenderObjectWidget { const _SliverFillRemainingWithScrollable({ Key key, Widget child, }) : super(key: key, child: child);

@override RenderSliverFillRemainingWithScrollable createRenderObject( BuildContext context) => RenderSliverFillRemainingWithScrollable(); }

class RenderSliverFillRemainingWithScrollable extends RenderSliverSingleBoxAdapter { /// Creates a [RenderSliver] that wraps a scrollable [RenderBox] which is /// sized to fit the remaining space in the viewport. RenderSliverFillRemainingWithScrollable({RenderBox child}) : super(child: child);

@override void performLayout() { final SliverConstraints constraints = this.constraints; // TODO(Piinks): This may fill too much space for NestedScrollView, https://github.com/flutter/flutter/issues/46028 final double extent = constraints.remainingPaintExtent - math.min(constraints.overlap, 0.0);

if (child != null)
  child.layout(constraints.asBoxConstraints(
    minExtent: extent,
    maxExtent: extent + 10000,
  ));

final double paintedChildSize =
    calculatePaintOffset(constraints, from: 0.0, to: extent);
assert(paintedChildSize.isFinite);
assert(paintedChildSize >= 0.0);
geometry = SliverGeometry(
  scrollExtent: paintedChildSize + 500,
  paintExtent: paintedChildSize,
  maxPaintExtent: paintedChildSize,
  hasVisualOverflow: extent > constraints.remainingPaintExtent ||
      constraints.scrollOffset > 0.0,
);
if (child != null) setChildParentData(child, constraints, geometry);

} }

Piinks commented 4 years ago

SliverFillRemaining is intended by default to contain a scrollable. It is used as part of the 'inner' scrollable to create the NestedScrollView widget. SliverFillRemaining.hasScrollBody is true by default.

The NestedScrollView widget handles a lot of the other eccentricities of nested scroll views, which sounds like the pain points that are being experienced here. Placing a ScrollablePositionedList inside of a CustomScrollView is nested scrolling. Have you considered trying the NestedScrollView widget?

wwwdata commented 3 years ago

I also ran into this problem, and I can confirm a NestedScrollView widget does work! I am using a SliverAppBar in the headerSliverBuilder and ScrollablePositionedList in the body.

Thank you @Piinks

dvlj commented 3 years ago

@wwwdata with only SliverAppBar in headerSliverBuilder it make sense, but not working if there are more than one widget in headerSliverBuilder, you can't have same controller to control both NestedScrollView and ScrollablePositionedList to move consistently...

saddamhussaints392 commented 2 years ago

Hi @tarobins

I'm also facing issue, I need to use CupertinoSliverRefreshControl() so, i'm using CustomScrollView() and inside CustomScrollView() i need use initialScrollIndex so, i'm using ScrollablePositionedList.builder(). If i click any item in that, it is navigating to that index(that item) but getting white screen and there is no exception in the terminal.

 CustomScrollView(
      slivers: [
        CupertinoSliverRefreshControl(
          onRefresh: refreshList,
        ),
        SliverList(delegate: SliverChildBuilderDelegate((context, index) {
          return Scrollbar(
             child: ScrollablePositionedList.builder(
                      /******/
             );
        }))
      ],
    )
DennisKinuthia commented 9 months ago

I also ran into this problem, and I can confirm a NestedScrollView widget does work! I am using a SliverAppBar in the headerSliverBuilder and ScrollablePositionedList in the body.

Thank you @Piinks

I have been trying to come up with a work around for this issue. I was using a NestedScrollView with the ScrollablePositionedList in the body as well. You can pass a scroll controller to the NestedScrollView and then pass the same controller to the Widget that has your ScrollablePositionedList then listen to the scrollOffsetListener and use it to manually expand and collapse the NestedScrollView SliverAppBar. Granted its not the best of solutions but if you are on a time sensitive task it will work for the mean time as we wait for the Flutter Team to come up with a handy solution. Should be any day now 🤕

rbarzel commented 7 months ago

One more vote for adding something like SliverAppBar to ScrollablePositionedList. Thanks! That is all.

giantss commented 6 months ago

It is really important to add SliverAppBar to ScrollablePositionedList. Why has there been no progress?