Kurs React Native · Moduł 2: Listy i style

StatusBar i Theming

3 min czytania ·
W tej lekcji2

Kontroluj wygląd status bara i stwórz system motywów dla aplikacji.

StatusBar

import { SafeAreaView, View, Text, StyleSheet, StatusBar, Pressable } from 'react-native';
import { useState } from 'react';

export default function StatusBarExample() {
  const [isDark, setIsDark] = useState(true);

  return (
    <SafeAreaView style={[
      styles.container,
      { backgroundColor: isDark ? '#1a5276' : '#ecf0f1' }
    ]}>
      <StatusBar
        barStyle={isDark ? 'light-content' : 'dark-content'}
        backgroundColor={isDark ? '#1a5276' : '#ecf0f1'}
        animated={true}
      />

      <View style={styles.content}>
        <Text style={[styles.title, { color: isDark ? '#fff' : '#2c3e50' }]}>
          Status Bar Demo
        </Text>

        <Pressable
          style={[styles.button, { backgroundColor: isDark ? '#2980b9' : '#3498db' }]}
          onPress={() => setIsDark(!isDark)}
        >
          <Text style={styles.buttonText}>
            Przełącz na {isDark ? 'jasny' : 'ciemny'} motyw
          </Text>
        </Pressable>
      </View>
    </SafeAreaView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  content: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    padding: 20,
  },
  title: {
    fontSize: 24,
    fontWeight: 'bold',
    marginBottom: 30,
  },
  button: {
    paddingVertical: 14,
    paddingHorizontal: 24,
    borderRadius: 8,
  },
  buttonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '600',
  },
});

System motywów z Context

// context/ThemeContext.tsx
import { createContext, useContext, useState, ReactNode } from 'react';

interface Theme {
  dark: boolean;
  colors: {
    background: string;
    card: string;
    text: string;
    textSecondary: string;
    primary: string;
    border: string;
  };
}

const lightTheme: Theme = {
  dark: false,
  colors: {
    background: '#f5f6fa',
    card: '#ffffff',
    text: '#2c3e50',
    textSecondary: '#7f8c8d',
    primary: '#3498db',
    border: '#dcdde1',
  },
};

const darkTheme: Theme = {
  dark: true,
  colors: {
    background: '#1a5276',
    card: '#2c3e50',
    text: '#ffffff',
    textSecondary: '#bdc3c7',
    primary: '#3498db',
    border: '#34495e',
  },
};

interface ThemeContextType {
  theme: Theme;
  toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

export function ThemeProvider({ children }: { children: ReactNode }) {
  const [isDark, setIsDark] = useState(true);

  const theme = isDark ? darkTheme : lightTheme;

  const toggleTheme = () => setIsDark(!isDark);

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

export function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within ThemeProvider');
  }
  return context;
}
// components/ThemedCard.tsx
import { View, Text, StyleSheet } from 'react-native';
import { useTheme } from '../context/ThemeContext';

interface ThemedCardProps {
  title: string;
  description: string;
}

export default function ThemedCard({ title, description }: ThemedCardProps) {
  const { theme } = useTheme();

  return (
    <View style={[styles.card, { backgroundColor: theme.colors.card }]}>
      <Text style={[styles.title, { color: theme.colors.text }]}>
        {title}
      </Text>
      <Text style={[styles.description, { color: theme.colors.textSecondary }]}>
        {description}
      </Text>
    </View>
  );
}

const styles = StyleSheet.create({
  card: {
    padding: 16,
    borderRadius: 12,
    marginBottom: 12,
  },
  title: {
    fontSize: 18,
    fontWeight: '600',
    marginBottom: 4,
  },
  description: {
    fontSize: 14,
  },
});
// App.tsx
import { SafeAreaView, View, StyleSheet, StatusBar, Pressable, Text } from 'react-native';
import { ThemeProvider, useTheme } from './context/ThemeContext';
import ThemedCard from './components/ThemedCard';

function HomeScreen() {
  const { theme, toggleTheme } = useTheme();

  return (
    <SafeAreaView style={[styles.container, { backgroundColor: theme.colors.background }]}>
      <StatusBar barStyle={theme.dark ? 'light-content' : 'dark-content'} />

      <View style={styles.content}>
        <ThemedCard
          title="Skarb Atlantydy"
          description="Legendarny klejnot z głębin oceanu"
        />
        <ThemedCard
          title="Trójząb Posejdona"
          description="Broń władcy mórz"
        />

        <Pressable
          style={[styles.button, { backgroundColor: theme.colors.primary }]}
          onPress={toggleTheme}
        >
          <Text style={styles.buttonText}>
            {theme.dark ? 'Jasny motyw' : 'Ciemny motyw'}
          </Text>
        </Pressable>
      </View>
    </SafeAreaView>
  );
}

export default function App() {
  return (
    <ThemeProvider>
      <HomeScreen />
    </ThemeProvider>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  content: {
    flex: 1,
    padding: 20,
  },
  button: {
    marginTop: 20,
    paddingVertical: 14,
    borderRadius: 8,
    alignItems: 'center',
  },
  buttonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '600',
  },
});

Gratulacje! Opanowałeś/aś zaawansowane komponenty i stylowanie w React Native!

Kod do tej lekcji: App.tsx
import React, { createContext, useContext, useState, ReactNode } from 'react';
import { SafeAreaView, View, Text, Pressable, StyleSheet } from 'react-native';

const lightTheme = {
  dark: false,
  background: '#f5f6fa',
  card: '#ffffff',
  text: '#2c3e50',
};

const darkTheme = {
  dark: true,
  background: '#1a5276',
  card: '#2c3e50',
  text: '#ffffff',
};

const ThemeContext = createContext({ theme: darkTheme, toggleTheme: () => {} });

function ThemeProvider({ children }: { children: ReactNode }) {
  const [isDark, setIsDark] = useState(true);

  // TODO: wybierz motyw na podstawie stanu isDark.
  //   const theme = isDark ? darkTheme : lightTheme;
  const theme = darkTheme;

  // TODO: stwórz funkcję toggleTheme, która odwraca isDark.
  //   const toggleTheme = () => setIsDark(!isDark);
  const toggleTheme = () => {};

  // TODO: przekaż { theme, toggleTheme } do propsa value Providera.
  return (
    <ThemeContext.Provider value={{ theme: darkTheme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

function HomeScreen() {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <SafeAreaView style={[styles.container, { backgroundColor: theme.background }]}>
      <View style={[styles.card, { backgroundColor: theme.card }]}>
        <Text style={[styles.text, { color: theme.text }]}>Skarb Atlantydy</Text>
      </View>
      <Pressable style={styles.button} onPress={toggleTheme}>
        <Text style={styles.buttonText}>
          {theme.dark ? 'Jasny motyw' : 'Ciemny motyw'}
        </Text>
      </Pressable>
    </SafeAreaView>
  );
}

export default function App() {
  return (
    <ThemeProvider>
      <HomeScreen />
    </ThemeProvider>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
  },
  card: {
    padding: 16,
    borderRadius: 12,
    marginBottom: 20,
  },
  text: {
    fontSize: 18,
    fontWeight: '600',
  },
  button: {
    paddingVertical: 14,
    borderRadius: 8,
    alignItems: 'center',
    backgroundColor: '#3498db',
  },
  buttonText: {
    color: '#fff',
    fontSize: 16,
    fontWeight: '600',
  },
});

Hasła z dokumentacji w tej lekcji

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Która wartość barStyle StatusBar pokazuje jasne ikony (dla ciemnego tła)?

  2. 2. Co zwraca hook useColorScheme()?

Zadania praktyczne w grze

  • Układanie w pionie

    Uporządkuj kroki tworzenia systemu motywów z Context API:

  • Edytor kodu

    Uzupełnij kod ThemeProvider i ThemeContext.

Przydatne artykuły