-
Notifications
You must be signed in to change notification settings - Fork 28.7k
WIP - Allowing scrolling content to inform insets #107182
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xu-baolin would you mind taking a peek at the scrollbar part of this when you have a chance?
I made all of those scrollbar bug fixes and cleanup PRs while working on this, and I might be missing another bug. Or maybe this idea won't work. :(
This allows the scrollbar to respect things like SliverAppBar, no longer overlapping it. It mostly works, but sometimes it will get out of sync and overlap. I can't quite figure it out, I may have been looking at it too long. 🙄
See what you think of the various SliverAppBar settings:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: RawScrollbar(
thumbVisibility: true,
thickness: 20,
thumbColor: Colors.red,
child: CustomScrollView(
primary: true,
slivers: [
const SliverAppBar(
floating: true,
// pinned: true,
// snap: true,
// stretch: true,
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => Text('Item $index'),
childCount: 200,
)
),
],
),
)
),
);
}
}
@@ -161,6 +161,7 @@ class RefreshIndicator extends StatefulWidget { | |||
/// value is calculated from that offset instead of the parent's edge. | |||
final double displacement; | |||
|
|||
/// TODO(Piinks): Update docs |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also todo, add ScrollMetricsNotification here for initialization.
@@ -236,7 +236,7 @@ class _GlowingOverscrollIndicatorState extends State<GlowingOverscrollIndicator> | |||
confirmationNotification.dispatch(context); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also todo, add ScrollMetricsNotification here for initialization.
Also, here is the design doc for more context: https://docs.google.com/document/d/1-yEwhPP3-qZ1-a-o0TorF0GP50En4Uwhp2g4OKj5Xzs/edit?usp=sharing |
@Piinks Hi, I just read the design document and downloaded your code today. |
Thank you very much! I really appreciate your expertise on this. :) |
@@ -595,12 +601,18 @@ abstract class RenderSliverFloatingPersistentHeader extends RenderSliverPersiste | |||
final double maxExtent = this.maxExtent; | |||
final double paintExtent = maxExtent - _effectiveScrollOffset!; | |||
final double layoutExtent = maxExtent - constraints.scrollOffset; | |||
final double clampedPaintExtent = paintExtent.clamp(0.0, constraints.remainingPaintExtent); | |||
final EdgeInsets contentInsets = _getInsetsForAxisDirection( | |||
clampedPaintExtent + stretchOffset, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1, On mobile platform, the AppBar consume the top padding(system status bar) and paint it. This cause the Scrollbar mismatch at the initial state.
2, I also saw that the contentInsets was not updated synchronized when user scrolling. This cause the scrollbar shake and jump. This needs further investigation.
In my opinion, this is a meaningful new feature and would love to work on this feature with you if you don't mind.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1, On mobile platform, the AppBar consume the top padding(system status bar) and paint it. This cause the Scrollbar mismatch at the initial state.
Oh really? I thought since the app bar paints into that status bar space, the paintExtent would properly represent it in the reported insets.
2, I also saw that the contentInsets was not updated synchronized when user scrolling. This cause the scrollbar shake and jump. This needs further investigation.
Yeah exactly, I have been chasing this for a while trying to figure it out. I thought maybe we need to update the scrollbar painter more often, or maybe not listen to scroll metrics notifications while scroll notification are happening... I have not quote cracked it yet.
In my opinion, this is a meaningful new feature and would love to work on this feature with you if you don't mind.
I would love that too! I always enjoy collaborating with you. Thank you!
@@ -595,12 +601,18 @@ abstract class RenderSliverFloatingPersistentHeader extends RenderSliverPersiste | |||
final double maxExtent = this.maxExtent; | |||
final double paintExtent = maxExtent - _effectiveScrollOffset!; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm currently investigating the cause of the demo's bar shaking.
1,Here, we calculate the insets depended on the scroll offset, but updateGeometry
only be called when layout occurs.
If the user scrolling without perform layout(you know this always happens), the content inset will not updated. Once the layout occurs after more scrolling, the content insets will updated and cause the bar shaking.
It seems that we also should update the geometry when scroll position changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Demo (Tested on desktop to avoid status bar issues.)
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: RawScrollbar(
thumbVisibility: true,
thickness: 20,
thumbColor: Colors.red,
child: ScrollConfiguration(
behavior: ScrollConfiguration.of(context).copyWith(scrollbars: false),
child: CustomScrollView(
primary: true,
slivers: [
const SliverAppBar(
floating: true,
toolbarHeight: 100,
// pinned: true,
// snap: true,
// stretch: true,
),
SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) => Text('Item $index'),
childCount: 200,
)
),
],
),
),
)
),
);
}
}
Looking at these changes I cannot help but wonder if it even makes sense to have a SliverAppBar. Also while attempting to implement a SliverPinnedFooter (see Kavantix/sliver_tools#20) I sort of came to this realization. So what I’m trying to say is did we ever consider re-evaluating if SliverAppBar needs to be a sliver at all. Wouldn’t all of this be a lot easier if it was a box widget that you would wrap around the scrollview, it then could still use the scrollcontroller/position to determine things like expand and stretch and position and size the scrollview accordingly. This would automatically mean that the scrollbar and such will be rendered only on the actual scrollable part without having to add anything to the sliver geometry and having to think about all possible cases this could be used in. |
Hey @Kavantix thank you for sharing your insights. I have wondered the same recently, as far as taking SliverAppBar's functionality out of the sliver and into a box. A couple of months ago I learned about the ScrollNotificationObserver that was added to AppBar in order to perform the scrolledUnder behavior from material design. I think it might be possible then to have the AppBar stretch, and float etc. by listening to scrolling events, I have not tried it yet though. I have heard from a number of folks that SliverAppBar is really why they end up using slivers, and sometimes undesirably just so they can get those effects. Definitely worth investigating. I have not been able to resolve some edge case issues with this proposal, so I may consider experimenting with AppBar next as a solution. |
Interesting using scroll notifications means that the scroll under effect is always a frame behind. I guess that’s acceptable for it, it would not be for the AppBar though. I’ll see if I can come up with a proof of concept for this |
Managed to get a (hacky) PoC working in this gist It has a collapsible AppBar that can be pinned and/or floating. |
Wow! Thank you @Kavantix! That is really neat. |
Finally circling back here. I am planning on moving forward with this without the scrollbar. I think this makes sense for edge scrolling widgets like the overscroll indicators and refresh indicators. They always come from the end of the scroll view on one side or the other. With a scrollbar, any edge pinning sliver like SliverPersistentHeader can be introduced anywhere in a custom scroll view, and trying to dynamically adapt to the content isn't very reliable or consistent - or pretty in some cases. I'll reopen this as a new PR, and include some guidance on scrollbars. |
What ended up being the guidance on scrollbars? |
This is a WIP, and primarily for full-picture reference.
Actually landing this will be split up into multiple PRs.
Issues:
Fixes #107322
Design Doc
This allows scrolling content to contribute inset information to inform things like overscroll indicators and scrollbars.
Widgets that contribute inset information:
Widgets that use this inset information:
Status: Still need to confirm a few test cases (on separate branches)
There is a scrollbar cleanup that needs to land first, and a couple of other bugs I found in the course of working on this.
Pre-launch Checklist
///
).If you need help, consider asking for advice on the #hackers-new channel on Discord.