Skip to content
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

Nested SliverStickyHeader #5

Closed
bunopus opened this issue Jun 29, 2018 · 14 comments
Closed

Nested SliverStickyHeader #5

bunopus opened this issue Jun 29, 2018 · 14 comments
Assignees
Labels
enhancement New feature or request

Comments

@bunopus
Copy link

bunopus commented Jun 29, 2018

Thank you for your library, it's super useful. I have one question: Is it possible to achieve something like this:

As you can see header with dates sticks to the screen as well as the side header with time (it is a https://github.com/google/iosched app). Example app have great section with side headers, but i tried to nest SliverStickyHeader and it doesn't work. Second Header doesn't stick to screen.

Code

// code from example app
class MainScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    List<Widget> slivers = new List<Widget>();

    // App Bar
    slivers.add(getAppBar());

    slivers.add( new SliverStickyHeader(
      // Tabs header
      header:_buildHeader(1, text: 'Tabs'),
      // Side header
      sliver: SliverStickyHeader(
       overlapsContent: true,
       header: _buildSideHeader(1),
       sliver: new SliverPadding(
       // other code is same, create grid

  }

  Widget getAppBar() {
    return SliverAppBar(
      expandedHeight: 60.0,
      floating: true,
      brightness: Brightness.light,
      title: Text('Main AppBar',
          style: TextStyle(color: Colors.black.withOpacity(1.0))),
      elevation: 0.0,
      centerTitle: true,
      backgroundColor: Color.fromRGBO(255, 255, 255, 1.0),
    );
  }
@letsar letsar added the enhancement New feature or request label Jun 29, 2018
@letsar letsar self-assigned this Jun 29, 2018
@letsar
Copy link
Owner

letsar commented Jun 29, 2018

Hi @bunopus, thanks for the feedback 😃.
All the slivers are in a CustomScrollView I presume?
I am working on a way to have Nested SliverStickyHeader for another feature, but for now I am stucked 😞 .
I will have another look to this thanks to your example.
I will keep you posted.

@letsar letsar closed this as completed in #6 Jun 30, 2018
@letsar letsar reopened this Jun 30, 2018
@letsar
Copy link
Owner

letsar commented Jun 30, 2018

I reproduced the issue, and saw that the SliverStickyHeader was painted under the AppBar. I think I will have to play with the SliverOverlapAbsorber to fix this.

sticky_header_pinned_under_app_bar

@letsar
Copy link
Owner

letsar commented Jun 30, 2018

I found how to do it 😁!

sticky_header_pinned_app_bar_ok

It's a little bit complex since you'll have to play with SliverOverlapAbsorber. If you find a simpler solution will you share it here please?

You can find the entire code for the view above here: https://gist.github.com/letsar/2e3cc98d328b3e84170abacf154e545f

@letsar letsar closed this as completed Jun 30, 2018
@bunopus
Copy link
Author

bunopus commented Jun 30, 2018

@letsar Wow! 😍
Thank you so much! Will try it right now!

@letsar
Copy link
Owner

letsar commented Jun 30, 2018

You're welcome 😄 .
If you find this library useful, don't hesitate to ⭐️ it 😉

@bunopus
Copy link
Author

bunopus commented Jun 30, 2018

Tried your solution, and noticed one thing: app bar title ("Main AppBar") remains on the screen with tab bar. It's not exactly what i want. I think user don't need to see title all the time. In the original Google IO app (in first post) app bar title hides when user scrolls page. Maybe you know how to achieve that? Found PR flutter/flutter#8345 and it says that you can pass floating: true, pinned: true but it doesn't work :-(

@bunopus
Copy link
Author

bunopus commented Jun 30, 2018

Achieved this by adding another SliverAppBar :-)
https://gist.github.com/bunopus/4a99dc9def2932e0aff2629f3905093a#file-tab_bar_view-dart-L36-L39
Thanks for your help!

@letsar
Copy link
Owner

letsar commented Jun 30, 2018

Your're welcome and yes you're right it's not exactly what you wanted but it's more a SliverAppBar issue than a SliverStickyHeader one 😉.

@workerbee22
Copy link

@bunopus You said you achieved this by adding another SliverAppBar but this means you have 2 SliverAppBars? Your gist link code gives me 2 x bars.

https://gist.github.com/bunopus/4a99dc9def2932e0aff2629f3905093a#file-tab_bar_view-dart-L36-L39

How did you make sure the second SliverAppBar ONLY has the TabBar in it?

@bunopus
Copy link
Author

bunopus commented Jan 9, 2019

@workerbee22

How did you make sure the second SliverAppBar ONLY has the TabBar in it?
Actually it turned out that it leads to bugs, so i leave it with Tab Bar on the screen, as Romain showed few messages above.

@workerbee22
Copy link

Thanks @bunopus looks like that might be the only option at this stage. I can get it to work just like the Google I/O app where the AppBar bit of the SliverAppBar does in fact scroll off screen leaving just the TabBar pinned.

To see this use this code in this issue: flutter/flutter#17518 , but with SliverAppBar having floating, pinned and snap all set to true.

!!! But as soon as I add flutter_sticky_header package inside a CustomScrollView as part of the TabBarView it stops working ie. AppBar bit of SliverAppBar no longer scrolls off screen.

So it seems like you can have either the floating app bar at the top OR sticky headers, but not both.

@cgestes
Copy link

cgestes commented Mar 8, 2022

This fork has commits related to nested SliverStickyHeader :)

https://github.com/UnderKoen/flutter_sticky_header

@fennel-ptorchia
Copy link

Is there an updated solution for this issue? This does not appear to work when using tabs and the nested scroll view, I'm looking to be able to pin headers that are inside of a custom scroll view in a tab bar view in the body of a nested scroll view.

@PixelPatrik
Copy link

When testing, my application crashed while changing tabs, because the handle did not remove listeners correctly. I needed to add the 'detach' function and remove the listener accordingly.

  @override
  void detach() {
    handle.removeListener(markNeedsLayout);
    super.detach();
  }

Here are the snippets if anyone is interested.

/// A sliver that has a sliver geometry based on the values stored in a
/// [SliverOverlapAbsorberHandle].
///
/// The [RenderSliverOverlapAbsorber] must be an earlier descendant of a common
/// ancestor [RenderViewport] (probably a [RenderNestedScrollViewViewport]), so
/// that it will always be laid out before the [RenderSliverObstructionInjector]
/// during a particular frame.
class RenderSliverObstructionInjector extends RenderSliverOverlapInjector {
/// Creates a sliver that is as tall as the value of the given [handle]'s extent.
///
/// The [handle] must not be null.
RenderSliverObstructionInjector({
  required SliverOverlapAbsorberHandle handle,
  RenderSliver? child,
})  : _handle = handle,
      super(handle: handle);

double _currentLayoutExtent = 0;
double _currentMaxExtent = 0;

/// The object that specifies how wide to make the gap injected by this render
/// object.
///
/// This should be a handle owned by a [RenderSliverOverlapAbsorber] and a
/// [RenderNestedScrollViewViewport].
@override
SliverOverlapAbsorberHandle get handle => _handle;
SliverOverlapAbsorberHandle _handle;
@override
set handle(SliverOverlapAbsorberHandle value) {
  if (handle == value) return;
  if (attached) {
    handle.removeListener(markNeedsLayout);
  }
  _handle = value;
  if (attached) {
    handle.addListener(markNeedsLayout);
    if (handle.layoutExtent != _currentLayoutExtent ||
        handle.scrollExtent != _currentMaxExtent) markNeedsLayout();
  }
}

@override
void attach(PipelineOwner owner) {
  super.attach(owner);
  handle.addListener(markNeedsLayout);
  if (handle.layoutExtent != _currentLayoutExtent ||
      handle.scrollExtent != _currentMaxExtent) {
    markNeedsLayout();
  }
}

@override
void detach() {
  handle.removeListener(markNeedsLayout);
  super.detach();
}

@override
void performLayout() {
  _currentLayoutExtent = handle.layoutExtent ?? 0;
  _currentMaxExtent = handle.layoutExtent ?? 0;
  geometry = SliverGeometry(
    scrollExtent: 0.0,
    paintExtent: _currentLayoutExtent,
    maxPaintExtent: _currentMaxExtent,
  );
}
}
class SliverObstructionInjector extends SliverOverlapInjector {
  /// Creates a sliver that is as tall as the value of the given [handle]'s
  /// layout extent.
  ///
  /// The [handle] must not be null.
  ///
  const SliverObstructionInjector(
      {super.key, required SliverOverlapAbsorberHandle handle, super.sliver})
      : super(handle: handle);

  @override
  RenderSliverObstructionInjector createRenderObject(BuildContext context) {
    return RenderSliverObstructionInjector(
      handle: handle,
    );
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants