Skip to content

Commit 2b56011

Browse files
Simekfacebook-github-bot
authored andcommitted
Add Dark Mode support to the App template and NewAppScreen components (#28711)
Summary: This PR adds support for the dark mode and dynamic theme changing to the default App template and to the related `NewAppScreen` components. Using `useColorScheme` hook forced me to refactor a bit main `App.js` file, but I think those changes are step in the right direction according to way in which React Native is used in larger apps, so new `Section` component has been extracted to reduce code redundancy/repetition inside `App`. Additional color `darker` has been added to the `Colors` statics from `NewAppScreen` because `dark` was too bright for the Dark Mode backgrounds. Also main `StoryBoard` on iOS has been updated to use theme based colors instead of static or hardcoded ones. There was also an unused, empty `Label` which I have removed. ~~I'm not so much experienced with Android. If someone could also update Android splash screen (if Android requires such change) it will be nice. I want to look at this later using simulator.~~ > I have updated the Android splash screen and tested this change on the Android emulator. If you have any comment or corrections feel free to post them out, I would like to put more work into this PR if it's needed. Dark Mode this days is a part of near every OS, so it could be considered as a standard feature. I hope those changes helps people which struggle with the basic theming implementation (+ there is now an example of hook and `children` prop usage in the template). ## Changelog [Internal] [Added] - Add dark mode support to the default app template Pull Request resolved: #28711 Test Plan: I have tested the App from the template on the iOS device and in Android emulator with RN `0.63.0-rc`. Screen recording on iOS (demonstarates both modes, both splash screens and transition): ![ezgif-6-e24ee8e839c9](https://user-images.githubusercontent.com/719641/80025923-a04b0300-84e1-11ea-824a-b4363db48892.gif) Screenshot of iOS app in Dark Mode: ![IMG_6542](https://user-images.githubusercontent.com/719641/79885748-c98f6480-83f7-11ea-8c73-1351a721d5d6.PNG) Screenshot of iOS app splash screen in Dark Mode: ![IMG_6544](https://user-images.githubusercontent.com/719641/79960431-add29f80-8485-11ea-985c-b39176024ffa.PNG) Screenshot of Android app in the emulator: ![Screenshot_1587566100](https://user-images.githubusercontent.com/719641/79995454-88f72000-84b7-11ea-810b-dfb70de03c2a.png) Differential Revision: D21236148 Pulled By: shergin fbshipit-source-id: 0c8a9534d3a3f8f8099af939243a889ac4df6cda
1 parent fa69356 commit 2b56011

File tree

7 files changed

+154
-102
lines changed

7 files changed

+154
-102
lines changed

Libraries/NewAppScreen/components/Colors.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,6 @@ export default {
1616
lighter: '#F3F3F3',
1717
light: '#DAE1E7',
1818
dark: '#444',
19+
darker: '#222',
1920
black: '#000',
2021
};

Libraries/NewAppScreen/components/Header.js

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,40 @@
1111
'use strict';
1212
import Colors from './Colors';
1313
import type {Node} from 'react';
14-
import {Text, StyleSheet, ImageBackground} from 'react-native';
14+
import {ImageBackground, StyleSheet, Text, useColorScheme} from 'react-native';
1515
import React from 'react';
1616

17-
const Header = (): Node => (
18-
<ImageBackground
19-
accessibilityRole={'image'}
20-
source={require('./logo.png')}
21-
style={styles.background}
22-
imageStyle={styles.logo}>
23-
<Text style={styles.text}>Welcome to React</Text>
24-
</ImageBackground>
25-
);
17+
const Header = (): Node => {
18+
const isDarkMode = useColorScheme() === 'dark';
19+
return (
20+
<ImageBackground
21+
accessibilityRole="image"
22+
source={require('./logo.png')}
23+
style={[
24+
styles.background,
25+
{
26+
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
27+
},
28+
]}
29+
imageStyle={styles.logo}>
30+
<Text
31+
style={[
32+
styles.text,
33+
{
34+
color: isDarkMode ? Colors.white : Colors.black,
35+
},
36+
]}>
37+
Welcome to React
38+
</Text>
39+
</ImageBackground>
40+
);
41+
};
2642

2743
const styles = StyleSheet.create({
2844
background: {
2945
paddingBottom: 40,
3046
paddingTop: 96,
3147
paddingHorizontal: 32,
32-
backgroundColor: Colors.lighter,
3348
},
3449
logo: {
3550
opacity: 0.2,
@@ -48,7 +63,6 @@ const styles = StyleSheet.create({
4863
fontSize: 40,
4964
fontWeight: '600',
5065
textAlign: 'center',
51-
color: Colors.black,
5266
},
5367
});
5468

Libraries/NewAppScreen/components/LearnMoreLinks.js

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,14 @@
1212
import Colors from './Colors';
1313
import type {Node} from 'react';
1414
import openURLInBrowser from 'react-native/Libraries/Core/Devtools/openURLInBrowser';
15-
import {View, Text, StyleSheet, TouchableOpacity} from 'react-native';
16-
import React from 'react';
15+
import {
16+
StyleSheet,
17+
Text,
18+
TouchableOpacity,
19+
useColorScheme,
20+
View,
21+
} from 'react-native';
22+
import React, {Fragment} from 'react';
1723

1824
const links = [
1925
{
@@ -70,24 +76,40 @@ const links = [
7076
},
7177
];
7278

73-
const LinkList = (): Node => (
74-
<View style={styles.container}>
75-
{links.map(({id, title, link, description}) => {
76-
return (
77-
<React.Fragment key={id}>
78-
<View style={styles.separator} />
79+
const LinkList = (): Node => {
80+
const isDarkMode = useColorScheme() === 'dark';
81+
return (
82+
<View style={styles.container}>
83+
{links.map(({id, title, link, description}) => (
84+
<Fragment key={id}>
85+
<View
86+
style={[
87+
styles.separator,
88+
{
89+
backgroundColor: isDarkMode ? Colors.dark : Colors.light,
90+
},
91+
]}
92+
/>
7993
<TouchableOpacity
80-
accessibilityRole={'button'}
94+
accessibilityRole="button"
8195
onPress={() => openURLInBrowser(link)}
8296
style={styles.linkContainer}>
8397
<Text style={styles.link}>{title}</Text>
84-
<Text style={styles.description}>{description}</Text>
98+
<Text
99+
style={[
100+
styles.description,
101+
{
102+
color: isDarkMode ? Colors.lighter : Colors.dark,
103+
},
104+
]}>
105+
{description}
106+
</Text>
85107
</TouchableOpacity>
86-
</React.Fragment>
87-
);
88-
})}
89-
</View>
90-
);
108+
</Fragment>
109+
))}
110+
</View>
111+
);
112+
};
91113

92114
const styles = StyleSheet.create({
93115
container: {
@@ -112,11 +134,9 @@ const styles = StyleSheet.create({
112134
paddingVertical: 16,
113135
fontWeight: '400',
114136
fontSize: 18,
115-
color: Colors.dark,
116137
},
117138
separator: {
118-
backgroundColor: Colors.light,
119-
height: 1,
139+
height: StyleSheet.hairlineWidth,
120140
},
121141
});
122142

template/App.js

Lines changed: 82 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -7,102 +7,126 @@
77
*/
88

99
import React from 'react';
10+
import type {Node} from 'react';
1011
import {
1112
SafeAreaView,
12-
StyleSheet,
1313
ScrollView,
14-
View,
15-
Text,
1614
StatusBar,
15+
StyleSheet,
16+
Text,
17+
useColorScheme,
18+
View,
1719
} from 'react-native';
1820

1921
import {
20-
Header,
21-
LearnMoreLinks,
2222
Colors,
2323
DebugInstructions,
24+
Header,
25+
LearnMoreLinks,
2426
ReloadInstructions,
2527
} from 'react-native/Libraries/NewAppScreen';
2628

27-
const App: () => React$Node = () => {
29+
const Section = ({children, title}): Node => {
30+
const isDarkMode = useColorScheme() === 'dark';
2831
return (
29-
<>
30-
<StatusBar barStyle="dark-content" />
31-
<SafeAreaView>
32-
<ScrollView
33-
contentInsetAdjustmentBehavior="automatic"
34-
style={styles.scrollView}>
35-
<Header />
36-
{global.HermesInternal == null ? null : (
37-
<View style={styles.engine}>
38-
<Text style={styles.footer}>Engine: Hermes</Text>
39-
</View>
40-
)}
41-
<View style={styles.body}>
42-
<View style={styles.sectionContainer}>
43-
<Text style={styles.sectionTitle}>Step One</Text>
44-
<Text style={styles.sectionDescription}>
45-
Edit <Text style={styles.highlight}>App.js</Text> to change this
46-
screen and then come back to see your edits.
47-
</Text>
48-
</View>
49-
<View style={styles.sectionContainer}>
50-
<Text style={styles.sectionTitle}>See Your Changes</Text>
51-
<Text style={styles.sectionDescription}>
52-
<ReloadInstructions />
53-
</Text>
54-
</View>
55-
<View style={styles.sectionContainer}>
56-
<Text style={styles.sectionTitle}>Debug</Text>
57-
<Text style={styles.sectionDescription}>
58-
<DebugInstructions />
59-
</Text>
60-
</View>
61-
<View style={styles.sectionContainer}>
62-
<Text style={styles.sectionTitle}>Learn More</Text>
63-
<Text style={styles.sectionDescription}>
64-
Read the docs to discover what to do next:
65-
</Text>
66-
</View>
67-
<LearnMoreLinks />
68-
</View>
69-
</ScrollView>
70-
</SafeAreaView>
71-
</>
32+
<View style={styles.sectionContainer}>
33+
<Text
34+
style={[
35+
styles.sectionTitle,
36+
{
37+
color: isDarkMode ? Colors.white : Colors.black,
38+
},
39+
]}>
40+
{title}
41+
</Text>
42+
<Text
43+
style={[
44+
styles.sectionDescription,
45+
{
46+
color: isDarkMode ? Colors.light : Colors.dark,
47+
},
48+
]}>
49+
{children}
50+
</Text>
51+
</View>
52+
);
53+
};
54+
55+
const App: () => Node = () => {
56+
const isDarkMode = useColorScheme() === 'dark';
57+
58+
const backgroundStyle = {
59+
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
60+
};
61+
62+
const hermes = global.HermesInternal ? (
63+
<View style={styles.engine}>
64+
<Text
65+
style={[
66+
styles.footer,
67+
{
68+
color: isDarkMode ? Colors.light : Colors.dark,
69+
},
70+
]}>
71+
Engine: Hermes
72+
</Text>
73+
</View>
74+
) : null;
75+
76+
return (
77+
<SafeAreaView style={backgroundStyle}>
78+
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
79+
<ScrollView
80+
contentInsetAdjustmentBehavior="automatic"
81+
style={backgroundStyle}>
82+
<Header />
83+
{hermes}
84+
<View
85+
style={{
86+
backgroundColor: isDarkMode ? Colors.black : Colors.white,
87+
}}>
88+
<Section title="Step One">
89+
Edit <Text style={styles.highlight}>App.js</Text> to change this
90+
screen and then come back to see your edits.
91+
</Section>
92+
<Section title="See Your Changes">
93+
<ReloadInstructions />
94+
</Section>
95+
<Section title="Debug">
96+
<DebugInstructions />
97+
</Section>
98+
<Section title="Learn More">
99+
Read the docs to discover what to do next:
100+
</Section>
101+
<LearnMoreLinks />
102+
</View>
103+
</ScrollView>
104+
</SafeAreaView>
72105
);
73106
};
74107

75108
const styles = StyleSheet.create({
76-
scrollView: {
77-
backgroundColor: Colors.lighter,
78-
},
79109
engine: {
80110
position: 'absolute',
81111
right: 0,
82112
},
83-
body: {
84-
backgroundColor: Colors.white,
85-
},
86113
sectionContainer: {
87114
marginTop: 32,
88115
paddingHorizontal: 24,
89116
},
90117
sectionTitle: {
91118
fontSize: 24,
92119
fontWeight: '600',
93-
color: Colors.black,
94120
},
95121
sectionDescription: {
96122
marginTop: 8,
97123
fontSize: 18,
98124
fontWeight: '400',
99-
color: Colors.dark,
100125
},
101126
highlight: {
102127
fontWeight: '700',
103128
},
104129
footer: {
105-
color: Colors.dark,
106130
fontSize: 12,
107131
fontWeight: '600',
108132
padding: 4,

template/android/app/src/main/res/values/styles.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<resources>
22

33
<!-- Base application theme. -->
4-
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
4+
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
55
<!-- Customize your theme here. -->
66
<item name="android:textColor">#000000</item>
77
</style>

template/ios/HelloWorld/AppDelegate.m

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
3636
moduleName:@"HelloWorld"
3737
initialProperties:nil];
3838

39-
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
39+
if (@available(iOS 13.0, *)) {
40+
rootView.backgroundColor = [UIColor systemBackgroundColor];
41+
} else {
42+
rootView.backgroundColor = [UIColor whiteColor];
43+
}
4044

4145
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
4246
UIViewController *rootViewController = [UIViewController new];

template/ios/HelloWorld/LaunchScreen.storyboard

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,32 +16,21 @@
1616
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
1717
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
1818
<subviews>
19-
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="obG-Y5-kRd">
20-
<rect key="frame" x="0.0" y="647" width="375" height="0.0"/>
21-
<fontDescription key="fontDescription" type="system" pointSize="17"/>
22-
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
23-
<nil key="highlightedColor"/>
24-
</label>
2519
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="HelloWorld" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb">
2620
<rect key="frame" x="0.0" y="202" width="375" height="43"/>
2721
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
28-
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
2922
<nil key="highlightedColor"/>
3023
</label>
3124
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Powered by React Native" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="MN2-I3-ftu">
3225
<rect key="frame" x="0.0" y="626" width="375" height="21"/>
3326
<fontDescription key="fontDescription" type="system" pointSize="17"/>
34-
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
3527
<nil key="highlightedColor"/>
3628
</label>
3729
</subviews>
38-
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
30+
<color key="backgroundColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
3931
<constraints>
40-
<constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="obG-Y5-kRd" secondAttribute="centerX" id="5cz-MP-9tL"/>
4132
<constraint firstItem="Bcu-3y-fUS" firstAttribute="bottom" secondItem="MN2-I3-ftu" secondAttribute="bottom" constant="20" id="OZV-Vh-mqD"/>
4233
<constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="GJd-Yh-RWb" secondAttribute="centerX" id="Q3B-4B-g5h"/>
43-
<constraint firstItem="obG-Y5-kRd" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" symbolic="YES" id="SfN-ll-jLj"/>
44-
<constraint firstAttribute="bottom" secondItem="obG-Y5-kRd" secondAttribute="bottom" constant="20" id="Y44-ml-fuU"/>
4534
<constraint firstItem="MN2-I3-ftu" firstAttribute="centerX" secondItem="Bcu-3y-fUS" secondAttribute="centerX" id="akx-eg-2ui"/>
4635
<constraint firstItem="MN2-I3-ftu" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" id="i1E-0Y-4RG"/>
4736
<constraint firstItem="GJd-Yh-RWb" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="bottom" multiplier="1/3" constant="1" id="moa-c2-u7t"/>

0 commit comments

Comments
 (0)