Lekcja 22. Preferences w .NET MAUI — zapamiętywanie ustawień użytkownika
ŚredniPo 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
Dodaj
Pickerz wyborem rozmiaru czcionki (Mały/Średni/Duży) zapisywany w kolejnym kluczu Preferences, i zastosuj wybrany rozmiar doFontSizegłównej etykiety strony.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ą).Dodaj przycisk usuwający TYLKO zapamiętane imię (przez
Preferences.Default.Remove(KluczImie)), bez czyszczenia pozostałych ustawień — a dodatkowo, wykorzystującContainsKey, 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 typu — Preferences.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).