react-native-tab-view
react-native-tab-view copied to clipboard
Collapsible Header With Tab View
Motivation
The UI of having a collapsible header above a Tab View is a commonly requested pattern (#411), but it is surprisingly challenging for newer RN devs to implement. Creating a wrapper for this functionality and some examples for using the wrapper will help allow them to have a baseline for how to add this pattern into their own apps.
Prior Examples
@satya164 Provided https://snack.expo.io/@satya164/collapsible-header-with-tabview as an example. It's a valuable start for understanding how to work on this, but the snack itself can't be used without significant additional work due to the additional of large amounts of padding above the content when switching tabs in certain cases #441.
This example appears to be complete, but it's a rather larger example that is challenging to quickly bring into a different projects that just wants the minimum code needed to reproduce the baseline effect.
@JungHsuan Provided a medium article on his approach, which was practically production ready and had the exact UI flow that was buttery smooth and consistent in all cases. I started with this approach, and converted it into something more re-usable in different situations, trying to make it quicker for other developers to pull into their projects.
UI Animation
Current known limitations/issues
-
Since the collapsible header view becomes absolutely positioned and is animated, you'll need to adjust
zIndex
of the headers above it (see example code) -
In order to support scrolling the
TabBar
up, we must set a minimum height for the scrollable content. If you have one tab with only a small amount of content in there, it will be allowed to scroll up with large padding below it. This maybe be possible fix via measuring the content, but that would be an enhancement. Edit: Discussed possible fix here. -
The
CollapsibleHeader
prop
structure I initially proposed may not be ideal. It basically forces use to create an array ofrenderItem
anddata
, which is used by each tab separately to draw its content. But the idea behind this even for non-virtualized content, we need aScrollView
orFlatList
wrapper in there to drive this behavior, so we want to bake this functionality into the wrapper while still making implementation lightweight. This also has the side effect of fully supporting both tab content that are rendered at once, or usingFlatList
virtualized rendering. -
I attempted to use
SectionList
to drive this, but challenges with measuring scroll distances shut down my early attempts. -
If you scroll tab 1 all the way down, swap to tab 2, scroll it up any amount, and come back to tab 1, you'll notice tab 1 is now at the top, with the collapsible header being set at the same height. This isn't a real 'limitation' as it's simply necessary for the UI to be smooth, but it's good to be aware of.
Test plan
This is an early draft version of the PR. I am submitting in this state to see if there is interest in me polishing it up.
TODO's:
- [ ] Stricter typings (I used
any
in many places where it can be better typed) - [ ] Removal of inline functions
- [ ] Adding example of correctly using underlying
FlatList
- [ ] There is some warning spam right now that needs to be cleaned up
When the header's height > screen height or i want to handle the header, it cant work because the header cant scroll
@jehartzog @hejun041 @sagrawalcb @sjmueller @fantattitude You can try the react-native-head-tab-view. (tabs with collapsible header)
@zyslife Thanks for putting that together. In this case, I'm hoping to add the functionality as an optional inclusion to react-native-tab-view
as I think that will help the larger base of devs who already use react-native-tab-view
in their RN project and what to support this behavior.
Is there any interest in merging this in? I'm still happy to clean this up some more, write docs, but I've learned to wait until I hear back from maintainers.
I´m trying to implement this solution with SectionList right now. Regarding the limitations of point 4, are you referring to the fact that the only scrollTo function that is supportted in sectionList is the scrollToLocation one?
@alexco2 Yes, that is what I saw. I only looked for ~10 minutes into it, as I suspected the scrollToLocation
would be more complicated due to being dependent on what kind of data was added. I didn't really look into it deeply though.
@jehartzog scrollToLocation
expects itemIndex
and sectionIndex
. Both can be set to 0 though. With viewOffset
you can then control the scrolling position with the use of pixel like in your example with the flatlist. Two problems then came up.
First, if the sectionlist of the inactive route doesn´t have enough rows so that the header can not be completely collapsed, the function syncScrollOffset
somehow updates the header of the active route with the amount of translateY
the inactive route can be scrolled with. So, if the active route was scrolled with 50 pixel but the inactive route can just be scrolled 5 pixel because of to little rows, the active collapsible header gets updated with a translateY
value of 5. My workaround is to dynamically extend a listfooter so that the list can alwas be scrolled to collapse the header completly.
Also, if the inactive route does not have any data in it, the scrollToLocation
function throws an out of bounds error because the itemIndex = 0
and sectionIndex = 0
do not exist. Weirdly using -1 each does not fix this. My workaround is to just deactivate the second route if there is no data, since it does not have any information for the user anyways.
@zyslife Thanks for putting that together. In this case, I'm hoping to add the functionality as an optional inclusion to
react-native-tab-view
as I think that will help the larger base of devs who already usereact-native-tab-view
in their RN project and what to support this behavior.Is there any interest in merging this in? I'm still happy to clean this up some more, write docs, but I've learned to wait until I hear back from maintainers.
Thanks for your invitation,If I have enough energy, I should be able to react-native-tab-view
add this feature. But not right now. I can only make react-native- head-tab-View
work right now ,out of my working hours. I hope I can do something in the future.
Hi folks I'm coming late to the conversation but was also working on this, I've just made a PR, let me know if you guys see how to improve it :)
We've been using the code based on this PR successfully in our prod app for a while, but going forward I'd probably pickup the outside packages published by #1096 rather than continue to maintain our fork. Just throwing this in for anyone looking to add these features.
On the other hand, repo owners decide they want to pull in this PR, I will be happy to spend the time to clean it up, get it back up to date, and get it merged. Just need someone w/ review authority to give thumbs up or down.
@jehartzog thanks for you solve my issue!!
On the other hand, repo owners decide they want to pull in this PR, I will be happy to spend the time to clean it up, get it back up to date, and get it merged. Just need someone w/ review authority to give thumbs up or down.
@satya164
After consulting this with @satya164 we decided that we want to keep the library as small as possible (and easy to maintain) so I suggest you to publish this as a separate library.
@okwasniewski Would this feature be a huge increase in size to the library? This feature would be extremely useful but i definitely understand the reasoning haha