0
# React Native Platform APIs
1
2
React Native provides comprehensive platform APIs for accessing device information, system state, and platform-specific functionality across iOS and Android.
3
4
## Installation
5
6
```bash
7
npm install react-native
8
```
9
10
## Device Information APIs
11
12
### Platform
13
14
Access platform-specific information and enable conditional code execution.
15
16
```javascript { .api }
17
// ESM
18
import {Platform} from 'react-native';
19
20
// CommonJS
21
const {Platform} = require('react-native');
22
23
// Platform detection
24
console.log(Platform.OS); // 'ios' | 'android' | 'windows' | 'macos' | 'web'
25
console.log(Platform.Version); // iOS: '14.0', Android: 30
26
27
// iOS specific checks
28
if (Platform.OS === 'ios') {
29
console.log('Running on iOS');
30
console.log('Is iPad:', Platform.isPad); // boolean
31
}
32
33
// Platform-specific values
34
const styles = StyleSheet.create({
35
container: {
36
marginTop: Platform.OS === 'ios' ? 20 : 0,
37
...Platform.select({
38
ios: {
39
backgroundColor: 'red',
40
},
41
android: {
42
backgroundColor: 'blue',
43
},
44
default: {
45
backgroundColor: 'gray',
46
},
47
}),
48
},
49
});
50
51
// Platform selection utility
52
const instructions = Platform.select({
53
ios: 'Press Cmd+R to reload',
54
android: 'Double tap R on your keyboard to reload',
55
default: 'Press F5 to reload',
56
});
57
58
// Version comparisons
59
if (Platform.Version >= 21) {
60
// Android API 21+ specific code
61
}
62
```
63
64
```typescript { .api }
65
interface PlatformStatic {
66
// Platform identification
67
OS: 'ios' | 'android' | 'windows' | 'macos' | 'web';
68
Version: string | number; // iOS: string, Android: number
69
70
// iOS specific
71
isPad?: boolean;
72
isTesting?: boolean;
73
74
// Utility methods
75
select<T>(specifics: PlatformSelectSpec<T>): T;
76
}
77
78
interface PlatformSelectSpec<T> {
79
ios?: T;
80
android?: T;
81
windows?: T;
82
macos?: T;
83
web?: T;
84
native?: T;
85
default?: T;
86
}
87
```
88
89
### Dimensions
90
91
Get device screen and window dimensions with automatic updates on orientation changes.
92
93
```javascript { .api }
94
import {Dimensions} from 'react-native';
95
96
// Get current dimensions
97
const screenData = Dimensions.get('screen');
98
const windowData = Dimensions.get('window');
99
100
console.log('Screen dimensions:', screenData);
101
// {width: 414, height: 896, scale: 3, fontScale: 1}
102
103
console.log('Window dimensions:', windowData);
104
// {width: 414, height: 818, scale: 3, fontScale: 1}
105
106
// Listen for dimension changes
107
const subscription = Dimensions.addEventListener('change', ({window, screen}) => {
108
console.log('New window dimensions:', window);
109
console.log('New screen dimensions:', screen);
110
});
111
112
// Clean up listener
113
return () => subscription?.remove();
114
115
// Calculate responsive sizes
116
const {width: screenWidth, height: screenHeight} = Dimensions.get('window');
117
const isLandscape = screenWidth > screenHeight;
118
const isTablet = screenWidth >= 768;
119
120
const styles = StyleSheet.create({
121
container: {
122
width: screenWidth * 0.9,
123
height: isTablet ? screenHeight * 0.7 : screenHeight * 0.5,
124
},
125
});
126
127
// Hook for reactive dimensions
128
import {useWindowDimensions} from 'react-native';
129
130
function ResponsiveComponent() {
131
const {width, height, scale, fontScale} = useWindowDimensions();
132
133
return (
134
<View style={{
135
width: width * 0.8,
136
height: height * 0.6,
137
}}>
138
<Text>Responsive content</Text>
139
</View>
140
);
141
}
142
```
143
144
```typescript { .api }
145
interface ScaledSize {
146
width: number;
147
height: number;
148
scale: number;
149
fontScale: number;
150
}
151
152
interface DimensionsStatic {
153
// Get dimensions
154
get(dim: 'window' | 'screen'): ScaledSize;
155
156
// Event listening
157
addEventListener(
158
type: 'change',
159
handler: (dims: {window: ScaledSize; screen: ScaledSize}) => void
160
): {remove: () => void};
161
}
162
163
interface DimensionsChangeEvent {
164
window: ScaledSize;
165
screen: ScaledSize;
166
}
167
```
168
169
### PixelRatio
170
171
Access device pixel density information for high-DPI displays and responsive design.
172
173
```javascript { .api }
174
import {PixelRatio} from 'react-native';
175
176
// Get pixel ratio
177
const pixelRatio = PixelRatio.get();
178
console.log('Device pixel ratio:', pixelRatio); // 2, 3, etc.
179
180
// Get font scale
181
const fontScale = PixelRatio.getFontScale();
182
console.log('Font scale:', fontScale); // 1, 1.3, etc.
183
184
// Convert dp to pixels
185
const dpValue = 100;
186
const pixelValue = PixelRatio.getPixelSizeForLayoutSize(dpValue);
187
console.log(`${dpValue}dp = ${pixelValue}px`);
188
189
// Round to nearest pixel
190
const exactPixel = PixelRatio.roundToNearestPixel(100.4);
191
console.log('Rounded pixel:', exactPixel); // 100 or 101
192
193
// Responsive image sizing
194
const imageSize = {
195
width: PixelRatio.getPixelSizeForLayoutSize(200),
196
height: PixelRatio.getPixelSizeForLayoutSize(150),
197
};
198
199
// High DPI image selection
200
function getImageSource() {
201
const ratio = PixelRatio.get();
202
if (ratio >= 3) {
203
return require('./image@3x.png');
204
} else if (ratio >= 2) {
205
return require('./image@2x.png');
206
} else {
207
return require('./image.png');
208
}
209
}
210
211
// Pixel-perfect styling
212
const styles = StyleSheet.create({
213
hairlineWidth: {
214
borderBottomWidth: StyleSheet.hairlineWidth, // 1 / PixelRatio.get()
215
},
216
pixelPerfect: {
217
width: PixelRatio.roundToNearestPixel(100.7),
218
height: PixelRatio.roundToNearestPixel(50.3),
219
},
220
});
221
```
222
223
```typescript { .api }
224
interface PixelRatioStatic {
225
// Pixel ratio information
226
get(): number;
227
getFontScale(): number;
228
229
// Conversion utilities
230
getPixelSizeForLayoutSize(layoutSize: number): number;
231
roundToNearestPixel(layoutSize: number): number;
232
233
// Constants
234
startDetecting?(): void;
235
stopDetecting?(): void;
236
}
237
```
238
239
### DeviceInfo
240
241
Access device-specific information and constants.
242
243
```javascript { .api }
244
import {DeviceInfo} from 'react-native';
245
246
// Device information (varies by platform)
247
console.log('Device info:', DeviceInfo);
248
249
// Common usage patterns for device detection
250
const isIOS = Platform.OS === 'ios';
251
const isAndroid = Platform.OS === 'android';
252
253
// Screen size categories
254
const {width} = Dimensions.get('window');
255
const isPhone = width < 768;
256
const isTablet = width >= 768;
257
258
// Device capability detection
259
const pixelRatio = PixelRatio.get();
260
const isHighDPI = pixelRatio >= 2;
261
262
// Platform version checks
263
const isModernAndroid = Platform.OS === 'android' && Platform.Version >= 23;
264
const isModernIOS = Platform.OS === 'ios' && parseFloat(Platform.Version) >= 14;
265
```
266
267
```typescript { .api }
268
interface DeviceInfoStatic {
269
// Device constants and information
270
[key: string]: any;
271
}
272
```
273
274
## System State APIs
275
276
### AppState
277
278
Monitor application state changes (active, background, inactive) and respond to app lifecycle events.
279
280
```javascript { .api }
281
import {AppState} from 'react-native';
282
283
// Get current app state
284
const currentState = AppState.currentState;
285
console.log('Current app state:', currentState); // 'active' | 'background' | 'inactive'
286
287
// Listen for app state changes
288
const handleAppStateChange = (nextAppState) => {
289
console.log('App state changed to:', nextAppState);
290
291
if (nextAppState === 'active') {
292
// App came to foreground
293
console.log('App is now active');
294
} else if (nextAppState === 'background') {
295
// App went to background
296
console.log('App is now in background');
297
} else if (nextAppState === 'inactive') {
298
// App is transitioning between states (iOS only)
299
console.log('App is inactive');
300
}
301
};
302
303
// Add event listener
304
const subscription = AppState.addEventListener('change', handleAppStateChange);
305
306
// Remove event listener
307
subscription.remove();
308
309
// React hook for app state
310
import {useEffect, useState} from 'react';
311
312
function useAppState() {
313
const [appState, setAppState] = useState(AppState.currentState);
314
315
useEffect(() => {
316
const subscription = AppState.addEventListener('change', setAppState);
317
return () => subscription.remove();
318
}, []);
319
320
return appState;
321
}
322
323
// Usage in component
324
function AppStateComponent() {
325
const appState = useAppState();
326
327
useEffect(() => {
328
if (appState === 'active') {
329
// Refresh data when app becomes active
330
refreshData();
331
}
332
}, [appState]);
333
334
return (
335
<View>
336
<Text>App State: {appState}</Text>
337
</View>
338
);
339
}
340
341
// Pause/resume functionality
342
useEffect(() => {
343
const subscription = AppState.addEventListener('change', (nextAppState) => {
344
if (nextAppState === 'background') {
345
// Pause expensive operations
346
pauseAnimations();
347
pauseTimers();
348
} else if (nextAppState === 'active') {
349
// Resume operations
350
resumeAnimations();
351
resumeTimers();
352
}
353
});
354
355
return () => subscription.remove();
356
}, []);
357
```
358
359
```typescript { .api }
360
type AppStateStatus = 'active' | 'background' | 'inactive';
361
362
interface AppStateStatic {
363
// Current state
364
currentState: AppStateStatus;
365
366
// Event listening
367
addEventListener(
368
type: 'change',
369
listener: (state: AppStateStatus) => void
370
): {remove: () => void};
371
372
// Legacy methods (deprecated)
373
removeEventListener?(type: 'change', listener: Function): void;
374
}
375
```
376
377
### BackHandler
378
379
Handle hardware back button on Android devices.
380
381
```javascript { .api }
382
import {BackHandler} from 'react-native';
383
384
// Basic back handler (Android only)
385
useEffect(() => {
386
const backAction = () => {
387
Alert.alert('Hold on!', 'Are you sure you want to go back?', [
388
{
389
text: 'Cancel',
390
onPress: () => null,
391
style: 'cancel',
392
},
393
{text: 'YES', onPress: () => BackHandler.exitApp()},
394
]);
395
return true; // Prevent default behavior
396
};
397
398
const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
399
400
return () => backHandler.remove();
401
}, []);
402
403
// Navigation back handling
404
import {useNavigation} from '@react-navigation/native';
405
406
function ScreenWithCustomBack() {
407
const navigation = useNavigation();
408
409
useEffect(() => {
410
const backAction = () => {
411
if (hasUnsavedChanges) {
412
Alert.alert(
413
'Unsaved Changes',
414
'You have unsaved changes. Are you sure you want to leave?',
415
[
416
{text: 'Stay', style: 'cancel'},
417
{
418
text: 'Leave',
419
style: 'destructive',
420
onPress: () => navigation.goBack(),
421
},
422
]
423
);
424
return true; // Prevent default
425
}
426
return false; // Allow default behavior
427
};
428
429
const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
430
return () => backHandler.remove();
431
}, [navigation, hasUnsavedChanges]);
432
433
return <YourScreenContent />;
434
}
435
436
// Exit app confirmation
437
function useExitAppConfirmation() {
438
useEffect(() => {
439
const backAction = () => {
440
Alert.alert('Exit App', 'Do you want to exit?', [
441
{text: 'No', style: 'cancel'},
442
{text: 'Yes', onPress: () => BackHandler.exitApp()},
443
]);
444
return true;
445
};
446
447
const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
448
return () => backHandler.remove();
449
}, []);
450
}
451
452
// Modal back handling
453
function ModalComponent({visible, onClose}) {
454
useEffect(() => {
455
if (!visible) return;
456
457
const backAction = () => {
458
onClose();
459
return true; // Handled
460
};
461
462
const backHandler = BackHandler.addEventListener('hardwareBackPress', backAction);
463
return () => backHandler.remove();
464
}, [visible, onClose]);
465
466
return (
467
<Modal visible={visible}>
468
{/* Modal content */}
469
</Modal>
470
);
471
}
472
```
473
474
```typescript { .api }
475
interface BackHandlerStatic {
476
// Event handling
477
addEventListener(
478
eventName: 'hardwareBackPress',
479
handler: () => boolean
480
): {remove: () => void};
481
482
// App control
483
exitApp(): void;
484
485
// Legacy methods (deprecated)
486
removeEventListener?(eventName: 'hardwareBackPress', handler: Function): void;
487
}
488
```
489
490
### Keyboard
491
492
Monitor and control keyboard visibility and behavior.
493
494
```javascript { .api }
495
import {Keyboard} from 'react-native';
496
497
// Listen for keyboard events
498
useEffect(() => {
499
const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', (event) => {
500
console.log('Keyboard shown:', event.endCoordinates);
501
// {screenX, screenY, width, height, duration, easing}
502
});
503
504
const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', (event) => {
505
console.log('Keyboard hidden:', event.endCoordinates);
506
});
507
508
const keyboardWillShowListener = Keyboard.addListener('keyboardWillShow', (event) => {
509
console.log('Keyboard will show:', event.endCoordinates);
510
});
511
512
const keyboardWillHideListener = Keyboard.addListener('keyboardWillHide', (event) => {
513
console.log('Keyboard will hide:', event.endCoordinates);
514
});
515
516
return () => {
517
keyboardDidShowListener.remove();
518
keyboardDidHideListener.remove();
519
keyboardWillShowListener.remove();
520
keyboardWillHideListener.remove();
521
};
522
}, []);
523
524
// Dismiss keyboard
525
const dismissKeyboard = () => {
526
Keyboard.dismiss();
527
};
528
529
<TouchableWithoutFeedback onPress={dismissKeyboard}>
530
<View style={styles.container}>
531
<TextInput placeholder="Tap outside to dismiss keyboard" />
532
</View>
533
</TouchableWithoutFeedback>
534
535
// Keyboard height tracking
536
function useKeyboardHeight() {
537
const [keyboardHeight, setKeyboardHeight] = useState(0);
538
539
useEffect(() => {
540
const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', (event) => {
541
setKeyboardHeight(event.endCoordinates.height);
542
});
543
544
const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => {
545
setKeyboardHeight(0);
546
});
547
548
return () => {
549
keyboardDidShowListener.remove();
550
keyboardDidHideListener.remove();
551
};
552
}, []);
553
554
return keyboardHeight;
555
}
556
557
// Adjust view for keyboard
558
function KeyboardAwareComponent() {
559
const [bottomPadding, setBottomPadding] = useState(0);
560
561
useEffect(() => {
562
const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', (event) => {
563
setBottomPadding(event.endCoordinates.height);
564
});
565
566
const keyboardDidHideListener = Keyboard.addListener('keyboardDidHide', () => {
567
setBottomPadding(0);
568
});
569
570
return () => {
571
keyboardDidShowListener.remove();
572
keyboardDidHideListener.remove();
573
};
574
}, []);
575
576
return (
577
<View style={[styles.container, {paddingBottom: bottomPadding}]}>
578
<TextInput style={styles.input} />
579
</View>
580
);
581
}
582
```
583
584
```typescript { .api }
585
interface KeyboardEvent {
586
endCoordinates: {
587
screenX: number;
588
screenY: number;
589
width: number;
590
height: number;
591
};
592
startCoordinates?: {
593
screenX: number;
594
screenY: number;
595
width: number;
596
height: number;
597
};
598
duration?: number;
599
easing?: string;
600
}
601
602
interface KeyboardStatic {
603
// Keyboard control
604
dismiss(): void;
605
606
// Event listeners
607
addListener(
608
eventName: KeyboardEventName,
609
callback: (event: KeyboardEvent) => void
610
): {remove: () => void};
611
612
// Legacy methods (deprecated)
613
removeListener?(eventName: KeyboardEventName, callback: Function): void;
614
removeAllListeners?(eventName: KeyboardEventName): void;
615
}
616
617
type KeyboardEventName =
618
| 'keyboardWillShow'
619
| 'keyboardDidShow'
620
| 'keyboardWillHide'
621
| 'keyboardDidHide'
622
| 'keyboardWillChangeFrame'
623
| 'keyboardDidChangeFrame';
624
```
625
626
## External Integration APIs
627
628
### Linking
629
630
Handle deep links, URL schemes, and external app integration.
631
632
```javascript { .api }
633
import {Linking} from 'react-native';
634
635
// Open external URLs
636
const openURL = async (url) => {
637
try {
638
const supported = await Linking.canOpenURL(url);
639
if (supported) {
640
await Linking.openURL(url);
641
} else {
642
Alert.alert('Error', `Don't know how to open URL: ${url}`);
643
}
644
} catch (error) {
645
Alert.alert('Error', 'An error occurred');
646
}
647
};
648
649
// Open various URL schemes
650
openURL('https://www.example.com'); // Web browser
651
openURL('mailto:support@example.com'); // Email client
652
openURL('tel:+1234567890'); // Phone dialer
653
openURL('sms:+1234567890'); // SMS
654
openURL('maps:0,0?q=restaurant'); // Maps app
655
openURL('fb://profile/123456789'); // Facebook app
656
657
// Get initial URL (app launched via deep link)
658
useEffect(() => {
659
const getInitialURL = async () => {
660
try {
661
const initialUrl = await Linking.getInitialURL();
662
if (initialUrl) {
663
console.log('App opened with URL:', initialUrl);
664
handleDeepLink(initialUrl);
665
}
666
} catch (error) {
667
console.error('Error getting initial URL:', error);
668
}
669
};
670
671
getInitialURL();
672
}, []);
673
674
// Listen for incoming URLs (app already running)
675
useEffect(() => {
676
const handleUrlChange = (event) => {
677
console.log('Received URL:', event.url);
678
handleDeepLink(event.url);
679
};
680
681
const subscription = Linking.addEventListener('url', handleUrlChange);
682
return () => subscription.remove();
683
}, []);
684
685
// Deep link handler
686
const handleDeepLink = (url) => {
687
const route = url.replace(/.*?:\/\//g, '');
688
const [routeName, paramsString] = route.split('?');
689
690
// Parse parameters
691
const params = {};
692
if (paramsString) {
693
paramsString.split('&').forEach(param => {
694
const [key, value] = param.split('=');
695
params[key] = decodeURIComponent(value);
696
});
697
}
698
699
// Navigate based on deep link
700
switch (routeName) {
701
case 'profile':
702
navigation.navigate('Profile', params);
703
break;
704
case 'product':
705
navigation.navigate('Product', {id: params.id});
706
break;
707
default:
708
navigation.navigate('Home');
709
}
710
};
711
712
// Custom URL scheme examples
713
// myapp://profile/123
714
// myapp://product?id=456&color=red
715
716
// Universal links (iOS) and App links (Android)
717
// https://myapp.com/profile/123
718
// https://myapp.com/product?id=456
719
720
// Check if URL can be opened
721
const checkURL = async (url) => {
722
try {
723
const canOpen = await Linking.canOpenURL(url);
724
console.log(`Can open ${url}:`, canOpen);
725
} catch (error) {
726
console.error('Error checking URL:', error);
727
}
728
};
729
730
// Open settings
731
const openSettings = () => {
732
Linking.openSettings();
733
};
734
```
735
736
```typescript { .api }
737
interface LinkingStatic {
738
// URL handling
739
canOpenURL(url: string): Promise<boolean>;
740
openURL(url: string): Promise<void>;
741
openSettings(): Promise<void>;
742
743
// Deep link handling
744
getInitialURL(): Promise<string | null>;
745
addEventListener(
746
type: 'url',
747
handler: (event: {url: string}) => void
748
): {remove: () => void};
749
750
// Legacy methods (deprecated)
751
removeEventListener?(type: 'url', handler: Function): void;
752
}
753
754
interface LinkingEvent {
755
url: string;
756
}
757
```
758
759
### Share
760
761
Invoke the native share dialog to share content with other apps.
762
763
```javascript { .api }
764
import {Share} from 'react-native';
765
766
// Basic text sharing
767
const shareText = async () => {
768
try {
769
const result = await Share.share({
770
message: 'Check out this amazing app!',
771
});
772
773
if (result.action === Share.sharedAction) {
774
if (result.activityType) {
775
console.log('Shared with activity type:', result.activityType);
776
} else {
777
console.log('Shared');
778
}
779
} else if (result.action === Share.dismissedAction) {
780
console.log('Share dismissed');
781
}
782
} catch (error) {
783
Alert.alert('Error', error.message);
784
}
785
};
786
787
// Share with title (Android)
788
const shareWithTitle = async () => {
789
try {
790
await Share.share({
791
message: 'Amazing content to share',
792
title: 'Share Title', // Android only
793
});
794
} catch (error) {
795
console.error(error);
796
}
797
};
798
799
// Share URL
800
const shareURL = async () => {
801
try {
802
await Share.share({
803
message: 'Check out this website',
804
url: 'https://www.example.com', // iOS only
805
});
806
} catch (error) {
807
console.error(error);
808
}
809
};
810
811
// Share with options (iOS)
812
const shareWithOptions = async () => {
813
try {
814
await Share.share(
815
{
816
message: 'Content to share',
817
url: 'https://example.com',
818
},
819
{
820
// iOS only
821
excludedActivityTypes: [
822
'com.apple.UIKit.activity.PostToWeibo',
823
'com.apple.UIKit.activity.Print',
824
],
825
dialogTitle: 'Share this content', // Android only
826
subject: 'Email Subject', // Email subject
827
tintColor: '#007AFF', // iOS only
828
}
829
);
830
} catch (error) {
831
console.error(error);
832
}
833
};
834
835
// Share different content types
836
const shareImage = async (imageUri) => {
837
try {
838
await Share.share({
839
message: 'Check out this image!',
840
url: imageUri, // Local file:// or remote https:// URL
841
});
842
} catch (error) {
843
console.error(error);
844
}
845
};
846
847
// Share with dynamic content
848
const shareProduct = async (product) => {
849
try {
850
const shareContent = {
851
message: `Check out ${product.name}: ${product.description}`,
852
url: `https://myapp.com/product/${product.id}`,
853
};
854
855
const shareOptions = {
856
dialogTitle: 'Share Product',
857
subject: `${product.name} - Product Recommendation`,
858
};
859
860
await Share.share(shareContent, shareOptions);
861
} catch (error) {
862
console.error('Share failed:', error);
863
}
864
};
865
866
// Component with share functionality
867
function ShareButton({content, title}) {
868
const handleShare = async () => {
869
try {
870
await Share.share({
871
message: content,
872
title,
873
});
874
} catch (error) {
875
Alert.alert('Share Failed', 'Unable to share content');
876
}
877
};
878
879
return (
880
<TouchableOpacity onPress={handleShare} style={styles.shareButton}>
881
<Text>Share</Text>
882
</TouchableOpacity>
883
);
884
}
885
```
886
887
```typescript { .api }
888
interface ShareContent {
889
message?: string;
890
title?: string; // Android only
891
url?: string; // iOS only
892
}
893
894
interface ShareOptions {
895
// iOS only
896
excludedActivityTypes?: string[];
897
tintColor?: string;
898
899
// Android only
900
dialogTitle?: string;
901
902
// Cross-platform
903
subject?: string; // For email
904
}
905
906
interface ShareResult {
907
action: 'sharedAction' | 'dismissedAction';
908
activityType?: string; // iOS only
909
}
910
911
interface ShareStatic {
912
share(content: ShareContent, options?: ShareOptions): Promise<ShareResult>;
913
914
// Action constants
915
sharedAction: 'sharedAction';
916
dismissedAction: 'dismissedAction';
917
}
918
```
919
920
## System Integration APIs
921
922
### Appearance
923
924
Detect and listen to system appearance changes (light/dark mode).
925
926
```javascript { .api }
927
import {Appearance} from 'react-native';
928
929
// Get current color scheme
930
const colorScheme = Appearance.getColorScheme();
931
console.log('Current color scheme:', colorScheme); // 'light' | 'dark' | null
932
933
// Listen for appearance changes
934
useEffect(() => {
935
const subscription = Appearance.addChangeListener(({colorScheme}) => {
936
console.log('Color scheme changed to:', colorScheme);
937
updateTheme(colorScheme);
938
});
939
940
return () => subscription.remove();
941
}, []);
942
943
// Theme context with appearance
944
const ThemeContext = createContext();
945
946
export function ThemeProvider({children}) {
947
const [colorScheme, setColorScheme] = useState(Appearance.getColorScheme());
948
949
useEffect(() => {
950
const subscription = Appearance.addChangeListener(({colorScheme}) => {
951
setColorScheme(colorScheme);
952
});
953
954
return () => subscription.remove();
955
}, []);
956
957
const theme = {
958
colors: colorScheme === 'dark' ? darkColors : lightColors,
959
colorScheme,
960
};
961
962
return (
963
<ThemeContext.Provider value={theme}>
964
{children}
965
</ThemeContext.Provider>
966
);
967
}
968
969
// Using appearance in components
970
function ThemedComponent() {
971
const colorScheme = useColorScheme(); // React Native hook
972
973
const styles = StyleSheet.create({
974
container: {
975
backgroundColor: colorScheme === 'dark' ? '#000' : '#fff',
976
color: colorScheme === 'dark' ? '#fff' : '#000',
977
},
978
});
979
980
return (
981
<View style={styles.container}>
982
<Text>Themed content</Text>
983
</View>
984
);
985
}
986
987
// Dynamic styling based on appearance
988
const createStyles = (colorScheme) => StyleSheet.create({
989
container: {
990
flex: 1,
991
backgroundColor: colorScheme === 'dark' ? '#121212' : '#ffffff',
992
},
993
text: {
994
color: colorScheme === 'dark' ? '#ffffff' : '#000000',
995
},
996
card: {
997
backgroundColor: colorScheme === 'dark' ? '#1e1e1e' : '#f5f5f5',
998
borderColor: colorScheme === 'dark' ? '#333333' : '#e0e0e0',
999
},
1000
});
1001
1002
function App() {
1003
const [colorScheme, setColorScheme] = useState(Appearance.getColorScheme());
1004
1005
useEffect(() => {
1006
const subscription = Appearance.addChangeListener(({colorScheme}) => {
1007
setColorScheme(colorScheme);
1008
});
1009
1010
return () => subscription.remove();
1011
}, []);
1012
1013
const styles = createStyles(colorScheme);
1014
1015
return (
1016
<View style={styles.container}>
1017
<Text style={styles.text}>App with dynamic theming</Text>
1018
</View>
1019
);
1020
}
1021
```
1022
1023
```typescript { .api }
1024
type ColorSchemeName = 'light' | 'dark' | null;
1025
1026
interface AppearanceStatic {
1027
// Current appearance
1028
getColorScheme(): ColorSchemeName;
1029
1030
// Event listening
1031
addChangeListener(
1032
listener: (preferences: {colorScheme: ColorSchemeName}) => void
1033
): {remove: () => void};
1034
1035
// Legacy methods (deprecated)
1036
removeChangeListener?(listener: Function): void;
1037
}
1038
1039
interface AppearancePreferences {
1040
colorScheme: ColorSchemeName;
1041
}
1042
```
1043
1044
## Network and Storage APIs
1045
1046
### I18nManager
1047
1048
Manage internationalization and right-to-left (RTL) layout support.
1049
1050
```javascript { .api }
1051
import {I18nManager} from 'react-native';
1052
1053
// Check RTL status
1054
console.log('Is RTL:', I18nManager.isRTL); // boolean
1055
1056
// Get text direction
1057
console.log('Text direction:', I18nManager.getConstants().isRTL);
1058
1059
// Force RTL (requires app restart)
1060
if (!I18nManager.isRTL) {
1061
I18nManager.forceRTL(true);
1062
// App restart required
1063
}
1064
1065
// Allow RTL layout
1066
I18nManager.allowRTL(true);
1067
1068
// RTL-aware styling
1069
const styles = StyleSheet.create({
1070
container: {
1071
flexDirection: 'row',
1072
paddingStart: 20, // Uses paddingLeft in LTR, paddingRight in RTL
1073
paddingEnd: 10, // Uses paddingRight in LTR, paddingLeft in RTL
1074
},
1075
text: {
1076
textAlign: I18nManager.isRTL ? 'right' : 'left',
1077
writingDirection: I18nManager.isRTL ? 'rtl' : 'ltr',
1078
},
1079
});
1080
1081
// RTL-aware icon direction
1082
function DirectionalIcon({name, style}) {
1083
const iconName = I18nManager.isRTL ? `${name}-left` : `${name}-right`;
1084
return <Icon name={iconName} style={style} />;
1085
}
1086
1087
// RTL-aware navigation
1088
function RTLAwareNavigation() {
1089
const slideDirection = I18nManager.isRTL ? 'right' : 'left';
1090
1091
return (
1092
<Stack.Navigator
1093
screenOptions={{
1094
gestureDirection: I18nManager.isRTL ? 'horizontal-inverted' : 'horizontal',
1095
}}
1096
>
1097
<Stack.Screen name="Home" component={HomeScreen} />
1098
</Stack.Navigator>
1099
);
1100
}
1101
```
1102
1103
```typescript { .api }
1104
interface I18nManagerStatic {
1105
// RTL state
1106
isRTL: boolean;
1107
1108
// Configuration
1109
allowRTL(allowRTL: boolean): void;
1110
forceRTL(forceRTL: boolean): void;
1111
swapLeftAndRightInRTL(flipStyles: boolean): void;
1112
1113
// Constants
1114
getConstants(): {
1115
isRTL: boolean;
1116
doLeftAndRightSwapInRTL: boolean;
1117
};
1118
}
1119
```
1120
1121
## Development and Debug APIs
1122
1123
### DevSettings
1124
1125
Access development-time settings and actions (development builds only).
1126
1127
```javascript { .api }
1128
import {DevSettings} from 'react-native';
1129
1130
// Add developer menu item (development only)
1131
if (__DEV__) {
1132
DevSettings.addMenuItem('Clear Cache', () => {
1133
// Custom cache clearing logic
1134
clearAppCache();
1135
});
1136
1137
DevSettings.addMenuItem('Reset Database', () => {
1138
Alert.alert(
1139
'Reset Database',
1140
'Are you sure?',
1141
[
1142
{text: 'Cancel', style: 'cancel'},
1143
{text: 'Reset', onPress: resetDatabase},
1144
]
1145
);
1146
});
1147
1148
DevSettings.addMenuItem('Toggle Debug Mode', () => {
1149
toggleDebugMode();
1150
});
1151
}
1152
1153
// Reload app programmatically (development only)
1154
const reloadApp = () => {
1155
if (__DEV__) {
1156
DevSettings.reload();
1157
}
1158
};
1159
1160
// Development utilities
1161
function DeveloperTools() {
1162
if (!__DEV__) return null;
1163
1164
return (
1165
<View style={styles.devTools}>
1166
<Button title="Reload" onPress={() => DevSettings.reload()} />
1167
<Button title="Clear Logs" onPress={clearLogs} />
1168
</View>
1169
);
1170
}
1171
```
1172
1173
```typescript { .api }
1174
interface DevSettingsStatic {
1175
// Developer menu
1176
addMenuItem(title: string, handler: () => void): void;
1177
1178
// App control
1179
reload(): void;
1180
}
1181
```
1182
1183
This comprehensive documentation covers all the essential platform APIs in React Native, providing developers with the tools needed to create robust, cross-platform mobile applications that integrate seamlessly with device capabilities and system features.