Skyroc Native UI

PasswordInput

分格密码 / 验证码输入框

密码输入框(PasswordInput)把定长密码或短信验证码拆成若干格子,基于 react-native-confirmation-code-field 封装:真正承接触摸和键盘的是一个铺满整个组件、几乎全透明的 TextInput,格子只是它的可视化呈现。值由组件托管(useControllableState),受控与非受控共用一条路径,onComplete 在两种用法下按同一时机触发。

import { PasswordInput } from '@skyroc/native-ui';

基础用法

默认 6 位、掩码、数字键盘。输入长度达到 length 时触发一次 onComplete,同时底层输入框自动失焦收起键盘。

PasswordInputBasic.tsx
Loading…
import { PasswordInput, Text } from '@skyroc/native-ui';
import { useState } from 'react';
import { View } from 'react-native';

/** 默认 6 位、掩码、数字键盘 */
const PasswordInputBasic = () => {
  const [basic, setBasic] = useState('');
  const [completed, setCompleted] = useState('-');

  return (
    <View className="gap-3 bg-background p-4">
      <PasswordInput
        value={basic}
        onChangeText={setBasic}
        onComplete={setCompleted}
      />
      <Text className="text-sm text-muted-foreground">当前值:{basic || '(空)'}</Text>
      <Text className="text-sm text-muted-foreground">最近一次 onComplete:{completed}</Text>
    </View>
  );
};

export { PasswordInputBasic };

何时使用

  • 支付密码这类定长数字密码,以及短信验证码的录入。
  • 需要「输满即提交」的场景:用 onComplete 而不是自己在 onChangeText 里比长度。
  • 不定长、可含字母的普通密码请用 Inputtype="password",它才有可见性切换与清除按钮。

外观变体

variant 控制格子的分隔方式:

变体表现间距
merged格子紧贴,外层统一描边 + 圆角,内部用左边框分隔无间距,gutter 不生效
separated每格独立描边 + 圆角gutter 控制(默认 12)
PasswordInputVariant.tsx
Loading…
import { PasswordInput, Text } from '@skyroc/native-ui';
import { View } from 'react-native';

/** merged 靠外框描边 + 内部左边框分隔,separated 每格独立描边并由 gutter 拉开间距 */
const PasswordInputVariant = () => {
  return (
    <View className="gap-3 bg-background p-4">
      <PasswordInput defaultValue="12" />
      <Text className="text-sm text-muted-foreground">merged(默认),gutter 不生效</Text>
      <PasswordInput
        defaultValue="12"
        variant="separated"
      />
      <Text className="text-sm text-muted-foreground">separated,默认 gutter=12</Text>
      <PasswordInput
        defaultValue="12"
        gutter={4}
        variant="separated"
      />
      <Text className="text-sm text-muted-foreground">separated + gutter=4</Text>
    </View>
  );
};

export { PasswordInputVariant };

gutter 只在 separated 下写入 rootStylegapmerged 下格子必须紧贴,传了也不会生效。

尺寸

size 同时决定格子高度、掩码圆点直径与明文字号:

尺寸格子高度掩码圆点明文字号
sm408text-base(16)
md5010text-xl(20)
lg5612text-2xl(24)
PasswordInputSize.tsx
Loading…
import { PasswordInput, Text } from '@skyroc/native-ui';
import { View } from 'react-native';

const SIZES = ['sm', 'md', 'lg'] as const;

/** Size 同时驱动格子高度、掩码圆点尺寸与明文字号 */
const PasswordInputSize = () => {
  return (
    <View className="gap-3 bg-background p-4">
      {SIZES.map(s => (
        <View
          key={s}
          className="gap-2"
        >
          <Text className="text-sm font-medium text-foreground">size=&quot;{s}&quot;</Text>
          <PasswordInput
            defaultValue="1234"
            size={s}
          />
        </View>
      ))}
      <Text className="text-sm text-muted-foreground">圆点大小随 size 变化,不只是格子变高</Text>
    </View>
  );
};

export { PasswordInputSize };

格子宽度不由 size 决定:每个格子都是 flex-1,整体宽度由父容器给,格子数越多单格越窄。

长度

length 决定格子数量,同时是 onComplete 的触发阈值。

PasswordInputLength.tsx
Loading…
import { PasswordInput } from '@skyroc/native-ui';
import { View } from 'react-native';

const PasswordInputLength = () => {
  return (
    <View className="gap-3 bg-background p-4">
      <PasswordInput
        length={4}
        variant="separated"
      />
      <PasswordInput length={8} />
    </View>
  );
};

export { PasswordInputLength };

掩码

mask 默认为 true,已输入的格子显示圆点;设为 false 则直接显示字符本身,适合验证码这种不需要遮挡的场景。当前待输入的格子无论掩码与否都渲染闪烁光标。

PasswordInputMask.tsx
Loading…
import { PasswordInput, Text } from '@skyroc/native-ui';
import { useState } from 'react';
import { View } from 'react-native';

/** 关掉掩码直接显示字符 */
const PasswordInputMask = () => {
  const [plain, setPlain] = useState('');

  return (
    <View className="gap-3 bg-background p-4">
      <PasswordInput
        mask={false}
        value={plain}
        onChangeText={setPlain}
      />
      <Text className="text-sm text-muted-foreground">mask=false 时显示明文,字号跟随 size</Text>
    </View>
  );
};

export { PasswordInputMask };

提示与错误

info 在输入框下方显示一行说明;errorInfo 非空时优先显示,info 被顶掉,同时把外框与全部格子的边框置为 destructive 色。

PasswordInputError.tsx
Loading…
import { PasswordInput } from '@skyroc/native-ui';
import { useState } from 'react';
import { View } from 'react-native';

/** 认为正确的密码,用来演示 errorInfo 由输入派生而不是另存一份 state */
const CORRECT_PASSWORD = '123456';

/** 有 errorInfo 时 info 不显示,边框同时转红 */
const PasswordInputError = () => {
  const [verify, setVerify] = useState('');

  const verifyError = verify.length === CORRECT_PASSWORD.length && verify !== CORRECT_PASSWORD ? '密码错误' : '';

  return (
    <View className="gap-3 bg-background p-4">
      <PasswordInput
        errorInfo={verifyError}
        info={`输入 ${CORRECT_PASSWORD} 之外的 6 位数字会报错`}
        value={verify}
        onChangeText={setVerify}
      />
      <PasswordInput
        errorInfo="separated 下每个格子都转红"
        defaultValue="123"
        variant="separated"
      />
    </View>
  );
};

export { PasswordInputError };

组件没有内置校验,errorInfo 由业务侧根据当前值派生即可(demo 里就是输满 6 位后比对)。格子边框色的优先级为 错误 > 聚焦 > 默认:处于错误态时,当前格子也不再显示聚焦色。

命令式聚焦

ref 拿到的是承接触摸的那个底层 TextInput,可以直接调 focus / blur / clear,常用于进入验证码页面就自动弹出键盘。

PasswordInputRef.tsx
Loading…
import { Button, PasswordInput, Text } from '@skyroc/native-ui';
import { useRef } from 'react';
import { TextInput, View } from 'react-native';

/** ref 暴露 focus / blur,用于验证码页面进入即弹键盘一类场景 */
const PasswordInputRef = () => {
  const inputRef = useRef<TextInput>(null);

  return (
    <View className="gap-3 bg-background p-4">
      <PasswordInput
        ref={inputRef}
        variant="separated"
      />
      <View className="flex-row gap-3">
        <Button
          size="sm"
          onPress={() => inputRef.current?.focus()}
        >
          聚焦
        </Button>
        <Button
          size="sm"
          variant="outline"
          onPress={() => inputRef.current?.blur()}
        >
          失焦
        </Button>
      </View>
      <Text className="text-sm text-muted-foreground">输满 6 位后组件也会自动失焦</Text>
    </View>
  );
};

export { PasswordInputRef };

组件自己也用了这个 ref 做「输满自动失焦」,内外两个 ref 通过 useComposedRefs 合成,传入自己的 ref 不会破坏该行为。

样式覆盖

className 追加到根容器上,classNames 按 slot 细粒度覆盖:

slot作用位置
root最外层 View(包含输入区与下方提示文字)
security格子区域的外层容器,merged 下的外框与圆角在这里
cell单个格子(高度、边框、背景)
dot掩码圆点
symbolmask={false} 时的字符与闪烁光标
info下方提示文字
errorInfo下方错误文字
PasswordInputStyles.tsx
Loading…
import { PasswordInput } from '@skyroc/native-ui';
import { View } from 'react-native';

/** className 落在 root,classNames 逐槽覆盖 */
const PasswordInputStyles = () => {
  return (
    <View className="gap-3 bg-background p-4">
      <PasswordInput
        classNames={{ security: 'border-success rounded-none' }}
        defaultValue="12"
      />
      <PasswordInput
        classNames={{ cell: 'bg-secondary', dot: 'bg-primary' }}
        defaultValue="123"
      />
      <PasswordInput
        classNames={{ symbol: 'text-primary font-semibold' }}
        defaultValue="12"
        mask={false}
        variant="separated"
      />
    </View>
  );
};

export { PasswordInputStyles };

className 排在 classNames.root 之后参与合并,冲突时 className 优先。

原生属性透传

除下表列出的属性外,其余属性透传给底层 TextInput。组件预置了四个默认值 —— keyboardType="number-pad"autoCapitalize="none"allowFontScaling={false}maxFontSizeMultiplier={1} —— 它们展开在你的属性之前,可以逐项覆盖,例如改成字母密码。

PasswordInputNativeProps.tsx
Loading…
import { PasswordInput, Text } from '@skyroc/native-ui';
import { View } from 'react-native';

/** 组件默认数字键盘 + 不自动大写,两者都排在 rest 之前,可以被逐项覆盖 */
const PasswordInputNativeProps = () => {
  return (
    <View className="gap-3 bg-background p-4">
      <PasswordInput
        autoCapitalize="characters"
        keyboardType="default"
        mask={false}
        variant="separated"
      />
      <Text className="text-sm text-muted-foreground">覆盖默认值后可输入字母并自动大写</Text>
      <PasswordInput
        defaultValue="123"
        editable={false}
      />
      <Text className="text-sm text-muted-foreground">editable=false 时保留展示,但不可编辑</Text>
    </View>
  );
};

export { PasswordInputNativeProps };

关掉字体缩放是为了让格子高度不被系统字号撑破。以下属性由组件接管,透传会被忽略:valueonChangeTextrenderCellcellCount(用 length)、rootStyle(用 gutter)、textInputStylestyle

Sheet / BottomSheet 内使用时,把 component 换成 BottomSheetTextInput

import { BottomSheetTextInput } from '@gorhom/bottom-sheet';

<PasswordInput component={BottomSheetTextInput} />;

API

PasswordInput

除下表外,PasswordInput 透传 CodeField 及其底层 TextInput 的属性(keyboardTypeautoCapitalizeeditablereturnKeyType 等)。

属性说明类型默认值
variant格子的分隔方式'merged' | 'separated''merged'
size尺寸,决定格子高度、掩码圆点直径与明文字号'sm' | 'md' | 'lg''md'
length密码长度(格子数量),同时是 onComplete 的触发阈值number6
mask是否以圆点代替字符booleantrue
gutter格子间距(像素),仅 variant="separated" 时生效number12
value当前输入值(受控)string-
defaultValue输入值的非受控默认值string''
onChangeText值变化回调(value: string) => void-
onComplete输入长度达到 length 时触发(value: string) => void-
info输入框下方的提示文字,有 errorInfo 时不显示string-
errorInfo输入框下方的错误文字,非空时同时把外框与格子边框置为错误色string-
component替换底层输入组件,在 Sheet 内使用时传 BottomSheetTextInputComponentType<TextInputProps>TextInput
className根容器类名,合并在 classNames.root 之后string-
classNames各 slot 的类名覆盖,见「样式覆盖」一节SlotClassNames<PasswordInputSlots>-
ref底层 TextInput 的 ref,可调用 focus / blur / clear 等原生方法Ref<TextInput>-

类型

import type { PasswordInputProps, PasswordInputSlots } from '@skyroc/native-ui';

PasswordInputSlots

可通过 classNames 覆盖的 slot 名称。

'cell' | 'dot' | 'errorInfo' | 'info' | 'root' | 'security' | 'symbol'

SlotClassNames

classNames 的取值形态:把 slot 名映射到类名,每个 slot 都可选。本页用到的 slot 见 PasswordInputSlots。

Partial<Record<Slots, string>>

包内还导出了 passwordInputVariantsPasswordInputVariantProps,后者含 divider / status 两个由组件按格子下标与聚焦态内部计算的变体,不在对外属性中开放。