Skyroc Native UI

Button

触发即时操作的按钮组件

按钮(Button)用于触发即时操作,是移动端界面中最常用的交互元素。组件基于 React Native 的 Pressable 封装,按下时整体降低不透明度(active:opacity-80),并按尺寸补偿触摸热区。

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

基础用法

不传任何变体属性时为 solid + primary + md + rounded,把文字直接作为 children 传入即可。

ButtonBasic.tsx
Loading…
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最低无背景无边框,仅文字弱化操作、工具栏
ButtonVariant.tsx
Loading…
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中性不带语义倾向的中性操作
ButtonColor.tsx
Loading…
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 个图标尺寸:

尺寸高度字号图标间距触摸热区补偿适用场景
sm321466紧凑布局、列表内操作
md401682常规场景(默认)
lg5617100表单提交、主要 CTA
icon40(宽 40)2纯图标按钮
ButtonSize.tsx
Loading…
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 则在任意尺寸下都收成正圆。

形状圆角说明
roundedsm 较小,其余为大圆角标准圆角(默认)
pill全圆角药丸形,适用于标签式操作
circle全圆角 + 正方形比例、无横向内边距圆形图标按钮
ButtonShape.tsx
Loading…
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 让按钮占满父容器宽度,常用于表单底部和弹层的确认操作。

ButtonBlock.tsx
Loading…
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 };

插槽

leadingtrailing 分别在文字前后放置图标或其他内容,间距由 size 决定(见上表「图标间距」)。

ButtonSlot.tsx
Loading…
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-uiText@expo/vector-icons 之类的图标组件需要自己传 color

自定义内容

childrenstring / number 时会自动包一层 Text,样式来自 TextClassContext;传入自定义节点时不做包裹,节点内如果是 @skyroc/native-uiText,同样继承按钮的文字样式。

ButtonCustomContent.tsx
Loading…
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
indicatorloading 指示器的 colorClassName,只接受 accent-* 颜色类
ButtonStyles.tsx
Loading…
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 };

classNameclassNames.root 同时存在时,className 排在更后面参与合并,优先级更高。

交互

Button 透传 Pressable 的全部事件,onPress / onLongPress 直接绑定即可,无需再包一层 Pressable

ButtonInteraction.tsx
Loading…
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 };

加载

loadingtrue 时按钮自动禁用,并在 leading 位置渲染 ActivityIndicator——占用同一个位置而不是额外插入节点,避免按钮宽度跳动。指示器颜色跟随 variant / color,与文字色保持一致。

ButtonLoading.tsx
Loading…
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 这里不同)。

禁用

disabledloading 任一成立时,按钮不可点击并降低到 50% 不透明度。

ButtonDisabled.tsx
Loading…
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 映射到 accessibilityStatebusydisabled,读屏器可以正确播报状态。

API

Button

除下表外,Button 透传 Pressable 的全部属性(onPressonLongPresstestID 等)。

属性说明类型默认值
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占满父容器宽度booleanfalse
loading加载状态,自动禁用并在 leading 位置显示指示器booleanfalse
disabled禁用状态booleanfalse
leading前置内容,显示在文字之前ReactNode-
trailing后置内容,显示在文字之后ReactNode-
children按钮内容,string / number 会被自动包裹为 TextReactNode-
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

按钮视觉样式,视觉强度从强到弱。

'solid' | 'tonal' | 'outline' | 'ghost'

ButtonColor

按钮语义颜色,muted 的文字色不随 variant 变化。

'primary' | 'destructive' | 'secondary' | 'success' | 'warning' | 'info' | 'muted'

ButtonSize

按钮尺寸,同时决定高度、字号、内边距与 hitSlop 热区补偿。

'sm' | 'md' | 'lg' | 'icon'

ButtonShape

按钮圆角形态,circle 在任意尺寸下都收成正圆。

'rounded' | 'pill' | 'circle'

ButtonSlots

可通过 classNames 覆盖的 slot 名称。

'indicator' | 'root' | 'text'

SlotClassNames

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

Partial<Record<Slots, string>>