Taming 70 Flutter Flavors: flavorizr + Batch CI for White-Label Releases Kamero, an AI-powered event photography platform, has industrialized its Flutter white-label release process to manage 70 flavors. The team uses flutter_flavorizr for declarative native configuration, a Dart FlavorConfig for per-flavor branding, and batch CI scripts to automate builds and store uploads, eliminating manual Xcode/Gradle edits and repetitive uploads. How we ship dozens of branded photography apps from one Flutter codebase—without drowning in manual Xcode/Gradle edits or one-off store uploads. This is the approach we use at Kamero — an AI-powered event photography platform At Kamero, our product is a multi-tenant event photography app. Each photography studio often needs: That is not “swap a hex in JSON and ship one binary.” Store policy, OAuth clients, push configs, and client branding push you toward flavors —one installable app per tenant. At around 70 flavors , the naive approach dies: android/app/build.gradle and Xcode schemes per clientWe did not escape flavors. We industrialized them. 1. flutter flavorizr Declarative flavor defs → native projects + Dart enum 2. FlavorConfig Dart Per-flavor seed: title, splash, logo, brandColor, tenant ID + optional runtime color overlay from tenant profile 3. Batch CI scripts flavor list .json → build only enabled → upload to stores flavorizr owns native packaging. FlavorConfig owns first-paint branding in Dart. Scripts own release fan-out so humans do not click “build” seventy times. We use flutter flavorizr https://pub.dev/packages/flutter flavorizr with local customizations so we can extend processors . Flavors live as declarative config under pubspec.yaml : flavorizr: ide: "vscode" app: android: flavorDimensions: "app" flavors: acme w1: app: name: "Acme Studios" ios: bundleId: com.example.app.1 icon: assets/images/acme/logo.png firebase: config: config/acme/GoogleService-Info.plist android: applicationId: com.example.app.w1 icon: assets/images/acme/logo.png firebase: config: config/shared/google-services.json customConfig: manifestPlaceholders: '= deepLinkHost: "1" ' versionCode: 15004 versionName: '"1-3.0.6"' signingConfig: signingConfigs.release Running flavorizr generates / refreshes: flavorizr.gradle Flavor enum consumed at startup Naming convention: {clientSlug} w{tenantId} for example acme w1 . The wN suffix maps to a white-label / tenant ID used by the backend and feature gates. manifestPlaceholders per flavor config: path per OS signingConfig explicit in customConfig so Play uploads do not fail mysteriouslyAdding a client becomes a checklist, not archaeology. Native flavor only gets you package identity and assets. UI still needs brand tokens. At boot: Future