Skip to content

Feature 0002 — Auth & Design System

Establish a custom Flutter design system (built on Material 3) and ship the first auth slice (Supabase email + password) for the Treeper mobile app.

  • State management: flutter_bloc (app-wide AuthBloc, page-scoped Cubits)
  • Routing: go_router with refreshListenable driven by AuthBloc.stream
  • Backend: supabase_flutter (built-in secure session persistence)
  • Display font: Sora (Google Fonts)
  • Body font: Inter (Google Fonts)
  • Auth flows in scope: sign-up, sign-in, forgot-password, sign-out, session restore
  • Out of scope: magic link, OAuth, profile, onboarding, RLS

Design tokens (extracted from reference screenshots)

Section titled “Design tokens (extracted from reference screenshots)”
  • Surfaces: sage #DCE8D2, cream #F3EEE2, card #FFFFFF
  • Primary: forest #13251A (dark CTAs, nav)
  • Accent: lime #C5E04A (highlight CTAs, calendar selected, ticket band)
  • Text: primary #0F1A12, muted #6B7A6F, onDark #FFFFFF
  • Border subtle: #E6E1D4
  • Spacing: 4 / 8 / 12 / 16 / 20 / 24 / 32 / 40
  • Radii: sm 8, md 16, lg 24, xl 32, pill 999
  • Motion: fast 120ms, base 220ms, slow 360ms, easeOutCubic
  1. AppSurface (sage / cream)
  2. AppPillButton (primary / accent / ghost)
  3. AppTextField (rounded pill input + password toggle)
  4. AppCircleIconButton (white / dark / accent)
  5. AppChip (active / inactive with leading icon)
  6. AppInfoPill (icon + label)
  7. AppSegmented (One way / Round trip style)
  8. AppCard (image card with scrim)
  9. AppAvatarStack
  10. AppBottomNav
  11. AppLoadingPill
  12. AppWeatherChip
  13. AppCountryLabel
lib/
app/ # bootstrap, MaterialApp.router, root BlocProvider
core/
router/ # GoRouter, AuthRefreshListenable, route names
error/ # AppFailure
design_system/
tokens/ theme/ components/ showcase/
features/
auth/
data/ # AuthRepository, SupabaseAuthRepository
domain/ # AuthUser, AuthFailure
bloc/ # AuthBloc, SignInCubit, SignUpCubit, ForgotPasswordCubit
view/ # splash, sign_in, sign_up, forgot_password
home/ # auth-gated stub
  • AuthState.unknown (initial) → splash
  • AuthState.authenticated(user) → home
  • AuthState.unauthenticated → sign-in Driven by Supabase.instance.client.auth.onAuthStateChange.
  • /splash (initial), /sign-in, /sign-up, /forgot-password, /home
  • /dev/showcase exposed in debug builds only
  • redirect rule:
    • unknown/splash
    • unauthenticated + protected → /sign-in
    • authenticated + auth route → /home
  • --dart-define=SUPABASE_URL=… --dart-define=SUPABASE_ANON_KEY=…
  • apps/mobile/.env.example lists required keys
  • No secrets committed; anon key only in build args
  1. Tokens + theme + AppSurface + AppPillButton + AppTextField + showcase routePhase 1
  2. AppCircleIconButton, AppChip, AppInfoPill, AppSegmented added to showcase
  3. Supabase bootstrap (env, init, connectivity test)
  4. AuthRepository + SupabaseAuthRepository + tests
  5. AuthBloc + tests
  6. GoRouter + redirect + splash + home stub
  7. Sign-in page + SignInCubit
  8. Sign-up + forgot-password pages
  9. Polish: error snackbars, loading states, golden test for AppPillButton
  • 1–2: visual via showcase route on simulator
  • 3: connectivity smoke test on simulator
  • 4–5: unit tests with mocktail / bloc_test
  • 6–9: widget tests + manual smoke (sign-up → home, sign-out → sign-in, kill app → session restored)
  • Password-reset deep link (URL scheme io.supabase.treeper://reset) — wire when forgot-password page lands
  • Email confirmation flow toggle (dev: off, prod: on)
  • RLS policies — deferred until first user-owned table arrives