Avinash Parasurampuram
Avinash Parasurampuram

Reputation: 121

MultipleScreens(like a stack) on top of one another in react native

In react native, can we achieve a UI where there are multiple screens stacked on top of each other with each screen stacking on top of the other on click of a button.

Let's say, there is Screen A. Upon click of a button, Screen B is placed as a stack on Screen A and screen A is still partly visible as Screen B doesn't occupy the entire screen layout. Similarly, Screen C adds up to the stack upon the click of a button in Screen B and screen B is still visible along with screen A in the background. All these come with a side animation from the left (similar to navigation drawer). With a common button, all the above screens can be popped out from the stack and the previous screen comes to the top with a side animation again, this time to the right.

I wanted to understand if the above is possible in react native? If yes, how I can achieve it? I wish I can show the design but I cannot do that atm.

Upvotes: 0

Views: 2370

Answers (2)

Avinash Parasurampuram
Avinash Parasurampuram

Reputation: 121

I could achieve this functionality using a very cool library named 'react-native-modalfy'.

import React, { PureComponent } from 'react'
import {
  Easing,
  Dimensions,
  StatusBar,
  StyleSheet,
  Text,
  View,
} from 'react-native'
import { ModalProvider, createModalStack } from 'react-native-modalfy'

import CardModal from './modals/CardModal'
import Button from './components/Button'

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

const config = { ModalA: CardModal, ModalB: CardModal, ModalC: CardModal }

const defaultOptions = {
  transitionOptions: animatedValue => ({
    opacity: animatedValue.interpolate({
      inputRange: [0, 1, 2],
      outputRange: [0, 1, .9],
    }),
    transform: [
      {
        perspective: 2000
      },
      {
        translateX: animatedValue.interpolate({
          inputRange: [0, 1, 2],
          outputRange: [-width / 2, 0, 25],
        }),
      },
      {
        scale: animatedValue.interpolate({
          inputRange: [0, 1, 2],
          outputRange: [1.2, 1, .9],
        }),
      },
    ],
  }),
  animateInConfig: {
    easing: Easing.bezier(.42,-0.03,.27,.95),
    duration: 450,
  },
  animateOutConfig: {
    easing: Easing.bezier(.42,-0.03,.27,.95),
    duration: 450,
  },
}

const stack = createModalStack(config, defaultOptions)

class App extends PureComponent {
  render() {
    return (
      <ModalProvider stack={stack}>
        <View style={styles.container}>
          <StatusBar animated hidden translucent />
          <Text style={styles.title}>RNM</Text>
          <Button label="Open ModalA" modalToOpen="ModalA" color="tomato" />
          <Button label="Open ModalB" modalToOpen="ModalB" color="darkcyan" />
          <Button label="Open ModalC" modalToOpen="ModalC" color="deeppink" />
        </View>
      </ModalProvider>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'indigo',
    alignItems: 'center',
    justifyContent: 'center'
  },
  title: {
    color: 'white',
    fontSize: 54,
    fontWeight: 'bold',
    marginBottom: 50,
  },
})

export default App

import React, { PureComponent } from 'react'
import {
  Button as ButtonModule,
  Dimensions,
  StyleSheet,
  Text,
  View,
} from 'react-native'

import Button from '../components/Button'

const { width: ww, height: wh } = Dimensions.get('screen')

class CardModal extends PureComponent {
  componentDidMount() {
    const { modal } = this.props
    this.modalListenerID = modal.addListener('onAnimate', this._handleAnimation)
  }

  componentWillUnmount() {
    this.modalListenerID?.remove()
  }

  _handleAnimation = animatedValue => {
    const { currentModal } = this.props.modal
    console.info(`${currentModal}:`, animatedValue)
  }

  render() {
    const {
      currentModal,
      closeModal,
      closeModals,
      closeAllModals,
      params: { color },
    } = this.props.modal

    return (
      <View style={[styles.card]}>
        <Text style={styles.title(color)}>{currentModal}</Text>
        <Button label="Open ModalA" modalToOpen="ModalA" color="tomato" />
        <Button label="Open ModalB" modalToOpen="ModalB" color="darkcyan" />
        <Button label="Open ModalC" modalToOpen="ModalC" color="deeppink" />
        
        <ButtonModule title="Close" onPress={closeModal} color="dodgerblue" />
        <ButtonModule title={`Close all ${currentModal}`} onPress={() => closeModals(currentModal)} color="dodgerblue" />
        <ButtonModule title="Close all modals" onPress={closeAllModals} color="red" />
      </View>
    )
  }
}

const styles = StyleSheet.create({
  title: color => ({
    color,
    fontSize: 48,
    fontWeight: 'bold',
    marginBottom: 50,
  }),
  card: {
    marginRight : 90,
    width: ww,
    height: wh,
    backgroundColor: 'cyan',
    elevation: 0,
    alignItems: 'center',
    justifyContent: 'center',
    borderRadius: 0,
  },
})

export default CardModal

import React, { PureComponent } from 'react'
import { TouchableOpacity, StyleSheet, Text } from 'react-native'
import { withModal } from 'react-native-modalfy'

class Button extends PureComponent {
  openModal = () => {
    const { color, modalToOpen, modal } = this.props

    modal.openModal(modalToOpen, { color })
  }

  render() {
    const { color, label } = this.props

    return (
      <TouchableOpacity onPress={this.openModal} style={styles.button(color)}>
        <Text style={styles.label}>{label}</Text>
      </TouchableOpacity>
    )
  }
}

const styles = StyleSheet.create({
  button: backgroundColor => ({
    backgroundColor,
    paddingHorizontal: 60,
    paddingVertical: 21,
    borderRadius: 21,
    marginBottom: 30,
  }),
  label: {
    fontSize: 16,
    fontWeight: '800',
    color: 'white',
    textAlign: 'center',
  },
})

export default withModal(Button)

Upvotes: 0

With react-native-vertical-view-pager you can do it.

install with:

npm install --save react-native-vertical-view-pager

or with yarn (if you use yarn):

yarn add react-native-vertical-view-pager

and use as follow:

import React from 'react';
import { StyleSheet, Text, View, Dimensions } from 'react-native';
import VerticalViewPager from 'react-native-vertical-view-pager';

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

export default class App extends React.Component {
    render() {
        return (
            <VerticalViewPager
                showsVerticalScrollIndicator={false}>
                <View style={[styles.page_container, {backgroundColor: 'pink'}]}>
                    <Text>Page1: Open up App.js to start working on your app!</Text>
                </View>
                <View style={[styles.page_container, {backgroundColor: 'olive'}]}>
                    <Text>Page2: Changes you make will automatically reload.</Text>
                </View>
                <View style={[styles.page_container, {backgroundColor: 'lightblue'}]}>
                    <Text>Page3: Shake your phone to open the developer menu.</Text>
                </View>
            </VerticalViewPager>
        );
    }
}

const styles = StyleSheet.create({
    page_container: {
        flex: 1,
        justifyContent: 'center',
        alignItems: 'center',
        width,
        height
    }
});

with your screens on the place of the views.

Upvotes: 1

Related Questions