Skip to content

Commit de574b5

Browse files
committed
feat: modernize Switch to MD3
* BREAKING CHANGE: Switch props no longer extend native Switch. color, ios_backgroundColor, trackColor, thumbColor etc. are removed. Use the theme prop for color customization.
1 parent a4eded6 commit de574b5

8 files changed

Lines changed: 2061 additions & 468 deletions

File tree

Lines changed: 97 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,103 @@
11
import * as React from 'react';
2-
import { Platform, StyleSheet, View } from 'react-native';
2+
import { StyleSheet, View } from 'react-native';
33

4-
import { Palette, Switch, Text, TouchableRipple } from 'react-native-paper';
4+
import { Switch, Text, useTheme } from 'react-native-paper';
55

66
import ScreenWrapper from '../ScreenWrapper';
77

8+
const Row = ({
9+
label,
10+
children,
11+
}: {
12+
label: string;
13+
children: React.ReactNode;
14+
}) => (
15+
<View style={styles.row}>
16+
<Text>{label}</Text>
17+
<View style={styles.right}>{children}</View>
18+
</View>
19+
);
20+
821
const SwitchExample = () => {
9-
const [valueNormal, setNormalValue] = React.useState<boolean>(true);
10-
const [valueCustom, setCustomValue] = React.useState<boolean>(true);
22+
const theme = useTheme();
23+
const [defaultOn, setDefaultOn] = React.useState(true);
24+
const [defaultCheckedIconOn, setDefaultCheckedIconOn] = React.useState(true);
25+
const [defaultIconOn, setDefaultIconOn] = React.useState(true);
26+
const [customOn, setCustomOn] = React.useState(true);
27+
const [customIconOn, setCustomIconOn] = React.useState(true);
28+
const [disableAll, setDisableAll] = React.useState(false);
1129

12-
const switchValueNormalLabel = `switch ${
13-
valueNormal === true ? 'on' : 'off'
14-
}`;
15-
const switchValueCustomlLabel = `switch ${
16-
valueCustom === true ? 'on' : 'off'
17-
}`;
30+
const tertiaryTheme = React.useMemo(
31+
() => ({
32+
colors: {
33+
primary: theme.colors.tertiary,
34+
onPrimary: theme.colors.onTertiary,
35+
primaryContainer: theme.colors.tertiaryContainer,
36+
secondary: theme.colors.tertiary,
37+
},
38+
}),
39+
[theme]
40+
);
1841

19-
return Platform.OS === 'android' ? (
42+
return (
2043
<ScreenWrapper style={styles.container}>
21-
<TouchableRipple onPress={() => setNormalValue(!valueNormal)}>
22-
<View style={styles.row}>
23-
<Text>Normal {switchValueNormalLabel}</Text>
24-
<View pointerEvents="none">
25-
<Switch value={valueNormal} />
26-
</View>
27-
</View>
28-
</TouchableRipple>
29-
<TouchableRipple onPress={() => setCustomValue(!valueCustom)}>
30-
<View style={styles.row}>
31-
<Text>Custom {switchValueCustomlLabel}</Text>
32-
<View pointerEvents="none">
33-
<Switch value={valueCustom} color={Palette.tertiary50} />
34-
</View>
35-
</View>
36-
</TouchableRipple>
37-
<View style={styles.row}>
38-
<Text>Switch on (disabled)</Text>
39-
<Switch disabled value />
40-
</View>
41-
<View style={styles.row}>
42-
<Text>Switch off (disabled)</Text>
43-
<Switch disabled />
44-
</View>
45-
</ScreenWrapper>
46-
) : (
47-
<ScreenWrapper style={styles.container}>
48-
<View style={styles.row}>
49-
<Text>Normal {switchValueNormalLabel}</Text>
44+
<Row label="Default">
45+
<Switch
46+
value={defaultOn}
47+
onValueChange={setDefaultOn}
48+
disabled={disableAll}
49+
/>
50+
</Row>
51+
52+
<Row label="Default with icon when on">
53+
<Switch
54+
value={defaultCheckedIconOn}
55+
onValueChange={setDefaultCheckedIconOn}
56+
checkedIcon="check"
57+
disabled={disableAll}
58+
/>
59+
</Row>
60+
61+
<Row label="Default with icon">
5062
<Switch
51-
value={valueNormal}
52-
onValueChange={() => setNormalValue(!valueNormal)}
63+
value={defaultIconOn}
64+
onValueChange={setDefaultIconOn}
65+
checkedIcon="check"
66+
uncheckedIcon="close"
67+
disabled={disableAll}
5368
/>
54-
</View>
55-
<View style={styles.row}>
56-
<Text>Custom {switchValueCustomlLabel}</Text>
69+
</Row>
70+
71+
<Row label="Custom (tertiary theme)">
5772
<Switch
58-
value={valueCustom}
59-
onValueChange={() => setCustomValue(!valueCustom)}
60-
color={Palette.tertiary50}
73+
value={customOn}
74+
onValueChange={setCustomOn}
75+
theme={tertiaryTheme}
76+
disabled={disableAll}
6177
/>
62-
</View>
63-
<View style={styles.row}>
64-
<Text>Switch on (disabled)</Text>
65-
<Switch value disabled />
66-
</View>
67-
<View style={styles.row}>
68-
<Text>Switch off (disabled)</Text>
69-
<Switch value={false} disabled />
70-
</View>
78+
</Row>
79+
80+
<Row label="Custom with icon">
81+
<Switch
82+
value={customIconOn}
83+
onValueChange={setCustomIconOn}
84+
checkedIcon="white-balance-sunny"
85+
uncheckedIcon="moon-waning-crescent"
86+
theme={tertiaryTheme}
87+
disabled={disableAll}
88+
/>
89+
</Row>
90+
91+
<View
92+
style={[
93+
styles.separator,
94+
{ backgroundColor: theme.colors.outlineVariant },
95+
]}
96+
/>
97+
98+
<Row label="Disable all switches">
99+
<Switch value={disableAll} onValueChange={setDisableAll} />
100+
</Row>
71101
</ScreenWrapper>
72102
);
73103
};
@@ -85,6 +115,16 @@ const styles = StyleSheet.create({
85115
paddingVertical: 8,
86116
paddingHorizontal: 16,
87117
},
118+
right: {
119+
flexDirection: 'row',
120+
alignItems: 'center',
121+
gap: 12,
122+
},
123+
separator: {
124+
height: 1,
125+
marginHorizontal: 16,
126+
marginVertical: 16,
127+
},
88128
});
89129

90130
export default SwitchExample;

jest/testSetup.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ jest.mock('react-native-worklets', () =>
88
require('react-native-worklets/lib/module/mock')
99
);
1010

11+
jest.mock('react-native-reanimated', () =>
12+
require('react-native-reanimated/mock')
13+
);
14+
1115
jest.mock('@react-native-vector-icons/material-design-icons', () => {
1216
const React = require('react');
1317
const { Text } = require('react-native');

0 commit comments

Comments
 (0)