Coder 92
Coder 92

Reputation: 46

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,
  },
});

Upvotes: 0

Views: 2483

Answers (2)

Switch button with typescript

import React, {useEffect, useState} from 'react';
import {Animated, Easing, StyleSheet, Text, TouchableOpacity, View} from 
'react-native';
import theme from '@/constants/themeConstants';

type TProps = {
    value: boolean;
    onChange: () => void;
    onColor?: string;
    offColor?: string;
    label?: string;
    labelStyle?: any;
};

const Switch: React.FC<TProps> = ({
    value,
    onChange,
    onColor = 'green',
    offColor = 'grey',
    label = '',
    labelStyle,
}) => {
    const [isEnabled, setIsEnabled] = useState(false);

    useEffect(() => {
        value && setIsEnabled(value);
    }, [value]);

    const toggleSwitch = () => {
        setIsEnabled(!isEnabled);
        onChange();
    };

    const animatedValue = new Animated.Value(0);

    const moveToggle = animatedValue.interpolate({
        inputRange: [-0.2, 0.9],
        outputRange: [0, 20],
    });

    const color = value ? onColor : offColor;

    animatedValue.setValue(value ? 0 : 1);

    Animated.timing(animatedValue, {
        toValue: value ? 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={toggleSwitch} activeOpacity={1}>
            <View style={[styles.toggleContainer, {backgroundColor: 
             color}]}>
                <Animated.View style={[styles.toggleWheelStyle, 
{marginLeft: moveToggle}]} />
                </View>
            </TouchableOpacity>
        </View>
    );
};

export default Switch;

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,
    },
});

Upvotes: 0

tendai
tendai

Reputation: 1250

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.

Upvotes: 3

Related Questions