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:
- PostgREST: Directe REST API op PostgreSQL zonder extra backend code
- Storage: Audio en afbeeldingen worden gehost in Supabase Storage
- RLS Policies: Row-level security zorgt voor veilige read-only toegang via de anon key
- 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:
- Check cache voor opgeslagen tour
- Toon cached versie direct aan gebruiker
- Vraag API om updates in achtergrond
- 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 stopimages/*.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.