Naar hoofdinhoud

De uitdaging

Voor de Ancestor Tours app wilden we een schaalbare oplossing die meerdere steden en talen ondersteunt. De app moest offline werken, eenvoudig te onderhouden zijn, en snel nieuwe tours kunnen laden zonder app updates.

Technische architectuur

White-label SDK

De kern is een herbruikbare SDK (packages/tour_guide_sdk/) die alle tour-gerelateerde functionaliteit kapselt:

  • Models: Tour, Stop, QuizQuestion met taalondersteuning
  • Services: Audio playback, location tracking, asset loading
  • Providers: Riverpod state management voor caching en API calls
  • Widgets: Herbruikbare UI componenten

De app zelf bevat alleen app-specifieke configuratie en theming. Hierdoor kunnen we eenvoudig nieuwe tour apps bouwen voor andere steden.

Supabase als backend

We kozen voor Supabase boven een custom API om meerdere redenen:

  1. PostgREST: Directe REST API op PostgreSQL zonder extra backend code
  2. Storage: Audio en afbeeldingen worden gehost in Supabase Storage
  3. RLS Policies: Row-level security zorgt voor veilige read-only toegang via de anon key
  4. Snelheid: Schema en upload scripts waren binnen een dag klaar

De database heeft drie tabellen: tours, stops, en media met JSONB metadata voor flexibiliteit.

Offline-first met lokale fallback

De app ondersteunt drie modi via een feature flag:

# Alleen lokale bestanden (ontwikkeling)
make test-local

# Hybrid: API met lokale fallback (productie)
make test-hybrid

# Alleen API (testing)
make test-api

De Stop.audioPath getter checkt eerst de API URL, valt terug naar de lokale filename. Hierdoor werkt de app altijd, ook als de API tijdelijk onbereikbaar is.

Caching strategie

We gebruiken stale-while-revalidate met ETag validatie:

  1. Check cache voor opgeslagen tour
  2. Toon cached versie direct aan gebruiker
  3. Vraag API om updates in achtergrond
  4. Cache nieuwe versie als ETag verschilt

Dit geeft een snelle user experience met automatische updates.

Meertalige ondersteuning

Tours staan in app/assets/tours/{city}/{tour-name}/{language}/ met:

  • tour.yaml - Tour content (metadata, stops, quiz, pronunciations)
  • audio/*.mp3 - Audio files per stop
  • images/*.png - Afbeeldingen

Momenteel ondersteund: nl-NL (Nederlands) en en-EN (Engels). Beide volledig vertaald inclusief quiz vragen en uitspraakgidsen.

Upload workflow

Python scripts uploaden tours naar Supabase:

# Upload specifieke tour
make supabase-upload TOUR=dordrecht_marie-laduk-in-dordrecht LANG=nl-NL

# Upload alle tours
make supabase-upload-all

Het script leest de YAML, uploadt audio/images naar Storage, en insert de metadata in de database met één transactie.

Gebruikte technieken

  • Flutter 3.x - Cross-platform UI
  • Riverpod 3.x - State management
  • Supabase - Backend (PostgreSQL + Storage)
  • just_audio - Audio playback
  • shared_preferences - Lokale caching
  • Python - Upload automation
  • Makefile - Workflow commands

Resultaat

De app is nu klaar voor productie met:

  • ✅ Volledige offline ondersteuning
  • ✅ Meertalige tours (NL/EN)
  • ✅ Remote content updates zonder app release
  • ✅ Schaalbare SDK voor nieuwe steden
  • ✅ Gestructureerde upload workflow

Volgende stappen

De basis staat. Daarna bouwen we:

  • US-002: On-demand tour downloading met progress tracking
  • US-004: Network detection en automatische sync
  • US-005: Web CMS voor tour creatie en editing

De code is open source beschikbaar op GitHub.