切换按钮、开关与复选框
使用 Switch 组件获取布尔偏好设置,并通过切换状态和更新带样式的 View 构建自定义复选框。
切换按钮、开关与复选框 是 CoddyKit 上的免费 React Native Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 React Native Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 React Native Academy 课程共包含 4 节课。
本课时的部分内容尚未翻译,以英文显示。
The Switch Component
React Native's built-in Switch component renders the platform's native toggle control — a pill-shaped slider on iOS and a Material Design toggle on Android. It is a controlled component: you must manage its state with useState and provide both the value prop (the current boolean) and the onValueChange callback. Switch is ideal for simple on/off preferences like enabling notifications, dark mode, location access, or auto-save — anywhere a binary choice is needed.
import { Switch, View, Text, StyleSheet } from 'react-native';
import { useState } from 'react';
export default function NotificationsToggle() {
const [enabled, setEnabled] = useState(true);
return (
<View style={styles.row}>
<Text style={styles.label}>Push Notifications</Text>
<Switch
value={enabled}
onValueChange={setEnabled} // same as (val) => setEnabled(val)
/>
</View>
);
}
const styles = StyleSheet.create({
row: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', padding: 16, backgroundColor: '#fff', borderRadius: 12 },
label: { fontSize: 16, color: '#333' },
});Styling the Switch
The Switch component accepts color props to customize its appearance. trackColor takes an object with false and true keys for the track color in each state. thumbColor sets the knob color (use a function of the current value for dynamic coloring on Android). ios_backgroundColor sets the track color when the switch is off on iOS (since iOS ignores trackColor.false in some versions). These props let you match the switch to your app's brand colors rather than using the default system blue.
import { Switch, View, Text, StyleSheet } from 'react-native';
import { useState } from 'react';
export default function BrandedSwitch() {
const [active, setActive] = useState(false);
return (
<View style={styles.row}>
<Text style={styles.label}>Dark Mode</Text>
<Switch
value={active}
onValueChange={setActive}
trackColor={{ false: '#e0e0e0', true: '#4f86f7' }}
thumbColor={active ? '#fff' : '#f0f0f0'}
ios_backgroundColor='#e0e0e0'
/>
</View>
);
}
const styles = StyleSheet.create({
row: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', padding: 16 },
label: { fontSize: 16 },
});A Settings Screen with Multiple Switches
A typical settings screen has many toggle switches. Manage each setting in an object state variable and update individual fields using the spread pattern. Render each setting in a styled row with a Switch on the right. Separate sections with visual dividers or section headers. Pass the current value and an updater function to each row. When the user changes a setting, update state and optionally persist to AsyncStorage for next launch.
import { Switch, View, Text, StyleSheet, ScrollView } from 'react-native';
import { useState } from 'react';
export default function SettingsScreen() {
const [settings, setSettings] = useState({
notifications: true,
darkMode: false,
autoPlay: true,
locationAccess: false,
});
function toggle(key) {
setSettings(prev => ({ ...prev, [key]: !prev[key] }));
}
const rows = [
{ key: 'notifications', label: 'Push Notifications' },
{ key: 'darkMode', label: 'Dark Mode' },
{ key: 'autoPlay', label: 'Auto-Play Videos' },
{ key: 'locationAccess', label: 'Location Access' },
];
return (
<ScrollView style={styles.screen}>
<View style={styles.section}>
{rows.map(({ key, label }) => (
<View key={key} style={styles.row}>
<Text style={styles.label}>{label}</Text>
<Switch value={settings[key]} onValueChange={() => toggle(key)} trackColor={{ false: '#e0e0e0', true: '#4f86f7' }} />
</View>
))}
</View>
</ScrollView>
);
}
const styles = StyleSheet.create({
screen: { flex: 1, backgroundColor: '#f5f5f5' },
section: { margin: 16, backgroundColor: '#fff', borderRadius: 12, overflow: 'hidden' },
row: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingHorizontal: 16, paddingVertical: 14, borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: '#eee' },
label: { fontSize: 16, color: '#333' },
});Building a Custom Checkbox
React Native does not include a built-in checkbox. Build a custom one by managing a boolean state and toggling it on press. Render a square View with a border; when checked, fill it with the brand color and show a checkmark. Use TouchableOpacity or Pressable to detect the tap. This pattern is flexible — you can match any design system's checkbox style, add animations, or support indeterminate state (a minus sign for partially selected parent checkboxes in a tree).
import { TouchableOpacity, View, Text, StyleSheet } from 'react-native';
export default function Checkbox({ label, value, onChange }) {
return (
<TouchableOpacity
style={styles.row}
onPress={() => onChange(!value)}
activeOpacity={0.8}
accessibilityRole='checkbox'
accessibilityState={{ checked: value }}
>
<View style={[styles.box, value && styles.checked]}>
{value && <Text style={styles.check}>✓</Text>}
</View>
<Text style={styles.label}>{label}</Text>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
row: { flexDirection: 'row', alignItems: 'center', gap: 12, paddingVertical: 8 },
box: { width: 24, height: 24, borderRadius: 6, borderWidth: 2, borderColor: '#ccc', alignItems: 'center', justifyContent: 'center', backgroundColor: '#fff' },
checked: { backgroundColor: '#4f86f7', borderColor: '#4f86f7' },
check: { color: '#fff', fontSize: 14, fontWeight: 'bold' },
label: { fontSize: 16, color: '#333' },
});Checkbox Group State Management
For a group of checkboxes where users can select multiple options — like choosing notification types or filtering a product list — manage the selections as a Set or array of selected IDs in the parent component. Pass each checkbox its checked state (based on whether its ID is in the selections) and a toggle callback. The toggle callback adds or removes the item's ID from the selections array. This pattern scales to any number of checkboxes without duplicating state logic.
import { View, Text } from 'react-native';
import { useState } from 'react';
import Checkbox from './Checkbox';
const OPTIONS = [
{ id: 'promotions', label: 'Promotions and deals' },
{ id: 'news', label: 'App news and updates' },
{ id: 'messages', label: 'New messages' },
{ id: 'reminders', label: 'Reminders' },
];
export default function NotificationPrefs() {
const [selected, setSelected] = useState(new Set(['messages']));
function toggle(id) {
setSelected(prev => {
const next = new Set(prev);
next.has(id) ? next.delete(id) : next.add(id);
return next;
});
}
return (
<View style={{ padding: 16 }}>
<Text style={{ fontSize: 18, fontWeight: 'bold', marginBottom: 12 }}>Notify me about:</Text>
{OPTIONS.map(opt => (
<Checkbox
key={opt.id}
label={opt.label}
value={selected.has(opt.id)}
onChange={() => toggle(opt.id)}
/>
))}
<Text style={{ marginTop: 16, color: '#666' }}>{selected.size} selected</Text>
</View>
);
}Radio Button Group Pattern
A radio button group lets users pick exactly one option from a set. React Native has no built-in radio button, so implement it as a single-selection variant of the checkbox group. The state is a single value (the selected option's ID) instead of a Set. Each option renders as a circular checkbox: when selected, the circle fills with the brand color. Pressing an option sets the state to that option's ID, automatically deselecting all others. Include accessibilityRole='radio' and accessibilityState={{ checked }} for screen reader support.
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { useState } from 'react';
const PLANS = ['Free', 'Pro', 'Enterprise'];
export default function PlanSelector() {
const [selected, setSelected] = useState('Free');
return (
<View style={{ padding: 16, gap: 8 }}>
<Text style={{ fontSize: 18, fontWeight: 'bold', marginBottom: 8 }}>Choose a plan</Text>
{PLANS.map(plan => (
<TouchableOpacity
key={plan}
style={styles.option}
onPress={() => setSelected(plan)}
accessibilityRole='radio'
accessibilityState={{ checked: selected === plan }}
>
<View style={[styles.radio, selected === plan && styles.radioSelected]}>
{selected === plan && <View style={styles.dot} />}
</View>
<Text style={styles.planLabel}>{plan}</Text>
</TouchableOpacity>
))}
</View>
);
}
const styles = StyleSheet.create({
option: { flexDirection: 'row', alignItems: 'center', gap: 12, padding: 14, backgroundColor: '#fff', borderRadius: 10, borderWidth: 1.5, borderColor: '#eee' },
radio: { width: 22, height: 22, borderRadius: 11, borderWidth: 2, borderColor: '#ccc', alignItems: 'center', justifyContent: 'center' },
radioSelected: { borderColor: '#4f86f7' },
dot: { width: 10, height: 10, borderRadius: 5, backgroundColor: '#4f86f7' },
planLabel: { fontSize: 16, color: '#333', fontWeight: '500' },
});Animated Toggle Component
Build a custom animated toggle that slides a knob using Animated.Value. Start with a value of 0 (off) or 1 (on) and animate it with Animated.timing when the user taps. Use interpolate to map the animated value (0→1) to a horizontal translation (0→knobTravel) for the knob position, and to a color change for the track. This creates a smooth, branded switch that matches your app's design system exactly — without the platform inconsistencies of the built-in Switch component.
import { Animated, TouchableOpacity, StyleSheet } from 'react-native';
import { useRef, useState } from 'react';
export default function AnimatedToggle({ value, onChange }) {
const anim = useRef(new Animated.Value(value ? 1 : 0)).current;
function toggle() {
const next = !value;
Animated.timing(anim, { toValue: next ? 1 : 0, duration: 180, useNativeDriver: false }).start();
onChange(next);
}
const trackColor = anim.interpolate({ inputRange: [0, 1], outputRange: ['#e0e0e0', '#4f86f7'] });
const knobX = anim.interpolate({ inputRange: [0, 1], outputRange: [2, 22] });
return (
<TouchableOpacity onPress={toggle} activeOpacity={1}>
<Animated.View style={[styles.track, { backgroundColor: trackColor }]}>
<Animated.View style={[styles.knob, { transform: [{ translateX: knobX }] }]} />
</Animated.View>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
track: { width: 52, height: 30, borderRadius: 15, justifyContent: 'center' },
knob: { width: 26, height: 26, borderRadius: 13, backgroundColor: '#fff', shadowColor: '#000', shadowOffset: { width: 0, height: 1 }, shadowOpacity: 0.25, shadowRadius: 2, elevation: 2 },
});Slider for Range Values
For numeric range selection (volume, brightness, price range), use a slider. The community package @react-native-community/slider provides a cross-platform slider component. Set minimumValue, maximumValue, and step. The onValueChange callback fires as the user drags, and onSlidingComplete fires when they release. Style the filled track with minimumTrackTintColor and the empty track with maximumTrackTintColor. Display the current value in a Text label above or beside the slider.
import Slider from '@react-native-community/slider';
// npx expo install @react-native-community/slider
import { View, Text, StyleSheet } from 'react-native';
import { useState } from 'react';
export default function VolumeControl() {
const [volume, setVolume] = useState(50);
return (
<View style={styles.container}>
<Text style={styles.label}>Volume: {Math.round(volume)}%</Text>
<Slider
minimumValue={0}
maximumValue={100}
step={1}
value={volume}
onValueChange={setVolume}
minimumTrackTintColor='#4f86f7'
maximumTrackTintColor='#ddd'
thumbTintColor='#4f86f7'
style={styles.slider}
/>
</View>
);
}
const styles = StyleSheet.create({
container: { padding: 24 },
label: { fontSize: 18, fontWeight: '600', marginBottom: 8 },
slider: { width: '100%', height: 40 },
});Accessibility for Toggles
Make toggles and switches accessible to screen reader users. The built-in Switch is fully accessible out of the box — VoiceOver reads its label and current state. For custom checkboxes and radio buttons, set accessibilityRole ('checkbox' or 'radio') and accessibilityState={{ checked: value }}. For the toggle row, use accessible={true} and accessibilityLabel on the wrapping touchable so the entire row (including the label) is announced as one actionable item. Test with VoiceOver (iOS) or TalkBack (Android) before shipping.
import { Switch, View, Text } from 'react-native';
export default function AccessibleSettingRow({ label, value, onChange, description }) {
return (
// The whole row is accessible: tapping anywhere toggles
<View
accessible
accessibilityLabel={label}
accessibilityHint={description}
accessibilityRole='switch'
accessibilityState={{ checked: value }}
style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', padding: 16 }}
>
<View style={{ flex: 1 }}>
<Text style={{ fontSize: 16, color: '#333' }}>{label}</Text>
{description && <Text style={{ fontSize: 13, color: '#999' }}>{description}</Text>}
</View>
<Switch
value={value}
onValueChange={onChange}
importantForAccessibility='no-hide-descendants' // prevent double announcement
/>
</View>
);
}Select All / None Pattern
When working with multiple checkboxes, adding a Select All toggle improves usability. Track the selections in a Set. The Select All checkbox has three states: checked (all selected), unchecked (none selected), and indeterminate (some selected). In React Native, represent the indeterminate visual state as a dash icon instead of a checkmark. When the user taps Select All, either add all item IDs to the Set (if not all are selected) or clear the Set (if all are selected). This pattern is common in email clients and file managers for bulk actions.
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
import { useState } from 'react';
const ITEMS = ['Alpha', 'Beta', 'Gamma', 'Delta'];
export default function SelectAll() {
const [selected, setSelected] = useState(new Set());
const allSelected = selected.size === ITEMS.length;
const someSelected = selected.size > 0 && !allSelected;
function toggleAll() {
setSelected(allSelected ? new Set() : new Set(ITEMS));
}
function toggleItem(item) {
setSelected(prev => {
const next = new Set(prev);
next.has(item) ? next.delete(item) : next.add(item);
return next;
});
}
return (
<View style={{ padding: 16 }}>
<TouchableOpacity style={styles.header} onPress={toggleAll}>
<View style={[styles.box, (allSelected || someSelected) && styles.checked]}>
<Text style={styles.check}>{someSelected ? '−' : allSelected ? '✓' : ''}</Text>
</View>
<Text style={{ fontWeight: 'bold', fontSize: 16 }}>Select All</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
header: { flexDirection: 'row', alignItems: 'center', gap: 12, paddingVertical: 12 },
box: { width: 24, height: 24, borderRadius: 6, borderWidth: 2, borderColor: '#ccc', alignItems: 'center', justifyContent: 'center' },
checked: { backgroundColor: '#4f86f7', borderColor: '#4f86f7' },
check: { color: '#fff', fontWeight: 'bold', fontSize: 14 },
});Persisting Toggle State to AsyncStorage
User preferences captured by toggles and switches should persist across app restarts. Save each preference to AsyncStorage whenever it changes, and restore all preferences from AsyncStorage when the settings screen mounts. Use a useEffect with no dependencies to load saved preferences on mount, and a separate useEffect with the settings object as a dependency to save whenever settings change. Wrap the read in a try/catch to handle AsyncStorage errors gracefully — if reading fails, the app uses default values silently.
import AsyncStorage from '@react-native-async-storage/async-storage';
import { useState, useEffect } from 'react';
const SETTINGS_KEY = '@user_settings';
const DEFAULT_SETTINGS = {
notifications: true,
darkMode: false,
autoPlay: true,
};
export function usePersistedSettings() {
const [settings, setSettings] = useState(DEFAULT_SETTINGS);
// Load on mount
useEffect(() => {
AsyncStorage.getItem(SETTINGS_KEY)
.then(stored => {
if (stored) setSettings(JSON.parse(stored));
})
.catch(() => {}); // use defaults on error
}, []);
// Persist whenever settings change
useEffect(() => {
AsyncStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
}, [settings]);
function toggle(key) {
setSettings(prev => ({ ...prev, [key]: !prev[key] }));
}
return { settings, toggle };
}Quick Check
Test your understanding of React Native Mobile Development concepts from this lesson.
Lesson Recap
In this lesson you learned: Switch is the built-in binary toggle controlled by value and onValueChange props, custom checkboxes use boolean state and a square View that conditionally shows a checkmark, and radio groups manage a single selected-ID string to enforce mutual exclusion. Next up we build a complete login form combining TextInput, switches, and validation.
常见问题解答
「切换按钮、开关与复选框」课时是免费的吗?
是的 — 「切换按钮、开关与复选框」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 React Native Academy 课程的其余内容,请升级到 CoddyKit PRO。 React Native Academy 课程共包含 4 节课。
「切换按钮、开关与复选框」这节课中我会学到什么?
使用 Switch 组件获取布尔偏好设置,并通过切换状态和更新带样式的 View 构建自定义复选框。 你通过在浏览器中直接运行的动手代码来练习 React Native Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 React Native Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 React Native Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「切换按钮、开关与复选框」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 React Native Academy 课中编写并运行代码吗?
能。每节 React Native Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。