ScaleRocket/Mobile

Thème

Mode sombre avec useColorScheme, détection du thème système et contexte de thème pour un style cohérent.

Thème

ScaleRocket Mobile supporte les modes clair et sombre nativement, en suivant le thème système de l'appareil par défaut avec la possibilité de le remplacer manuellement.

Détection du thème système

React Native fournit useColorScheme pour détecter le réglage de l'appareil :

import { useColorScheme } from "react-native";

export default function MyScreen() {
  const colorScheme = useColorScheme(); // "light" | "dark" | null

  return (
    <View style={{
      backgroundColor: colorScheme === "dark" ? "#111827" : "#FFFFFF"
    }}>
      <Text style={{ color: colorScheme === "dark" ? "#F9FAFB" : "#111827" }}>
        Hello World
      </Text>
    </View>
  );
}

Contexte de thème

Au lieu d'appeler useColorScheme partout, ScaleRocket utilise un contexte de thème centralisé :

import { createContext, useContext, useState, useEffect } from "react";
import { useColorScheme as useDeviceTheme } from "react-native";
import AsyncStorage from "@react-native-async-storage/async-storage";

type Theme = "light" | "dark" | "system";

interface ThemeContextType {
  theme: Theme;
  colorScheme: "light" | "dark";
  setTheme: (theme: Theme) => void;
  colors: typeof lightColors;
}

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

export function ThemeProvider({ children }: { children: React.ReactNode }) {
  const deviceTheme = useDeviceTheme();
  const [theme, setThemeState] = useState<Theme>("system");

  useEffect(() => {
    AsyncStorage.getItem("theme").then((saved) => {
      if (saved) setThemeState(saved as Theme);
    });
  }, []);

  const setTheme = (newTheme: Theme) => {
    setThemeState(newTheme);
    AsyncStorage.setItem("theme", newTheme);
  };

  const colorScheme = theme === "system"
    ? (deviceTheme ?? "light")
    : theme;

  const colors = colorScheme === "dark" ? darkColors : lightColors;

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

export const useTheme = () => useContext(ThemeContext)!;

Jetons de couleur

Définissez des jetons de couleur sémantiques pour les deux thèmes :

export const lightColors = {
  background: "#FFFFFF",
  surface: "#F9FAFB",
  text: "#111827",
  textSecondary: "#6B7280",
  primary: "#3B82F6",
  border: "#E5E7EB",
  error: "#EF4444",
  success: "#10B981",
};

export const darkColors = {
  background: "#111827",
  surface: "#1F2937",
  text: "#F9FAFB",
  textSecondary: "#9CA3AF",
  primary: "#60A5FA",
  border: "#374151",
  error: "#F87171",
  success: "#34D399",
};

Utilisation du thème dans les composants

Accédez aux couleurs depuis le contexte de thème :

import { useTheme } from "@/lib/theme";

function ProfileCard() {
  const { colors } = useTheme();

  return (
    <View style={{
      backgroundColor: colors.surface,
      borderColor: colors.border,
      borderWidth: 1,
      borderRadius: 12,
      padding: 16,
    }}>
      <Text style={{ color: colors.text, fontSize: 18, fontWeight: "600" }}>
        Profile
      </Text>
      <Text style={{ color: colors.textSecondary, marginTop: 4 }}>
        Edit your information
      </Text>
    </View>
  );
}

Sélecteur de thème

Laissez les utilisateurs choisir entre clair, sombre et système :

function ThemeSettings() {
  const { theme, setTheme } = useTheme();

  const options: { label: string; value: Theme }[] = [
    { label: "Light", value: "light" },
    { label: "Dark", value: "dark" },
    { label: "System", value: "system" },
  ];

  return (
    <View style={{ gap: 8 }}>
      {options.map((option) => (
        <TouchableOpacity
          key={option.value}
          onPress={() => setTheme(option.value)}
          style={{
            flexDirection: "row",
            justifyContent: "space-between",
            padding: 16,
          }}
        >
          <Text>{option.label}</Text>
          {theme === option.value && (
            <Ionicons name="checkmark" size={20} color="#3B82F6" />
          )}
        </TouchableOpacity>
      ))}
    </View>
  );
}

Configuration dans le Root Layout

// app/_layout.tsx
import { ThemeProvider } from "@/lib/theme";

export default function RootLayout() {
  return (
    <ThemeProvider>
      <Slot />
    </ThemeProvider>
  );
}

Thème de la barre de navigation

Mettez à jour la barre d'état et la barre de navigation pour correspondre :

import { StatusBar } from "expo-status-bar";

function App() {
  const { colorScheme } = useTheme();
  return <StatusBar style={colorScheme === "dark" ? "light" : "dark"} />;
}

On this page