通知是 App 与用户沟通的重要方式。通过通知,App 可以告诉用户有新的游戏上架、特惠信息、或者其他重要事件。这篇文章来实现一个完整的通知设置系统,让用户能够灵活控制接收哪些通知。

通知类型的定义
首先需要定义 App 中有哪些类型的通知。不同的通知类型可以独立控制,用户可以选择只接收某些类型的通知。
先定义通知类型的接口:
tsx
interface NotificationSettings {
gameRelease: boolean; // 新游戏发布
specialOffers: boolean; // 特惠信息
priceDrops: boolean; // 价格下降
achievements: boolean; // 成就相关
newsUpdates: boolean; // 新闻更新
}
这个接口定义了五种通知类型:
gameRelease- 新游戏发布通知,用户可以及时了解最新游戏specialOffers- 特惠信息通知,用户不会错过优惠priceDrops- 价格下降通知,用户关注的游戏降价时会收到提醒achievements- 成就相关通知,用户完成成就时会收到通知newsUpdates- 新闻更新通知,用户可以及时了解游戏新闻
灵活设计 - 通过定义多种通知类型,用户可以精细化控制通知。有些用户可能只关心特惠信息,有些用户可能只关心新游戏发布。这样的设计能满足不同用户的需求。
然后在全局状态中添加通知设置:
tsx
interface AppState {
notificationSettings: NotificationSettings;
// ... 其他状态
}
通知设置页面的状态管理
通知设置页面需要从全局状态中获取当前的通知设置,然后提供 UI 让用户修改。先看状态的获取:
tsx
export const NotificationSettingsScreen = () => {
const {notificationSettings, updateNotificationSettings} = useApp();
这里获取了两个东西:
notificationSettings- 当前的通知设置对象,包含各种通知类型的开关状态updateNotificationSettings- 更新通知设置的函数
为什么分离通知设置? 虽然通知设置也可以放在通用的
updateSettings函数中,但单独提供updateNotificationSettings函数可以让代码更清晰。这样通知相关的逻辑都集中在一起,便于维护。
通知类型列表
通知设置页面的核心是一个通知类型列表,每个类型都有一个开关。用户可以通过开关来启用或禁用某种通知。
先看通知列表的定义:
tsx
const notificationTypes = [
{key: 'gameRelease', label: '新游戏发布', description: '当有新游戏发布时通知我'},
{key: 'specialOffers', label: '特惠信息', description: '当游戏打折时通知我'},
{key: 'priceDrops', label: '价格下降', description: '我关注的游戏降价时通知我'},
{key: 'achievements', label: '成就相关', description: '完成成就时通知我'},
{key: 'newsUpdates', label: '新闻更新', description: '有新的游戏新闻时通知我'},
];
这个数组定义了所有的通知类型。每个类型包含三个信息:
key- 用于在状态中查找对应的开关label- 通知类型的名称,显示给用户description- 通知类型的描述,帮助用户理解这个通知是干什么的
可维护性 - 通过数组来定义通知类型,如果要添加新的通知类型,只需要在数组中添加一项就行。不需要修改 UI 代码。
然后用 map 遍历这个数组来生成 UI:
tsx
<View style={styles.notificationList}>
{notificationTypes.map(type => (
<View key={type.key} style={styles.notificationItem}>
<View style={styles.notificationInfo}>
<Text style={styles.notificationLabel}>{type.label}</Text>
<Text style={styles.notificationDescription}>{type.description}</Text>
</View>
<Switch
value={notificationSettings[type.key as keyof NotificationSettings]}
onValueChange={(value) => updateNotificationSettings(type.key, value)}
trackColor={{false: '#2a475e', true: '#66c0f4'}}
thumbColor={notificationSettings[type.key as keyof NotificationSettings] ? '#fff' : '#8f98a0'}
/>
</View>
))}
</View>
这段代码的关键点:
- 遍历数组 -
map遍历通知类型数组,为每个类型生成一个 UI 项 - 动态访问 -
notificationSettings[type.key as keyof NotificationSettings]根据 key 动态访问通知设置中的对应值 - 开关绑定 -
Switch组件的value绑定到通知设置,onValueChange在用户切换时更新设置
类型安全 - 用
as keyof NotificationSettings来告诉 TypeScript 这个 key 是有效的。这样可以避免类型错误。
通知项的样式
每个通知项包含标签、描述和开关三部分。需要合理安排它们的布局和样式。
先看通知项的样式:
tsx
notificationItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: 12,
paddingHorizontal: 16,
borderBottomWidth: 1,
borderBottomColor: '#2a475e',
backgroundColor: '#1b2838',
},
notificationInfo: {flex: 1, marginRight: 12},
notificationLabel: {fontSize: 14, fontWeight: '600', color: '#fff', marginBottom: 4},
notificationDescription: {fontSize: 12, color: '#8f98a0'},
样式设计的要点:
- 布局 -
flexDirection: 'row'让信息和开关水平排列,justifyContent: 'space-between'让它们分别靠左和靠右 - 信息区 -
flex: 1让信息区占据剩余空间,marginRight: 12给开关留出空间 - 标签 - 用较大的字体和白色显示,是视觉焦点
- 描述 - 用较小的字体和灰色显示,作为辅助说明
- 分割线 - 每个项底部加一条分割线,让列表看起来更清晰
通知权限的请求
在实际应用中,App 需要向系统请求通知权限。用户需要在系统设置中允许 App 发送通知,App 才能真正发送通知。
先看权限请求的逻辑:
tsx
import {PermissionsAndroid} from 'react-native';
const requestNotificationPermission = async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS,
{
title: '通知权限',
message: '我们需要您的许可来发送通知',
buttonNeutral: '稍后再说',
buttonNegative: '拒绝',
buttonPositive: '允许',
}
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log('通知权限已获得');
} else {
console.log('通知权限被拒绝');
}
} catch (err) {
console.warn(err);
}
};
这段代码的关键点:
- 权限请求 - 用
PermissionsAndroid.request()向系统请求通知权限 - 权限名称 -
POST_NOTIFICATIONS是 Android 13+ 中发送通知所需的权限 - 对话框配置 - 配置权限请求对话框的标题、消息和按钮文字
- 结果处理 - 根据用户的选择(允许或拒绝)进行相应的处理
权限管理 - 在 Android 13+ 中,App 需要明确请求通知权限。用户可以在系统设置中随时撤销权限。App 需要优雅地处理权限被拒绝的情况。
通知权限的检查
在页面加载时,需要检查 App 是否已经获得通知权限。如果没有,可以提示用户请求权限。
先看权限检查的逻辑:
tsx
useEffect(() => {
checkNotificationPermission();
}, []);
const checkNotificationPermission = async () => {
try {
const hasPermission = await PermissionsAndroid.check(
PermissionsAndroid.PERMISSIONS.POST_NOTIFICATIONS
);
if (!hasPermission) {
// 权限未获得,可以提示用户
setPermissionGranted(false);
} else {
setPermissionGranted(true);
}
} catch (err) {
console.warn(err);
}
};
这段代码的逻辑:
- 权限检查 - 用
PermissionsAndroid.check()检查是否已获得通知权限 - 状态更新 - 根据检查结果更新本地状态
- 错误处理 - 用 try-catch 捕获可能的错误
权限状态 - 通过检查权限状态,App 可以决定是否显示权限请求提示。如果权限已获得,就不需要再提示。
通知权限提示
如果用户还没有授予通知权限,可以显示一个提示,引导用户请求权限。
先看权限提示的 UI:
tsx
{!permissionGranted && (
<View style={styles.permissionPrompt}>
<Text style={styles.promptTitle}>启用通知</Text>
<Text style={styles.promptDescription}>
为了接收通知,您需要在系统设置中允许 App 发送通知
</Text>
<TouchableOpacity
style={styles.promptButton}
onPress={requestNotificationPermission}
>
<Text style={styles.promptButtonText}>请求权限</Text>
</TouchableOpacity>
</View>
)}
这个提示包含三部分:
- 标题 - 告诉用户这是关于什么的
- 描述 - 解释为什么需要这个权限
- 按钮 - 用户可以点击按钮来请求权限
用户体验 - 通过友好的提示和清晰的说明,用户能理解为什么 App 需要这个权限,更容易同意授予权限。
页面的整体结构
通知设置页面的整体布局分为几部分:
tsx
return (
<View style={styles.container}>
<Header title="通知设置" showBack />
<ScrollView style={styles.content}>
{/* 权限提示 */}
{!permissionGranted && <PermissionPrompt />}
{/* 通知类型列表 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>通知类型</Text>
{/* 通知项列表 */}
</View>
{/* 其他设置 */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>其他设置</Text>
{/* 其他设置项 */}
</View>
</ScrollView>
<TabBar />
</View>
);
页面结构很清晰:
- Header - 顶部导航栏,显示"通知设置"标题
- 权限提示 - 如果权限未获得,显示权限请求提示
- 通知类型列表 - 显示各种通知类型的开关
- 其他设置 - 可以添加其他通知相关的设置
- TabBar - 底部导航栏
分层设计 - 通过分层来组织内容,让页面看起来更清晰。用户可以快速找到想要的设置。
通知设置的持久化
通知设置需要保存到本地,这样用户的设置才能在应用关闭后保留。
先看保存通知设置的函数:
tsx
const saveNotificationSettings = async (settings: NotificationSettings) => {
try {
await AsyncStorage.setItem('notificationSettings', JSON.stringify(settings));
} catch (error) {
console.error('Error saving notification settings:', error);
}
};
这个函数的作用:
- 序列化 - 用
JSON.stringify()将设置对象转换成字符串 - 存储 - 用
AsyncStorage.setItem()将字符串存储到本地 - 错误处理 - 用 try-catch 捕获可能的错误
然后看加载通知设置的函数:
tsx
const loadNotificationSettings = async () => {
try {
const data = await AsyncStorage.getItem('notificationSettings');
return data ? JSON.parse(data) : getDefaultNotificationSettings();
} catch (error) {
console.error('Error loading notification settings:', error);
return getDefaultNotificationSettings();
}
};
const getDefaultNotificationSettings = (): NotificationSettings => ({
gameRelease: true,
specialOffers: true,
priceDrops: true,
achievements: false,
newsUpdates: true,
});
这段代码的逻辑:
- 读取数据 - 用
AsyncStorage.getItem()从本地读取数据 - 反序列化 - 用
JSON.parse()将字符串转换回对象 - 默认值 - 如果没有保存的设置,使用默认值
- 错误处理 - 如果读取失败,也使用默认值
默认值设计 - 通过提供合理的默认值,即使用户是第一次使用 App,也能获得良好的体验。默认值应该是大多数用户都想要的设置。
样式汇总
通知设置页面的样式采用 Steam 的深色主题。先看容器和基本样式:
tsx
const styles = StyleSheet.create({
container: {flex: 1, backgroundColor: '#171a21'},
content: {flex: 1, paddingVertical: 16},
section: {marginBottom: 24, paddingHorizontal: 16},
sectionTitle: {fontSize: 14, fontWeight: 'bold', color: '#8f98a0', marginBottom: 12},
notificationList: {backgroundColor: '#1b2838', borderRadius: 8, overflow: 'hidden'},
notificationItem: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingVertical: 12,
paddingHorizontal: 16,
borderBottomWidth: 1,
borderBottomColor: '#2a475e',
},
notificationInfo: {flex: 1, marginRight: 12},
notificationLabel: {fontSize: 14, fontWeight: '600', color: '#fff', marginBottom: 4},
notificationDescription: {fontSize: 12, color: '#8f98a0'},
然后是权限提示的样式:
tsx
permissionPrompt: {
marginBottom: 24,
paddingHorizontal: 16,
paddingVertical: 12,
backgroundColor: '#2a475e',
borderRadius: 8,
borderLeftWidth: 4,
borderLeftColor: '#66c0f4',
},
promptTitle: {fontSize: 14, fontWeight: '600', color: '#fff', marginBottom: 4},
promptDescription: {fontSize: 12, color: '#8f98a0', marginBottom: 12},
promptButton: {paddingVertical: 8, paddingHorizontal: 12, backgroundColor: '#66c0f4', borderRadius: 6},
promptButtonText: {fontSize: 12, color: '#fff', fontWeight: '600', textAlign: 'center'},
});
配色说明:
#171a21- 最深的背景色,用于页面底色#1b2838- 列表背景色#2a475e- 分割线和提示框背景色#66c0f4- Steam 标志蓝,用于强调和按钮#8f98a0- 灰色,用于次要文字#fff- 白色,用于主要文字
小结
通知设置页面展示了如何实现一个完整的通知系统:
- 通知类型定义 - 定义多种通知类型,让用户能精细化控制
- 灵活的 UI 生成 - 通过数组 + map 的方式生成 UI,方便添加新的通知类型
- 权限管理 - 正确处理系统权限的请求和检查
- 用户引导 - 通过友好的提示引导用户授予权限
- 数据持久化 - 使用 AsyncStorage 保存用户的通知设置
- 默认值设计 - 提供合理的默认值,确保良好的初始体验
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net