I'm pretty sure this is my first question I'm posting on here, so I hope I do this properly and provide all of the relevant information.
Context:
This is a twofold personal project. I am building this app strictly for my partner and I, so no major constraints and I'm open to all suggestions/recommendations. Her and I both travel often and speak several foreign languages, but we are both still learning more everyday and wish for a personalized app we can both use and share together, so I began to create this app for our communication and language development skills.
This project also happens to be a great opportunity for me to learn and grow my skills with React Native & native Kotlin/Swift development (I tried JavaFx for a while, but I'm a full-time front end web developer so React Native seems like a much more natural approach for UI development and Kotlin/Swift for native/back end).
It's essentially just a study app, combining features I like from Quizlet, Anki, Pimsleur, etc.., and combining some chat functionality amongst other things, but that's out of scope for this issue. Please see my problem statement for the main issue.
Problem statement:
I am having an issue with the StatusBar at the top of the screen still leaving a blank white space after setting it to be hidden. This issue is persisting despite everything I've tried and researching already.
Goal:
I wish to have this display either the background color I desire when the StatusBar is hidden, or the ImageBackground depending on which activity/screen it's on. i.e. No white space or notch at the top of the screen.
What I've already tried:
- I've already tried
backgroundColor
as transparent and set the translucent
property to true. - In app.json I've tried configuring it to use
dark
for the userInterfaceStyle property. - I've tried using
react-native-safe-area-context
package with their SafeAreaProvider
around the entire app and used SafeAreaView
around the component itself (I'm aware that the regular SafeAreaView is for iOS only and I need this for Android specifically, I just thought maybe this context package was worth a shot though). - Set
backgroundColor
to black - Set
style
to dark and barStyle
to dark-content - I've tried using
expo-status-bar
and react-native
StatusBar (oddly enough, even though I'm using expo, I had even more styling issues with the expo-status-bar)
I'm running out of ideas to try.. None of the above did anything when implementing it. As soon as I hide the StatusBar, I always have that white space at the top and it's quite annoying.
Code:
app.json
{"expo": {"name": "...","slug": "...","version": "1.0.0","orientation": "portrait","icon": "./assets/icon.png","userInterfaceStyle": "dark","splash": {"image": "./assets/....gif","resizeMode": "contain","backgroundColor": "#000000" },"assetBundlePatterns": ["**/*" ],"ios": {"supportsTablet": true,"bundleIdentifier": "com...." },"android": {"package": "com....","adaptiveIcon": {"foregroundImage": "./assets/....gif","backgroundColor": "#000000" } },"androidStatusBar": {"barStyle": "dark-content","backgroundColor": "#000000","hidden": true,"translucent": true },"web": {"favicon": "./assets/favicon.png" } }}
Flashcards.js
import { useState, useEffect } from 'react';import { View, Text, Pressable, StyleSheet, FlatList, Dimensions, ImageBackground, StatusBar } from 'react-native';import { SafeAreaView } from 'react-native-safe-area-context';import Logo from '../assets/....gif';const DEVICE_WIDTH = Dimensions.get('window').width;const SAMPLE = [ { id: 0, name: "Русскиекарточки", description: "These are sample Russian flashcards.", }, { id: 1, name: "Flashcards tiếng Việt", description: "These are sample Vietnamese flashcards.", }, { id: 2, name: '中文抽認卡', description: 'These are sample Chinese flashcards.', }, { id: 3, name: 'כרטיסיפלאשבעברית', description: 'These are sample Hebrew flashcards.', }, { id: 4, name: 'Data structures', description: 'These are sample data structure flashcards.', }, { id: 5, name: 'Countries', description: 'These are sample culture flashcards from countries around the world.', }, { id: 6, name: 'Cultures', description: 'These are sample cultural tradition flashcards.', }, { id: 7, name: 'Exercises', description: 'These are sample flashcards about healthy exercises.', }, { id: 8, name: 'Final', description: 'This is the final collection just being used to see if it will make it scrollable.', }];export default function Flashcards({ navigation }) { const [collections, setCollections] = useState([]); const [selectedCollection, setSelectedCollection] = useState(null); useEffect(() => { setCollections(SAMPLE) }, []) const Item = ({ name, onPress, description }) => (<Pressable onPress={onPress} style={styles.item}><Text style={styles.itemTitle}>{name}</Text><Text style={styles.itemDescription}>{description}</Text></Pressable> ); const handleClick = (item) => { setSelectedCollection(item.id); navigation.navigate('StudyMode', { name: item.name, }); }; return (<SafeAreaView style={styles.container}><StatusBar hidden backgroundColor="black" // translucent style="dark" barStyle="dark-content" /><ImageBackground source={Logo} style={styles.image} resizeMode="cover"><View style={styles.titleContainer}><Text style={styles.title}>Flashcards</Text></View><View style={styles.collectionsContainer}><FlatList data={SAMPLE} style={styles.flatlist} renderItem={({ item }) => { return <Item onPress={() => handleClick(item)} name={item.name} description={item.description} /> }} keyExtractor={item => item.id} /></View></ImageBackground></SafeAreaView> )}const styles = StyleSheet.create({ container: { flex: 1, flexDirection: 'column', marginTop: StatusBar.currentHeight + 10 || 10, backgroundColor: '#000000', alignItems: 'center', justifyContent: 'center', paddingBottom: 60, }, image: { flex: 1, justifyContent: 'center', contentFit: 'cover', }, titleContainer: { borderBottomWidth: 6, borderColor: 'red', borderRadius: 12, backgroundColor: 'black', justifyContent: 'center', alignItems: 'center', marginLeft: 12, marginRight: 12, marginTop: 14, marginBottom: 10, }, title: { color: 'white', fontFamily: 'Agright', fontSize: 32, }, collectionsContainer: { borderWidth: 6, borderColor: 'red', borderRadius: 12, backgroundColor: 'black', width: DEVICE_WIDTH - 10, }, flatlist: { flexGrow: 0, height: '100%', }, item: { borderRadius: 12, borderWidth: 2, borderColor: 'red', width: '99%', alignItems: 'center', marginLeft: 2, marginRight: 2, marginTop: 6, marginBottom: 6, paddingVertical: 6, backgroundColor: '#000000', }, itemTitle: { fontFamily: 'Agright', fontSize: 26, color: 'white', }, itemDescription: { fontFamily: 'Agright', fontSize: 13, color: 'white', }});
App.js
import { useState, useEffect, useContext } from 'react';import { NavigationContainer } from '@react-navigation/native';import { createNativeStackNavigator } from '@react-navigation/native-stack';import { SafeAreaProvider } from 'react-native-safe-area-context';import * as SplashScreen from 'expo-splash-screen';import * as Font from 'expo-font';import { Platform, NativeModules } from 'react-native';import { IntlProvider } from 'react-intl';import en from './locales/en.json';import he from './locales/he.json';import ru from './locales/ru.json';import uk from './locales/uk.json';import vi from './locales/vi.json';import zh from './locales/zh.json';/** * Components */import Login from './screens/Login';import Home from './screens/Home';import Flashcards from './screens/Flashcards';import StudyMode from './screens/StudyMode';import Texts from './screens/Texts';import Translator from './screens/Translator';import Dictionary from './screens/Dictionary';import Chat from './screens/Chat';/** * Hooks */import useFonts from './hooks/useFonts';/** * Context */import { LocaleContextProvider, LocaleContext } from './context/LocaleContext';import { DbContextProvider } from './database/DbContext';/** * Messages (Localization) */const messages = {'en': en,'en_US': en,'he': he,'ru': ru,'uk': uk,'vi': vi,'zh': zh,};const devicePlatform = Platform.OS;const _deviceLocale = devicePlatform === 'android' ? NativeModules.I18nManager.localeIdentifier : NativeModules.SettingsManager.settings.AppleLocale || NativeModules.SettingsManager.settings.AppleLanguages[0];const deviceLocale = _deviceLocale.split(/[-_]/)[0];SplashScreen.preventAutoHideAsync();const Stack = createNativeStackNavigator();const screenOptions = { headerStyle: { backgroundColor: '#000000', }, headerTintColor: '#ffffff', headerTitleStyle: { fontFamily: 'Agright', textAlign: 'center' }, headerTitleAlign: 'center',};export default function App() { const [appIsReady, setAppIsReady] = useState(false); const [isAuthNd, setIsAuthNd] = useState(false); const [isVisible, setIsVisible] = useState(false); const [fontsLoaded] = Font.useFonts({ Agright: require('./assets/fonts/AgrightRegular-qZ5dr.otf'), }) const { changeLanguage, currentLang } = useContext(LocaleContext); const loadFonts = async () => { try { await Font.loadAsync({ Agright: require('./assets/fonts/AgrightRegular-qZ5dr.otf'), Fridays: require('./assets/fonts/Fridays-AWjM.ttf'), MyChemicalRomance: require('./assets/fonts/MyChemicalRomance-1X5Z.ttf'), Toxia: require('./assets/fonts/Toxia-OwOA.ttf'), Varukers: require('./assets/fonts/VarukersPersonalUse-K70Be.ttf'), }); } catch (err) { console.error(err, err.stack); } finally { setAppIsReady(true); } }; const loadHomeScreen = async () => { if (appIsReady) { await SplashScreen.hideAsync(); } }; useEffect(() => { if (fontsLoaded) { setAppIsReady(true); } }, [fontsLoaded]); useEffect(() => { if (appIsReady) { loadHomeScreen(); } }, [appIsReady]) if (!fontsLoaded) { return null; } return (<SafeAreaProvider><DbContextProvider><LocaleContextProvider><IntlProvider messages={messages[currentLang]} locale={deviceLocale} defaultLocale="en"><NavigationContainer><Stack.Navigator> {!isAuthNd ? (<Stack.Screen name="Login" component={Login} options={{ ...screenOptions, headerShown: false, }} /> ) : (<Stack.Screen name="Home" component={Home} options={{ ...screenOptions, headerShown: false, }} /> ) }<Stack.Screen name="Home" component={Home} options={{ ...screenOptions, headerShown: false, }} /><Stack.Screen name="Flashcards" component={Flashcards} options={{ ...subScreenOptions, headerShown: false, }} /><Stack.Screen name="StudyMode" component={StudyMode} options={{ ...subScreenOptions, headerShown: false, }} /><Stack.Screen name="Texts" component={Texts} options={{ ...subScreenOptions, headerShown: false, }} /><Stack.Screen name="Translator" component={Translator} options={{ ...subScreenOptions, headerShown: false, }} /><Stack.Screen name="Dictionary" component={Dictionary} options={{ ...screenOptions, headerShown: false, }} /><Stack.Screen name="Chat" component={Chat} options={{ ...subScreenOptions, headerShown: false, }} /></Stack.Navigator></NavigationContainer></IntlProvider></LocaleContextProvider></DbContextProvider></SafeAreaProvider> );}
Using
"dependencies": {"@react-navigation/native": "^6.1.6","@react-navigation/native-stack": "^6.9.12","@react-oauth/google": "^0.11.0","axios": "^1.4.0","expo": "~48.0.15","expo-application": "~5.1.1","expo-asset": "~8.9.1","expo-auth-session": "~4.0.3","expo-crypto": "~12.2.1","expo-file-system": "~15.2.2","expo-font": "^11.1.1","expo-image": "~1.0.1","expo-splash-screen": "~0.18.2","expo-sqlite": "~11.1.1","expo-web-browser": "~12.1.1","isaac": "^0.0.5","react": "18.2.0","react-intl": "^6.4.4","react-native": "0.71.8","react-native-bcrypt": "^2.4.0","react-native-crypto": "^2.2.0","react-native-safe-area-context": "4.5.0","uuid": "^9.0.0","expo-system-ui": "~2.2.1" },"devDependencies": {"@babel/core": "^7.20.0" },
Thank you so much for your help!