Ekran — Statek i Garderoba
Przegląd
Statek to osobista strefa dziecka — hub z awatarem, todo listą, odznakami, XP i skrótami do garderoby i profilu. Garderoba to ekran personalizacji awatara z kategoriami ubrań i akcesoriów.
Routing (GoRouter)
dart
// Statek jest częścią ShellRoute (bottom nav)
GoRoute(path: '/spaceship', builder: (_, __) => SpaceshipScreen()),
// Garderoba jako sub-route
GoRoute(path: '/spaceship/wardrobe', builder: (_, __) => WardrobeScreen()),Riverpod Providers
dart
// Profil użytkownika
final userProfileProvider = FutureProvider<UserProfile>((ref) {
return ref.watch(userRepositoryProvider).getMe();
});
// Statystyki użytkownika (XP, level, streak)
final userStatsProvider = FutureProvider<UserStats>((ref) {
return ref.watch(userRepositoryProvider).getStats();
});
// Todo lista (3 ostatnie na statek, pełna w profilu)
final todosProvider = FutureProvider<List<Todo>>((ref) {
return ref.watch(todoRepositoryProvider).getTodos();
});
// Ostatnie 5 odznak
final recentBadgesProvider = FutureProvider<List<Badge>>((ref) {
return ref.watch(userRepositoryProvider).getBadges();
});
// ── Garderoba ──
// Katalog przedmiotów awatara (pogrupowane wg kategorii)
final avatarItemsProvider = FutureProvider<AvatarItemCatalog>((ref) {
return ref.watch(avatarRepositoryProvider).getItems();
});
// Aktualnie wybrane przedmioty (preview)
final avatarPreviewProvider = StateNotifierProvider<AvatarPreviewNotifier, AvatarConfig>((ref) {
return AvatarPreviewNotifier(ref.watch(userProfileProvider).value?.avatar);
});
// Aktywna kategoria w garderobie
final wardrobeCategoryProvider = StateProvider<WardrobeCategory>((ref) {
return WardrobeCategory.hats;
});Statek (Spaceship Screen)
ASCII Wireframe
┌──────────────────────────────────┐
│ Mój Statek ⚙️ │
├──────────────────────────────────┤
│ │
│ ┌──────────────┐ │
│ │ │ │
│ │ 👤 │ │
│ │ (awatar │ │
│ │ dziecka) │ │
│ │ │ │
│ └──────────────┘ │
│ │
│ Kacper Poziom 5 │
│ ████████████░░░░ 1240/1500 XP │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ 👕 │ │ 👤 │ │
│ │Garderoba │ │ Profil │ │
│ └──────────┘ └──────────┘ │
│ │
│ ── Zadania do zrobienia ── │
│ │
│ ┌────────────────────────────┐ │
│ │ ○ Obejrzyj film o │ │
│ │ dinozaurach │ │
│ ├────────────────────────────┤ │
│ │ ○ Rozwiąż quiz │ │
│ │ "Zwierzęta Polski" │ │
│ ├────────────────────────────┤ │
│ │ ✅ Narysuj swoje │ │
│ │ wymarzone zwierzę │ │
│ └────────────────────────────┘ │
│ Zobacz wszystkie → │
│ │
│ ── Moje odznaki ── │
│ │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ 🏅 │ │ 🎖️ │ │ 🥇 │ │ 🏆 │ │
│ └────┘ └────┘ └────┘ └────┘ │
│ +3 więcej → │
│ │
├──────────────────────────────────┤
│ 🌍 🪐 🚀 👤 │
│ Globus Kosmos Statek Profil│
└──────────────────────────────────┘Komponenty — Statek
| Komponent | Opis |
|---|---|
AvatarDisplay | Duży awatar dziecka (center, animowany idle) |
XpLevelBar | Pasek XP z numerem poziomu, imię dziecka |
QuickLinkCard | Karta szybkiego dostępu: Garderoba, Profil |
TodoPreviewList | Lista 3 ostatnich todo (z checkboxami) |
TodoItem | Pojedyncze todo: checkbox + tekst + opcjonalny deadline |
BadgeShowcase | Horyzontalny rząd ostatnich odznak (max 5) |
BadgeTile | Ikona odznaki (tappable → detail) |
SectionHeader | Nagłówek sekcji z linkiem "Zobacz wszystkie" |
Logika — Statek
- Przy wejściu → równoległe pobranie: profil, stats, todos, badges
- Awatar wyświetlony z aktualnym ubraniem
- Todo lista — checkbox toggle →
PATCH /app/todos/:id - "Garderoba" → nawigacja do
/spaceship/wardrobe - "Profil" → nawigacja do
/profile - "Zobacz wszystkie" (todo) → nawigacja do pełnej listy todo
- Tap na odznakę → bottom sheet ze szczegółami odznaki
Endpointy — Statek
| Metoda | Endpoint | Opis |
|---|---|---|
| GET | /app/users/me | Profil użytkownika |
| GET | /app/users/me/stats | Statystyki (XP, level, streak) |
| GET | /app/todos | Lista zadań do zrobienia |
| GET | /app/users/me/badges | Zdobyte odznaki |
| PATCH | /app/todos/:id | Zmień status todo |
User Profile Response:
json
{
"data": {
"id": "child-1",
"display_name": "Kacper",
"avatar": {
"face": "smile_01",
"hair": "curly_brown",
"hat": "cap_red",
"top": "tshirt_blue",
"bottom": "jeans_dark",
"shoes": "sneakers_white",
"accessories": ["backpack_green"]
},
"avatar_url": "https://..."
}
}Stats Response:
json
{
"data": {
"level": 5,
"xp": 1240,
"xp_to_next_level": 1500,
"tasks_completed": 42,
"worlds_visited": 3,
"days_streak": 7,
"badges_count": 12
}
}Todos Response:
json
{
"data": [
{
"id": "todo-1",
"text": "Obejrzyj film o dinozaurach",
"status": "pending",
"source": "parent",
"task_id": "task-v1",
"due_date": null
},
{
"id": "todo-2",
"text": "Rozwiąż quiz \"Zwierzęta Polski\"",
"status": "pending",
"source": "system",
"task_id": "task-q1",
"due_date": "2026-03-15"
},
{
"id": "todo-3",
"text": "Narysuj swoje wymarzone zwierzę",
"status": "completed",
"source": "parent",
"task_id": "task-c1",
"due_date": null
}
]
}Badges Response:
json
{
"data": [
{
"id": "badge-dino",
"name": "Odkrywca Dinozaurów",
"description": "Ukończ wszystkie zadania o dinozaurach",
"icon_url": "https://...",
"earned_at": "2026-03-05T14:30:00Z"
}
]
}Garderoba (Wardrobe Screen)
ASCII Wireframe
┌──────────────────────────────────┐
│ ← Garderoba Zapisz │
├──────────────────────────────────┤
│ │
│ ┌──────────────┐ │
│ │ │ │
│ │ 👤 │ │
│ │ (podgląd │ │
│ │ awatara │ │
│ │ na żywo) │ │
│ │ │ │
│ └──────────────┘ │
│ │
├──────────────────────────────────┤
│ [🎩][💇][😊][👕][👖][👟][💎] │
│ czap włos twarz góra dół buty │
│ dodat. │
├──────────────────────────────────┤
│ │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ 🎩 │ │ 🧢 │ │ 🎓 │ │ 👑 │ │
│ │ │ │ │ │ │ │ 🔒 │ │
│ └────┘ └────┘ └────┘ └────┘ │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ 🤠 │ │ 🪖 │ │ ⛑️ │ │ 🎀 │ │
│ │ 🔒 │ │ 🔒 │ │ │ │ │ │
│ └────┘ └────┘ └────┘ └────┘ │
│ ┌────┐ ┌────┐ ┌────┐ ┌────┐ │
│ │ 🧢 │ │ 🎩 │ │ 🪄 │ │ ❌ │ │
│ │ │ │ 🔒 │ │ 🔒 │ │brak│ │
│ └────┘ └────┘ └────┘ └────┘ │
│ │
└──────────────────────────────────┘Po wybraniu zablokowanego przedmiotu
┌────────────────────────────────┐
│ │
│ 🔒 Korona Królewska │
│ │
│ Żeby odblokować ten │
│ przedmiot, zdobądź │
│ odznakę "Król Wiedzy". │
│ │
│ Postęp: ██████░░░░ 60% │
│ │
│ [ OK ] │
│ │
└────────────────────────────────┘Komponenty — Garderoba
| Komponent | Opis |
|---|---|
AvatarPreview | Podgląd awatara na żywo (aktualizuje się przy wyborze) |
CategoryTabBar | Zakładki kategorii (ikony): czapki, włosy, twarz, góra, dół, buty, dodatki |
ItemGrid | Siatka przedmiotów (GridView, 4 kolumny) |
ItemTile | Kafelek przedmiotu: ikona/miniatura + stan (odblokowany / zablokowany) |
ItemTile.unlocked | Jasny kafelek, tappable, zaznaczony = ramka brand-primary |
ItemTile.locked | Przyciemniony kafelek z ikoną kłódki |
ItemTile.none | Kafelek "brak" (❌) — zdjęcie przedmiotu z tej kategorii |
LockedItemDialog | Dialog z informacją jak odblokować przedmiot |
SaveButton | Przycisk "Zapisz" w app bar (aktywny gdy zmieniony) |
Logika — Garderoba
- Przy wejściu → pobranie katalogu przedmiotów (
GET /app/avatar/items) - Podgląd awatara inicjalizowany z aktualnego profilu
- Tap na kategorię → zmiana wyświetlanej siatki przedmiotów
- Tap na odblokowany przedmiot → aktualizacja podglądu awatara (live preview)
- Tap na zablokowany przedmiot →
LockedItemDialog - Tap na "brak" → usunięcie przedmiotu z kategorii (np. zdejmij czapkę)
- "Zapisz" →
PATCH /app/avatar→ powrót do statku
Kategorie przedmiotów
| Kategoria | Klucz | Ikona |
|---|---|---|
| Czapki | hats | 🎩 |
| Włosy | hair | 💇 |
| Twarz | face | 😊 |
| Góra | tops | 👕 |
| Dół | bottoms | 👖 |
| Buty | shoes | 👟 |
| Dodatki | accessories | 💎 |
Endpointy — Garderoba
| Metoda | Endpoint | Opis |
|---|---|---|
| GET | /app/avatar/items | Katalog przedmiotów awatara |
| PATCH | /app/avatar | Zmień wygląd awatara |
Avatar Items Response:
json
{
"data": {
"categories": [
{
"key": "hats",
"label": "Czapki",
"items": [
{
"id": "hat-cap-red",
"name": "Czerwona czapka",
"thumbnail_url": "https://...",
"unlocked": true,
"equipped": true
},
{
"id": "hat-crown",
"name": "Korona Królewska",
"thumbnail_url": "https://...",
"unlocked": false,
"unlock_condition": {
"type": "badge",
"badge_id": "badge-king",
"badge_name": "Król Wiedzy",
"progress_percent": 60
}
}
]
},
{
"key": "hair",
"label": "Włosy",
"items": [
{
"id": "hair-curly-brown",
"name": "Kręcone brązowe",
"thumbnail_url": "https://...",
"unlocked": true,
"equipped": true
}
]
}
]
}
}PATCH Avatar Request:
json
{
"hat": "hat-cap-red",
"hair": "hair-curly-brown",
"face": "smile_01",
"top": "tshirt_blue",
"bottom": "jeans_dark",
"shoes": "sneakers_white",
"accessories": ["backpack_green", "glasses_round"]
}PATCH Avatar Response:
json
{
"data": {
"avatar_url": "https://...",
"updated": true
}
}Stany Ekranu
Statek
| Stan | Widok |
|---|---|
| Ładowanie | Skeleton awatara + skeleton sekcji |
| Dane | Pełny layout z awatarem, todo, odznakami |
| Błąd | ErrorView z przyciskiem retry |
Garderoba
| Stan | Widok |
|---|---|
| Ładowanie | Skeleton preview + skeleton grid |
| Dane | Podgląd awatara + siatka przedmiotów |
| Zapisywanie | SaveButton z loading spinner |
| Błąd zapisu | Snackbar z komunikatem + przycisk retry |
| Bez zmian | SaveButton disabled (szary) |
Uwagi Techniczne
- Awatar renderowany jako kompozycja warstw (Stack z PNG): tło → twarz → włosy → ubranie → czapka → dodatki
- Live preview: każda zmiana natychmiast aktualizuje Stack (bez requestu do API)
- Zapis tylko przy "Zapisz" (nie automatyczny)
- Cache przedmiotów awatara — katalog rzadko się zmienia
- Todo toggle:
PATCH /app/todos/:idz body{ "status": "completed" }— optimistic update - Sekcja odznak: tap → bottom sheet z nazwą, opisem, datą zdobycia