question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[AppBar] Hide on scroll

See original GitHub issue

It is a common UX pattern to hide the AppBar when scrolling down on small screens.

There are different levels of implementation that are possible:

  • Minimal: Straight-up display: none the AppBar when scrolling down under the AppBar height. Even in that minimal setup, the case of the initial load when the user is already scrolled down has to be handled (I think always showing the bar at the initial load is the right UX). And it probably shouldn’t auto-hide on large screen (unless an option is passed maybe).
  • With “headroom”: Frequently when you scroll and release the screen or touchpad, the hand shakiness causes the scroll to go back a little bit, which makes the AppBar appear / disappear unexpectedly. Some headroom helps fixing this behavior.
  • Sliding: A smooth slide up / down transition makes it much more pleasant than display: none. Note: The AppBar shadow has to be taken into consideration, to not cause a shadow at the top of the page.
  • Multi-line AppBars: In the case where the AppBar has a second line of nav items, these should not get automatically hidden. I’m not sure if it’s possible to even do that second line of items with Material UI, but it’s worth noting here.
  • Android-like: When you scroll down from the top and from anywhere on the page, the AppBar stays where it is (like if it was in a simulated-position: static from the place you start scrolling down from, but it’s actually still a position: fixed I think) and is only hidden by as much as you scroll. And when you scroll back up, the AppBar is revealed as much as you scroll up. In both cases, if you don’t scroll up or down enough (less than half of the height of the AppBar), it snaps back to hiding or revealing itself and if you scroll more than half of the AppBar, it snaps to finishing the hiding / reveal completely.

I implemented it up to the Sliding part as a HOC to apply to AppBar but it’s not as nice as having it natively supported by Material UI (demo of my implementation). Some Android examples.

The snapping feature is apparently a thing that got added in Android Library 23.1 (section Design).

  • This is a v1.x issue.
  • I have searched the issues of this repository and believe that this is not a duplicate.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:7
  • Comments:12 (8 by maintainers)

github_iconTop GitHub Comments

2reactions
oliviertassinaricommented, Apr 26, 2019

I agree that it’s a useful feature and that a hook would be an excellent fit for the problem.

@cvanem Regarding the performance, you have an issue here. We need to reduce the number of re-rendering to the number of time the public API changes. Once we have a working hook, we might want to add a in like prop to the AppBar component to animate between the exit and enter state. Collapse doesn’t provide the same animation. Maybe Slide

@chr-knafl Do you have a live example on Vuetify side? I can’t see it working in your link.

Here is how I would approach the problem with v4, it’s not perfect but a great start:

import React from "react";
import { Slide } from "@material-ui/core";

function getScrollY(scroller) {
  return scroller.pageYOffset !== undefined
    ? scroller.pageYOffset
    : scroller.scrollTop !== undefined
    ? scroller.scrollTop
    : (document.documentElement || document.body.parentNode || document.body)
        .scrollTop;
}

const useHideOnScroll = options => {
  const { threshold, scroller } = options;

  const scrollRef = React.useRef();
  const [hide, setHide] = React.useState(false);

  const handleScroll = React.useCallback(() => {
    const scrollY = getScrollY(scroller || window);
    const prevScrollY = scrollRef.current;
    scrollRef.current = scrollY;

    setHide(
      scrollY < prevScrollY
        ? false
        : scrollY > prevScrollY &&
          scrollY > (threshold != null ? threshold : 100)
        ? true
        : false
    );
  }, [scroller, threshold]);

  React.useEffect(() => {
    window.addEventListener("scroll", handleScroll);
    return () => {
      window.removeEventListener("scroll", handleScroll);
    };
  }, [handleScroll]);

  return hide;
};

export default function HideOnScroll(props) {
  const { children, threshold, scroller, ...other } = props;

  const hide = useHideOnScroll({ threshold, scroller });

  return (
    <Slide direction="down" appear={false} in={!hide} {...other}>
      {children}
    </Slide>
  );
}

https://codesandbox.io/s/5x8kq3nwjn (notice the addition of Box, CssBaseline and Container)

Apr-26-2019 23-07-20

1reaction
cvanemcommented, Apr 29, 2019

@oliviertassinari

Thanks for the feedback. I like your simplified approach much better.

For the Scroll hook logic, a few things:

  1. I prefer a more generic hook that can be customized and open to more use cases. After reading, take a look at my new approach in useScrollTrigger.js. It uses the default trigger behavior from useAppBarScroll, but also allows customization. I think it is better as it addresses items a, b and c below, but I am fine if you want to use a more direct useAppBarScroll approach. Just curious, are there any other material-ui components that could benefit from a shared scroll trigger hook? I am considering publishing a react-use-scroll-trigger library as I haven’t been able to find an implementation that does everything I want, which is:

    • a) Minimize hook update frequency and return a flag, aka trigger, only when a condition is met.
    • b) Implement a default trigger condition and also allow it to be customized.
    • c) Default the scroll target to window and also allow a ref to be passed in while correctly handling the ref state. More info here.
  2. Do we want the ability to specify a scroll target ref other than window? If so, I think we will need to provide a setRef callback instead of simply passing in the ref. Same reason/link as item c above

Here is my new approach, with your file structure: https://codesandbox.io/embed/7zjk7n51o0

See the changes in these files:

  • AppBar.js - Added PaperProps as I couldn’t figure out a way to pass down the paper classes needed for the elevation transition.
  • ElevateOnScroll.js - Update to inject paper classes needed for the elevation transitions.
  • UseScrollTrigger.js - Proposed updated hook logic as described above (items a & b & c).
  • GrowScroll.js - Created to show an example of using an external ref (not using window).
Read more comments on GitHub >

github_iconTop Results From Across the Web

Hide Appbar on Scroll Flutter? - Stack Overflow
If I understood you correctly, following code should make the app bar hide on scroll while TabBar remains visible: Null safe code:
Read more >
Flutter — Hide / show AppBar while scrolling - Medium
The SliverAppBar provided by Flutter, supports a floating app bar that hides upon scrolling down. But there's one downside about it, ...
Read more >
Hidden Appbar in Flutter - GeeksforGeeks
Hidden Appbar is the Appbar, when we are scrolling the main body of the Application, the Appbar also scrolled and goes to hidden....
Read more >
Place a floating app bar above a list - Flutter documentation
To create a floating app bar, place the app bar inside a CustomScrollView that also contains the list of items. This synchronizes the...
Read more >
scroll_app_bar | Flutter Package - Pub.dev
Hide or show app bar while scrolling. This package works without custom scroll views and slivers. So, you can use this widget in...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found