Search code examples
react-nativereact-functional-componentuiswitch

Switch UI desing like IOS for android in react native


I am trying to implement a Switch component in react native but the switch different in the android platform. I did explore a lot but I didn't find any references finally i found but it is in type script and a class-based component can someone help me to convert in JSX and functional-based component?

import * as React from 'react';
import {
  Animated,
  Easing,
  StyleSheet,
  Text,
  TouchableOpacity,
  View,
} from 'react-native';

interface Props {
  onColor: string;
  offColor: string;
  label: string;
  onToggle: () => void;
  style: object;
  isOn: boolean;
  labelStyle: object;
}

interface DefaultProps {
  onColor: string;
  offColor: string;
  label: string;
  onToggle: () => void;
  style: object;
  isOn: boolean;
  labelStyle: object;
}

export default class Toggle extends React.PureComponent<Props> {
  animatedValue = new Animated.Value(0);

  static defaultProps: DefaultProps = {
    onColor: '#4cd137',
    offColor: '#ecf0f1',
    label: '',
    onToggle: () => {},
    style: {},
    isOn: false,
    labelStyle: {},
  };

  render() {
    const moveToggle = this.animatedValue.interpolate({
      inputRange: [0, 1],
      outputRange: [0, 20],
    });

    const {
      isOn,
      onColor,
      offColor,
      style,
      onToggle,
      labelStyle,
      label,
    } = this.props;

    const color = isOn ? onColor : offColor;

    this.animatedValue.setValue(isOn ? 0 : 1);

    Animated.timing(this.animatedValue, {
      toValue: isOn ? 1 : 0,
      duration: 300,
      easing: Easing.linear,
    }).start();

    return (
      <View style={styles.container}>
        {!!label && <Text style={[styles.label, labelStyle]}>{label}</Text>}

        <TouchableOpacity
          onPress={() => {
            typeof onToggle === 'function' && onToggle();
          }}>
          <View
            style={[styles.toggleContainer, style, { backgroundColor: color }]}>
            <Animated.View
              style={[
                styles.toggleWheelStyle,
                {
                  marginLeft: moveToggle,
                },
              ]}
            />
          </View>
        </TouchableOpacity>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flexDirection: 'row',
    alignItems: 'center',
  },
  toggleContainer: {
    width: 50,
    height: 30,
    marginLeft: 3,
    borderRadius: 15,
    justifyContent: 'center',
  },
  label: {
    marginRight: 2,
  },
  toggleWheelStyle: {
    width: 25,
    height: 25,
    backgroundColor: 'white',
    borderRadius: 12.5,
    shadowColor: '#000',
    shadowOffset: {
      width: 0,
      height: 2,
    },
    shadowOpacity: 0.2,
    shadowRadius: 2.5,
    elevation: 1.5,
  },
});

Solution

  • Basically, as a functional component in jsx, the above would translate to:

    import * as React from 'react';
    import {
      Animated,
      Easing,
      StyleSheet,
      Text,
      TouchableOpacity,
      View,
    } from 'react-native';
    import PropTypes from 'prop-types';
    
    const Toggle = props => {
      const animatedValue = new Animated.Value(0);
    
      const moveToggle = animatedValue.interpolate({
        inputRange: [0, 1],
        outputRange: [0, 20],
      });
    
      const {isOn, onColor, offColor, style, onToggle, labelStyle, label} = props;
    
      const color = isOn ? onColor : offColor;
    
      animatedValue.setValue(isOn ? 0 : 1);
    
      Animated.timing(animatedValue, {
        toValue: isOn ? 1 : 0,
        duration: 300,
        easing: Easing.linear,
        useNativeDriver: false,
      }).start();
    
      return (
        <View style={styles.container}>
          {!!label && <Text style={[styles.label, labelStyle]}>{label}</Text>}
    
          <TouchableOpacity onPress={typeof onToggle === 'function' && onToggle}>
            <View style={[styles.toggleContainer, style, {backgroundColor: color}]}>
              <Animated.View
                style={[
                  styles.toggleWheelStyle,
                  {
                    marginLeft: moveToggle,
                  },
                ]}
              />
            </View>
          </TouchableOpacity>
        </View>
      );
    };
    
    Toggle.propTypes = {
      onColor: PropTypes.string,
      offColor: PropTypes.string,
      label: PropTypes.string,
      onToggle: PropTypes.func,
      style: PropTypes.object,
      isOn: PropTypes.bool.isRequired,
      labelStyle: PropTypes.object,
    };
    
    Toggle.defaultProps = {
      onColor: '#4cd137',
      offColor: '#ecf0f1',
      label: '',
      onToggle: () => {},
      style: {},
      isOn: false,
      labelStyle: {},
    };
    
    export default Toggle;
    
    const styles = StyleSheet.create({
      container: {
        flexDirection: 'row',
        alignItems: 'center',
      },
      toggleContainer: {
        width: 50,
        height: 30,
        marginLeft: 3,
        borderRadius: 15,
        justifyContent: 'center',
      },
      label: {
        marginRight: 2,
      },
      toggleWheelStyle: {
        width: 25,
        height: 25,
        backgroundColor: 'white',
        borderRadius: 12.5,
        shadowColor: '#000',
        shadowOffset: {
          width: 0,
          height: 2,
        },
        shadowOpacity: 0.2,
        shadowRadius: 2.5,
        elevation: 1.5,
      },
    });
    

    USAGE:

    import React, {useState} from 'react';
    import {View} from 'react-native';
    import Toggle from '..path/to/toggle';
    
    const Screen = () => {
      const [toggleIsOn, setToggle] = useState(false);
      return (
        <View>
          <Toggle
            isOn={toggleIsOn}
            onToggle={() => {
              setToggle(!toggleIsOn);
            }}
          />
        </View>
      );
    };
    
    export default Screen;
    

    You would need to install prop-types (yarn install prop-types) so you can specify types or use flow because js out of the box isn't type safe.