Skip to content

Commit bf33a8e

Browse files
feat: Add support for static PlatformColor (software-mansion#8341)
## Summary This PR adds support for static PlatformColor, that can be used withing `useAnimatedStyle` - in the future maybe we will add possibility of animating it (but it's more complicated as it would be done on native side) To-do: - [x] polish up the example ## Test plan
1 parent 22f3010 commit bf33a8e

4 files changed

Lines changed: 112 additions & 1 deletion

File tree

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import React, { useEffect } from 'react';
2+
import { View, Text, Button, Platform, StyleSheet } from 'react-native';
3+
import Animated, {
4+
useSharedValue,
5+
useAnimatedStyle,
6+
PlatformColor,
7+
withTiming,
8+
} from 'react-native-reanimated';
9+
10+
const Description = () => (
11+
<View style={styles.contentContainer}>
12+
<Text style={styles.header}>What is PlatformColor?</Text>
13+
<Text style={styles.paragraph}>
14+
PlatformColor gives you possibility to use platform-specific semantic
15+
colors - for example
16+
<Text style={{ fontWeight: 'bold' }}> systemBlue</Text> on iOS or
17+
<Text style={{ fontWeight: 'bold' }}>
18+
@android:color/holo_blue_bright
19+
</Text>{' '}
20+
on Android - ensuring your app automatically adapts to the phone theme and
21+
stays consistent with native UI elements.
22+
</Text>
23+
<Text style={styles.header}>What this example does?</Text>
24+
<Text style={styles.paragraph}>
25+
This example should animate width correctly when static platform-defined
26+
color is applied. In the future we should extend this feature to support
27+
fully animated PlatformColor transitions.
28+
</Text>
29+
</View>
30+
);
31+
32+
export default function PlatformColorExample() {
33+
const width = useSharedValue(100);
34+
35+
useEffect(() => {
36+
const interval = setInterval(() => {
37+
width.value = withTiming(width.value === 100 ? 200 : 100);
38+
}, 2000);
39+
return () => clearInterval(interval);
40+
}, []);
41+
42+
const animatedStyle = useAnimatedStyle(() => ({
43+
width: width.value,
44+
backgroundColor:
45+
Platform.OS === 'ios'
46+
? PlatformColor('systemBlue')
47+
: PlatformColor('@android:color/holo_blue_bright'),
48+
}));
49+
50+
return (
51+
<View style={styles.container}>
52+
<Description />
53+
<Animated.View style={[styles.box, animatedStyle]} />
54+
<Button
55+
title="Toggle width"
56+
onPress={() => {
57+
width.value = withTiming(width.value === 100 ? 200 : 100);
58+
}}
59+
/>
60+
</View>
61+
);
62+
}
63+
64+
const styles = StyleSheet.create({
65+
container: {
66+
flex: 1,
67+
padding: 16,
68+
justifyContent: 'space-evenly',
69+
alignItems: 'center',
70+
backgroundColor: '#E3E6EA',
71+
},
72+
header: {
73+
fontSize: 20,
74+
fontWeight: 'bold',
75+
marginBottom: 10,
76+
color: '#001a72',
77+
fontFamily: 'Poppins',
78+
},
79+
paragraph: { marginBottom: 10, fontFamily: 'Poppins', color: '#001a72' },
80+
box: { height: 100, width: 100, borderRadius: 20 },
81+
contentContainer: {
82+
justifyContent: 'center',
83+
alignItems: 'center',
84+
gap: 10,
85+
padding: 16,
86+
borderRadius: 10,
87+
backgroundColor: 'white',
88+
},
89+
});

apps/common-app/src/apps/reanimated/examples/index.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import PagerExample from './CustomHandler/PagerExample';
3232
import DispatchCommandExample from './DispatchCommandExample';
3333
import DragAndSnapExample from './DragAndSnapExample';
3434
import DynamicColorIOSExample from './DynamicColorIOSExample';
35+
import PlatformColorExample from './PlatformColorExample';
3536
import EmojiWaterfallExample from './EmojiWaterfallExample';
3637
import EmptyExample from './EmptyExample';
3738
import ExtrapolationExample from './ExtrapolationExample';
@@ -589,6 +590,11 @@ export const EXAMPLES: Record<string, Example> = {
589590
screen: DynamicColorIOSExample,
590591
icon: '🌗',
591592
},
593+
PlatformColorExample: {
594+
title: 'PlatformColor',
595+
screen: PlatformColorExample,
596+
icon: '🎨',
597+
},
592598

593599
// Old examples
594600
AnimatedStyleUpdateExample: {

packages/react-native-reanimated/src/common/processors/colors.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,17 @@ type DynamicColorValue = ColorValue & {
2626
};
2727
};
2828

29+
type PlatformColorValue = ColorValue & { semantic?: Array<string> } & {
30+
resource_paths?: Array<string>;
31+
};
32+
33+
export function PlatformColor(...names: Array<string>): PlatformColorValue {
34+
'worklet';
35+
// eslint-disable-next-line camelcase
36+
const mapped = IS_IOS ? { semantic: names } : { resource_paths: names };
37+
return mapped as PlatformColorValue;
38+
}
39+
2940
export function DynamicColorIOS(
3041
tuple: DynamicColorIOSTuple
3142
): DynamicColorValue {

packages/react-native-reanimated/src/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,12 @@ export {
4747
} from './animation';
4848
export type { ParsedColorArray } from './Colors';
4949
export { convertToRGBA, isColor } from './Colors';
50-
export { DynamicColorIOS, processColor, ReanimatedLogLevel } from './common';
50+
export {
51+
DynamicColorIOS,
52+
PlatformColor,
53+
processColor,
54+
ReanimatedLogLevel,
55+
} from './common';
5156
export type {
5257
AnimatableValue,
5358
AnimatableValueObject,

0 commit comments

Comments
 (0)