PasswordInput
分格密码 / 验证码输入框
密码输入框(PasswordInput)把定长密码或短信验证码拆成若干格子,基于 react-native-confirmation-code-field 封装:真正承接触摸和键盘的是一个铺满整个组件、几乎全透明的 TextInput,格子只是它的可视化呈现。值由组件托管(useControllableState),受控与非受控共用一条路径,onComplete 在两种用法下按同一时机触发。
import { PasswordInput } from '@skyroc/native-ui';基础用法
默认 6 位、掩码、数字键盘。输入长度达到 length 时触发一次 onComplete,同时底层输入框自动失焦收起键盘。
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里比长度。 - 不定长、可含字母的普通密码请用
Input的type="password",它才有可见性切换与清除按钮。
外观变体
variant 控制格子的分隔方式:
| 变体 | 表现 | 间距 |
|---|---|---|
merged | 格子紧贴,外层统一描边 + 圆角,内部用左边框分隔 | 无间距,gutter 不生效 |
separated | 每格独立描边 + 圆角 | 由 gutter 控制(默认 12) |
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 下写入 rootStyle 的 gap;merged 下格子必须紧贴,传了也不会生效。
尺寸
size 同时决定格子高度、掩码圆点直径与明文字号:
| 尺寸 | 格子高度 | 掩码圆点 | 明文字号 |
|---|---|---|---|
sm | 40 | 8 | text-base(16) |
md | 50 | 10 | text-xl(20) |
lg | 56 | 12 | text-2xl(24) |
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="{s}"</Text>
<PasswordInput
defaultValue="1234"
size={s}
/>
</View>
))}
<Text className="text-sm text-muted-foreground">圆点大小随 size 变化,不只是格子变高</Text>
</View>
);
};
export { PasswordInputSize };格子宽度不由 size 决定:每个格子都是 flex-1,整体宽度由父容器给,格子数越多单格越窄。
长度
length 决定格子数量,同时是 onComplete 的触发阈值。
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 则直接显示字符本身,适合验证码这种不需要遮挡的场景。当前待输入的格子无论掩码与否都渲染闪烁光标。
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 色。
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,常用于进入验证码页面就自动弹出键盘。
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 | 掩码圆点 |
symbol | mask={false} 时的字符与闪烁光标 |
info | 下方提示文字 |
errorInfo | 下方错误文字 |
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} —— 它们展开在你的属性之前,可以逐项覆盖,例如改成字母密码。
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 };关掉字体缩放是为了让格子高度不被系统字号撑破。以下属性由组件接管,透传会被忽略:value、onChangeText、renderCell、cellCount(用 length)、rootStyle(用 gutter)、textInputStyle、style。
在 Sheet / BottomSheet 内使用时,把 component 换成 BottomSheetTextInput:
import { BottomSheetTextInput } from '@gorhom/bottom-sheet';
<PasswordInput component={BottomSheetTextInput} />;API
PasswordInput
除下表外,PasswordInput 透传 CodeField 及其底层 TextInput 的属性(keyboardType、autoCapitalize、editable、returnKeyType 等)。
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| variant | 格子的分隔方式 | 'merged' | 'separated' | 'merged' |
| size | 尺寸,决定格子高度、掩码圆点直径与明文字号 | 'sm' | 'md' | 'lg' | 'md' |
| length | 密码长度(格子数量),同时是 onComplete 的触发阈值 | number | 6 |
| mask | 是否以圆点代替字符 | boolean | true |
| gutter | 格子间距(像素),仅 variant="separated" 时生效 | number | 12 |
| value | 当前输入值(受控) | string | - |
| defaultValue | 输入值的非受控默认值 | string | '' |
| onChangeText | 值变化回调 | (value: string) => void | - |
| onComplete | 输入长度达到 length 时触发 | (value: string) => void | - |
| info | 输入框下方的提示文字,有 errorInfo 时不显示 | string | - |
| errorInfo | 输入框下方的错误文字,非空时同时把外框与格子边框置为错误色 | string | - |
| component | 替换底层输入组件,在 Sheet 内使用时传 BottomSheetTextInput | ComponentType<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 名称。
SlotClassNames
classNames 的取值形态:把 slot 名映射到类名,每个 slot 都可选。本页用到的 slot 见 PasswordInputSlots。
包内还导出了 passwordInputVariants 与 PasswordInputVariantProps,后者含 divider / status 两个由组件按格子下标与聚焦态内部计算的变体,不在对外属性中开放。