Ekrany — Autentykacja
Przegląd
Modul autentykacji obsługuje dwa typy użytkowników: dziecko i rodzic. Dziecko loguje się PINem (4 cyfry) i wybiera profil z listy awatarów zapisanych na urządzeniu. Rodzic loguje się kodem weryfikacyjnym wysyłanym na e-mail (passwordless).
Routing (GoRouter)
final authRoutes = [
GoRoute(path: '/welcome', builder: (_, __) => WelcomeScreen()),
GoRoute(path: '/register/child', builder: (_, __) => ChildRegistrationScreen()),
GoRoute(path: '/register/parent',builder: (_, __) => ParentRegistrationScreen()),
GoRoute(path: '/login/child', builder: (_, __) => ChildLoginScreen()),
GoRoute(path: '/login/parent', builder: (_, __) => ParentLoginScreen()),
];Redirect guard — jeśli brak tokenu w Secure Storage, przekieruj na /welcome.
Riverpod Providers
// Stan autentykacji
final authStateProvider = StateNotifierProvider<AuthNotifier, AuthState>((ref) {
return AuthNotifier(ref.watch(authRepositoryProvider));
});
// Profile dzieci zapisane na urządzeniu
final deviceProfilesProvider = FutureProvider<List<ChildProfile>>((ref) {
return ref.watch(authRepositoryProvider).getDeviceProfiles();
});
// Krok rejestracji dziecka (multi-step)
final childRegStepProvider = StateProvider<int>((ref) => 0);
// Dane formularza rejestracji dziecka
final childRegFormProvider = StateNotifierProvider<ChildRegFormNotifier, ChildRegForm>((ref) {
return ChildRegFormNotifier();
});
// Timer na ponowne wysłanie kodu e-mail
final resendTimerProvider = StateProvider<int>((ref) => 0);
// Walidacja PIN (4 cyfry)
final pinInputProvider = StateProvider<String>((ref) => '');1. Welcome Screen
Pierwszy ekran po uruchomieniu aplikacji. Dziecko lub rodzic wybiera swoją rolę.
ASCII Wireframe
┌──────────────────────────────────┐
│ ☆ LUMOS ISLANDS ☆ │
│ (logo + animacja tła) │
│ │
│ ┌────────────┐ ┌────────────┐ │
│ │ │ │ │ │
│ │ 👧 🧒 │ │ 👨 👩 │ │
│ │ │ │ │ │
│ │ DZIECKO │ │ RODZIC │ │
│ │ │ │ │ │
│ │ "Chcę się │ │ "Chcę │ │
│ │ uczyć!" │ │ zarządzać"│ │
│ │ │ │ │ │
│ └────────────┘ └────────────┘ │
│ │
│ Masz już konto? Zaloguj się │
│ │
└──────────────────────────────────┘Komponenty
| Komponent | Opis |
|---|---|
AnimatedBackground | Animowane tło (cząsteczki / fale) |
LumosLogo | Logo aplikacji z animacją wejścia |
RoleCard | Duża karta z ikoną, tytułem i opisem (2 sztuki) |
TextLinkButton | Link "Masz już konto? Zaloguj się" |
Logika
- Tap
DZIECKO→ nawigacja do/register/child(nowy) lub/login/child(istniejący) - Tap
RODZIC→ nawigacja do/register/parent(nowy) lub/login/parent(istniejący) - Link "Zaloguj się" → bottom sheet z wyborem: logowanie dziecka / rodzica
Endpointy
Brak — ekran czysto nawigacyjny.
2. Rejestracja Dziecka (multi-step)
Wielokrokowy formularz tworzenia profilu dziecka. Dane zapisywane lokalnie na urządzeniu + wysyłane na serwer.
ASCII Wireframe
Krok 1: Rok urodzenia Krok 2: Język
┌──────────────────────────┐ ┌──────────────────────────┐
│ ← Cofnij │ │ ← Cofnij │
│ │ │ │
│ Ile masz lat? │ │ W jakim języku │
│ │ │ chcesz się uczyć? │
│ ┌────────────────────┐ │ │ │
│ │ ◀ 2018 ▶ │ │ │ ┌──────┐ ┌──────┐ │
│ │ (scroll picker) │ │ │ │ 🇵🇱 │ │ 🇬🇧 │ │
│ └────────────────────┘ │ │ │Polski │ │Angiel│ │
│ │ │ └──────┘ └──────┘ │
│ [●○○○] krok 1/4 │ │ ┌──────┐ ┌──────┐ │
│ │ │ │ 🇩🇪 │ │ 🇪🇸 │ │
│ [ DALEJ ] │ │ │Niemie│ │Hiszpa│ │
│ │ │ └──────┘ └──────┘ │
└──────────────────────────┘ │ │
│ [●●○○] krok 2/4 │
│ [ DALEJ ] │
└──────────────────────────┘
Krok 3: Wybór awatara Krok 4: Ustawienie PINu
┌──────────────────────────┐ ┌──────────────────────────┐
│ ← Cofnij │ │ ← Cofnij │
│ │ │ │
│ Wybierz swój awatar! │ │ Ustaw swój PIN │
│ │ │ (4 cyfry) │
│ ┌──────────┐ │ │ │
│ │ 👤 │ │ │ ● ● ○ ○ │
│ │(wybrany) │ │ │ │
│ └──────────┘ │ │ ┌───┬───┬───┐ │
│ │ │ │ 1 │ 2 │ 3 │ │
│ ◀ ┌────┐┌────┐┌────┐ ▶ │ │ ├───┼───┼───┤ │
│ │ 🧒 ││ 👧 ││ 🧑 │ │ │ │ 4 │ 5 │ 6 │ │
│ └────┘└────┘└────┘ │ │ ├───┼───┼───┤ │
│ (przewiń — 8 presetów) │ │ │ 7 │ 8 │ 9 │ │
│ 4 męskie + 4 żeńskie │ │ ├───┼───┼───┤ │
│ │ │ │ │ 0 │ ⌫ │ │
│ [●●●○] krok 3/4 │ │ └───┴───┴───┘ │
│ [ DALEJ ] │ │ │
└──────────────────────────┘ │ [●●●●] krok 4/4 │
│ [ GOTOWE! ] │
└──────────────────────────┘Komponenty
| Komponent | Opis |
|---|---|
StepIndicator | Dots indicator (4 kroki) |
YearPicker | Scroll picker z rokiem urodzenia |
LanguageGrid | Siatka flag z nazwami języków |
AvatarPicker | Horyzontalny scroll 8 losowych presetów (4 męskie + 4 żeńskie), tap = wybór |
PinSetupPad | Klawiatura numeryczna 4-cyfrowa z kropkami potwierdzenia |
BackButton | Powrót do poprzedniego kroku |
LumosButton | Przycisk "DALEJ" / "GOTOWE!" |
Logika
- Użytkownik przechodzi przez 4 kroki (PageView z kontrolerem)
- Każdy krok walidowany przed przejściem dalej
- Na ostatnim kroku (PIN) — wywołanie API rejestracji
- Sukces → profil zapisany na urządzeniu (Secure Storage) → nawigacja do onboardingu
Endpointy
| Metoda | Endpoint | Opis |
|---|---|---|
| POST | /app/auth/register/child | Rejestracja profilu dziecka |
Request:
{
"birth_year": 2018,
"language": "pl",
"avatar_id": "preset_boy_03",
"pin": "1234",
"device_id": "uuid-device"
}Response:
{
"data": {
"id": "child-uuid",
"token": "jwt-token",
"profile": {
"display_name": "Odkrywca",
"avatar_url": "https://..."
}
}
}3. Rejestracja Rodzica
Rejestracja rodzica przez e-mail z kodem weryfikacyjnym (passwordless).
ASCII Wireframe
Krok 1: E-mail Krok 2: Kod weryfikacyjny
┌──────────────────────────┐ ┌──────────────────────────┐
│ ← Cofnij │ │ ← Cofnij │
│ │ │ │
│ Rejestracja Rodzica │ │ Wpisz kod │
│ │ │ │
│ Podaj swój adres e-mail │ │ Wysłaliśmy 8-znakowy │
│ │ │ kod na: │
│ ┌────────────────────┐ │ │ anna@example.com │
│ │ anna@example.com │ │ │ │
│ └────────────────────┘ │ │ ┌──┬──┬──┬──┬──┬──┬──┬──┐│
│ │ │ │ A│ 3│ K│ │ │ │ │ ││
│ Będziemy wysyłać │ │ └──┴──┴──┴──┴──┴──┴──┴──┘│
│ kody logowania na │ │ │
│ ten adres. │ │ Nie dostałeś kodu? │
│ │ │ Wyślij ponownie (42s) │
│ [ WYŚLIJ KOD ] │ │ │
│ │ │ [ ZWERYFIKUJ ] │
└──────────────────────────┘ └──────────────────────────┘Komponenty
| Komponent | Opis |
|---|---|
EmailInput | Pole e-mail z walidacją formatu |
CodeInput | 8 pól na znaki kodu alfanumerycznego (auto-focus, auto-submit) |
ResendTimer | Odliczanie do ponownego wysłania kodu |
LumosButton | Przycisk "WYŚLIJ KOD" / "ZWERYFIKUJ" |
Logika
- Rodzic wpisuje e-mail → API wysyła kod
- Rodzic wpisuje 8-znakowy kod → API weryfikuje
- Sukces → token JWT → nawigacja do głównego ekranu (panel rodzica)
- Błąd → komunikat "Nieprawidłowy kod" + możliwość ponownego wysłania
Endpointy
| Metoda | Endpoint | Opis |
|---|---|---|
| POST | /app/auth/register/parent | Wysyłka kodu na e-mail |
| POST | /app/auth/verify-email | Weryfikacja kodu |
Register Request:
{
"email": "anna@example.com"
}Verify Request:
{
"email": "anna@example.com",
"code": "A3K7M2X9"
}Verify Response:
{
"data": {
"id": "parent-uuid",
"token": "jwt-token",
"is_new": true
}
}4. Login Dziecka
Dziecko wybiera swój profil (awatar) z listy zapisanych na urządzeniu, następnie wpisuje 4-cyfrowy PIN.
ASCII Wireframe
Krok 1: Wybór profilu Krok 2: Wpisanie PINu
┌──────────────────────────┐ ┌──────────────────────────┐
│ │ │ ← Cofnij │
│ Kto się dzisiaj uczy? │ │ │
│ │ │ Cześć, Kacper! │
│ ┌──────┐ ┌──────┐ │ │ │
│ │ 🧒 │ │ 👧 │ │ │ ┌──────────┐ │
│ │ │ │ │ │ │ │ 🧒 │ │
│ │Kacper│ │Zosia │ │ │ └──────────┘ │
│ └──────┘ └──────┘ │ │ │
│ ┌──────┐ ┌──────┐ │ │ Wpisz swój PIN │
│ │ 🧒 │ │ + │ │ │ │
│ │ │ │ Nowy │ │ │ ● ○ ○ ○ │
│ │ Jan │ │profil│ │ │ │
│ └──────┘ └──────┘ │ │ ┌───┬───┬───┐ │
│ │ │ │ 1 │ 2 │ 3 │ │
│ │ │ ├───┼───┼───┤ │
│ Zaloguj się jako rodzic │ │ │ 4 │ 5 │ 6 │ │
│ │ │ ├───┼───┼───┤ │
└──────────────────────────┘ │ │ 7 │ 8 │ 9 │ │
│ ├───┼───┼───┤ │
│ │ │ 0 │ ⌫ │ │
│ └───┴───┴───┘ │
│ │
│ Zapomniałem PIN │
└──────────────────────────┘Komponenty
| Komponent | Opis |
|---|---|
ProfileGrid | Siatka awatarów (z urządzenia) + karta "Nowy profil" |
ProfileCard | Karta z awatarem i imieniem dziecka |
PinPad | Klawiatura numeryczna z 4 kropkami statusu |
AddProfileCard | Karta "+" do dodawania nowego profilu |
TextLinkButton | Link "Zaloguj się jako rodzic" / "Zapomniałem PIN" |
Logika
- Pobranie listy profili z urządzenia (Secure Storage) + opcjonalnie z API
- Dziecko tapuje swój awatar → przejście do ekranu PIN
- Wpisanie 4 cyfr → automatyczna weryfikacja (bez przycisku)
- Sukces → token JWT → nawigacja do globusa (lub onboardingu)
- Błędny PIN → animacja potrząsania + reset pól
Endpointy
| Metoda | Endpoint | Opis |
|---|---|---|
| GET | /app/auth/profiles | Lista profili na urządzeniu |
| POST | /app/auth/login/child | Logowanie dziecka (PIN) |
Profiles Response:
{
"data": [
{
"id": "child-1",
"display_name": "Kacper",
"avatar_url": "https://..."
},
{
"id": "child-2",
"display_name": "Zosia",
"avatar_url": "https://..."
}
]
}Login Request:
{
"profile_id": "child-1",
"pin": "1234",
"device_id": "uuid-device"
}Login Response:
{
"data": {
"token": "jwt-token",
"profile": {
"id": "child-1",
"display_name": "Kacper",
"avatar_url": "https://...",
"level": 5,
"xp": 1240
},
"onboarding_completed": true
}
}5. Login Rodzica
Logowanie rodzica przez e-mail + kod weryfikacyjny (passwordless, identyczny flow jak rejestracja).
ASCII Wireframe
Krok 1: E-mail Krok 2: Kod weryfikacyjny
┌──────────────────────────┐ ┌──────────────────────────┐
│ ← Cofnij │ │ ← Cofnij │
│ │ │ │
│ Logowanie Rodzica │ │ Wpisz kod │
│ │ │ │
│ Podaj swój adres e-mail │ │ Wysłaliśmy 8-znakowy │
│ │ │ kod na: │
│ ┌────────────────────┐ │ │ anna@example.com │
│ │ anna@example.com │ │ │ │
│ └────────────────────┘ │ │ ┌──┬──┬──┬──┬──┬──┬──┬──┐│
│ │ │ │ │ │ │ │ │ │ │ ││
│ │ │ └──┴──┴──┴──┴──┴──┴──┴──┘│
│ Nie masz konta? │ │ │
│ Zarejestruj się │ │ Nie dostałeś kodu? │
│ │ │ Wyślij ponownie (58s) │
│ [ WYŚLIJ KOD ] │ │ │
│ │ │ [ ZALOGUJ SIĘ ] │
└──────────────────────────┘ └──────────────────────────┘Komponenty
| Komponent | Opis |
|---|---|
EmailInput | Pole e-mail z walidacją formatu (reuse z rejestracji) |
CodeInput | 8 pól na znaki kodu alfanumerycznego (reuse z rejestracji) |
ResendTimer | Odliczanie do ponownego wysłania kodu |
LumosButton | Przycisk "WYŚLIJ KOD" / "ZALOGUJ SIĘ" |
TextLinkButton | Link "Nie masz konta? Zarejestruj się" |
Logika
- Rodzic wpisuje e-mail → API wysyła kod weryfikacyjny
- Rodzic wpisuje 8-znakowy kod → API weryfikuje
- Sukces → token JWT → nawigacja do ekranu rodzica (zarządzanie dziećmi)
- Jeśli e-mail nie istnieje → komunikat z linkiem do rejestracji
Endpointy
| Metoda | Endpoint | Opis |
|---|---|---|
| POST | /app/auth/login/parent | Wysyłka kodu na e-mail |
| POST | /app/auth/verify-email | Weryfikacja kodu |
Login Request:
{
"email": "anna@example.com"
}Verify Request:
{
"email": "anna@example.com",
"code": "B5T8N3P1"
}Verify Response:
{
"data": {
"id": "parent-uuid",
"token": "jwt-token",
"children": [
{ "id": "child-1", "display_name": "Kacper" },
{ "id": "child-2", "display_name": "Zosia" }
]
}
}Wspólne Elementy UI
| Komponent | Wykorzystanie |
|---|---|
LumosButton | Wszystkie ekrany — primary, secondary, text variant |
BackButton | Nawigacja wstecz w GoRouter (context.pop()) |
LoadingOverlay | Wyświetlany podczas requestów API |
ErrorSnackbar | Komunikaty błędów (walidacja, serwer) |
AnimatedBackground | Welcome screen, tło logowania |
Bezpieczeństwo
- PIN przechowywany wyłącznie na serwerze (hash + salt)
- Token JWT w Flutter Secure Storage (Keychain / Keystore)
- Device ID generowany przy pierwszym uruchomieniu i zapisywany w Secure Storage
- Kod e-mail ważny 10 minut, max 3 próby
- Rate limiting na endpointach autentykacji (429 Too Many Requests)