Skip to content

Ekran — Profil i Ranking

Przegląd

Ekran profilu dziecka wyświetla awatar, statystyki, kolekcję odznak i dostęp do ustawień. Opcjonalnie zawiera sekcję rankingu (leaderboard) z tabelą wyników i pozycją aktualnego użytkownika.

Routing (GoRouter)

dart
// Profil jest częścią ShellRoute (bottom nav)
GoRoute(path: '/profile', builder: (_, __) => ProfileScreen()),

// Szczegóły odznaki
GoRoute(
  path: '/profile/badge/:badgeId',
  builder: (context, state) => BadgeDetailScreen(
    badgeId: state.pathParameters['badgeId']!,
  ),
),

// Ustawienia
GoRoute(path: '/profile/settings', builder: (_, __) => SettingsScreen()),

// Ranking (opcjonalnie osobna strona lub sekcja)
GoRoute(path: '/profile/ranking', builder: (_, __) => RankingScreen()),

Riverpod Providers

dart
// Profil użytkownika (reuse z spaceship)
final userProfileProvider = FutureProvider<UserProfile>((ref) {
  return ref.watch(userRepositoryProvider).getMe();
});

// Statystyki (reuse z spaceship)
final userStatsProvider = FutureProvider<UserStats>((ref) {
  return ref.watch(userRepositoryProvider).getStats();
});

// Pełna lista odznak
final allBadgesProvider = FutureProvider<List<Badge>>((ref) {
  return ref.watch(userRepositoryProvider).getBadges();
});

// Ranking (paginowany)
final rankingProvider = StateNotifierProvider<RankingNotifier, PaginatedList<RankingEntry>>(
  (ref) => RankingNotifier(ref.watch(rankingRepositoryProvider)),
);

// Pozycja aktualnego użytkownika w rankingu
final myRankProvider = FutureProvider<int>((ref) {
  return ref.watch(rankingRepositoryProvider).getMyRank();
});

Profil

ASCII Wireframe

┌──────────────────────────────────┐
│  Profil                     ⚙️  │
├──────────────────────────────────┤
│                                  │
│         ┌──────────────┐         │
│         │              │         │
│         │    👤        │         │
│         │   (awatar)   │         │
│         │              │         │
│         └──────────────┘         │
│                                  │
│          Kacper                   │
│          Poziom 5                 │
│  ████████████░░░░ 1240/1500 XP   │
│                                  │
│  ┌────────────────────────────┐  │
│  │                            │  │
│  │  ┌──────┐ ┌──────┐        │  │
│  │  │  42  │ │   3  │        │  │
│  │  │zadań │ │światów│        │  │
│  │  │ukończ│ │odwiedz│        │  │
│  │  └──────┘ └──────┘        │  │
│  │  ┌──────┐ ┌──────┐        │  │
│  │  │  7   │ │  12  │        │  │
│  │  │ dni  │ │odznak│        │  │
│  │  │streak│ │      │        │  │
│  │  └──────┘ └──────┘        │  │
│  │                            │  │
│  └────────────────────────────┘  │
│                                  │
│  ── Moje odznaki (12) ──        │
│                                  │
│  ┌────┐ ┌────┐ ┌────┐ ┌────┐   │
│  │ 🏅 │ │ 🎖️ │ │ 🥇 │ │ 🏆 │   │
│  │Dino│ │Geog│ │Quiz│ │7dni│   │
│  └────┘ └────┘ └────┘ └────┘   │
│  ┌────┐ ┌────┐ ┌────┐ ┌────┐   │
│  │ 🌟 │ │ 📚 │ │ 🎨 │ │ 🎮 │   │
│  │Lvl5│ │10vid│ │Art │ │Game│   │
│  └────┘ └────┘ └────┘ └────┘   │
│  ┌────┐ ┌────┐ ┌────┐ ┌────┐   │
│  │ 🔬 │ │ 🌍 │ │ 🎧 │ │ 🧩 │   │
│  │Lab │ │Glob│ │Pod │ │Puzz│   │
│  └────┘ └────┘ └────┘ └────┘   │
│                                  │
│  ── Ranking ──                   │
│  [ ZOBACZ RANKING → ]            │
│                                  │
├──────────────────────────────────┤
│  🌍        🪐        🚀     👤  │
│ Globus    Kosmos    Statek  Profil│
└──────────────────────────────────┘

Szczegóły odznaki (bottom sheet)

┌──────────────────────────────────┐
│                                  │
│         ┌──────────┐             │
│         │   🏅     │             │
│         │  (duża   │             │
│         │  ikona)  │             │
│         └──────────┘             │
│                                  │
│    Odkrywca Dinozaurów           │
│                                  │
│    Ukończ wszystkie zadania      │
│    o dinozaurach.                │
│                                  │
│    Zdobyta: 5 marca 2026         │
│                                  │
│    [ ZAMKNIJ ]                    │
│                                  │
└──────────────────────────────────┘

Komponenty — Profil

KomponentOpis
AvatarDisplayAwatar dziecka (duży, display mode)
UserNameLevelImię + numer poziomu
XpProgressBarPasek XP z liczbami (current / next level)
StatsGridSiatka 2x2 ze statystykami
StatCardKarta statystyki: duża liczba + label
BadgeCollectionSiatka odznak (GridView, 4 kolumny)
BadgeTileKafelek odznaki: ikona + krótka nazwa
BadgeDetailSheetBottom sheet ze szczegółami odznaki
RankingLinkPrzycisk "ZOBACZ RANKING"
SettingsButtonIkona ustawień w app bar

Logika — Profil

  1. Przy wejściu → pobranie: profil, stats, badges
  2. Awatar wyświetlony z aktualnym ubraniem
  3. Statystyki w siatce 2x2
  4. Odznaki w grid — tap → BadgeDetailSheet
  5. "ZOBACZ RANKING" → nawigacja do /profile/ranking
  6. Ikona ⚙️ → nawigacja do /profile/settings

Endpointy — Profil

MetodaEndpointOpis
GET/app/users/meProfil użytkownika
GET/app/users/me/statsStatystyki
GET/app/users/me/badgesPełna lista odznak

Ranking

ASCII Wireframe

┌──────────────────────────────────┐
│  ← Ranking                      │
├──────────────────────────────────┤
│                                  │
│  ┌────────────────────────────┐  │
│  │ 🥇 1. Zosia      2450 XP  │  │
│  │    👧                      │  │
│  └────────────────────────────┘  │
│  ┌────────────────────────────┐  │
│  │ 🥈 2. Kuba       2380 XP  │  │
│  │    🧒                      │  │
│  └────────────────────────────┘  │
│  ┌────────────────────────────┐  │
│  │ 🥉 3. Marysia    2210 XP  │  │
│  │    👧                      │  │
│  └────────────────────────────┘  │
│  ┌────────────────────────────┐  │
│  │    4. Tomek       2100 XP  │  │
│  │    🧒                      │  │
│  └────────────────────────────┘  │
│  ┌────────────────────────────┐  │
│  │    5. Jan         1950 XP  │  │
│  │    🧒                      │  │
│  └────────────────────────────┘  │
│                                  │
│  ...                             │
│                                  │
│  ┌────────────────────────────┐  │
│  │   12. Ola        1400 XP  │  │
│  │    👧                      │  │
│  └────────────────────────────┘  │
│                                  │
│  ════════════════════════════    │
│  ┌────────────────────────────┐  │
│  │ ★ 8. Kacper     1240 XP ★ │  │
│  │    👤 (Ty)                 │  │
│  └────────────────────────────┘  │
│  ════════════════════════════    │
│                                  │
│  (ładowanie kolejnych...)        │
│                                  │
└──────────────────────────────────┘

Komponenty — Ranking

KomponentOpis
RankingListListView z infinite scroll (cursor pagination)
RankingEntryWiersz: pozycja, awatar, imię, XP
RankingEntry.podiumTop 3: medal (🥇🥈🥉), większy awatar, wyróżniony kolor
RankingEntry.currentAktualny użytkownik: wyróżniony tłem (brand-primary/10%), gwiazdki
MyRankStickySticky footer z pozycją aktualnego użytkownika (widoczny gdy user nie jest na ekranie)

Logika — Ranking

  1. Przy wejściu → GET /app/rankings (pierwsza strona)
  2. Infinite scroll → kolejne strony (cursor pagination)
  3. Pozycja aktualnego użytkownika wyróżniona w liście
  4. Jeśli użytkownik nie jest widoczny na ekranie → sticky footer z jego pozycją
  5. Top 3 z medalami i wyróżnionym stylem

Endpoint — Ranking

MetodaEndpointOpis
GET/app/rankingsTablica wyników

Query params:

ParamTypDomyślnieOpis
cursorstringKursor paginacji
limitint20Maks. elementów

Response:

json
{
  "data": [
    {
      "position": 1,
      "user_id": "child-zosia",
      "display_name": "Zosia",
      "avatar_url": "https://...",
      "xp": 2450,
      "level": 7,
      "is_current_user": false
    },
    {
      "position": 2,
      "user_id": "child-kuba",
      "display_name": "Kuba",
      "avatar_url": "https://...",
      "xp": 2380,
      "level": 7,
      "is_current_user": false
    },
    {
      "position": 8,
      "user_id": "child-1",
      "display_name": "Kacper",
      "avatar_url": "https://...",
      "xp": 1240,
      "level": 5,
      "is_current_user": true
    }
  ],
  "next_cursor": "eyJwb3MiOjIwfQ==",
  "has_more": true,
  "my_position": 8
}

Ustawienia

ASCII Wireframe

┌──────────────────────────────────┐
│  ← Ustawienia                    │
├──────────────────────────────────┤
│                                  │
│  Konto                           │
│  ┌────────────────────────────┐  │
│  │ Zmień PIN            →    │  │
│  ├────────────────────────────┤  │
│  │ Zmień język           →    │  │
│  ├────────────────────────────┤  │
│  │ Powiąż z rodzicem     →    │  │
│  └────────────────────────────┘  │
│                                  │
│  Aplikacja                       │
│  ┌────────────────────────────┐  │
│  │ Motyw          [Auto ▼]   │  │
│  ├────────────────────────────┤  │
│  │ Dźwięki        [● włącz]  │  │
│  ├────────────────────────────┤  │
│  │ Powiadomienia  [● włącz]  │  │
│  └────────────────────────────┘  │
│                                  │
│  Informacje                      │
│  ┌────────────────────────────┐  │
│  │ O aplikacji           →    │  │
│  ├────────────────────────────┤  │
│  │ Polityka prywatności  →    │  │
│  └────────────────────────────┘  │
│                                  │
│  [ WYLOGUJ SIĘ ]                 │
│                                  │
│  Wersja 1.0.0 (build 42)        │
│                                  │
└──────────────────────────────────┘

Komponenty — Ustawienia

KomponentOpis
SettingsSectionSekcja z nagłówkiem i listą opcji
SettingsItem.navigationOpcja z chevronem (→) — nawigacja
SettingsItem.toggleOpcja z Switch (toggle)
SettingsItem.dropdownOpcja z dropdown (np. motyw)
LogoutButtonPrzycisk wylogowania (destructive style)
VersionLabelLabel z wersją aplikacji

Stany Ekranu

Profil

StanWidok
ŁadowanieSkeleton awatara + skeleton stats + skeleton badges
DanePełny profil z awatarem, statystykami, odznakami
BłądErrorView z przyciskiem retry

Ranking

StanWidok
ŁadowanieSkeleton entries (5 wierszy z shimmer)
DaneLista rankingowa z wyróżnionym userem
Pusty"Brak danych rankingu"
BłądErrorView z przyciskiem retry
Ładowanie więcejSpinner na dole listy

Uwagi Techniczne

  • Profil i stats cachowane z TTL 5 min — odświeżanie pull-to-refresh
  • Badges ładowane pełną listą (nie paginowane — typowo max 50-100)
  • Ranking paginowany kursorem (20 per strona)
  • Sticky footer z pozycją użytkownika — Stack z Positioned na dole
  • Top 3 ranking entries: większy rozmiar (height 80 vs 60), gradient tła
  • Wylogowanie czyści token z Secure Storage + redirect na /welcome
  • Ustawienia motywu zapisywane lokalnie (SharedPreferences)

Lumos Islands v2 - Dokumentacja Projektowa