Dataniedziela, 9 sierpnia 2026 Czas07:51:34
← Aplikacje mobilne (.NET MAUI)

Lekcja 22. Preferences w .NET MAUI — zapamiętywanie ustawień użytkownika

Średni

Po co się tego uczymy?

Prawie każda aplikacja mobilna ma jakieś ustawienia użytkownika — wybrany motyw kolorystyczny, włączone/wyłączone powiadomienia, zapamiętane imię. Zapisywanie takich MAŁYCH pojedynczych wartości przez pełną bazę SQLite albo plik JSON byłoby przesadą — do tego istnieje znacznie prostszy mechanizm: klasa Preferences, przechowująca dane jako proste pary klucz-wartość.

Teoria

Preferences to wbudowany w MAUI mechanizm przechowywania MAŁYCH ustawień jako par klucz-wartość (podobnie do localStorage w przeglądarce, jeśli znasz JavaScript). NIE jest to baza danych i NIE nadaje się do przechowywania dużych list obiektów (do tego służy SQLite z wcześniejszych lekcji) ani danych wrażliwych jak hasła czy tokeny (do tego służy osobna klasa SecureStorage, szyfrująca zawartość) — Preferences to najlepsze narzędzie do prostych rzeczy typu: imię użytkownika, wybrany motyw, stan przełącznika.

Podstawowe metody (wszystkie dostępne przez Preferences.Default):

Metoda Zastosowanie
Set(klucz, wartosc) zapisuje wartość pod danym kluczem (nadpisuje, jeśli klucz już istniał)
Get(klucz, wartoscDomyslna) odczytuje wartość; jeśli klucz nie istnieje, zwraca podaną wartość domyślną (NIGDY nie rzuca wyjątku braku klucza)
ContainsKey(klucz) sprawdza, czy dany klucz w ogóle istnieje w zapisanych preferencjach
Remove(klucz) usuwa POJEDYNCZĄ wartość spod podanego klucza
Clear() usuwa WSZYSTKIE zapisane preferencje aplikacji naraz

Preferences obsługuje bezpośrednio proste typy danych: string, int, bool, double, float, long, DateTime — nie trzeba (i nie da się wprost) zapisać w ten sposób całej listy czy własnej klasy złożonej; do tego, jeśli naprawdę potrzeba, trzeba by ręcznie zserializować dane do pojedynczego stringa JSON.

Stosowanie motywu programistycznie. Właściwość Application.Current.UserAppTheme (typu AppTheme: Unspecified/Light/Dark) pozwala wymusić jasny lub ciemny motyw całej aplikacji z poziomu kodu — niezależnie od ustawień systemowych urządzenia. Dobra praktyka: zapisany wybór motywu odczytujemy i STOSUJEMY od razu przy starcie aplikacji (nie tylko po kliknięciu "Zapisz"), żeby użytkownik zawsze widział swój ostatnio wybrany motyw.

Schemat

Start aplikacji (konstruktor MainPage)
        │
        ▼
WczytajUstawienia()
        │
        ├── Preferences.Default.Get("uzytkownik_imie", "")       → ImieEntry.Text
        ├── Preferences.Default.Get("uzytkownik_motyw", 0)       → MotywPicker.SelectedIndex
        ├── Preferences.Default.Get("uzytkownik_powiadomienia", true) → PowiadomieniaSwitch.IsToggled
        │
        ▼
ZastosujMotyw(...) → Application.Current.UserAppTheme = ...

Button "Zapisz ustawienia" (Clicked)
        │
        ▼
Preferences.Default.Set(klucz, aktualnaWartoscZKontrolki) ×3
        │
        ▼
ZastosujMotyw(...) ponownie

Button "Przywróć domyślne" (Clicked)
        │
        ▼
Preferences.Default.Clear() → WczytajUstawienia() (wraca do wartości domyślnych)

Przykład z życia

Aplikacja do czytania e-booków zapamiętuje wybrany rozmiar czcionki i motyw (jasny/ciemny/nocny), aplikacja fitness zapamiętuje jednostki (kg czy funty), a aplikacja z listą zadań zapamiętuje, czy pokazywać wykonane pozycje — wszystkie te drobne, pojedyncze ustawienia idealnie pasują do mechanizmu Preferences, bez potrzeby uruchamiania całej bazy danych dla kilku wartości.

MainPage.xaml

<!-- MainPage.xaml -->
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="UstawieniaMauiApp.MainPage"
             Title="Ustawienia">

    <VerticalStackLayout Padding="24" Spacing="14">
        <Label Text="Ustawienia użytkownika" FontSize="24" FontAttributes="Bold" />

        <Entry x:Name="ImieEntry" Placeholder="Imię użytkownika" />

        <Picker x:Name="MotywPicker" Title="Wybierz motyw">
            <Picker.Items>
                <x:String>Systemowy</x:String>
                <x:String>Jasny</x:String>
                <x:String>Ciemny</x:String>
            </Picker.Items>
        </Picker>

        <HorizontalStackLayout>
            <Label Text="Powiadomienia" VerticalOptions="Center" />
            <Switch x:Name="PowiadomieniaSwitch" />
        </HorizontalStackLayout>

        <Button Text="Zapisz ustawienia" Clicked="Zapisz_Clicked" />
        <Button Text="Przywróć domyślne" Clicked="Wyczysc_Clicked" />
        <Label x:Name="StatusLabel" />
    </VerticalStackLayout>
</ContentPage>

MainPage.xaml.cs

// MainPage.xaml.cs
using Microsoft.Maui.Storage;

namespace UstawieniaMauiApp;

public partial class MainPage : ContentPage
{
    private const string KluczImie = "uzytkownik_imie";
    private const string KluczMotyw = "uzytkownik_motyw";
    private const string KluczPowiadomienia = "uzytkownik_powiadomienia";

    public MainPage()
    {
        InitializeComponent();
        WczytajUstawienia();
    }

    private void WczytajUstawienia()
    {
        ImieEntry.Text = Preferences.Default.Get(KluczImie, "");
        MotywPicker.SelectedIndex = Preferences.Default.Get(KluczMotyw, );
        PowiadomieniaSwitch.IsToggled = Preferences.Default.Get(KluczPowiadomienia, true);

        ZastosujMotyw(MotywPicker.SelectedIndex);
    }

    private void Zapisz_Clicked(object sender, EventArgs e)
    {
        Preferences.Default.Set(KluczImie, ImieEntry.Text ?? "");
        Preferences.Default.Set(KluczMotyw, MotywPicker.SelectedIndex);
        Preferences.Default.Set(KluczPowiadomienia, PowiadomieniaSwitch.IsToggled);

        ZastosujMotyw(MotywPicker.SelectedIndex);
        StatusLabel.Text = "Ustawienia zostały zapisane.";
    }

    private void Wyczysc_Clicked(object sender, EventArgs e)
    {
        Preferences.Default.Clear();
        WczytajUstawienia();
        StatusLabel.Text = "Przywrócono ustawienia domyślne.";
    }

    private static void ZastosujMotyw(int indeks)
    {
        Application.Current!.UserAppTheme = indeks switch
        {
            1 => AppTheme.Light,
            2 => AppTheme.Dark,
            _ => AppTheme.Unspecified
        };
    }
}

Komentarz i wyjaśnienie kodu

Klucze (KluczImie, KluczMotyw, KluczPowiadomienia) są zdefiniowane jako stałe (const) na górze klasy — dzięki temu literówka w nazwie klucza spowoduje błąd kompilacji (odwołanie do nieistniejącej stałej), a nie ciche, trudne do znalezienia niedopasowanie stringów w różnych miejscach kodu.

ZastosujMotyw jest wywoływana w DWÓCH miejscach: raz przy starcie (WczytajUstawienia), żeby motyw był poprawny od razu po otwarciu aplikacji, i drugi raz po kliknięciu "Zapisz", żeby zmiana była widoczna natychmiast, bez potrzeby restartowania aplikacji. Wyrażenie switch (tzw. switch expression) to zwięzły sposób zapisania wielowariantowego przypisania — czytelniejszy niż seria if/else if dla trzech możliwych wartości.

Ćwiczenie samodzielne

Utwórz projekt z powyższym przykładem. Zapisz jakieś ustawienia, ZAMKNIJ aplikację całkowicie, uruchom ponownie i sprawdź, czy pola formularza są wypełnione zapamiętanymi wartościami (a nie wartościami domyślnymi) — to potwierdzi, że dane faktycznie przetrwały restart aplikacji.

Zadania do pracy własnej

  1. Dodaj Picker z wyborem rozmiaru czcionki (Mały/Średni/Duży) zapisywany w kolejnym kluczu Preferences, i zastosuj wybrany rozmiar do FontSize głównej etykiety strony.

  2. Dodaj zapamiętywanie DATY ostatniego uruchomienia aplikacji (Preferences.Default.Set("ostatnie_uruchomienie", DateTime.Now) przy starcie) i wyświetl użytkownikowi komunikat w stylu "Ostatnio byłeś tu: [data]" przy KOLEJNYM uruchomieniu (przed nadpisaniem nową datą).

  3. Dodaj przycisk usuwający TYLKO zapamiętane imię (przez Preferences.Default.Remove(KluczImie)), bez czyszczenia pozostałych ustawień — a dodatkowo, wykorzystując ContainsKey, pokaż przy starcie aplikacji specjalny ekran powitalny "Witaj po raz pierwszy!" WYŁĄCZNIE wtedy, gdy klucz z imieniem jeszcze nigdy nie został zapisany.

Typowe błędy

Próba przechowywania w Preferences danych wrażliwych (hasła, tokeny logowania) — Preferences NIE szyfruje danych; do takich informacji zawsze używać SecureStorage.

Próba zapisania w Preferences całej listy albo obiektu złożonego bezpośrednio — mechanizm obsługuje tylko proste typy danych; dla list/obiektów trzeba by ręcznie zserializować je do jednego stringa (np. JSON) i tak zapisać, choć zwykle to znak, że lepiej pasuje SQLite.

Odczyt bez podania wartości domyślnej dopasowanej do typuPreferences.Default.Get(klucz, 0) zwróci int, a próba przypisania wyniku do zmiennej string nie skompiluje się; wartość domyślna podana w Get określa też typ zwracanej wartości.

Nawiązanie do egzaminu zawodowego

To bezpośrednia realizacja wymagania INF.04.6.2 "przechowywanie danych/preferencji" — jeden z najprostszych, a zarazem najczęściej wykorzystywanych mechanizmów trwałości danych w aplikacjach mobilnych. Wraz z plikami TXT/JSON i bazą SQLite z wcześniejszych lekcji, znasz teraz WSZYSTKIE trzy poziomy przechowywania danych w MAUI: proste ustawienia (Preferences), średniej wielkości dane (pliki), i duże, ustrukturyzowane zbiory danych (SQLite).