Button
触发即时操作的按钮组件
按钮(Button)用于触发即时操作,是移动端界面中最常用的交互元素。组件基于 React Native 的 Pressable 封装,按下时整体降低不透明度(active:opacity-80),并按尺寸补偿触摸热区。
import { Button } from '@skyroc/native-ui';基础用法
不传任何变体属性时为 solid + primary + md + rounded,把文字直接作为 children 传入即可。
import { Button } from '@skyroc/native-ui';
import { View } from 'react-native';
const ButtonBasic = () => {
return (
<View className="items-start bg-background p-4">
<Button>默认按钮</Button>
</View>
);
};
export { ButtonBasic };何时使用
- 需要一个即时操作的触发器时,使用按钮。
- 同一屏内主操作(
solid+primary)建议只出现一次,其余降级为tonal/outline/ghost。 - 表单提交、底部行动区推荐配合
block通栏,符合移动端的拇指操作习惯。
变体
通过 variant 控制视觉强度,从高到低依次为:
| 变体 | 视觉强度 | 表现 | 适用场景 |
|---|---|---|---|
solid | 最强 | 主题色填充背景,前景色文字 | 主操作(默认) |
tonal | 中 | 主题色 15% 淡背景,主题色文字 | 次级操作 |
outline | 中低 | 主题色描边,无背景 | 并列的可选操作 |
ghost | 最低 | 无背景无边框,仅文字 | 弱化操作、工具栏 |
import { Button } from '@skyroc/native-ui';
import { View } from 'react-native';
const ButtonVariant = () => {
return (
<View className="flex-row flex-wrap gap-3 bg-background p-4">
<Button
className="min-w-32 flex-1"
variant="solid"
>
solid
</Button>
<Button
className="min-w-32 flex-1"
variant="tonal"
>
tonal
</Button>
<Button
className="min-w-32 flex-1"
variant="outline"
>
outline
</Button>
<Button
className="min-w-32 flex-1"
variant="ghost"
>
ghost
</Button>
</View>
);
};
export { ButtonVariant };颜色
color 提供 7 种语义色:
| 颜色 | 语义 | 适用场景 |
|---|---|---|
primary | 主操作 | 页面中最重要的行为 |
destructive | 危险操作 | 删除、注销等不可逆操作 |
secondary | 次级操作 | 辅助性、低优先级操作 |
success | 成功确认 | 操作成功、确认完成 |
warning | 警告提示 | 需要注意的操作 |
info | 信息提示 | 信息性操作 |
muted | 中性 | 不带语义倾向的中性操作 |
import { Button } from '@skyroc/native-ui';
import { View } from 'react-native';
const COLORS = ['primary', 'destructive', 'secondary', 'success', 'warning', 'info', 'muted'] as const;
const ButtonColor = () => {
return (
<View className="flex-row flex-wrap gap-3 bg-background p-4">
{COLORS.map(color => (
<Button
className="min-w-32 flex-1"
color={color}
key={color}
>
{color}
</Button>
))}
</View>
);
};
export { ButtonColor };muted 与其他颜色不同:它在所有变体下的文字色统一为 text-muted-foreground,不随 variant 切换。
尺寸
size 提供 3 个常规尺寸和 1 个图标尺寸:
| 尺寸 | 高度 | 字号 | 图标间距 | 触摸热区补偿 | 适用场景 |
|---|---|---|---|---|---|
sm | 32 | 14 | 6 | 6 | 紧凑布局、列表内操作 |
md | 40 | 16 | 8 | 2 | 常规场景(默认) |
lg | 56 | 17 | 10 | 0 | 表单提交、主要 CTA |
icon | 40(宽 40) | — | — | 2 | 纯图标按钮 |
import { Button, Text } from '@skyroc/native-ui';
import { View } from 'react-native';
const ButtonSize = () => {
return (
<View className="flex-row flex-wrap items-center gap-3 bg-background p-4">
<Button size="sm">sm</Button>
<Button size="md">md</Button>
<Button size="lg">lg</Button>
<Button size="icon">
<Text className="text-xl">+</Text>
</Button>
</View>
);
};
export { ButtonSize };热区补偿通过 hitSlop 实现,让每个尺寸的实际可点区域都不低于 44pt —— 这是 iOS HIG 与 Material 的共同底线,不需要你在业务侧再包一层。
形状
shape 控制圆角形态。rounded 的圆角随尺寸变化(sm 稍小),circle 则在任意尺寸下都收成正圆。
| 形状 | 圆角 | 说明 |
|---|---|---|
rounded | sm 较小,其余为大圆角 | 标准圆角(默认) |
pill | 全圆角 | 药丸形,适用于标签式操作 |
circle | 全圆角 + 正方形比例、无横向内边距 | 圆形图标按钮 |
import { Button, Text } from '@skyroc/native-ui';
import { View } from 'react-native';
const ButtonShape = () => {
return (
<View className="flex-row flex-wrap items-center gap-3 bg-background p-4">
<Button shape="rounded">rounded</Button>
<Button shape="pill">pill</Button>
<Button
shape="circle"
size="icon"
>
<Text className="text-xl">+</Text>
</Button>
</View>
);
};
export { ButtonShape };通栏
设置 block 让按钮占满父容器宽度,常用于表单底部和弹层的确认操作。
import { Button } from '@skyroc/native-ui';
import { View } from 'react-native';
const ButtonBlock = () => {
return (
<View className="gap-3 bg-background p-4">
<Button block>通栏按钮</Button>
<Button
block
variant="outline"
>
通栏描边按钮
</Button>
</View>
);
};
export { ButtonBlock };插槽
leading 和 trailing 分别在文字前后放置图标或其他内容,间距由 size 决定(见上表「图标间距」)。
import { Button, Text } from '@skyroc/native-ui';
import { View } from 'react-native';
const ButtonSlot = () => {
return (
<View className="gap-3 bg-background p-4">
<Button leading={<Text>↓</Text>}>下载文件</Button>
<Button
variant="outline"
trailing={<Text>→</Text>}
>
下一步
</Button>
{/* leading / trailing 同时存在,文字被夹在中间 */}
<Button
variant="tonal"
leading={<Text>♡</Text>}
trailing={<Text>→</Text>}
>
收藏并继续
</Button>
</View>
);
};
export { ButtonSlot };插槽内容不会自动继承按钮的文字颜色:TextClassContext 只作用于 @skyroc/native-ui 的 Text,@expo/vector-icons 之类的图标组件需要自己传 color。
自定义内容
children 为 string / number 时会自动包一层 Text,样式来自 TextClassContext;传入自定义节点时不做包裹,节点内如果是 @skyroc/native-ui 的 Text,同样继承按钮的文字样式。
import { Button, Text } from '@skyroc/native-ui';
import { View } from 'react-native';
const ButtonCustomContent = () => {
return (
<View className="flex-row flex-wrap items-center gap-3 bg-background p-4">
<Button variant="outline">{2026}</Button>
<Button variant="tonal">
<View className="flex-row items-center gap-2">
<Text>★</Text>
<Text>自定义节点</Text>
</View>
</Button>
</View>
);
};
export { ButtonCustomContent };样式覆盖
className 追加到根容器(Pressable)上,classNames 按 slot 细粒度覆盖,两者都会与内置变体合并,冲突时以你传入的类名为准。
| slot | 作用位置 |
|---|---|
root | 根容器 Pressable |
text | 文字类名,通过 TextClassContext 下发给子 Text |
indicator | loading 指示器的 colorClassName,只接受 accent-* 颜色类 |
import { Button } from '@skyroc/native-ui';
import { View } from 'react-native';
const ButtonStyles = () => {
return (
<View className="gap-3 bg-background p-4">
<Button
className="border-2 border-dashed"
variant="outline"
>
className 容器样式
</Button>
<Button
classNames={{ root: 'bg-info/15', text: 'font-bold text-info' }}
variant="tonal"
>
classNames slot 样式
</Button>
</View>
);
};
export { ButtonStyles };className 与 classNames.root 同时存在时,className 排在更后面参与合并,优先级更高。
交互
Button 透传 Pressable 的全部事件,onPress / onLongPress 直接绑定即可,无需再包一层 Pressable。
import { Button, Text } from '@skyroc/native-ui';
import { useState } from 'react';
import { View } from 'react-native';
const ButtonInteraction = () => {
const [message, setMessage] = useState('等待操作');
function handlePress() {
setMessage('触发 onPress');
}
function handleLongPress() {
setMessage('触发 onLongPress');
}
return (
<View className="gap-3 bg-background p-4">
<Button
onLongPress={handleLongPress}
onPress={handlePress}
>
点击或长按
</Button>
<Text className="text-center text-sm text-muted-foreground">当前结果:{message}</Text>
</View>
);
};
export { ButtonInteraction };加载
loading 为 true 时按钮自动禁用,并在 leading 位置渲染 ActivityIndicator——占用同一个位置而不是额外插入节点,避免按钮宽度跳动。指示器颜色跟随 variant / color,与文字色保持一致。
import { Button } from '@skyroc/native-ui';
import { useState } from 'react';
import { View } from 'react-native';
const ButtonLoading = () => {
const [loading, setLoading] = useState(false);
function handlePress() {
setLoading(true);
setTimeout(() => setLoading(false), 2000);
}
return (
<View className="gap-3 bg-background p-4">
<Button
loading={loading}
variant="tonal"
onPress={handlePress}
>
{loading ? '提交中…' : '点击提交'}
</Button>
{/* 指示器颜色跟随 variant / color,与文字色保持一致 */}
<Button
loading
variant="tonal"
>
tonal 加载
</Button>
<Button
loading
variant="outline"
>
outline 加载
</Button>
</View>
);
};
export { ButtonLoading };注意 loading 会顶掉你传入的 leading 内容(web 端的实现是保留自定义 leading,native 这里不同)。
禁用
disabled 或 loading 任一成立时,按钮不可点击并降低到 50% 不透明度。
import { Button } from '@skyroc/native-ui';
import { View } from 'react-native';
const ButtonDisabled = () => {
return (
<View className="flex-row flex-wrap gap-3 bg-background p-4">
<Button
className="min-w-32 flex-1"
disabled
>
solid
</Button>
<Button
className="min-w-32 flex-1"
disabled
variant="tonal"
>
tonal
</Button>
<Button
className="min-w-32 flex-1"
disabled
variant="outline"
>
outline
</Button>
<Button
className="min-w-32 flex-1"
disabled
variant="ghost"
>
ghost
</Button>
</View>
);
};
export { ButtonDisabled };无障碍
按钮默认设置了 role="button",并把 loading / disabled 映射到 accessibilityState 的 busy 与 disabled,读屏器可以正确播报状态。
API
Button
除下表外,Button 透传 Pressable 的全部属性(onPress、onLongPress、testID 等)。
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| variant | 视觉样式 | 'solid' | 'tonal' | 'outline' | 'ghost' | 'solid' |
| color | 主题颜色 | 'primary' | 'destructive' | 'secondary' | 'success' | 'warning' | 'info' | 'muted' | 'primary' |
| size | 尺寸,同时决定高度、字号、内边距与触摸热区补偿 | 'sm' | 'md' | 'lg' | 'icon' | 'md' |
| shape | 圆角形态 | 'rounded' | 'pill' | 'circle' | 'rounded' |
| block | 占满父容器宽度 | boolean | false |
| loading | 加载状态,自动禁用并在 leading 位置显示指示器 | boolean | false |
| disabled | 禁用状态 | boolean | false |
| leading | 前置内容,显示在文字之前 | ReactNode | - |
| trailing | 后置内容,显示在文字之后 | ReactNode | - |
| children | 按钮内容,string / number 会被自动包裹为 Text | ReactNode | - |
| className | 容器类名,合并到变体样式之后 | string | - |
| classNames | 各 slot 的类名覆盖,text 通过 TextClassContext 下发给子 Text,indicator 作用于 loading 指示器的 colorClassName,只接受 accent-* 颜色类 | SlotClassNames<'indicator' | 'root' | 'text'> | - |
| ref | 底层 Pressable 的 ref,用于 measure / 滚动定位等命令式操作 | Ref<View> | - |
类型
import type {
ButtonColor,
ButtonProps,
ButtonShape,
ButtonSize,
ButtonSlots,
ButtonVariant
} from '@skyroc/native-ui';ButtonVariant / ButtonColor / ButtonSize / ButtonShape 均由 buttonVariants 推导,与上面的 Props 表一一对应。
ButtonVariant
按钮视觉样式,视觉强度从强到弱。
ButtonColor
按钮语义颜色,muted 的文字色不随 variant 变化。
ButtonSize
按钮尺寸,同时决定高度、字号、内边距与 hitSlop 热区补偿。
ButtonShape
按钮圆角形态,circle 在任意尺寸下都收成正圆。
ButtonSlots
可通过 classNames 覆盖的 slot 名称。
SlotClassNames
classNames 的取值形态:把 slot 名映射到类名,每个 slot 都可选。本页用到的 slot 见 ButtonSlots。