Ekran — Mapa Kraju
Przegląd
Mapa kraju to ilustrowany widok 2D z pinezkami reprezentującymi lokacje w danym świecie (kraju). Pinezki mają trzy stany: zablokowana (szara), odblokowana (kolorowa), ukończona (złota gwiazdka). Dziecko tapuje pinezkę, żeby przejść do ekranu lokacji.
Routing (GoRouter)
dart
GoRoute(
path: '/world/:worldId',
builder: (context, state) => MapScreen(
worldId: state.pathParameters['worldId']!,
),
),Riverpod Providers
dart
// Szczegóły świata (nazwa, opis, tło mapy)
final worldDetailProvider = FutureProvider.family<World, String>((ref, worldId) {
return ref.watch(worldRepositoryProvider).getWorld(worldId);
});
// Lista lokacji na mapie
final worldLocationsProvider = FutureProvider.family<List<Location>, String>((ref, worldId) {
return ref.watch(worldRepositoryProvider).getLocations(worldId);
});
// Aktualnie wybrana lokacja (po tapnięciu pinu)
final selectedLocationProvider = StateProvider<Location?>((ref) => null);ASCII Wireframe
┌──────────────────────────────────┐
│ ← Polska 🇵🇱 │
├──────────────────────────────────┤
│ │
│ ┌────────────────────────────┐ │
│ │ │ │
│ │ (ilustrowana mapa) │ │
│ │ │ │
│ │ 🔒 ⭐ │ │
│ │ Fabryka Biblioteka │ │
│ │ │ │
│ │ 📍 │ │
│ │ Camp │ │
│ │ │ │
│ │ 📍 🔒 │ │
│ │ Boisko Muzeum │ │
│ │ │ │
│ │ 📍 │ │
│ │ Park │ │
│ │ │ │
│ └────────────────────────────┘ │
│ │
│ Legenda: │
│ 📍 Odblokowana ⭐ Ukończona │
│ 🔒 Zablokowana │
│ │
│ Lokacje: 3/5 odblokowane │
│ Postęp: ████████░░ 60% │
│ │
└──────────────────────────────────┘Stan po tapnięciu pinezki
┌──────────────────────────────────┐
│ ← Polska 🇵🇱 │
├──────────────────────────────────┤
│ │
│ ┌────────────────────────────┐ │
│ │ │ │
│ │ (mapa przygaszona) │ │
│ │ │ │
│ │ [📍] │ │
│ │ (pulsuje) │ │
│ │ │ │
│ └────────────────────────────┘ │
│ │
│ ┌────────────────────────────┐ │
│ │ │ │
│ │ 📍 Camp Odkrywców │ │
│ │ │ │
│ │ 🎬 3 video 📝 5 quizów │ │
│ │ 🎧 2 podcasty 🎮 1 gra │ │
│ │ │ │
│ │ Postęp: ████░░░░ 40% │ │
│ │ XP do zdobycia: 450 │ │
│ │ │ │
│ │ [ WEJDŹ → ] │ │
│ │ │ │
│ └────────────────────────────┘ │
│ │
└──────────────────────────────────┘Pinezka zablokowana — tap
┌────────────────────────────────┐
│ │
│ 🔒 Fabryka Wynalazków │
│ │
│ Żeby odblokować tę lokację, │
│ ukończ 3 zadania w Camp │
│ Odkrywców. │
│ │
│ Postęp: ██░░░░ 1/3 │
│ │
│ [ OK ] │
│ │
└────────────────────────────────┘Komponenty
| Komponent | Opis |
|---|---|
MapView | Widok mapy: ilustrowane tło + pinezki. Obsługuje pan i zoom |
LocationPin | Pinezka na mapie — 3 warianty wizualne |
LocationPin.locked | Szara pinezka z ikoną kłódki |
LocationPin.unlocked | Kolorowa pinezka (kolor zależny od typu lokacji) |
LocationPin.completed | Złota pinezka z gwiazdką |
LocationInfoCard | Bottom sheet z informacjami o lokacji po tapnięciu pinu |
LockedInfoDialog | Dialog wyjaśniający warunki odblokowania |
MapAppBar | App bar z przyciskiem back, nazwą kraju i flagą |
ProgressBar | Pasek postępu świata (% ukończonych lokacji) |
MapLegend | Legenda stanów pinezek |
Logika
- Przy wejściu — pobranie danych świata (
GET /app/worlds/:id) i lokacji (GET /app/worlds/:id/locations) - Pinezki rozmieszczone na mapie na podstawie współrzędnych
x,y(0.0 - 1.0, relatywne do tła) - Tap na odblokowaną pinezkę →
LocationInfoCard(bottom sheet) - Tap na zablokowaną pinezkę →
LockedInfoDialogz warunkami odblokowania - Przycisk "WEJDŹ" → nawigacja do
/location/:locationId - Przycisk back → powrót na globus (
context.pop())
Warunki odblokowania lokacji
Lokacje mogą być blokowane na podstawie:
- Ukończenia N zadań w innej lokacji
- Osiągnięcia określonego poziomu XP
- Ukończenia konkretnego zadania (quest chain)
Warunki przychodzą z API w polu unlock_conditions.
Endpointy
| Metoda | Endpoint | Opis |
|---|---|---|
| GET | /app/worlds/:id | Szczegóły świata (nazwa, opis, mapa) |
| GET | /app/worlds/:id/locations | Lista lokacji na mapie |
World Response:
json
{
"data": {
"id": "world-pl",
"name": "Polska",
"flag_emoji": "🇵🇱",
"description": "Odkryj najpiękniejsze miejsca w Polsce!",
"map_background_url": "https://...",
"location_count": 5,
"completed_count": 2,
"progress_percent": 40
}
}Locations Response:
json
{
"data": [
{
"id": "loc-camp",
"name": "Camp Odkrywców",
"type": "camp",
"status": "unlocked",
"x": 0.45,
"y": 0.55,
"task_summary": {
"video": 3,
"quiz": 5,
"podcast": 2,
"game": 1,
"creative": 0
},
"progress_percent": 40,
"xp_available": 450,
"thumbnail_url": "https://..."
},
{
"id": "loc-factory",
"name": "Fabryka Wynalazków",
"type": "factory",
"status": "locked",
"x": 0.20,
"y": 0.25,
"unlock_conditions": {
"type": "tasks_completed",
"location_id": "loc-camp",
"required": 3,
"current": 1
},
"thumbnail_url": "https://..."
},
{
"id": "loc-library",
"name": "Biblioteka",
"type": "library",
"status": "completed",
"x": 0.75,
"y": 0.20,
"task_summary": {
"video": 2,
"quiz": 4,
"podcast": 1,
"game": 0,
"creative": 1
},
"progress_percent": 100,
"xp_available": 0,
"thumbnail_url": "https://..."
}
]
}Stany Pinezek
| Status | Wygląd | Interakcja |
|---|---|---|
locked | Szara ikona z kłódką, cień minimalny | Tap → LockedInfoDialog |
unlocked | Kolorowa ikona (brand-primary), cień, lekka animacja pulsowania | Tap → LocationInfoCard |
completed | Złota ikona z gwiazdką, efekt shimmer | Tap → LocationInfoCard (z odznaką ukończenia) |
Stany Ekranu
| Stan | Widok |
|---|---|
| Ładowanie | Mapa z skeleton pinezkami (pulsujące kółka) |
| Dane | Pełna mapa z pinezkami w odpowiednich stanach |
| Błąd | Mapa bez pinezek + ErrorView z przyciskiem retry |
Uwagi Techniczne
- Tło mapy to ilustracja (PNG/SVG) dostarczona przez API (
map_background_url) - Pinezki pozycjonowane za pomocą
Stack+Positioned(fractional coordinates) - Pan i zoom obsługiwane przez
InteractiveViewer - Animacja wejścia pinezek — pojawiają się sekwencyjnie (staggered animation, 50ms delay)
- Cache tła mapy w pamięci (CachedNetworkImage)