Skip to content
99ersstudio
All work
Mobile Apps/Live

Verein App

Offline-first DSGVO-konformer Vereinsmanager: members, fees, events, docs, announcements. Free under 25 active members.

46
tests
6a
phases
25 mbr
free under

The problem

A 30-member Schützenverein or Kleingartenverein does not need a SaaS subscription, a six-figure ERP, or a Steuerberater-grade audit trail. They need a phone-shaped tool that holds the Mitgliederliste, tracks who paid the Jahresbeitrag, schedules the Jahreshauptversammlung, and exports a CSV when the Kassenwart asks for it — all in German, all DSGVO-konform, and ideally without a backend that can leak. The free tier of every commercial alternative is either crippled or evaporates the moment the club crosses a member count.

How we built it

  1. 01Built every feature offline-first against `SharedPreferences` JSON repositories — `MemberRepository`, `FeeRepository`, `PaymentRepository`, `EventRepository`, `AnnouncementRepository`, `DocumentRepository` — so the entire CRUD surface works on a plane with no signal.
  2. 02Modelled fees properly: `MembershipFee` plus a separate `Payment` with four statuses (`offen`, `bezahlt`, `ueberfaellig`, `erlassen`) and a `PaymentStatusEngine` that derives `effectiveStatus` from due date vs. today — the UI never needs to ask which status to render.
  3. 03Wrote a recurrence generator for events (weekly / biweekly / monthly with month-end clamping) plus an `EventDetailScreen` with per-member RSVP `SegmentedButton`. RSVP counts roll up into the Dashboard and the upcoming-events tab.
  4. 04Shipped DE-formatted CSV export — semicolon-separated, comma decimals, proper escaping — for members and year-filterable payments, handed to the system share sheet via `share_plus`. The Kassenwart pastes the result straight into Excel.
  5. 05Added a free-tier gate (`PremiumController` + `freeTierExceededProvider`) that caps the free experience at 25 active members, with upgrade banners on Dashboard and Settings. The premium toggle persists locally; the actual paywall (RevenueCat) and the cloud-sync path (Supabase) wait for credentials.

Outcome

Phases 1–5 plus 6a shipped — feature-complete offline. 46 of 46 tests pass (`PaymentStatusEngine`, `csv_export`, `event_repository.generateSeries`, `fee_payment_repository`, `feature_gate`), `flutter analyze` clean. UI is German-only, includes a Haftungsausschluss in Settings, and never makes a network call. Six routes, three audiences for announcements, six document categories, and a Dashboard wired to live counts.

Stack

FlutterDartRiverpodGoRoutershare_plus

Flutter / Dart · Riverpod for state · GoRouter for navigation · `SharedPreferences` JSON repositories · Material 3 with a green seed (light + dark) · `share_plus` for CSV / share-sheet handoff · zero backend until Phase 6b.

Next up

Phase 5b: real Datei-Upload via `file_picker` + storage permissions. Phase 6b: optional Supabase auth / database / storage sync (credential-blocked). RevenueCat for the live paywall once a club commits.

More case studies