Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React Native: Setting flex:1 on a ScrollView contentContainerStyle causes overlapping of components

Problem:

I have a ScrollView with 2 subviews and I want the first of them (lets call it ViewA) to have {flex: 1} so the other one (ViewB) will stick to the bottom of the screen - but only if their total height is smaller that the screen. Of course that if they are higher than the screen I want it to scroll as usual.

Case 1 (GOOD): ViewA with long text, ViewB scrolls with it. https://rnplay.org/apps/slCivA

Case 2 (BAD): ViewA with short text, ViewB doesn't stick to the bottom. https://rnplay.org/apps/OmQakQ

Tried Solution:

So I set the ScrollView's style AND contentContainerStyle to be flex: 1. I Also set ViewA's style to flex:1. But when I do it, the ScrollView's contentContainer view is fixed to the screen height, thus not able to scroll if needed and even worse - ViewB overlaps ViewA.

Case 3 (BAD): ViewB sticks to the bottom, but the whole thing doesn't scroll. https://rnplay.org/apps/wZgtWA

If it's a bug - how to fix/workaround it? If it's the expected behaviour - how can I achieve what I've described?

Thanks.

like image 778
David Avikasis Avatar asked Nov 12 '15 15:11

David Avikasis


People also ask

Does Flex work in ScrollView?

ScrollView is a component which doesn't inherit the use of flex .

Which is better FlatList or ScrollView?

ScrollView renders all its react child components at once, but this has a performance downside. FlatList renders items lazily, when they are about to appear, and removes items that scroll way off-screen to save memory and processing time.

What is flexGrow in react native?

flexGrow describes how any space within a container should be distributed among its children along the main axis. After laying out its children, a container will distribute any remaining space according to the flex grow values specified by its children.


2 Answers

Try feeding {flexGrow: 1} to the contentContainerStyle prop instead

like image 64
Collin Tharp Avatar answered Nov 15 '22 20:11

Collin Tharp


Ok, so i wasn't able to get it work by styling alone, but here's how I did it:

  1. Measure the height of the footer after it rendered (using onLayout, and measure)
  2. on the measure callback, I add the height (if needed) to a spacer view between ViewA and ViewB in order to place ViewB on the bottom.
  3. In order to avoid showing 'jumps' to the user, I hide (with opacity) ViewB until its position is fixed.

my CJSX code (stripped and simplified version):

Dimensions = require('Dimensions')
windowSize = Dimensions.get('window')

MyClass = React.createClass
  mixins: [TimerMixin]

  render: ->
    <ScrollView style={styles.container} automaticallyAdjustContentInsets={false}>
      <View style={styles.mainContainer}>
        ... THIS IS VIEW A ...      
      </View>
      {
        if [email protected]
          <View>
            <ActivityIndicatorIOS/>
          </View>
      }
      <View style={[styles.spacer, {height: @state.spacerSize || 0}]}></View>
      <View onLayout={@measureFooterPosition} style={[styles.extras, {opacity: if @state.footerWasFixed then 1 else 0 }]} ref='extras'>
        ... THIS IS VIEW B ...       
      </View>
    </ScrollView>


  measureFooterPosition: ->
    @refs.extras.measure(@fixFooterPosition)

  fixFooterPosition: (ox, oy, width, height) ->
    footerPosition = Math.round(oy + height)
    diff = windowSize.height - footerPosition
    if diff > 0
      @setState(spacerSize: diff)
    if [email protected]
      @setTimeout (=>
        @setState(footerWasFixed: true)
      ), 30

styles = StyleSheet.create(
  container:
    backgroundColor: 'grey'
    flex: 1
  spacer:
    backgroundColor: 'white'
  mainContainer:
    flex: 1
    backgroundColor: 'white'
  extras:
    backgroundColor: 'white')

It's very simplified code, (my view has requirements much more specific then this..) but I hope it helps anyone.

like image 44
David Avikasis Avatar answered Nov 15 '22 20:11

David Avikasis