Skyroc Native UI

Toast

轻提示,命令式调用为主

轻提示(Toast)在屏幕中央或上下方浮出一条短反馈。主用法是命令式函数(showToast 等),面板通过 Portal 挂载(zIndex 1000),因此在任意位置调用都能盖在页面之上。

import { showToast, showSuccessToast, showFailToast, showLoadingToast, closeToast } from '@skyroc/native-ui';

命令式调用依赖 PortalHost,请确保 App 内已经挂了它。

类型

type 决定内置图标:text(无图标)、success(勾)、fail(叉)、loading(转圈)。四个快捷函数分别对应这些类型,参数可以直接传字符串(等价于 { message })。

ToastTypes.tsx
Loading…
import { Button, closeToast, showFailToast, showLoadingToast, showSuccessToast, showToast } from '@skyroc/native-ui';
import { View } from 'react-native';

const ToastTypes = () => {
  return (
    <View className="flex-row flex-wrap items-center gap-3 bg-background px-6 py-4">
      <Button onPress={() => showToast('这是一条文字提示')}>文字</Button>
      <Button onPress={() => showSuccessToast('操作成功')}>成功</Button>
      <Button onPress={() => showFailToast('操作失败')}>失败</Button>
      <Button onPress={() => showLoadingToast('加载中...')}>加载(常驻)</Button>
      <Button
        variant="outline"
        onPress={closeToast}
      >
        关闭全部
      </Button>
    </View>
  );
};

export { ToastTypes };

loading 默认常驻duration 为 0),需要手动 close()update() 成其他类型。这个常驻是从 type 推导出来的,而不是写死 duration: 0 —— 所以 loading 被 update 成 success 后无需任何特判就会自然恢复成 2000ms 自动关闭。

何时使用

  • 操作后的轻量反馈:保存成功、网络失败、加载中。
  • 需要用户确认或有操作按钮时用 Dialog;系统级、贴边显示的横幅用 Notify

位置

position 支持 top / middle(默认)/ bottom。同一位置的多条 Toast 归入同一组纵向排列。

ToastPositions.tsx
Loading…
import type { ToastPosition } from '@skyroc/native-ui';
import { Button, showToast } from '@skyroc/native-ui';
import { View } from 'react-native';

const POSITIONS: ToastPosition[] = ['top', 'middle', 'bottom'];

const ToastPositions = () => {
  return (
    <View className="flex-row flex-wrap items-center gap-3 bg-background px-6 py-4">
      {POSITIONS.map(position => (
        <Button
          key={position}
          variant="tonal"
          onPress={() => showToast({ message: position, position })}
        >
          {position}
        </Button>
      ))}
    </View>
  );
};

export { ToastPositions };

交互

  • duration:自动关闭延时(毫秒),0 表示常驻;不传时 loading 为 0、其余类型为 2000。
  • closeOnClick:点击 Toast 自身关闭。
  • forbidClick:在 Toast 下方铺一层透明遮罩拦截触摸,屏蔽背景交互。
ToastInteraction.tsx
Loading…
import { Button, showToast } from '@skyroc/native-ui';
import { View } from 'react-native';

const ToastInteraction = () => {
  function handleManualClose() {
    const instance = showToast({ duration: 0, message: '常驻,2 秒后由代码关闭' });

    setTimeout(() => instance.close(), 2000);
  }

  return (
    <View className="flex-row flex-wrap items-center gap-3 bg-background px-6 py-4">
      <Button
        variant="tonal"
        onPress={() => showToast({ closeOnClick: true, duration: 0, message: '点我关闭' })}
      >
        点击关闭
      </Button>
      <Button
        variant="tonal"
        onPress={() => showToast({ forbidClick: true, message: '背景已被遮罩拦截', position: 'top' })}
      >
        禁止背景点击
      </Button>
      <Button
        variant="tonal"
        onPress={handleManualClose}
      >
        常驻 + 命令式关闭
      </Button>
    </View>
  );
};

export { ToastInteraction };

遮罩单独成一层铺满全屏,而不是复用定位容器 —— top / bottom 的定位容器只占一条,拿它拦触摸挡不住整屏。

生命周期

showToast 返回一个实例,带 close()update(options)update 原地更新同一实例,不重新创建。onClose 在超时关闭、点击关闭、命令式关闭三条路径下都只触发一次

ToastLifecycle.tsx
Loading…
import { Button, Text, allowMultipleToast, showLoadingToast, showToast } from '@skyroc/native-ui';
import { useState } from 'react';
import { View } from 'react-native';

const ToastLifecycle = () => {
  const [closeCount, setCloseCount] = useState(0);

  function handleCountedToast() {
    showToast({
      message: '关闭时计数 +1',
      onClose: () => setCloseCount(prev => prev + 1)
    });
  }

  function handleLoadingThenSuccess() {
    const instance = showLoadingToast('上传中...');

    setTimeout(() => {
      // loading 常驻是从 type 推导的,改成 success 后自动恢复 2 秒关闭,无需再传 duration
      instance.update({ message: '上传成功', type: 'success' });
    }, 1500);
  }

  function handleMultiple() {
    allowMultipleToast(true);

    showToast({ message: '第一条', position: 'top' });
    showToast({ message: '第二条', position: 'top' });
    showToast({ message: '第三条', position: 'top' });

    allowMultipleToast(false);
  }

  return (
    <View className="bg-background px-6 py-4">
      <View className="mb-2 flex-row flex-wrap items-center gap-3">
        <Button
          variant="tonal"
          onPress={handleLoadingThenSuccess}
        >
          loading → success
        </Button>
        <Button
          variant="tonal"
          onPress={handleCountedToast}
        >
          onClose 计数
        </Button>
        <Button
          variant="tonal"
          onPress={handleMultiple}
        >
          同时显示多条
        </Button>
      </View>
      <Text color="muted">onClose 已触发 {closeCount} 次(每关闭一次只应 +1)</Text>
    </View>
  );
};

export { ToastLifecycle };

默认同一时刻只显示一条(新的会顶掉旧的)。需要并存时调用 allowMultipleToast(true),用完记得关掉。

自定义图标与样式

icon 覆盖内置图标,className / classNames 覆盖样式:

slot作用位置
root提示卡片本体(底色、圆角、内边距)
icon图标容器
message消息文字
ToastCustom.tsx
Loading…
import { Button, Text, showToast } from '@skyroc/native-ui';
import { View } from 'react-native';

const ToastCustom = () => {
  return (
    <View className="flex-row flex-wrap items-center gap-3 bg-background px-6 py-4">
      <Button
        variant="tonal"
        onPress={() =>
          showToast({
            icon: <Text className="text-3xl">🎉</Text>,
            message: '自定义图标'
          })
        }
      >
        Emoji 图标
      </Button>
      <Button
        variant="tonal"
        onPress={() =>
          showToast({
            className: 'rounded-none bg-primary',
            classNames: { message: 'text-base text-primary-foreground' },
            message: 'className 覆盖'
          })
        }
      >
        className 覆盖
      </Button>
    </View>
  );
};

export { ToastCustom };

有图标时卡片是 min-w-28 max-w-40 的方块,无图标时是 max-w-[85%] 的单行条 —— 两者的内边距也不同。

声明式用法

<Toast> 组件就地渲染,由 show 控制显隐、onUpdateShow 通知外部(组件到点后只通知,真正的隐藏仍由调用方改 show 完成)。

ToastDeclarative.tsx
Loading…
import { Button, Toast } from '@skyroc/native-ui';
import { useState } from 'react';
import { View } from 'react-native';

const ToastDeclarative = () => {
  const [declarativeShow, setDeclarativeShow] = useState(false);

  return (
    <View className="bg-background px-6 py-4">
      <View className="flex-row flex-wrap items-center gap-3">
        <Button
          variant="outline"
          onPress={() => setDeclarativeShow(true)}
        >
          显示受控 Toast
        </Button>
      </View>

      <View className="items-center">
        <Toast
          message="受控 Toast,2 秒后自动关闭"
          show={declarativeShow}
          type="success"
          onUpdateShow={setDeclarativeShow}
        />
      </View>
    </View>
  );
};

export { ToastDeclarative };

声明式与命令式共用同一份 duration 默认值规则,不会出现「showLoadingToast 常驻、<Toast type="loading" /> 却 2 秒消失」这类分叉。但 positionforbidClick 在声明式下不可用(类型上已排除):它就地渲染在你写下它的位置,贴屏定位与遮罩由命令式路径的渲染器承担。

全局默认配置

import { setToastDefaultOptions, resetToastDefaultOptions, allowMultipleToast } from '@skyroc/native-ui';

setToastDefaultOptions({ position: 'top' }); // 全局默认
setToastDefaultOptions('loading', { forbidClick: true }); // 按类型默认
resetToastDefaultOptions('loading'); // 重置某个类型
resetToastDefaultOptions(); // 全部重置

合并优先级是「全局默认 < 类型默认 < 调用参数」。

API

命令式函数

function showToast(options: ToastOptions | string): ToastInstance;
function showSuccessToast(options: ToastOptions | string): ToastInstance;
function showFailToast(options: ToastOptions | string): ToastInstance;
function showLoadingToast(options: ToastOptions | string): ToastInstance;
function closeToast(): void;
function allowMultipleToast(value?: boolean): void;
function setToastDefaultOptions(options: ToastOptions): void;
function setToastDefaultOptions(type: ToastType, options: ToastOptions): void;
function resetToastDefaultOptions(type?: ToastType): void;

ToastOptions

属性说明类型默认值
message消息内容,字符串会自动包一层 TextReactNode-
type类型,决定内置图标'text' | 'success' | 'fail' | 'loading''text'
position垂直位置'top' | 'middle' | 'bottom''middle'
duration自动关闭延时(毫秒),0 表示常驻;不传时 loading 为 0、其余为 2000number-
icon自定义图标,覆盖内置图标ReactNode-
closeOnClick是否允许点击 Toast 关闭booleanfalse
forbidClick是否禁止背景点击(铺一层透明遮罩拦截触摸)booleanfalse
onClose关闭时回调,三条关闭路径都只触发一次() => void-
className卡片本体类名,合并在 classNames.root 之后string-
classNames各 slot 的类名覆盖SlotClassNames<ToastSlots>-

Toast

声明式组件,属性为 ToastOptions 去掉 position / forbidClick,另加:

属性说明类型默认值
show是否显示booleanfalse
onUpdateShow显示状态变更回调(到点或点击关闭时触发)(show: boolean) => void-

类型

import type { ToastInstance, ToastOptions, ToastPosition, ToastProps, ToastSlots, ToastType } from '@skyroc/native-ui';

ToastType

提示类型,决定内置图标;loading 默认常驻。

'fail' | 'loading' | 'success' | 'text'

ToastPosition

垂直位置。

'bottom' | 'middle' | 'top'

ToastSlots

可通过 classNames 覆盖的 slot 名称。

'icon' | 'message' | 'root'

SlotClassNames

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

Partial<Record<Slots, string>>

ToastInstance

showToast 系列函数的返回值。

字段类型说明
close*() => void关闭当前 Toast。
update*(options: ToastOptions) => void原地更新当前 Toast 的内容,保持同一实例、不重新创建。

包内还导出了 toastVariantstoastPositionVariantsToastRenderer(由命令式路径自动挂载,一般不需要直接使用)。