kavantix / sliver_tools Goto Github PK
View Code? Open in Web Editor NEWA set of useful sliver tools that are missing from the flutter framework
License: MIT License
A set of useful sliver tools that are missing from the flutter framework
License: MIT License
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
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.
Tried out sliver stack but it doesnt paint anything in the screen
A widget that stacks its children just like the Stack widget.
Must have:
Nice to have:
Is it possible to implement SliverPinnedFooter?
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.
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).
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?
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:
Is there a way to detect the overlapping content using a SliverPinnedHeader
like you can do with a SliverPersistentHeaderDelegate
? I'm ultimately looking to change the background of the SliverPinnedHeader when content overlaps.
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
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.
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.
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)
════════════════════════════════════════════════════════════════════════════════════════════════════
in the usage example. The number of Sections is fixed.
if the number is unlimited, how to create a widget
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
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!
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
When SliverPinnedHeader
is pinned
, is there a way to prevent any scroll gestures on it? As of now, scroll gestures cause the CustomScrollView
to scroll.
I want to set the app bar elevation to 0 when the header sticks to the top. So is there any way to listen when the header sticks to the top and when it doesn't?
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.
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.
Thanks for the plugin, but I could update it, it's been a long time without updating. Thank you.
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:
Questions:
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)
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;
}
}
}
With Null Safety increasing in popularity, it would be great to have this package migrated.
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.
Hello, i couldnt run SliverAnimatedSwitcher, can you provide an example usage on Readme so we can understand.
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.
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.
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)
Also, I propose to rename SliverClip to SliverClipRect to align with Flutter naming for Box-based widgets.
Are there any article or tutorials?..
Instead of Listview inside Listview. we have used CustomScrollview -> SliverList , SliverList for better performance.
Does this library perform same as slivers or it affect the performance of the app
?
Hi Pieter,
Thank you for an amazing plugin. Is it possible to add SliverIndexedStack that works like an IndexedStack but accepts Slivers.
Best,
Shashi
Touch inputs are working initially, but when the header is pinned to the leading-edge, touch inputs are ignored.
Future work: Add ability to use a padding value
Originally posted by @Kavantix in #7 (comment)
Should have these constructor properties:
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.
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
Before I ask my question... the explanation of what I'm trying to achieve...
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
like this one https://pub.dev/packages/visibility_detector (or this https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)
but for sliver (with better performance and without delay if possible)
use case: highlight some links/buttons/sections if SliverList is in viewport while scrolling
I create the following unit test with a working scenario and a failing one: https://gist.github.com/remonh87/bd6d8583e70955b23f2fdccfb390a9b3
When I try to app on a child of the SliverPositioned it does not react on the ontap. I did some debug printing and noticed that the hittest is not by the positioned in my example and will be passed through to the slivertoboxadapter.
Expected behaviour: onTap to be executed
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!
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.
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
Test covering:
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"?
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)
This error occurs when using the MultiSliver.
The following video shows the error:
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)
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.
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!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.