cesardeazevedo / react-native-bottom-sheet-behavior

react-native wrapper for android BottomSheetBehavior
MIT License
1.16k stars 114 forks source link

How would you implement an overlay to the content below the BottomSheet? #27

Open ferrannp opened 7 years ago

ferrannp commented 7 years ago

Just like explained here https://material.io/guidelines/components/bottom-sheets.html#bottom-sheets-usage. Is there an easy way?

I think about having an overlay view that use some animation opacity based on the current position of the BottomSheet (full color when is expanded and starting fading color when collapsing). The overlay should also be clickable to close the bottom sheet. Maybe you have an example of it? Thanks :)

cesardeazevedo commented 7 years ago

I would do something like that, there it is.


import React, { Component, PropTypes } from 'react'
import {
  Text,
  View,
  Animated,
  StatusBar,
  Dimensions,
  StyleSheet,
} from 'react-native'

import Icon from 'react-native-vector-icons/Ionicons'

import {
  BottomSheetBehavior,
  CoordinatorLayout,
} from 'react-native-bottom-sheet-behavior'

const { width } = Dimensions.get('window')

class SimpleView extends Component {
  static contextTypes = {
    openDrawer: PropTypes.func,
  };

  state = {
    scrollY: new Animated.Value(0),
  };

  handleSlide = (e) => {
    Animated.event(
      [{ nativeEvent: { offset: this.state.scrollY }}, { useNativeDriver: true }]
    )(e, this.state)
  }

  render() {
    const opacity = this.state.scrollY.interpolate({
      inputRange:  [0, 1],
      outputRange: [0, 0.65],
    })
    return (
      <CoordinatorLayout style={styles.container}>
        <StatusBar translucent backgroundColor="rgba(0, 0, 0, 0.2)" />
        <View style={styles.content}>
          <View style={styles.toolbarWrapper}>
            <Icon.ToolbarAndroid
              navIconName={'md-menu'}
              style={styles.toolbar}
              titleColor="white"
              title="Simple Bottom Sheet"
              onIconClicked={() => this.context.openDrawer()}
            />
          </View>
        </View>
        <Animated.View style={[styles.overlay, {opacity}]} />
        <BottomSheetBehavior
          peekHeight={80}
          hideable={false}
          onSlide={this.handleSlide}>
          <View style={styles.bottomSheet}>
            <View style={styles.bottomSheetHeader}>
              <Text style={styles.label}>BottomSheetBehavior !</Text>
            </View>
            <View style={styles.bottomSheetContent}>
            </View>
          </View>
        </BottomSheetBehavior>
      </CoordinatorLayout>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
  },
  content: {
    alignItems: 'center',
    justifyContent: 'center',
    backgroundColor: '#fff',
  },
  overlay: {
    position: 'absolute',
    top: -24,
    left: 0,
    right: 0,
    bottom: 0,
    opacity: 0,
    backgroundColor: 'black',
  },
  toolbarWrapper: {
    paddingTop: 24,
    marginBottom: 24,
    backgroundColor: '#4389f2',
  },
  toolbar: {
    width,
    height: 56,
  },
  bottomSheet: {
    backgroundColor: '#4389f2',
  },
  bottomSheetHeader: {
    padding: 28,
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
  },
  bottomSheetContent: {
    height: 200,
    padding: 2,
    alignItems: 'center',
    backgroundColor: '#fff',
  },
  label: {
    fontSize: 18,
    fontWeight: 'bold',
    color: '#fff',
  },
})

export default SimpleView

opacity

ferrannp commented 7 years ago

Nice! Thank you! I was doing something similar, I added a touchable so I can collapse the BottomSheet when clicking the overlay, it might be useful to someone:

<TouchableWithoutFeedback onPress={this._setBottomSheetState}>
  <Animated.View style={[styles.overlay, {
    opacity: this.state.scrollY.interpolate({
      inputRange: [0, 1],
      outputRange: [0, 0.4],
    })
  }]}
    pointerEvents={!this._isOpen() ? "none" : "auto"}
  />
</TouchableWithoutFeedback>

Pity we don't have this in JS so can work on iOS too, but for now, it's good in Android thanks to this lib :)

ferrannp commented 7 years ago

@cesardeazevedo Thanks to Flow and checking API, I think we were not using useNativeDriver correctly, should be:

  _handleSlide = (e) => {
    Animated.event(
      [{ nativeEvent: { offset: this.state.scrollY }}],
      { useNativeDriver: true }
    )(e, this.state);
  };

The thing is, like that it crashes, with false it works. Any ideas?

cesardeazevedo commented 7 years ago

Oh, so sorry about that, completely my fault, i definitely was using wrong the useNativeDriver API.

One the things to work around would be to wrap the BottomSheetBehavior together with the createAnimatedComponent (the FlastList Example does it), that wouldn't cause any crashes, but the transition itself doesn't seems to take any effect with useNativeDrive: true, but i can be wrong about this approach, I will definitely investigate it, sorry about that.

ferrannp commented 7 years ago

@cesardeazevedo I think that for useNativeDrive to work, your library need to handle that in the native side (because with that set to true, it returns an object not a callback).

cesardeazevedo commented 7 years ago

You are right, i am also thinking in something on this direction, i am trying to getting in deep on the ScrollView implementation, and i think it could be more related on the way that we handle the Animated API together with createAnimatedComponent than the native itself, or something in between, i'm not sure, i'm still investigating it, i will reopen this issue for now.