Ekran — Wykonanie Zadania
Przegląd
Ekran wykonania zadania obsługuje 5 typow zadan, kazdy z dedykowanym layoutem: quiz, video, podcast, kreatywne, gra. Wszystkie typy dzielą wspólny progress bar, podgląd XP do zdobycia oraz animację ukończenia (confetti, XP, odznaka, level up).
Routing (GoRouter)
dart
GoRoute(
path: '/task/:taskId',
builder: (context, state) => TaskScreen(
taskId: state.pathParameters['taskId']!,
),
),Riverpod Providers
dart
// Pełne dane zadania
final taskDetailProvider = FutureProvider.family<TaskDetail, String>((ref, taskId) {
return ref.watch(taskRepositoryProvider).getTask(taskId);
});
// Stan wykonania zadania (odpowiedzi, postęp)
final taskProgressProvider = StateNotifierProvider.family<TaskProgressNotifier, TaskProgress, String>(
(ref, taskId) => TaskProgressNotifier(taskId),
);
// Aktualny krok/pytanie w zadaniu
final currentStepProvider = StateProvider.family<int, String>((ref, taskId) => 0);
// Wynik po ukończeniu (XP, badge, level up)
final taskResultProvider = StateProvider<TaskResult?>((ref) => null);
// Stan audio/video playera
final mediaPlayerProvider = StateNotifierProvider<MediaPlayerNotifier, MediaPlayerState>(
(ref) => MediaPlayerNotifier(),
);Layouty per Typ Zadania
1. Quiz
┌──────────────────────────────────┐
│ ← Quiz ✕ │
├──────────────────────────────────┤
│ ████████░░░░░░░░ 4/10 │
│ │
│ +50 XP │
│ │
│ ┌────────────────────────────┐ │
│ │ Pytanie 4: │ │
│ │ │ │
│ │ Jak nazywa się najwyższa │ │
│ │ góra w Polsce? │ │
│ │ │ │
│ │ (opcjonalnie: obrazek) │ │
│ └────────────────────────────┘ │
│ │
│ ┌────────────────────────────┐ │
│ │ ○ Rysy │ │
│ └────────────────────────────┘ │
│ ┌────────────────────────────┐ │
│ │ ○ Giewont │ │
│ └────────────────────────────┘ │
│ ┌────────────────────────────┐ │
│ │ ● Śnieżka ← wyb. │ │
│ └────────────────────────────┘ │
│ ┌────────────────────────────┐ │
│ │ ○ Kasprowy Wierch │ │
│ └────────────────────────────┘ │
│ │
│ [ SPRAWDŹ ] │
│ │
└──────────────────────────────────┘Po sprawdzeniu (poprawna):
┌──────────────────────────────────┐
│ ┌────────────────────────────┐ │
│ │ ✅ Rysy ← poprawna│ │
│ └────────────────────────────┘ │
│ ┌────────────────────────────┐ │
│ │ ○ Giewont │ │
│ └────────────────────────────┘ │
│ ┌────────────────────────────┐ │
│ │ ❌ Śnieżka ← błędna│ │
│ └────────────────────────────┘ │
│ ┌────────────────────────────┐ │
│ │ ○ Kasprowy Wierch │ │
│ └────────────────────────────┘ │
│ │
│ 💡 Rysy (2499 m) to najwyższy │
│ szczyt w polskich Tatrach. │
│ │
│ [ NASTĘPNE → ] │
└──────────────────────────────────┘2. Video
┌──────────────────────────────────┐
│ ← Video ✕ │
├──────────────────────────────────┤
│ │
│ ┌────────────────────────────┐ │
│ │ │ │
│ │ │ │
│ │ ▶ (video player) │ │
│ │ │ │
│ │ │ │
│ │ ▶ ████████░░ 5:32 / 8:00 │ │
│ └────────────────────────────┘ │
│ │
│ Dinozaury i ich świat │
│ +50 XP │
│ │
│ ── Po obejrzeniu ── │
│ │
│ Pytanie 1/3: │
│ Kiedy wyginęły dinozaury? │
│ │
│ ○ 65 mln lat temu │
│ ○ 100 mln lat temu │
│ ○ 10 mln lat temu │
│ │
│ [ SPRAWDŹ ] │
│ │
└──────────────────────────────────┘3. Podcast
┌──────────────────────────────────┐
│ ← Podcast ✕ │
├──────────────────────────────────┤
│ ████████████████ ukończony │
│ │
│ ┌────────────────────────────┐ │
│ │ │ │
│ │ (cover art) │ │
│ │ │ │
│ └────────────────────────────┘ │
│ │
│ Tajemnice oceanu │
│ +30 XP │
│ │
│ ┌────────────────────────────┐ │
│ │ ◀◀ ▶ / ⏸ ▶▶ │ │
│ │ ████████░░░░ 12:30/20:00 │ │
│ │ 1x 1.5x 2x │ │
│ └────────────────────────────┘ │
│ │
│ 📜 Transkrypt │
│ ┌────────────────────────────┐ │
│ │ [12:30] Oceany pokrywają │ │
│ │ ponad 70% powierzchni │ │
│ │ Ziemi. W głębinach żyją │ │
│ │ stworzenia, których... │ │
│ │ │ │
│ │ [13:15] Najgłębszy punkt │ │
│ │ to Rów Mariański... │ │
│ └────────────────────────────┘ │
│ │
│ [ UKOŃCZ PODCAST ] │
│ │
└──────────────────────────────────┘4. Zadanie Kreatywne
┌──────────────────────────────────┐
│ ← Kreatywne ✕ │
├──────────────────────────────────┤
│ +40 XP │
│ │
│ Narysuj swoje wymarzone │
│ zwierzę! │
│ │
│ Opisz, jakie ma cechy │
│ i gdzie mieszka. │
│ │
│ ┌────────────────────────────┐ │
│ │ │ │
│ │ Wybierz sposób: │ │
│ │ │ │
│ │ ┌──────┐ ┌──────┐ │ │
│ │ │ 📷 │ │ 🎨 │ │ │
│ │ │Zdjęcie│ │Rysunek│ │ │
│ │ └──────┘ └──────┘ │ │
│ │ ┌──────┐ │ │
│ │ │ 📝 │ │ │
│ │ │ Tekst│ │ │
│ │ └──────┘ │ │
│ │ │ │
│ └────────────────────────────┘ │
│ │
│ ┌────────────────────────────┐ │
│ │ Opis (opcjonalnie): │ │
│ │ _________________________ │ │
│ │ _________________________ │ │
│ └────────────────────────────┘ │
│ │
│ [ WYŚLIJ PRACĘ ] │
│ │
└──────────────────────────────────┘5. Mini-gra
┌──────────────────────────────────┐
│ ← Gra ✕ │
├──────────────────────────────────┤
│ Labirynt Wiedzy +60 XP │
│ │
│ ┌────────────────────────────┐ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ (embedded mini-game │ │
│ │ WebView / Flutter │ │
│ │ canvas / Flame) │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ │ │ │
│ └────────────────────────────┘ │
│ │
│ Czas: 02:45 Wynik: 850 │
│ │
└──────────────────────────────────┘Animacja Ukończenia
Po zakończeniu zadania (dowolny typ) wyświetlany jest ekran z wynikami:
┌──────────────────────────────────┐
│ │
│ 🎉 CONFETTI 🎉 │
│ │
│ │
│ ⭐ ŚWIETNIE! ⭐ │
│ │
│ │
│ ┌──────────────┐ │
│ │ +50 XP │ │
│ └──────────────┘ │
│ │
│ XP: 1240 → 1290 │
│ ██████████████░░ Lvl 5 │
│ │
│ ┌────────────────────────────┐ │
│ │ 🏅 Nowa odznaka! │ │
│ │ "Odkrywca Dinozaurów" │ │
│ └────────────────────────────┘ │
│ │
│ ┌────────────────────────────┐ │
│ │ 🎉 LEVEL UP! │ │
│ │ Poziom 5 → 6 │ │
│ └────────────────────────────┘ │
│ │
│ [ KONTYNUUJ ] │
│ │
└──────────────────────────────────┘Komponenty
Wspólne (wszystkie typy)
| Komponent | Opis |
|---|---|
TaskAppBar | App bar z nazwą typu, przyciskiem back i close |
TaskProgressBar | Pasek postępu (np. pytanie 4/10) |
XpPreview | Badge "+50 XP" widoczny podczas zadania |
CompletionOverlay | Pełnoekranowy overlay z wynikami po ukończeniu |
ConfettiAnimation | Animacja confetti (particle system) |
XpCounter | Animowany licznik XP (count up) |
BadgeEarnedCard | Karta zdobytej odznaki |
LevelUpCard | Karta awansu na wyższy poziom |
CloseConfirmDialog | "Czy na pewno chcesz wyjść? Postęp zostanie zapisany." |
Quiz
| Komponent | Opis |
|---|---|
QuestionCard | Treść pytania (tekst + opcjonalny obrazek) |
AnswerOption | Opcja odpowiedzi (radio — single choice, checkbox — multi choice) |
AnswerFeedback | Feedback: poprawna (zielona), błędna (czerwona) + wyjaśnienie |
CheckButton | Przycisk "SPRAWDŹ" (aktywny po wybraniu odpowiedzi) |
NextButton | Przycisk "NASTĘPNE" (po sprawdzeniu) |
Video
| Komponent | Opis |
|---|---|
VideoPlayer | Player z kontrolkami (play/pause, seek, fullscreen) |
VideoProgressBar | Pasek postępu video |
ComprehensionQuiz | Quiz wyświetlany po obejrzeniu (reuse QuestionCard + AnswerOption) |
Podcast
| Komponent | Opis |
|---|---|
AudioPlayer | Player: play/pause, prev/next 15s, seek bar |
SpeedSelector | Wybór prędkości: 1x, 1.5x, 2x |
CoverArt | Okładka podcastu |
TranscriptView | Przewijalny transkrypt z timestampami (auto-scroll do aktualnego) |
Kreatywne
| Komponent | Opis |
|---|---|
CreativePrompt | Treść polecenia |
InputMethodPicker | Wybór: zdjęcie, rysunek, tekst |
CameraCapture | Ekran aparatu (camera plugin) |
DrawingCanvas | Prosty canvas do rysowania (CustomPainter) |
TextInput | Pole tekstowe (multiline) |
SubmitButton | Przycisk "WYŚLIJ PRACĘ" |
Mini-gra
| Komponent | Opis |
|---|---|
GameContainer | Kontener gry (WebView lub Flutter canvas / Flame) |
GameHUD | Czas, wynik, życia (overlay nad grą) |
GameOverScreen | Ekran końca gry z wynikiem |
Logika
Przepływ ogólny
- Wejście na ekran →
GET /app/tasks/:id(pobranie danych) POST /app/tasks/:id/start(oznaczenie rozpoczęcia, timestamp)- Wykonanie zadania (quiz, video, itp.)
POST /app/tasks/:id/complete(wysyłka wyników) lubPOST /app/tasks/:id/submit-creative- Animacja ukończenia (confetti, XP, badge, level up)
- Przycisk "KONTYNUUJ" → powrót do lokacji (
context.pop())
Quiz — szczegóły
- Single choice: radio buttons, 1 poprawna odpowiedź
- Multi choice: checkboxy, 1+ poprawnych odpowiedzi
- Po "SPRAWDŹ" → feedback (poprawna/błędna) + wyjaśnienie
- Nawet przy błędnej odpowiedzi → przejście dalej (nauka, nie egzamin)
- Wynik = poprawne / wszystkie (przesyłany w complete)
Video — szczegóły
- Video musi być obejrzane w min. 90% żeby odblokować quiz
- Quiz (comprehension) po obejrzeniu: 2-3 pytania
- Jeśli brak quizu → video samo w sobie jest wystarczające
Zadanie kreatywne — szczegóły
- Upload: zdjęcie (JPEG, max 5MB), rysunek (PNG z canvas), tekst
- Wysyłka jako multipart/form-data na
/app/tasks/:id/submit-creative - Brak automatycznej oceny — review przez rodzica/moderatora (async)
- XP przyznawane za wysłanie (nie za ocenę)
Endpointy
| Metoda | Endpoint | Opis |
|---|---|---|
| GET | /app/tasks/:id | Pełne dane zadania |
| POST | /app/tasks/:id/start | Oznacz rozpoczęcie |
| POST | /app/tasks/:id/complete | Ukończ zadanie (wynik) |
| POST | /app/tasks/:id/submit-creative | Wyślij pracę kreatywną |
Task Detail Response (quiz):
json
{
"data": {
"id": "task-q1",
"title": "Zwierzęta Polski",
"type": "quiz",
"xp_reward": 40,
"thumbnail_url": "https://...",
"questions": [
{
"id": "q1",
"text": "Jak nazywa się najwyższa góra w Polsce?",
"image_url": null,
"type": "single_choice",
"answers": [
{ "id": "a1", "text": "Rysy" },
{ "id": "a2", "text": "Giewont" },
{ "id": "a3", "text": "Śnieżka" },
{ "id": "a4", "text": "Kasprowy Wierch" }
],
"correct_answer_ids": ["a1"],
"explanation": "Rysy (2499 m) to najwyższy szczyt w polskich Tatrach."
}
]
}
}Task Detail Response (video):
json
{
"data": {
"id": "task-v1",
"title": "Dinozaury i ich świat",
"type": "video",
"xp_reward": 50,
"video_url": "https://...",
"duration_seconds": 480,
"thumbnail_url": "https://...",
"comprehension_questions": [
{
"id": "cq1",
"text": "Kiedy wyginęły dinozaury?",
"type": "single_choice",
"answers": [
{ "id": "a1", "text": "65 mln lat temu" },
{ "id": "a2", "text": "100 mln lat temu" },
{ "id": "a3", "text": "10 mln lat temu" }
],
"correct_answer_ids": ["a1"]
}
]
}
}Complete Request:
json
{
"score": 8,
"max_score": 10,
"answers": [
{ "question_id": "q1", "answer_ids": ["a1"] },
{ "question_id": "q2", "answer_ids": ["a3"] }
],
"duration_seconds": 245
}Complete Response:
json
{
"data": {
"xp_earned": 50,
"total_xp": 1290,
"level_before": 5,
"level_after": 6,
"level_up": true,
"badges_earned": [
{
"id": "badge-dino",
"name": "Odkrywca Dinozaurów",
"icon_url": "https://...",
"description": "Ukończ wszystkie zadania o dinozaurach"
}
]
}
}Submit Creative Request (multipart):
POST /app/tasks/:id/submit-creative
Content-Type: multipart/form-data
file: (binary — image/jpeg, image/png)
description: "Moje wymarzone zwierzę to smok..."
input_type: "photo" | "drawing" | "text"Stany Ekranu
| Stan | Widok |
|---|---|
| Ładowanie | Skeleton z shimmer |
| W trakcie | Layout odpowiedni dla typu zadania |
| Zakończone | CompletionOverlay z wynikami |
| Błąd | ErrorView z przyciskiem retry |
| Wychodzenie | CloseConfirmDialog (jeśli zadanie w trakcie) |
Uwagi Techniczne
- Video player:
video_player+chewie(kontrolki) - Audio player:
just_audio+ custom UI - Rysunek:
CustomPainterzGestureDetector(kolorowe pędzle, gumka, cofnij) - Mini-gry:
flutter_inappwebview(WebView) lub Flame engine (natywne) - Upload plików: Dio multipart + progress indicator
- Postęp quizu zapisywany lokalnie (na wypadek przerwania) — sync z serwerem przy complete
WillPopScope/PopScope→ dialog potwierdzenia wyjścia