跳到主要内容

Composer 消息输入

受控草稿、发送和停止,适用于聊天页与独立输入区。

交互示例​

交互示例加载中…

用法​

import { useState } from 'react';
import { Composer } from '@unif/react-native-chat';

function InputDemo() {
const [text, setText] = useState('');
return (
<Composer
value={text}
onChangeText={setText}
primaryAction={{ kind: 'send', onPress: (value) => console.log(value) }}
/>
);
}

行为与接入​

value 与 onChangeText 共同维护受控草稿。空值未聚焦时,文字与操作按钮在同一行;聚焦或有文字后,操作区移到文字下方。两种布局始终使用同一个 Textarea,切换不会重建输入或清空文字。更多入口容器的原生层级在展开和收起之间保持稳定,以保留输入焦点。

发送使用图标按钮并交付原文,是否清空由调用方决定。仅有空白时默认隐藏发送,附件单独发送可设置 allowEmpty。primaryAction.kind 使用 send、stop 或 busy;label 是对应图标的可访问名称,busy 只展示等待状态。

扩展操作通过 actions 提供,点击加号打开输入区上方的竖向菜单。选择操作、再次点击加号、触碰输入或聚焦时收起菜单。语音的采集与权限处理由 voice 回调交给应用;聆听区域只呈现外部状态与文字,不生成音量、追加草稿或自动发送。

输入表面默认 card,由 Composer 绘制统一圆角、描边和阴影;plain 交给外层提供表面。内部使用 Design Textarea 的 plain 表面,保留文字内间距,不叠加第二层底色或焦点描边。

minInputHeight 与 maxInputHeight 是 Textarea 整体高度,包含其文字内间距。最小高度默认 44;默认上限为四行文字预算加 plain 上下内间距:round(type.body × 1.4) × 4 × fontScale + 2 × space[4],且不小于最小高度。应用字号只缩放文字预算一次;显式高度按提供的布局单位使用,超过上限在输入内部滚动。

页面负责键盘避让与安全区。header 和 footer 是输入前后的内容位置;独立于卡片的常驻说明由外层组合。onHeightChange 交付整个 Composer 的实测高度,ref 仅提供 focus() 与 blur()。

API​

查看完整 TypeScript 类型

以下内容由包根公开类型生成,与当前源码保持一致。

import type { ReactNode } from 'react';

import type { StyleProp, ViewStyle } from 'react-native';

import type { IconName, TextFieldHandle } from '@unif/react-native-design';

import type { ChatAction } from '@unif/react-native-chat';

export interface ComposerSendAction {
kind: 'send';
onPress(value: string): void;
allowEmpty?: boolean;
disabled?: boolean;
/** 图标按钮的可访问名称,默认“发送”。 */
label?: string;
}

export interface ComposerStopAction {
kind: 'stop';
onPress(): void;
disabled?: boolean;
label?: string;
}

export interface ComposerBusyAction {
kind: 'busy';
label?: string;
}

export type ComposerPrimaryAction =
ComposerSendAction | ComposerStopAction | ComposerBusyAction;

export type ComposerVoiceStatus =
'idle' | 'starting' | 'listening' | 'finishing';

export interface ComposerVoiceControl {
status: ComposerVoiceStatus;
transcript?: string;
disabled?: boolean;
onStart(): void;
onStop(): void;
onCancel(): void;
}

export interface ComposerProps {
value: string;
onChangeText(value: string): void;
primaryAction: ComposerPrimaryAction;
editable?: boolean;
disabled?: boolean;
placeholder?: string;
inputAccessibilityLabel?: string;
actions?: readonly ChatAction[];
voice?: ComposerVoiceControl;
header?: ReactNode;
footer?: ReactNode;
/** card 提供统一表面;plain 由外层提供表面,内部 Textarea 均不单独绘制表面。 */
surface?: 'card' | 'plain';
/** Textarea 整体最小高度(含内间距),默认 44,不低于最小触达高度。 */
minInputHeight?: number;
/** Textarea 整体最大高度;默认四行文字预算加 plain 内间距,随应用字号变化。 */
maxInputHeight?: number;
onFocusChange?(focused: boolean): void;
onHeightChange?(height: number): void;
style?: StyleProp<ViewStyle>;
testID?: string;
}

export type ComposerHandle = TextFieldHandle;