如何设计React Native OTP输入屏?

drnojrws  于 2023-10-22  发布在  React
关注(0)|答案(9)|浏览(158)

我是react native设计的新手。让我知道如何实现下面显示的屏幕

是否有必要使用4个TextInput或可能使用一个?

w8ntj3qf

w8ntj3qf1#

试试这个软件包https://github.com/Twotalltotems/react-native-otp-input,它在两个平台上都能工作得最好。

fae0ux8s

fae0ux8s2#

试试这个npm包> react-native OTP/Confirmation fields
下面是可用选项的屏幕截图,您的示例位于下划线下。

下面是下划线示例的代码。

import React, {useState} from 'react';
import {SafeAreaView, Text, View} from 'react-native';

import {
  CodeField,
  Cursor,
  useBlurOnFulfill,
  useClearByFocusCell,
} from 'react-native-confirmation-code-field';

const CELL_COUNT = 4;

const UnderlineExample = () => {
  const [value, setValue] = useState('');
  const ref = useBlurOnFulfill({value, cellCount: CELL_COUNT});
  const [props, getCellOnLayoutHandler] = useClearByFocusCell({
    value,
    setValue,
  });

  return (
    <SafeAreaView style={styles.root}>
      <Text style={styles.title}>Underline example</Text>
      <CodeField
        ref={ref}
        {...props}
        value={value}
        onChangeText={setValue}
        cellCount={CELL_COUNT}
        rootStyle={styles.codeFieldRoot}
        keyboardType="number-pad"
        textContentType="oneTimeCode"
        renderCell={({index, symbol, isFocused}) => (
          <View
            // Make sure that you pass onLayout={getCellOnLayoutHandler(index)} prop to root component of "Cell"
            onLayout={getCellOnLayoutHandler(index)}
            key={index}
            style={[styles.cellRoot, isFocused && styles.focusCell]}>
            <Text style={styles.cellText}>
              {symbol || (isFocused ? <Cursor /> : null)}
            </Text>
          </View>
        )}
      />
    </SafeAreaView>
  );
};

const styles = StyleSheet.create({
root: {padding: 20, minHeight: 300},
  title: {textAlign: 'center', fontSize: 30},
  codeFieldRoot: {
    marginTop: 20,
    width: 280,
    marginLeft: 'auto',
    marginRight: 'auto',
  },
  cellRoot: {
    width: 60,
    height: 60,
    justifyContent: 'center',
    alignItems: 'center',
    borderBottomColor: '#ccc',
    borderBottomWidth: 1,
  },
  cellText: {
    color: '#000',
    fontSize: 36,
    textAlign: 'center',
  },
  focusCell: {
    borderBottomColor: '#007AFF',
    borderBottomWidth: 2,
  },
})

export default UnderlineExample;

来源:Github Link to above Code
希望能帮上忙!:)

ukqbszuj

ukqbszuj3#

有一个插件React Native Phone Verification适用于iOS和Android(跨平台)与此您可以使用验证码选择器匹配您的要求.

t98cgbkg

t98cgbkg4#

我们过去常常使用单个隐藏输入字段来完成它,如@Chethan的回答中所述。现在,由于RN已经在Android平台上支持返回按钮的回调(自RN 0.58甚至更早)。这可以通过一组文本输入的正常布局来实现。但我们还需要考虑iOS上的文本输入建议或Android上的自动填充。实际上,我们已经开发了一个库来处理这个问题。这里是blog来介绍这个库以及如何使用它。源代码是here

2skhul33

2skhul335#

@kd12345:你可以在这里做:

onChangeText={(val) => {
  setFieldTouched(`code${index + 1}`, true, false);
  setFieldValue(`code${index + 1}`, val);
  console.log(typeof val);
  // LITTLE MODIFICATION HERE
  if(index < 3 && val !== '') {
    references.current[index + 1].current.focus();
    // DO WHATEVER
  }
          
}}
fslejnso

fslejnso6#

您可以只使用一个隐藏的TextInput元素,并附加一个onChangeText函数和填充在文本视图中输入的值(您可以使用四个不同的文本视图的设计需要它)。如果用户点击文本视图,请确保将文本输入集中在点击文本视图上

mwecs4sa

mwecs4sa7#

在这里,我已经创建了一个屏幕六个文本输入OTP验证重新发送OTP功能与计数器计时器的90秒。在Android和iOS上进行了全面测试。

我已经使用react-native-confirmation-code-field作为带下划线的文本输入。下面是完整的代码。

import React, { useState, useEffect } from 'react';
import { SafeAreaView, Text, View ,TouchableOpacity} from 'react-native';
import { CodeField, Cursor, useBlurOnFulfill, useClearByFocusCell } from 
'react-native-confirmation-code-field';
import { Button } from '../../../components';
import { styles } from './style';

interface VerifyCodeProps {
}
const CELL_COUNT = 6;
const RESEND_OTP_TIME_LIMIT = 90;

export const VerifyCode: React.FC<VerifyCodeProps> = () => {
let resendOtpTimerInterval: any;

const [resendButtonDisabledTime, setResendButtonDisabledTime] = useState(
    RESEND_OTP_TIME_LIMIT,
);

//to start resent otp option
const startResendOtpTimer = () => {
    if (resendOtpTimerInterval) {
        clearInterval(resendOtpTimerInterval);
    }
    resendOtpTimerInterval = setInterval(() => {
        if (resendButtonDisabledTime <= 0) {
            clearInterval(resendOtpTimerInterval);
        } else {
            setResendButtonDisabledTime(resendButtonDisabledTime - 1);
        }
    }, 1000);
};

//on click of resend button
const onResendOtpButtonPress = () => {
    //clear input field
    setValue('')
    setResendButtonDisabledTime(RESEND_OTP_TIME_LIMIT);
    startResendOtpTimer();

    // resend OTP Api call
    // todo
    console.log('todo: Resend OTP');
};

//declarations for input field
const [value, setValue] = useState('');
const ref = useBlurOnFulfill({ value, cellCount: CELL_COUNT });
const [props, getCellOnLayoutHandler] = useClearByFocusCell({
    value,
    setValue,
});

//start timer on screen on launch
useEffect(() => {
    startResendOtpTimer();
    return () => {
        if (resendOtpTimerInterval) {
            clearInterval(resendOtpTimerInterval);
        }
    };
}, [resendButtonDisabledTime]);

return (
    <SafeAreaView style={styles.root}>
        <Text style={styles.title}>Verify the Authorisation Code</Text>
        <Text style={styles.subTitle}>Sent to 7687653902</Text>
        <CodeField
            ref={ref}
            {...props}
            value={value}
            onChangeText={setValue}
            cellCount={CELL_COUNT}
            rootStyle={styles.codeFieldRoot}
            keyboardType="number-pad"
            textContentType="oneTimeCode"
            renderCell={({ index, symbol, isFocused }) => (
                <View
                    onLayout={getCellOnLayoutHandler(index)}
                    key={index}
                    style={[styles.cellRoot, isFocused && styles.focusCell]}>
                    <Text style={styles.cellText}>
                        {symbol || (isFocused ? <Cursor /> : null)}
                    </Text>
                </View>
            )}
        />
        {/* View for resend otp  */}
        {resendButtonDisabledTime > 0 ? (
            <Text style={styles.resendCodeText}>Resend Authorisation Code in {resendButtonDisabledTime} sec</Text>
        ) : (
                <TouchableOpacity
                    onPress={onResendOtpButtonPress}>
                    <View style={styles.resendCodeContainer}>
                        <Text style={styles.resendCode} > Resend Authorisation Code</Text>
                        <Text style={{ marginTop: 40 }}> in {resendButtonDisabledTime} sec</Text>
                    </View>
                </TouchableOpacity >
            )
        }
        <View style={styles.button}>
            <Button buttonTitle="Submit"
                onClick={() =>
                    console.log("otp is ", value)
                } />
        </View>
    </SafeAreaView >
);
}

此屏幕的样式文件在下面的代码中给出:

import { StyleSheet } from 'react-native';
import { Color } from '../../../constants';

export const styles = StyleSheet.create({
root: {
    flex: 1,
    padding: 20,
    alignContent: 'center',
    justifyContent: 'center'
},
title: {
    textAlign: 'left',
    fontSize: 20,
    marginStart: 20,
    fontWeight:'bold'
},
subTitle: {
    textAlign: 'left',
    fontSize: 16,
    marginStart: 20,
    marginTop: 10
},
codeFieldRoot: {
    marginTop: 40,
    width: '90%',
    marginLeft: 20,
    marginRight: 20,
},
cellRoot: {
    width: 40,
    height: 40,
    justifyContent: 'center',
    alignItems: 'center',
    borderBottomColor: '#ccc',
    borderBottomWidth: 1,
 },
 cellText: {
    color: '#000',
    fontSize: 28,
    textAlign: 'center',
},
focusCell: {
    borderBottomColor: '#007AFF',
    borderBottomWidth: 2,
},

button: {
    marginTop: 20
},
resendCode: {
    color: Color.BLUE,
    marginStart: 20,
    marginTop: 40,
},
resendCodeText: {
    marginStart: 20,
    marginTop: 40,
},
resendCodeContainer: {
    flexDirection: 'row',
    alignItems: 'center'
}
})

希望对很多人有帮助。快乐编码!!

neskvpey

neskvpey8#

我解决了这个问题的6位数otp以下切森的答案。首先创建一个数组“otp”,初始化状态为otp ='-','-'],然后创建一个otpVal字符串,状态如下

const [otp, setOtp] = useState(['-', '-', '-', '-', '-', '-']);
const [otpVal, setOtpVal] = useState('');

现在渲染otp框的实际逻辑如下。

<TextInput
                    onChangeText={value => {
                        if (isNaN(value)) {
                            return;
                        }
                        if (value.length > 6) {
                            return;
                        }
                        let val =
                            value + '------'.substr(0, 6 - value.length);
                        let a = [...val];
                        setOtpVal(a);
                        setOtp(value);
                    }}
                    style={{ height: 0 }}
                    autoFocus = {true}
                />
                <View style={styles.otpBoxesContainer}>
                    {[0, 1, 2, 3, 4, 5].map((item, index) => (
                        <Text style={styles.otpBox} key={index}>
                            {otp[item]}
                        </Text>
                    ))}
                </View>

otpBoxesContainer和otpBox的样式如下:

otpBoxesContainer: {
    flexDirection: 'row'
},
otpBox: {
    padding: 10,
    marginRight: 10,
    borderWidth: 1,
    borderColor: lightGrey,
    height: 45,
    width: 45,
    textAlign: 'center'
}

现在,由于TextInput的高度设置为0,它不会显示给用户,但它仍然接受输入。我们修改和存储输入的方式,我们可以显示它,就像在单独的输入框中输入值一样。

jtjikinw

jtjikinw9#

我也遇到了同样的问题,我设法开发了一个很好的工作解决方案。忽略提供程序,我使用它是为了我自己的目的,只是为了设置表单值。
行为:
1.用户输入第一个pin号
1.下一个输入是聚焦的
1.用户删除号码
1.号码已删除
1.以前的投入是重点

编码

// Dump function to print standard Input field. Mine is a little customised in 
// this example, but it does not affects the logics

const CodeInput = ({name, reference, placeholder, ...props}) => (
  <Input
    keyboardType="number-pad"
    maxLength={1}
    name={name}
    placeholder={placeholder}
    reference={reference}
    textAlign="center"
    verificationCode
    {...props}
  />
);
// Logics of jumping between inputs is here below. Ignore context providers it's for my own purpose.

const CodeInputGroup = ({pins}) => {
  const {setFieldTouched, setFieldValue, response} = useContext(FormContext);
  const references = useRef([]);

  references.current = pins.map(
    (ref, index) => (references.current[index] = createRef()),
  );

  useEffect(() => {
    console.log(references.current);
    references.current[0].current.focus();
  }, []);

  useEffect(() => {
    response &&
      response.status !== 200 &&
      references.current[references.current.length - 1].current.focus();
  }, [response]);

  return pins.map((v, index) => (
    <CodeInput
      key={`code${index + 1}`}
      name={`code${index + 1}`}
      marginLeft={index !== 0 && `${moderateScale(24)}px`}
      onChangeText={(val) => {
        setFieldTouched(`code${index + 1}`, true, false);
        setFieldValue(`code${index + 1}`, val);
        console.log(typeof val);
        index < 3 &&
          val !== '' &&
          references.current[index + 1].current.focus();
      }}
      onKeyPress={
        index > 0 &&
        (({nativeEvent}) => {
          if (nativeEvent.key === 'Backspace') {
            const input = references.current[index - 1].current;

            input.focus();
          }
        })
      }
      placeholder={`${index + 1}`}
      reference={references.current[index]}
    />
  ));
};
// Component caller
const CodeConfirmation = ({params, navigation, response, setResponse}) => {
  return (
    <FormContext.Provider
      value={{
        handleBlur,
        handleSubmit,
        isSubmitting,
        response,
        setFieldTouched,
        setFieldValue,
        values,
      }}>
      <CodeInputGroup pins={[1, 2, 3, 4]} />
    </FormContext.Provider>
  );
};

相关问题