Code Monkey home page Code Monkey logo

sliver_tools's Introduction

sliver_tools

pub package

A set of useful sliver tools that are missing from the flutter framework.

Here is a taste what you can make using this package

Demo

The structure of this app:

class Section extends State {
  @override
  Widget build(BuildContext context) {
    return MultiSliver(
      pushPinnedChildren: true,
      children: <Widget>[
        SliverPersistentHeader(
          pinned: true,
          ...
        ),
        if (!infinite)
          SliverAnimatedPaintExtent(
            child: SliverList(...),
          )
        else
          SliverList(...),
      ],
    );
  }
}

class NewsPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: <Widget>[
        Section(infinite: false),
        Section(infinite: true),
      ],
    );
  }
}

The MultiSliver widget allows for grouping of multiple slivers together such that they can be returned as a single widget. For instance when one wants to wrap a few slivers with some padding or an inherited widget.

Example

class WidgetThatReturnsASliver extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiSliver(
      pushPinnedChildren: false, // defaults to false
      children: <Widget>[
        SliverPersistentHeader(...),
        SliverList(...),
      ],
    );
  }
}

The pushPinnedChildren parameter allows for achieving a 'sticky header' effect by simply using pinned SliverPersistentHeader widgets (or any custom sliver that paints beyond its layoutExtent).

The SliverStack widget allows for stacking of both slivers and box widgets. This can be useful for adding some decoration to a sliver. Which is what some of the other widgets in this package use to get their desired effects.

Example

class WidgetThatReturnsASliver extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SliverStack(
      insetOnOverlap: false, // defaults to false
      children: <Widget>[
        SliverPositioned.fill(
          child: Container(
            decoration: BoxDecoration(
              color: Colors.white,
              boxShadow: const <BoxShadow>[
                BoxShadow(
                  offset: Offset(0, 4),
                  blurRadius: 8,
                  color: Colors.black26,
                )
              ],
              borderRadius: BorderRadius.circular(8),
            ),
          ),
        ),
        SliverList(...),
      ],
    );
  }
}

The insetOnOverlap handles whether the positioned children should be inset (made smaller) when the sliver has overlap from a previous sliver.

The SliverClip widget will add a clip around its child from the child's paintOrigin to its paintExtent. This is very useful and most likely what you want when using a pinned SliverPersistentHeader as child of the stack.

Example

class WidgetThatReturnsASliver extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SliverClip(
      clipOverlap: true, // defaults to true
      child: SliverList(...),
    );
  }
}

The clipOverlap parameter allows for configuring whether any overlap with the previous child should be clipped. This can be useful when one has a SliverPersitentHeader above a SliverList and does not want to give the header an opaque background but also prevent the list from drawing underneath the header.

The SliverAnimatedPaintExtent widget allows for having a smooth transition when a sliver changes the space it will occupy inside the viewport. For instance when using a SliverList with a button below it that loads the next few items.

Example

class WidgetThatReturnsASliver extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SliverAnimatedPaintExtent(
      duration: const Duration(milliseconds: 150),
      child: SliverList(...),
    );
  }
}

The SliverAnimatedSwitcher widget is simply a pre-configured AnimatedSwitcher widget. If one needs more options than supplied by this widget a regular AnimatedSwitcher can be used by giving it the defaultLayoutBuilder and defaultTransitionBuilder of SliverAnimatedSwitcher.

The SliverCrossAxisConstrained widget allows for limiting the cross axis extent of a sliver to a maximum value given by the maxCrossAxisExtent. For instance a long list of text items on an iPad would be too wide to read so one can wrap the SliverList in a SliverCrossAxisConstrained and limit its width to something more reasonable.

Example

class WidgetThatReturnsASliver extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SliverCrossAxisConstrained(
      maxCrossAxisExtent: 700,
      alignment: 0, // between -1.0 (left) and 1.0 (right)
      child: SliverList(...),
    );
  }
}

The SliverCrossAxisPadded widget allows for adding padding to the cross axis of a sliver. This can be done either by passing a paddingStart and/or paddingEnd or by using the symmetric constructor which takes a single padding value. When using paddingStart and paddingEnd in a vertical sliver it will depend on the TextDirection whether start is left or right.

Example

class WidgetThatReturnsASliver extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SliverCrossAxisPadded(
      paddingStart: 24,
      paddingEnd: 48,
      textDirection: TextDirection.ltr, // optional, defaults to the Directionality specified by the context
      child: SliverList(...),
    );
  }
}

The SliverPinnedHeader widget allows for easily making a pinned header. It will size itself to the size of the child and when it reaches the leading edge of the viewport stay there instead of scrolling off the screen.

Buy me a coffee ☕️

Buy Me A Coffee

sliver_tools's People

Contributors

fryette avatar hannnes1 avatar kavantix avatar knopp avatar manu-sncf avatar remonh87 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

sliver_tools's Issues

Add SliverStack

A widget that stacks its children just like the Stack widget.

Must have:

  • list of children
  • size depends on children

Nice to have:

  • alignment of children
  • using positioned children

[DOC] Please Add More example

Thank you for your great work, please add more gif and code examples, maybe I am too stupid, I don’t know what I can do with it

MultiSliver inside SingleChildScrollView: SliverGeometry is not valid: The "cacheExtent" is negative.

I have a CustomScrollView which needs to be recycled in another part of the app where it is used inside a SingleChildScrollView - in my production app this works as long as I don't have any MultiSliver widgets in the tree (which I use to add sticky headers to categories which are SliverLists).

Code to reproduce the issue:
https://github.com/dkbast/multi_sliver_bug_repro/blob/main/lib/main.dart

It looks like in this minimal example the version without a MultiSliver is also failing - this might be due to all widgets being in the same build function, as it is working for me on the other project.

══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
The following assertion was thrown during performLayout():
SliverGeometry is not valid: The "cacheExtent" is negative.
The RenderSliver that returned the offending geometry was: RenderMultiSliver#6fcfe relayoutBoundary=up27 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE:
  creator: MultiSliver ← ShrinkWrappingViewport ← IgnorePointer-[GlobalKey#5bf37] ← Semantics ←
    Listener ← _GestureSemantics ←
    RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#958af] ← Listener ← _ScrollableScope
    ← _ScrollSemantics-[GlobalKey#543dc] ← NotificationListener<ScrollMetricsNotification> ←
    RepaintBoundary ← ⋯
  parentData: layoutOffset=None (can use size)
  constraints: SliverConstraints(AxisDirection.down, GrowthDirection.forward, ScrollDirection.idle,
    scrollOffset: 0.0, remainingPaintExtent: Infinity, crossAxisExtent: 800.0, crossAxisDirection:
    AxisDirection.right, viewportMainAxisExtent: Infinity, remainingCacheExtent: Infinity,
    cacheOrigin: -0.0)
  geometry: SliverGeometry(scrollExtent: 580.0, paintExtent: 580.0, maxPaintExtent: 580.0,
    cacheExtent: NaN)

The relevant error-causing widget was:
  MultiSliver
  MultiSliver:file:///Users/damianbast/src/github.com/dkbast/multi_sliver_bug_repro/lib/main.dart:80:13

When the exception was thrown, this was the stack:
#0      SliverGeometry.debugAssertIsValid.<anonymous closure>.verify (package:flutter/src/rendering/sliver.dart:709:9)
#1      SliverGeometry.debugAssertIsValid.<anonymous closure> (package:flutter/src/rendering/sliver.dart:724:7)
#2      SliverGeometry.debugAssertIsValid (package:flutter/src/rendering/sliver.dart:748:6)
#3      RenderSliver.debugAssertDoesMeetConstraints (package:flutter/src/rendering/sliver.dart:1200:22)
#4      RenderObject.layout.<anonymous closure> (package:flutter/src/rendering/object.dart:1918:9)
#5      RenderObject.layout (package:flutter/src/rendering/object.dart:1920:8)
#6      RenderViewportBase.layoutChildSequence (package:flutter/src/rendering/viewport.dart:510:13)
#7      RenderShrinkWrappingViewport._attemptLayout (package:flutter/src/rendering/viewport.dart:1938:12)
#8      RenderShrinkWrappingViewport.performLayout (package:flutter/src/rendering/viewport.dart:1884:20)
#9      RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#10     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#11     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#12     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#13     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#14     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#15     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#16     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#17     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#18     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#19     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#20     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#21     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#22     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#23     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#24     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#25     RenderCustomPaint.performLayout (package:flutter/src/rendering/custom_paint.dart:545:11)
#26     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#27     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#28     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#29     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#30     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#31     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#32     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#33     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#34     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#35     _RenderSingleChildViewport.performLayout (package:flutter/src/widgets/single_child_scroll_view.dart:513:14)
#36     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#37     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#38     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#39     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#40     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#41     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#42     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#43     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#44     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#45     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#46     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#47     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#48     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#49     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#50     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#51     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#52     RenderCustomPaint.performLayout (package:flutter/src/rendering/custom_paint.dart:545:11)
#53     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#54     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#55     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#56     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#57     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#58     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#59     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#60     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#61     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#62     MultiChildLayoutDelegate.layoutChild (package:flutter/src/rendering/custom_layout.dart:171:12)
#63     _ScaffoldLayout.performLayout (package:flutter/src/material/scaffold.dart:1003:7)
#64     MultiChildLayoutDelegate._callPerformLayout (package:flutter/src/rendering/custom_layout.dart:240:7)
#65     RenderCustomMultiChildLayoutBox.performLayout (package:flutter/src/rendering/custom_layout.dart:403:14)
#66     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#67     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#68     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#69     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#70     _RenderCustomClip.performLayout (package:flutter/src/rendering/proxy_box.dart:1376:11)
#71     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#72     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#73     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#74     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#75     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#76     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#77     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#78     ChildLayoutHelper.layoutChild (package:flutter/src/rendering/layout_helper.dart:56:11)
#79     RenderStack._computeSize (package:flutter/src/rendering/stack.dart:552:43)
#80     RenderStack.performLayout (package:flutter/src/rendering/stack.dart:579:12)
#81     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#82     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#83     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#84     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#85     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#86     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#87     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#88     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#89     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#90     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#91     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#92     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#93     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#94     RenderOffstage.performLayout (package:flutter/src/rendering/proxy_box.dart:3460:14)
#95     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#96     RenderProxyBoxMixin.performLayout (package:flutter/src/rendering/proxy_box.dart:116:14)
#97     RenderObject.layout (package:flutter/src/rendering/object.dart:1915:7)
#98     _RenderTheatre.performLayout (package:flutter/src/widgets/overlay.dart:749:15)
#99     RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:1757:7)
#100    PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:887:18)
#101    RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:504:19)
#102    WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:892:13)
#103    RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:370:5)
#104    SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1146:15)
#105    SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1083:9)
#106    SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:997:5)
#110    _invoke (dart:ui/hooks.dart:151:10)
#111    PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:308:5)
#112    _drawFrame (dart:ui/hooks.dart:115:31)
(elided 3 frames from dart:async)

The following RenderObject was being processed when the exception was fired: RenderMultiSliver#6fcfe relayoutBoundary=up27 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE:
  creator: MultiSliver ← ShrinkWrappingViewport ← IgnorePointer-[GlobalKey#5bf37] ← Semantics ←
    Listener ← _GestureSemantics ←
    RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#958af] ← Listener ← _ScrollableScope
    ← _ScrollSemantics-[GlobalKey#543dc] ← NotificationListener<ScrollMetricsNotification> ←
    RepaintBoundary ← ⋯
  parentData: layoutOffset=None (can use size)
  constraints: SliverConstraints(AxisDirection.down, GrowthDirection.forward, ScrollDirection.idle,
    scrollOffset: 0.0, remainingPaintExtent: Infinity, crossAxisExtent: 800.0, crossAxisDirection:
    AxisDirection.right, viewportMainAxisExtent: Infinity, remainingCacheExtent: Infinity,
    cacheOrigin: -0.0)
  geometry: SliverGeometry(scrollExtent: 580.0, paintExtent: 580.0, maxPaintExtent: 580.0,
    cacheExtent: NaN)
This RenderObject had the following descendants (showing up to depth 5):
    child 1: RenderSliverToBoxAdapter#68df9 relayoutBoundary=up28 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
      child: RenderConstrainedBox#abb2e relayoutBoundary=up29 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
        child: _RenderColoredBox#b79bb NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
          child: RenderLimitedBox#29420 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
            child: RenderConstrainedBox#e8c02 NEEDS-PAINT
    child 2: RenderSliverList#d2cc2 relayoutBoundary=up28 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
      child with index 0: RenderIndexedSemantics#11826 relayoutBoundary=up29 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
        child: RenderRepaintBoundary#c3a53 relayoutBoundary=up30 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
          child: RenderSemanticsAnnotations#d8f9e relayoutBoundary=up31 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
            child: RenderMouseRegion#af3ca relayoutBoundary=up32 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
      child with index 1: RenderIndexedSemantics#24d37 relayoutBoundary=up29 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
        child: RenderRepaintBoundary#80f51 relayoutBoundary=up30 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
          child: RenderSemanticsAnnotations#dc001 relayoutBoundary=up31 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
            child: RenderMouseRegion#4d6cb relayoutBoundary=up32 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
      child with index 2: RenderIndexedSemantics#24660 relayoutBoundary=up29 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
        child: RenderRepaintBoundary#e5124 relayoutBoundary=up30 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
          child: RenderSemanticsAnnotations#f3a3d relayoutBoundary=up31 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
            child: RenderMouseRegion#e1bac relayoutBoundary=up32 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
      child with index 3: RenderIndexedSemantics#1b9df relayoutBoundary=up29 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
        child: RenderRepaintBoundary#1fca4 relayoutBoundary=up30 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
          child: RenderSemanticsAnnotations#7763f relayoutBoundary=up31 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
            child: RenderMouseRegion#1b069 relayoutBoundary=up32 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
      child with index 4: RenderIndexedSemantics#25e10 relayoutBoundary=up29 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
        child: RenderRepaintBoundary#59889 relayoutBoundary=up30 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
          child: RenderSemanticsAnnotations#31b90 relayoutBoundary=up31 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
            child: RenderMouseRegion#25270 relayoutBoundary=up32 NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
    ...(descendants list truncated after 25 lines)
════════════════════════════════════════════════════════════════════════════════════════════════════

Obsolete example

Thanks for the plugin, but I could update it, it's been a long time without updating. Thank you.

How to use SliverAnimatedPaintExtent and animated the Scroll position at the same time?

For example, in a list that has a button to load more, show progress, and then show an error message with retry button, the loading widget is changed to the one with the error message, I would like to scroll to the end to show the complete error message.

without SliverAnimatedPaintExtent I can do this

await scrollControllerAnimateTo(
  scrollController.position.maxScrollExtent,
  duration: Duration(milliseconds: 300),
  curve: Curves.easeOut,
);

but if I use the SliverAnimatedPaintExtent it only animates a little bit, since the maxScrollExtent is growing while the SliverAnimatedPaintExtent animation is running.

Is there any way to synchronize the AnimateTo of the scrollController with that of the SliverAnimatedPaintExtent.

a temporary solution for me would be to wait for the milliconds of the animation of SliverAnimatedPaintExtent and then do the jumpTo, but that delay is uncomfortable.

await Future <void> .delayed (paintExtentDuration);
await scrollControllerAnimateTo (
   scrollController.position.maxScrollExtent,
   duration: Duration (milliseconds: 300),
   curve: Curves.easeOut,
);

the idea is that they are animated in time, or with the least possible delay.

If you have a suggestion, I would appreciate it very much

Add more tests for MultiSliver

Test covering:

  • Single child
  • Switching from single child and back
  • CupertinoSliverRefreshControl
  • SliverPersistentHeader
  • SliverFillRemaining
  • SliverList
  • SliverGrid
  • Nested MultiSlivers

Add SliverAnimatedSwitcher

A widget that animates (by default fade) between child widgets when they get swapped

Should be able to use the AnimatedSwitcher widget together with SliverStack (#2)

Feature request: MultiSliver.builder

I would like to make a view like the schedule view of a calendar app.

A list view that is infinite in both direction, that can be indexed and that can have sticky header.

I am trying to adapt https://pub.dev/packages/indexed_list_view to support sliver children, I have thinking of returning MultiSliver instead of a SliverFixedExtentList, to be able to put SliverStickyHeader into the indexedlist.

Yeah not so simple !

So I would need a MultiSliver with a builder function.

If you have any feedback, this is very welcome :)

I looked everywhere and couldn't find anything.. maybe I am missing something obvious.

Thanks

Get if header is currently pinned

Thank you for this package. I have used this to achieve great results with the extended Sliver functionality. But I would love to ask one thing: Is it currently possible to get information on wether the SliverPersistendHeader is currently pinned or not. I would love to know that, because I then can adjust the header depending on whether the list below is currently visible or not.

And one little bonus I would love to know is if it is possible to expand the list below the header by tapping on the SliverPersistentHeader. If there is no default implementation for this I would use the scroll Controller for this.

Thanks!

FlutterException: Null check operator used on a null value

Stack:

#0      RenderSliverStack.performLayout (package:sliver_tools/src/rendering/sliver_stack.dart:190)
#1      RenderObject.layout (package:flutter/src/rendering/object.dart:1915)
#2      RenderViewportBase.layoutChildSequence (package:flutter/src/rendering/viewport.dart:510)
#3      RenderViewport._attemptLayout (package:flutter/src/rendering/viewport.dart:1580)
#4      RenderViewport.performLayout (package:flutter/src/rendering/viewport.dart:1489)
#5      RenderObject._layoutWithoutResize (package:flutter/src/rendering/object.dart:1757)
#6      PipelineOwner.flushLayout (package:flutter/src/rendering/object.dart:887)
#7      RendererBinding.drawFrame (package:flutter/src/rendering/binding.dart:504)
#8      WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:892)
#9      RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:370)
#10     SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1146)
#11     SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1083)
#12     SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:997)
#13     _rootRun (dart:async/zone.dart:1426)
#14     _CustomZone.run (dart:async/zone.dart:1328)
#15     _CustomZone.runGuarded (dart:async/zone.dart:1236)
#16     _invoke (dart:ui/hooks.dart:151)
#17     PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:308)
#18     _drawFrame (dart:ui/hooks.dart:115)

SliverAppBar with expanded height dependent on the children

Hi, thank you for the package!

Is it possible to achieve a pinned silverappbar with the height depending on its children with this package?

What I am trying to achieve is the first example on here: https://api.flutter.dev/flutter/widgets/NestedScrollView-class.html but such that the appbar is some widget whose height is unknown(and a tabbar on the bottom) until layout/render. I know the height of the tabs, that is the collapsed height.

Thank you!

Package Performance Hit?

Before I ask my question... the explanation of what I'm trying to achieve...

  • IM BUILDING a custom contact picker

I have headers for each section
A, B, etc...

I have the ability to scroll to a particular section because I know the height of
. the headers
. and how many items are in each section (all the contacts are of the same size)

At the moment I am using a CustomScrollView with a SliverFixedExtentList with a SliverChildBuilderDelegate

  • THE QUESTION
    Will using this plugin to make my headers sticky affect performance by essentially shrinking wrapping each of my SliverFixedExtentList? will performance be affected any other way? If so, by how much?

SliverGeometry has a paintOffset that exceeds the remainingPaintExtent from the constraints.

After upgrade to version 0.2.0, this error Came.(version 0.1.10 Works Great)

This is small repo to reproduce the error.
https://github.com/marcos930807/sliver_demo.
After open de app just Expand "Batidos/Milkshakes" and see console logs.

The Complete Error:
SliverGeometry has a paintOffset that exceeds the remainingPaintExtent from the constraints.
The render object whose geometry violates the constraints is the following: RenderMultiSliver#2d301 relayoutBoundary=up2 NEEDS-LAYOUT NEEDS-PAINT NEEDS-COMPOSITING-BITS-UPDATE
The remainingPaintExtent is 555.4666669999999, but the paintOrigin + paintExtent is 555.466667.

Maybe you have fallen prey to floating point rounding errors, and should explicitly apply the min() or max() functions, or the clamp() method, to the paintOrigin + paintExtent?

The paintOrigin and paintExtent must cause the child sliver to paint within the viewport, and so cannot exceed the remainingPaintExtent.

MultiSliver has unexpected behavior when use centerKey in CustomScrollView

Hi, I try to create a bidirectional lazy load list like the like flutter docs said
https://github.com/flutter/flutter/blob/5464c5bac742001448fe4fc0597be939379f88ea/packages/flutter/lib/src/widgets/scroll_view.dart#L502-L513

I create a gist where you can enable a disable the MultiSliver, and you can see that the scroll behavior is different, is looks like a stack, and also list is iterate in different order.

On the other hand, maybe do you know other way to create a list that append items to the top of the list by preserving the scroll, the solution that I used is from flutter/flutter#21541 (comment)

I used MultiSliver because my list is grouped and has pinned headers for each group, like this.

thank you

SliverCrossAxisConstrained but align left?

This is what I try to do:
ezgif-2-121711b582

class WidgetThatReturnsASliver extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SliverStack(
      insetOnOverlap: false, // defaults to false
      children: <Widget>[
        SliverPositioned(
           top: 0,
            right: 0,
            width: 300,
          child: Container(
            width: 300,
            height: 600,
            decoration: BoxDecoration(
              color: Colors.red,
              ],
              borderRadius: BorderRadius.circular(8),
            ),
          ),
        )

        SliverList(...), // I want this SliverList stay left, with a width of 700
      ],
    );
  }
}

I try wrap SliverList With A SliverPositioned(top: 0, left: 0, width 700), my sliverList take up the whole screen
I try SliverCrossAxisConstrained(max: 700) then my sliverList stay in the middle.
How can I achieve the UI?

SliverGeometry is not valid: The "maxPaintExtent" is less than the "paintExtent". with MultiSliver usage

Usage MultiSliver with SliverAppBar leads to exception SliverGeometry is not valid: The "maxPaintExtent" is less than the "paintExtent".

Scaffold(
    body: CustomScrollView(
      slivers: [
        const SliverAppBar(
          pinned: true,
          title: Text('Sample'),
        ),
        MultiSliver(
          children: [
            SliverToBoxAdapter(
              child: Container(
                height: 30, ///<--- changing this value to height bigger than app bar fixes issue
              ),
            ),
          ]
        ),
        MultiSliver(
            children: [
              SliverList(
                delegate: SliverChildBuilderDelegate(
                  (context, index) {
                    return Container(
                      height: 50,
                      width: double.infinity,
                      color: index.isEven ? Colors.red : Colors.blue,
                    );
                  },
                  childCount: 20,
                ),
              ),
              const SliverToBoxAdapter(
                child: Divider(),
              ),
            ]
        ),
      ],
    ),
  );

Flutter version: 2.0.5
Device: Android 11 (API 30) (emulator)

Reason for using child/children instead of sliver/slivers

First, thanks for this amazing package.

Slivers are a very powerful set of Widgets and this package makes it even more incredible.

I would like to suggest to rename the params children and child in the sliver widgets of this library to slivers and sliver.

Inside the Flutter framework, the widgets that only accept slivers use that name convention to indicate a normal widget should not be used. eg SliverSafeArea, SliverPadding, CustomScrollView

Calling it child and children is confusing and prompt to use normal Widgets and get runtime errors.

Happy to help with a PR.

Wrong calculations for multiple pinned SliverPersistentHeader inside a NestedScrollView

I had a section in my app which contains a appbar a top section and tab bar which all of them are implemented with NestedScrollView and MultiSliver. It was working fine but it came up problematic after I removed my pubspec.lock file.

Recent problem:
image

Previously correct behavior
image

Therefore I realized this feature of the lib is corrupted from version 0.2.5 to 0.2.8

I'm working on Flutter 2.10.5

I used the lib with 0.2.5 exact version as dependency_overrides and its working correctly now. But I wanted to inform you about this new bug

I can describe the wrong behavior in this way. Whenever you add more than one SliverPersistentHeader or SliverAppBar with pinned: true property, the first one works fine, but the rest of the slivers can't understand the position and they don't receive shrinkOffset changes. Also the body of the NestedScrollView goes behind these widgets(The ones after the first pinned widget).

MultiCrossAxisSliver

Hey, thanks for this great package!

I have a vertically scrolling MultiSliver. In wide layouts, I want two of the slivers in the MultiSliver to be arranged horizontally. I initially added them as a standard Row(), but that won't work since each child is a sliver. Is there already a way to do that with this package? If not, would there be any interest in adding something like "SliverRow"/"SliverFlex"? Or maybe something like "CrossAxisMultiSliver"?

SliverIndexedStack

Hi Pieter,

Thank you for an amazing plugin. Is it possible to add SliverIndexedStack that works like an IndexedStack but accepts Slivers.

Best,
Shashi

MultiSliver with several pinned headers in NestScrollView headerSliverBuilder need overscrolling to overlap

When I put MultiSliver in NestedScrollView.headerSliverBuilder with several pinned headers inside, I need to "overscroll" to begin overlapping the body. The problem doesn't occur with one pinned header.

In the demo, look at the cursor and the shadow beneath the TabBar.

demo

The code :

import 'package:flutter/material.dart';
import 'package:sliver_tools/sliver_tools.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String _title = 'Flutter Code Sample';

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: _title,
      home: MyStatelessWidget(),
    );
  }
}

class MyStatelessWidget extends StatelessWidget {
  const MyStatelessWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final List<String> tabs = <String>['Tab 1', 'Tab 2'];
    return DefaultTabController(
      length: tabs.length, // This is the number of tabs.
      child: Scaffold(
        body: NestedScrollView(
          headerSliverBuilder: (BuildContext context, bool innerBoxIsScrolled) {
            return <Widget>[
              SliverOverlapAbsorber(
                handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                sliver: MediaQuery.removePadding(
                  context: context,
                  removeBottom: true,
                  child: MultiSliver(
                    children: [
                      const SliverPinnedHeader(
                          child: SafeArea(child: Padding(padding: EdgeInsets.all(16), child: Text('Test')))),
                      SliverAppBar(
                        title: const Text('Books'), // This is the title in the app bar.
                        pinned: true,
                        expandedHeight: 150.0,
                        forceElevated: innerBoxIsScrolled,
                        bottom: TabBar(
                          tabs: tabs.map((String name) => Tab(text: name)).toList(),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ];
          },
          body: TabBarView(
            // These are the contents of the tab views, below the tabs.
            children: tabs.map((String name) {
              return SafeArea(
                top: false,
                bottom: false,
                child: Builder(
                  builder: (BuildContext context) {
                    return CustomScrollView(
                      key: PageStorageKey<String>(name),
                      slivers: <Widget>[
                        SliverOverlapInjector(
                          // This is the flip side of the SliverOverlapAbsorber
                          // above.
                          handle: NestedScrollView.sliverOverlapAbsorberHandleFor(context),
                        ),
                        SliverPadding(
                          padding: const EdgeInsets.all(8.0),
                          sliver: SliverFixedExtentList(
                            itemExtent: 48.0,
                            delegate: SliverChildBuilderDelegate(
                              (BuildContext context, int index) {
                                return ListTile(
                                  title: Text('Item $index'),
                                );
                              },
                              childCount: 30,
                            ),
                          ),
                        ),
                      ],
                    );
                  },
                ),
              );
            }).toList(),
          ),
        ),
      ),
    );
  }
}

The problem is also on iOS and Web.

Flutter doctor :

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.0.4, on macOS 12.5 21G72 darwin-arm, locale fr-FR)
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 13.2.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2021.2)
[✓] IntelliJ IDEA Ultimate Edition (version 2022.1.3)
[✓] VS Code (version 1.70.0)
[✓] Connected device (3 available)
[✓] HTTP Host Availability

Thanks for your help.

Add a widget similar to SliverFillRemaining but allows for a minimum scroll amount

Changes from 0.1.9 to 0.1.10 break passing constraints to children:

class ExampleWidget extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return MultiSliver(
      children: [
        SliverList(
                delegate: SliverChildBuilderDelegate((context, index) {
                  return ExampleItemWidget();
                }, childCount: exampleData.length),
              ),

        SliverFillRemaining(
            hasScrollBody: false,
            child: Container())
      ],
    );
  }
}

In 0.1.9 the SliverFillRemaining correctly filled the remaining space in the list if there are not enough items in the list to allow for the Multisliver to expand and fill the viewport in a CustomScrollView that already has a preceding flexible SliverAppBar. Or more succinctly 0.1.10 changes caused the child of SliverFillRemaining in MultiSliver to always have a height of 0.

SliverGeometry is not valid: The "maxPaintExtent" is less than the "paintExtent".

This error occurs when using the MultiSliver.
The following video shows the error:

errorvid.mp4

Code to reproduce this error:

class _MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    List<Widget> widgetList = List<Widget>.generate(
        5,
        (index) => MultiSliver(
              children: [
                SliverPersistentHeader(
                  pinned: true,
                  delegate: HeaderDelegate(),
                ),
                Container(height: 200, color: Colors.green),
                SliverPersistentHeader(
                  pinned: true,
                  delegate: HeaderDelegate(),
                ),
              ],
            ));
    return Scaffold(
        body: SafeArea(
      child: CustomScrollView(
        slivers: widgetList,
      ),
    ));
  }
}

class HeaderDelegate extends SliverPersistentHeaderDelegate {
  @override
  Widget build(
      BuildContext context, double shrinkOffset, bool overlapsContent) {
    return Container(height: 60, color: Colors.white);
  }

  @override
  double get maxExtent => 60;

  @override
  double get minExtent => 60;

  @override
  bool shouldRebuild(SliverPersistentHeaderDelegate oldDelegate) {
    return true;
  }
}

Flutter 2.2.1
Sliver Tools 0.2.4
Tested on Samsung Device (Android 10)

SliverPinnedHeader Enhancement

Hi @Kavantix

Thx for your awesome package! It makes Sliver more flexible and powerful!

I am wondering if it is possible to introduce more prop to control SliverPinnedHeader.

Such as a position to set where to pin (e.g. pin at the 300px from top) and a controller to control when to pin?

I think this may also solve #20 request

pushPinnedChildren with nested MultiSliver

I'm trying to create nested slivers with sticky headers. What I have is close, but I want only 1 'Main Section' to be stuck to the top, so that when Main Section#1 hits #0, it pushes #0 off screen.
nested_multisliver

Any idea how to achieve this?

I have pushPinnedChildren: true for both MultiSlivers, but the outer one seems to have no effect.

class MultiSliverView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return CustomScrollView(
      slivers: [
        MultiSliver(
          pushPinnedChildren: true, //seems this has no effect
          children: [
            for (int main in List.generate(10, (i) => i)) ...[
              SliverPinnedHeader(
                child: Container(
                  height: 60.0,
                  color: Colors.red,
                  padding: EdgeInsets.symmetric(horizontal: 16.0),
                  alignment: Alignment.centerLeft,
                  child: Text('Main Section #$main',
                      style:
                          const TextStyle(color: Colors.white, fontSize: 20)),
                ),
              ),
              MultiSliver(
                pushPinnedChildren: true, // this works fine
                children: [
                  for (var sub in List.generate(4, (i) => i)) ...[
                    SliverPinnedHeader(
                      child: Container(
                        height: 60.0,
                        color: Colors.lightBlue,
                        padding: EdgeInsets.symmetric(horizontal: 16.0),
                        alignment: Alignment.centerLeft,
                        child: Text(
                          'Sub Section #$sub',
                          style: const TextStyle(color: Colors.white),
                        ),
                      ),
                    ),
                    SliverList(
                      delegate: SliverChildBuilderDelegate(
                        (context, ii) => ListTile(
                          leading: Text('Main-$main Sub-$sub'),
                          title: Text('List tile #$ii'),
                        ),
                        childCount: 3,
                      ),
                    ),
                  ],
                ],
              )
            ],
          ],
        )
      ],
    );
  }
}```


Thanks!

SliverShaderMask

I would love to be able to add a shader mask to a Sliver, just like what ShaderMask does for regular widgets.

I'm not a sliver expert at all, but I tried with this implementation and it seems to work.

import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';

class SliverShaderMask extends SingleChildRenderObjectWidget {
  const SliverShaderMask({
    Key? key,
    required this.shaderCallback,
    required Widget child,
    this.blendMode = BlendMode.modulate,
  }) : super(key: key, child: child);

  /// Called to create the [dart:ui.Shader] that generates the mask.
  ///
  /// The shader callback is called with the current size of the child so that
  /// it can customize the shader to the size and location of the child.
  ///
  /// Typically this will use a [LinearGradient], [RadialGradient], or
  /// [SweepGradient] to create the [dart:ui.Shader], though the
  /// [dart:ui.ImageShader] class could also be used.
  final ShaderCallback shaderCallback;

  /// The [BlendMode] to use when applying the shader to the child.
  ///
  /// The default, [BlendMode.modulate], is useful for applying an alpha blend
  /// to the child. Other blend modes can be used to create other effects.
  final BlendMode blendMode;

  @override
  RenderSliverShaderMask createRenderObject(BuildContext context) {
    return RenderSliverShaderMask(
      shaderCallback: shaderCallback,
      blendMode: blendMode,
    );
  }

  @override
  void updateRenderObject(
      BuildContext context, covariant RenderSliverShaderMask renderObject) {
    renderObject
      ..shaderCallback = shaderCallback
      ..blendMode = blendMode;
  }
}

class RenderSliverShaderMask extends RenderProxySliver {
  /// Creates a render object that applies a mask generated by a [Shader] to its child.
  ///
  /// The [shaderCallback] and [blendMode] arguments must not be null.
  RenderSliverShaderMask({
    required ShaderCallback shaderCallback,
    BlendMode blendMode = BlendMode.modulate,
  })  : _shaderCallback = shaderCallback,
        _blendMode = blendMode;

  @override
  ShaderMaskLayer? get layer => super.layer as ShaderMaskLayer?;

  /// Called to creates the [Shader] that generates the mask.
  ///
  /// The shader callback is called with the current size of the child so that
  /// it can customize the shader to the size and location of the child.
  ///
  /// The rectangle will always be at the origin when called by
  /// [RenderShaderMask].
  ShaderCallback get shaderCallback => _shaderCallback;
  ShaderCallback _shaderCallback;
  set shaderCallback(ShaderCallback value) {
    if (_shaderCallback == value) return;
    _shaderCallback = value;
    markNeedsPaint();
  }

  /// The [BlendMode] to use when applying the shader to the child.
  ///
  /// The default, [BlendMode.modulate], is useful for applying an alpha blend
  /// to the child. Other blend modes can be used to create other effects.
  BlendMode get blendMode => _blendMode;
  BlendMode _blendMode;
  set blendMode(BlendMode value) {
    if (_blendMode == value) return;
    _blendMode = value;
    markNeedsPaint();
  }

  @override
  bool get alwaysNeedsCompositing => child != null;

  Rect get maskRect {
    final axisDirection = applyGrowthDirectionToAxisDirection(
        constraints.axisDirection, constraints.growthDirection);
    Rect rect;
    switch (axisDirection) {
      case AxisDirection.up:
        rect = Rect.fromLTWH(
          0,
          0,
          constraints.crossAxisExtent,
          geometry!.paintExtent,
        );
        break;
      case AxisDirection.right:
        rect = Rect.fromLTWH(
          geometry!.paintOrigin,
          0,
          geometry!.paintExtent,
          constraints.crossAxisExtent,
        );
        break;
      case AxisDirection.down:
        rect = Rect.fromLTWH(
          0,
          geometry!.paintOrigin,
          constraints.crossAxisExtent,
          geometry!.paintExtent,
        );
        break;
      case AxisDirection.left:
        rect = Rect.fromLTWH(
          0,
          0,
          geometry!.paintExtent,
          constraints.crossAxisExtent,
        );
        break;
    }
    return rect;
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    if (child != null) {
      layer ??= ShaderMaskLayer();
      final maskRect = this.maskRect;

      layer!
        ..shader = _shaderCallback(Offset.zero & maskRect.size)
        ..maskRect = (offset + maskRect.topLeft) & maskRect.size
        ..blendMode = _blendMode;
      context.pushLayer(layer!, super.paint, offset);
    } else {
      layer = null;
    }
  }
}

Multisliver: Local hitTest position on sliver child is calculated wrong.

The HitTestResult matrix should be offset by inverse of paint offset, but it's not.

Test that demonstrates this:

  testWidgets('sliver child hit test position is correct', (tester) async {
      const boxKey = Key('box');
      final controller = ScrollController();
      TapDownDetails? tapDownDetails;
      await tester.pumpWidget(Directionality(
        textDirection: TextDirection.ltr,
        child: CustomScrollView(
          scrollBehavior: NoScrollbarScrollBehaviour(),
          controller: controller,
          physics: const UnconstrainedScollPhysics(),
          slivers: [
            MultiSliver(
              children: [
                const SliverToBoxAdapter(child: SizedBox(height: 400)),
                SliverToBoxAdapter(
                  child: GestureDetector(
                    onTapDown: (details) {
                      tapDownDetails = details;
                    },
                    child: box(boxKey, 'Title', height: 1),
                  ),
                ),
              ],
            ),
          ],
        ),
      ));
      expect(tapDownDetails, isNull);
      final thebox = find.byKey(boxKey);
      await tester.tap(thebox);
      expect(tapDownDetails?.globalPosition, const Offset(400, 400.5));
      expect(tapDownDetails?.localPosition, const Offset(400, 0.5));
    });

Global position is reported correctly, but local position is not.

Add SliverClip.clipBehavior

Really good work on providing sliver primitives that should be part of Flutter, but unfortunately aren't!

One simple non-breaking change we would really appreciate: if we would be able to specify the clip behavior of SliverClip (and not assume it would default to Clip.hardEdge).

We noticed that SliverClip, as it is currently implemented, clips shadows from pinned slivers by which it is overlapped -- which isn't pretty. If we would be able to determine the clip behavior and even be able to set it to Clip.none (assuming we would take care of setting the background of the pinned slivers), the issue would be solved.

Is there support for TabBarView?

I'm wondering if it's possible to have a view with tabs under which there are scrollable list items and would it work with MultiSliver?
In other words a TabBar inside SliverPinnedHeader with below TabBarView so that you can swipe between tabs with gesture and as you scroll up it would push up this SliverPinnedHeader?

Something similar in behavior to:
https://stackoverflow.com/a/64651709

https://i.stack.imgur.com/TffF6.gif

That's a question to lib capabilities.

Best regards

SliverFloatingHeader

A sliver that acts like a SliverToBoxAdapter when the user scrolls towards the leading edge but acts like a pinned header when the user scrolls back

Requirements:

  • Size of header is determined by its child
  • The header scrolls back interactively

Questions:

  • What should the behavior be when multiple of these headers are added to a viewport
  • What if this is combined with a regular pinned header before/after it

Is it possible to render a SliverList inside a SliverList?

Hello, I'm a bit new to this topic of silvers, I'm looking for the following:
I have a CustomScrollView, where its children are a SliverAppBar and a SliverList, I want another SliverList to exist inside the SliverList.
I have tried to do it with this library and without it and neither of the two ways works for me.

image

In summary I need to create several big lists within another list, I don't want to use shrinkwrap because my application becomes really slow.

Floating point rounding warning

Hi,

I'm seinng an issue where the remaingPaintExtent and the paintOrigin + paintExtent are different based on some rounding errors with the MultiSliver component. The issue appeared when adding a CupertinoSliverRefreshControl to a MultiSliver and it happens during the swipe up experience. I don't have an easy example at hand but if necessary could try to create one.

The issue happens somewhere around here: https://github.com/Kavantix/sliver_tools/blob/master/lib/src/multi_sliver.dart#L298

I/flutter (16253): The remainingPaintExtent is 200.2530827300063, but the paintOrigin + paintExtent is
I/flutter (16253): 200.25308273000633.
I/flutter (16253): Maybe you have fallen prey to floating point rounding errors, and should explicitly apply the min()
I/flutter (16253): or max() functions, or the clamp() method, to the paintOrigin + paintExtent?
I/flutter (16253): The paintOrigin and paintExtent must cause the child sliver to paint within the viewport, and so
I/flutter (16253): cannot exceed the remainingPaintExtent.

I tried a local fix with the change documented below but my experience working with Slivers is not very big right now so I am not sure if this is a good fix or not?

    geometry = SliverGeometry(
      paintOrigin: minPaintOrigin,
      scrollExtent: precedingScrollExtent,
      paintExtent: min(totalPaintExtent - minPaintOrigin, constraints.remainingPaintExtent), // This is the change
      maxPaintExtent: maxPaintExtent - minPaintOrigin,
      layoutExtent: layoutExtent,
      cacheExtent: constraints.remainingCacheExtent - remainingCacheExtent,
      hasVisualOverflow: hasVisualOverflow,
      maxScrollObstructionExtent: maxScrollObstructionExtent,
      visible: visible && paintExtent > 0,
    );

So I am wondering if this is a bug in the library or not? And if yex, Is my fix correct or is there a better way to fix it?

Thanks for the great library and your input.

SliverEstimatedExtent

Add a sliver that only lays its child out if there is cache extent to be filled and otherwise uses a given estimate for its geometry

Questions:

  • How to give the estimate, might be useful to use a prototype child (that is cheap to layout)
  • Should we use the estimate when it is completely scrolled past the leading edge
  • Should the child build even if it will not be layed out

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.