React Native for OpenHarmony Alert 警告组件:语义化提示的设计与实现
文章摘要: Alert组件是一种平衡醒目性与干扰性的非阻断式提示控件,用于向用户传递重要但不强制响应的信息。作者基于开源项目实现了一个灵活可配置的Alert组件,支持多种语义类型(success/warning等)、三种视觉变体(filled/outlined/soft)以及标题、图标、关闭按钮等扩展功能。组件通过解构颜色主题实现样式统一,采用emoji作为默认图标简化实现,并精心设计了不同变体的
项目开源地址:https://atomgit.com/nutpi/rn_for_openharmony_element
Alert 是那种"看起来简单,但设计不好就会被忽略"的组件。
用户在使用应用时,总会遇到各种需要被告知的情况:操作成功了、出错了、有风险需要注意、有新消息需要了解。这些信息如果用普通文字展示,很容易被忽略;如果用弹窗展示,又太打扰用户。Alert 就是介于两者之间的选择——它足够醒目,但不会打断用户的操作流程。
我在项目里把提示类组件分成两类:
- [阻断式提示] 必须用户响应才能继续,比如 Modal、Dialog
- [非阻断式提示] 展示信息但不打断操作,比如 Alert、Toast
Alert 属于第二类。它的核心任务是"让用户注意到这条信息",但不强制用户做出响应。
这篇文章会基于项目中真实存在的代码,把 Alert 的实现拆开讲清楚。全文代码片段都来自项目中的真实文件,路径是 src/components/ui/Alert.tsx。
Alert 在项目里的位置
这套 UI 组件库的文件组织比较统一。Alert 相关的文件主要在这几处:
src/components/ui/Alert.tsxsrc/screens/demos/AlertDemo.tsx
建议先看 Alert.tsx,理解组件的结构和样式计算逻辑;再看 AlertDemo.tsx,了解组件在仓库里被期望怎么用。
依赖引入
import React from 'react';
import { View, Text, TouchableOpacity, StyleSheet, ViewStyle } from 'react-native';
import { UITheme, ColorType } from './theme';
这段引入包含几个关键模块:
View和Text是基础布局和文字组件,Alert 的主体结构就靠它们TouchableOpacity用于可点击的元素,这里是关闭按钮和操作按钮StyleSheet用于定义静态样式,性能比内联样式更好UITheme和ColorType来自项目的主题配置,保证组件风格统一
为什么用 TouchableOpacity 而不是 Pressable?TouchableOpacity 自带点击时的透明度变化反馈,对于这种小按钮来说足够了,不需要 Pressable 那么多的自定义能力。
AlertProps:接口设计
interface AlertProps {
type?: ColorType;
title?: string;
message: string;
icon?: string;
closable?: boolean;
onClose?: () => void;
action?: { label: string; onPress: () => void };
variant?: 'filled' | 'outlined' | 'soft';
style?: ViewStyle;
}
接口设计覆盖了 Alert 的所有使用场景,我来逐个解释:
-
type:语义类型,决定 Alert 的颜色。支持 primary、secondary、success、warning、danger、info 六种。不同类型传达不同的信息级别,比如 success 表示操作成功,danger 表示出错了
-
title:可选的标题。简单提示可以只有 message,重要提示可以加上 title 让信息层次更清晰
-
message:必填的提示内容。这是 Alert 最核心的信息,必须传入
-
icon:自定义图标。如果不传,会根据 type 自动选择默认图标
-
closable:是否显示关闭按钮。临时性的提示通常需要让用户能关闭它
-
onClose:关闭按钮的回调函数。配合 closable 使用,点击关闭按钮时触发
-
action:操作按钮配置。有些 Alert 需要用户做出响应,比如"立即更新"、“查看详情”
-
variant:样式变体,三种可选。filled 是填充背景,outlined 是描边,soft 是柔和的浅色背景
-
style:允许外部传入额外样式,用于微调位置或覆盖默认样式
组件函数签名与默认值
export const Alert: React.FC<AlertProps> = ({
type = 'info',
title,
message,
icon,
closable = false,
onClose,
action,
variant = 'soft',
style,
}) => {
这里用解构赋值的方式设置了默认值:
type = 'info':默认是信息类型。info 是最中性的类型,适合一般性的提示closable = false:默认不可关闭。大多数 Alert 是持久展示的,需要关闭功能时再显式开启variant = 'soft':默认使用柔和样式。soft 样式最不抢眼,适合大多数场景
为什么默认用 soft 而不是 filled?因为 Alert 通常是页面内容的一部分,不应该太抢眼。soft 样式用浅色背景,既能让用户注意到,又不会喧宾夺主。filled 样式更适合需要强调的场景。
颜色值获取
const colorValue = UITheme.colors[type];
这行代码把语义化的类型名转换成实际的颜色值。比如 type = 'success' 会得到 '#10B981'(绿色)。
这种设计的好处是:
- 使用时只需要关心语义(success、danger),不需要记颜色值
- 换主题时只需要改 UITheme,所有组件都会跟着变
- 保证了整个应用的颜色一致性
默认图标映射
const defaultIcons: Record<ColorType, string> = {
primary: 'ℹ️',
secondary: '📌',
success: '✅',
warning: '⚠️',
danger: '❌',
info: 'ℹ️',
};
这个对象定义了每种类型对应的默认图标:
- primary 和 info:用 ℹ️ 信息图标,表示一般性提示
- secondary:用 📌 图钉图标,表示次要信息或备注
- success:用 ✅ 对勾图标,表示操作成功
- warning:用 ⚠️ 警告图标,表示需要注意
- danger:用 ❌ 叉号图标,表示错误或危险
用 emoji 作为图标有几个好处:不需要引入图标库、跨平台显示一致、语义直观。如果你的项目用了图标库,也可以把这里改成图标组件。
样式计算函数
const getStyles = (): { bg: string; border: string; text: string } => {
switch (variant) {
case 'filled':
return { bg: colorValue, border: colorValue, text: UITheme.colors.white };
case 'outlined':
return { bg: 'transparent', border: colorValue, text: colorValue };
case 'soft':
return { bg: `${colorValue}15`, border: `${colorValue}30`, text: colorValue };
default:
return { bg: `${colorValue}15`, border: `${colorValue}30`, text: colorValue };
}
};
这个函数根据 variant 计算背景色、边框色和文字色。三种变体的视觉效果差异很大:
filled(填充):
- 背景色是主题色本身(colorValue)
- 边框色也是主题色
- 文字用白色,和深色背景形成对比
- 视觉冲击力最强,适合需要强调的场景
outlined(描边):
- 背景透明
- 边框是主题色
- 文字也是主题色
- 视觉上比较轻,适合不想太抢眼但又要有存在感的场景
soft(柔和):
- 背景是主题色加 15% 透明度(
${colorValue}15) - 边框是主题色加 30% 透明度
- 文字是主题色
- 最柔和的样式,适合大多数场景
这里用了一个小技巧:${colorValue}15 是在颜色值后面加两位十六进制数,表示透明度。15 大约是 8% 的透明度,30 大约是 19% 的透明度。这样可以基于主题色生成浅色版本,不需要额外定义。
const { bg, border, text } = getStyles();
调用函数并解构出三个颜色值,后面渲染时会用到。
容器渲染
return (
<View
style={[
styles.container,
{ backgroundColor: bg, borderColor: border },
style,
]}
>
Alert 的最外层容器,样式合并了三部分:
styles.container:静态的基础样式,包括布局方向、内边距、圆角、边框宽度{ backgroundColor: bg, borderColor: border }:动态计算的颜色style:外部传入的自定义样式
样式数组的顺序很重要:后面的会覆盖前面的。所以外部传入的 style 可以覆盖默认样式。
图标渲染
<Text style={styles.icon}>{icon || defaultIcons[type]}</Text>
图标的渲染逻辑很简单:
- 如果外部传了
icon,就用外部的 - 如果没传,就从
defaultIcons里根据 type 取默认图标
用 || 运算符实现这个逻辑。如果 icon 是 undefined 或空字符串,就会使用后面的默认值。
图标放在最左边,和内容区域有一定间距(marginRight),让整个 Alert 的视觉结构更清晰。
内容区域渲染
<View style={styles.content}>
{title && <Text style={[styles.title, { color: text }]}>{title}</Text>}
<Text style={[styles.message, { color: variant === 'filled' ? text : UITheme.colors.gray[700] }]}>
{message}
</Text>
内容区域包含标题和消息两部分:
标题渲染:
- 用
{title && ...}条件渲染,只有传了 title 才显示 - 文字颜色用前面计算的 text 值
- 样式里设置了 fontWeight: ‘600’ 让标题更醒目
消息渲染:
- message 是必填的,所以不需要条件判断
- 文字颜色有个特殊处理:filled 变体用 text 颜色(白色),其他变体用灰色(gray[700])
为什么 message 的颜色要特殊处理?因为 filled 变体的背景是深色的,如果 message 也用灰色会看不清。而 soft 和 outlined 变体的背景是浅色或透明的,用灰色的 message 可以和彩色的 title 形成层次感。
操作按钮渲染
{action && (
<TouchableOpacity onPress={action.onPress} style={styles.action}>
<Text style={[styles.actionText, { color: text }]}>{action.label}</Text>
</TouchableOpacity>
)}
</View>
操作按钮的渲染逻辑:
- 用
{action && ...}条件渲染,只有传了 action 才显示 action.onPress是点击回调,action.label是按钮文字- 按钮文字用主题色,和普通文字区分开,让用户知道这是可点击的
marginTop: UITheme.spacing.sm让按钮和上面的文字有间距
操作按钮的典型使用场景:
- “立即更新”——提示有新版本
- “查看详情”——提示有新消息
- “重试”——提示操作失败
关闭按钮渲染
{closable && (
<TouchableOpacity onPress={onClose} style={styles.closeBtn}>
<Text style={[styles.closeIcon, { color: text }]}>×</Text>
</TouchableOpacity>
)}
</View>
);
};
关闭按钮的渲染逻辑:
- 用
{closable && ...}条件渲染,只有 closable 为 true 才显示 - 点击时调用
onClose回调,由外部决定如何处理(通常是隐藏这个 Alert) - 用 × 符号作为关闭图标,简单直观
- 放在最右边,和内容区域有间距
关闭按钮的颜色也用 text 值,保证和整体风格一致。fontSize: 20 让关闭按钮足够大,方便点击。
样式定义
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
padding: UITheme.spacing.md,
borderRadius: UITheme.borderRadius.md,
borderWidth: 1,
},
容器的基础样式:
flexDirection: 'row':让图标、内容、关闭按钮横向排列padding: UITheme.spacing.md:内边距用主题的中等间距(12px)borderRadius: UITheme.borderRadius.md:圆角用主题的中等圆角(8px)borderWidth: 1:边框宽度 1px,颜色由动态样式控制
icon: { fontSize: 18, marginRight: UITheme.spacing.sm },
图标样式:
fontSize: 18:图标大小 18px,比正文稍大一点marginRight: UITheme.spacing.sm:右边距 8px,和内容区域保持间距
content: { flex: 1 },
内容区域样式:
flex: 1:占据剩余空间。这样图标在左边固定宽度,关闭按钮在右边固定宽度,中间的内容区域自适应
title: { fontSize: UITheme.fontSize.md, fontWeight: '600', marginBottom: 2 },
message: { fontSize: UITheme.fontSize.sm },
标题和消息的样式:
- 标题用 md 字号(14px),加粗(fontWeight: ‘600’),下边距 2px
- 消息用 sm 字号(12px),比标题小一号,形成层次感
action: { marginTop: UITheme.spacing.sm },
actionText: { fontSize: UITheme.fontSize.sm, fontWeight: '600' },
操作按钮样式:
marginTop: UITheme.spacing.sm:和上面的文字保持 8px 间距- 文字加粗,让用户知道这是可点击的
closeBtn: { marginLeft: UITheme.spacing.sm },
closeIcon: { fontSize: 20, fontWeight: '600' },
});
关闭按钮样式:
marginLeft: UITheme.spacing.sm:和内容区域保持 8px 间距fontSize: 20:关闭图标稍大一点,方便点击fontWeight: '600':加粗让 × 符号更清晰
Demo 页面解析
Demo 页面展示了 Alert 的各种用法,路径是 src/screens/demos/AlertDemo.tsx:
import React from 'react';
import { View, StyleSheet } from 'react-native';
import { ComponentShowcase, ShowcaseSection } from '../ComponentShowcase';
import { Alert } from '../../components/ui/Alert';
import { UITheme } from '../../components/ui/theme';
引入了展示框架组件和 Alert 组件。
export const AlertDemo: React.FC<{ onBack: () => void }> = ({ onBack }) => {
return (
<ComponentShowcase title="Alert" icon="⚠️" description="警告提示用于展示重要的提示信息" onBack={onBack}>
Demo 页面的外层容器,提供标题、图标、描述和返回按钮。
<ShowcaseSection title="类型" description="四种语义类型">
<Alert type="info" message="这是一条信息提示" />
<View style={{ height: 12 }} />
<Alert type="success" message="操作成功完成" />
<View style={{ height: 12 }} />
<Alert type="warning" message="请注意,这是一条警告" />
<View style={{ height: 12 }} />
<Alert type="danger" message="发生错误,请重试" />
</ShowcaseSection>
第一个展示区块展示四种语义类型。每种类型有不同的颜色和默认图标:
- info:蓝色,ℹ️ 图标,用于一般性提示
- success:绿色,✅ 图标,用于成功提示
- warning:橙色,⚠️ 图标,用于警告提示
- danger:红色,❌ 图标,用于错误提示
<View style={{ height: 12 }} /> 是间距占位,让多个 Alert 之间有空隙。
<ShowcaseSection title="带标题" description="显示标题和描述">
<Alert type="info" title="提示" message="这是一条带标题的信息提示,可以包含更详细的说明文字。" />
<View style={{ height: 12 }} />
<Alert type="success" title="成功" message="您的订单已成功提交,我们会尽快处理。" />
</ShowcaseSection>
第二个展示区块展示带标题的 Alert。标题让信息层次更清晰:
- 标题是简短的关键词(“提示”、“成功”)
- message 是详细的说明文字
这种结构适合需要传达较多信息的场景。
<ShowcaseSection title="样式变体" description="填充、描边、柔和三种样式">
<Alert type="primary" message="填充样式" variant="filled" />
<View style={{ height: 12 }} />
<Alert type="primary" message="描边样式" variant="outlined" />
<View style={{ height: 12 }} />
<Alert type="primary" message="柔和样式" variant="soft" />
</ShowcaseSection>
第三个展示区块对比三种样式变体。用同一个 type(primary)展示,方便用户看出样式差异:
- filled:深色背景 + 白色文字,最醒目
- outlined:透明背景 + 彩色边框,中等醒目
- soft:浅色背景 + 彩色文字,最柔和
选择哪种变体取决于你想要的视觉强度。一般来说,错误提示可以用 filled 强调,普通提示用 soft 就够了。
<ShowcaseSection title="可关闭" description="点击关闭按钮隐藏提示">
<Alert type="info" message="这是一条可关闭的提示" closable onClose={() => {}} />
<View style={{ height: 12 }} />
<Alert type="warning" title="警告" message="这是一条可关闭的警告提示" closable onClose={() => {}} />
</ShowcaseSection>
第四个展示区块展示可关闭的 Alert。closable 属性让 Alert 右边出现关闭按钮。
Demo 里的 onClose={() => {}} 是空函数,实际使用时应该传入真正的关闭逻辑,比如:
const [visible, setVisible] = useState(true);
{visible && <Alert closable onClose={() => setVisible(false)} ... />}
<ShowcaseSection title="带操作" description="提示框中添加操作按钮">
<Alert
type="info"
title="新版本可用"
message="发现新版本 v2.0.0,是否立即更新?"
action={{ label: '立即更新', onPress: () => {} }}
/>
</ShowcaseSection>
</ComponentShowcase>
);
};
第五个展示区块展示带操作按钮的 Alert。action 属性接收一个对象,包含 label(按钮文字)和 onPress(点击回调)。
这种 Alert 适合需要用户响应的场景:
- 版本更新提示
- 权限申请提示
- 引导用户完成某个操作
实际应用场景
表单验证提示
表单提交后显示验证结果:
const FormPage = () => {
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState(false);
const handleSubmit = async () => {
try {
await api.submit(formData);
setSuccess(true);
setError(null);
} catch (e) {
setError(e.message);
setSuccess(false);
}
};
return (
<View>
{error && (
<Alert
type="danger"
title="提交失败"
message={error}
closable
onClose={() => setError(null)}
/>
)}
{success && (
<Alert
type="success"
message="提交成功!"
closable
onClose={() => setSuccess(false)}
/>
)}
{/* 表单内容 */}
</View>
);
};
这个例子展示了 Alert 和表单的配合:
- 用 useState 管理错误和成功状态
- 提交失败时显示 danger 类型的 Alert,带标题和详细错误信息
- 提交成功时显示 success 类型的 Alert
- 都设置了 closable,让用户可以关闭提示
页面顶部通知
在页面顶部显示重要通知:
const HomePage = () => {
const [showNotice, setShowNotice] = useState(true);
return (
<View style={styles.container}>
{showNotice && (
<Alert
type="warning"
title="系统维护通知"
message="系统将于今晚 22:00-24:00 进行维护,届时部分功能可能无法使用。"
closable
onClose={() => setShowNotice(false)}
variant="filled"
/>
)}
{/* 页面内容 */}
</View>
);
};
这种场景适合用 filled 变体,让通知更醒目。用户看完后可以关闭,不影响后续使用。
功能引导提示
引导用户使用新功能:
const FeaturePage = () => {
const [dismissed, setDismissed] = useState(false);
if (dismissed) return null;
return (
<Alert
type="info"
title="新功能上线"
message="现在支持语音输入了,点击下方按钮体验。"
action={{
label: '立即体验',
onPress: () => {
navigation.navigate('VoiceInput');
setDismissed(true);
},
}}
closable
onClose={() => setDismissed(true)}
/>
);
};
这个例子结合了 action 和 closable:
- 用户可以点击"立即体验"跳转到新功能
- 也可以点击关闭按钮忽略这个提示
- 两种操作都会让提示消失
网络状态提示
显示网络连接状态:
const NetworkAlert = () => {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
const unsubscribe = NetInfo.addEventListener(state => {
setIsOnline(state.isConnected);
});
return () => unsubscribe();
}, []);
if (isOnline) return null;
return (
<Alert
type="danger"
message="网络连接已断开,请检查网络设置"
variant="filled"
action={{
label: '重试',
onPress: () => NetInfo.fetch(),
}}
/>
);
};
网络断开时显示 danger 类型的 Alert,用 filled 变体强调严重性。提供"重试"按钮让用户可以手动刷新网络状态。
设计建议
类型选择
选择 Alert 类型时,要考虑信息的性质:
- info:一般性提示,不需要用户特别关注。比如"您有 3 条未读消息"
- success:操作成功的反馈。比如"保存成功"、“提交成功”
- warning:需要用户注意但不是错误。比如"密码强度较弱"、“即将过期”
- danger:错误或危险操作。比如"登录失败"、“删除后无法恢复”
不要滥用 danger 类型。如果所有提示都是红色的,用户会对红色脱敏,真正的错误反而被忽略。
变体选择
三种变体的使用场景:
- soft:默认选择,适合大多数场景。视觉上柔和,不会打扰用户
- outlined:需要一点存在感但不想太抢眼。适合表单内的提示
- filled:需要强调的重要信息。适合错误提示、系统通知
一个页面上不要同时出现太多 filled 样式的 Alert,会让页面看起来很"吵"。
内容编写
Alert 的文案要简洁明了:
- 标题用 2-4 个字的关键词
- message 用一句话说清楚
- 如果信息太多,考虑用 Modal 而不是 Alert
好的例子:
- 标题:“保存成功”,message:“您的修改已保存”
- 标题:“网络错误”,message:“请检查网络连接后重试”
不好的例子:
- 标题:“操作结果通知”,message:“您刚才进行的保存操作已经成功完成,系统已经将您的数据保存到服务器上…”
和 Toast 的区别
Alert 和 Toast 都是提示类组件,但使用场景不同:
- Alert:持久展示,需要用户主动关闭或一直显示。适合重要信息、需要用户响应的提示
- Toast:自动消失,通常 2-3 秒后自动隐藏。适合轻量级的操作反馈
举个例子:
- “保存成功”——用 Toast,用户看一眼就够了
- “您的账户即将过期,请续费”——用 Alert,需要用户注意并可能采取行动
如果你不确定用哪个,问自己一个问题:这条信息用户需要仔细阅读吗?需要就用 Alert,不需要就用 Toast。
和 Modal 的区别
Alert 和 Modal 都可以展示重要信息,但交互方式不同:
- Alert:非阻断式,用户可以继续操作页面其他内容
- Modal:阻断式,用户必须响应后才能继续
举个例子:
- “系统将于今晚维护”——用 Alert,用户知道就行,不需要立即响应
- “确定要删除这条记录吗?”——用 Modal,需要用户明确确认
Alert 的 action 按钮是可选的响应,Modal 的按钮通常是必须的响应。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐


所有评论(0)