white_label_kit 0.0.6 copy "white_label_kit: ^0.0.6" to clipboard
white_label_kit: ^0.0.6 copied to clipboard

A complete multi-tenant white-label and flavor management toolkit for Flutter apps. Automatically configures Android Gradle and iOS Xcode schemes from a single YAML file.

white_label_kit #

pub package Dart License: MIT

The modern, automated flavor & multi-tenant white-label toolkit for Flutter.

Easily manage multiple branded apps, flavors, and client tenants from a single Flutter codebase. Define all your tenants in white_label.yaml, and let white_label_kit automate Android Gradle flavors, iOS Xcode build schemes, IDE configurations, and compile-time asset isolation.


πŸ’‘ Why white_label_kit? #

Managing multiple flavors or white-label client apps in Flutter usually means:

  • Hand-editing complex android/app/build.gradle.kts product flavors.
  • Manually creating and wiring iOS Xcode build configurations, schemes, and bundle identifiers.
  • Risking asset leakage where one tenant's logos or credentials accidentally get bundled into another tenant's app.
  • Manually configuring IDE debug and build tasks for every new flavor.

white_label_kit automates all of this:

  • πŸ“„ Single Source of Truth: Declare all tenants, bundle IDs, colors, API endpoints, environments, and feature flags in one white_label.yaml.
  • πŸ€– Native File Automation: Patches Android Gradle and iOS Xcode schemes automatically with dart run white_label_kit:configure.
  • 🎨 Icon & Splash Automation: Opt-in per-tenant launcher icon and native splash generation β€” including the iOS Xcode wiring step most guides forget (see Β§5).
  • 🌐 Per-Environment Runtime Config: The same tenant/brand can target staging/production/anything else via --env, without a second tenant (see Β§6).
  • πŸ›‘οΈ Asset Isolation: Guarantees only the active tenant's assets and configs are compiled into the binary β€” never every tenant's data baked into every build.
  • ⚑ Interactive CLI Runner: Launch dart run white_label_kit to easily run or build APK, AAB, and iOS apps without memorizing long commands.
  • πŸ’» 1-Click IDE Configurations: Generates ready-to-use Run/Build configurations for Android Studio, IntelliJ, and VS Code.
  • πŸ”’ Type-Safe Runtime API: Access tenant metadata cleanly in your Flutter widgets and services using WhiteLabelRuntime.
  • 🩺 doctor: One command that flags missing assets, stale generated files, and missing peer dependencies (flutter_native_splash) before a build fails on them.

πŸš€ Getting Started #

1. Add Dependency #

Add white_label_kit to your Flutter project's dependencies β€” not dev_dependencies:

flutter pub add white_label_kit

Or manually in pubspec.yaml:

dependencies:
  white_label_kit: ^0.0.5

Why a regular dependency, not dev-only: lib/white_label.g.dart (the generated file β€” see step 3) imports WhiteLabelRuntime/WhiteLabelTheme from this package and is compiled directly into your app, read by your own runtime code (whiteLabelRuntime.environment.apiBaseUrl, theme colors, feature flags, etc.). It is not purely a build-time codegen tool the way build_runner is β€” putting it under dev_dependencies would still happen to compile for a leaf app, but is the wrong semantic declaration for a package your shipped binary actually reads from at runtime, and would break if this package's code ever needed to reach another package that's only resolved via dependencies.

2. Initialize Configuration #

Generate a starter white_label.yaml in your project root:

dart run white_label_kit:init

3. Add Your Tenants / Brands #

Add a new brand with a single command:

dart run white_label_kit:add-tenant acme "Acme App" com.example.acme --logo path/to/acme_logo.png

This automatically creates the configuration entry in white_label.yaml and the asset folder tenants/acme/, copying your real logo in if --logo was given. Omit --logo and it writes a placeholder tenants/acme/logo.png instead β€” replace that file with the real logo before shipping (this is the one step white_label_kit genuinely can't automate for you: it doesn't know what your brand's logo looks like).

4. Configure Android & iOS Native Files #

Sync all native Gradle flavors, Xcode schemes, and IDE run configurations:

dart run white_label_kit:configure

Run dart run white_label_kit:doctor any time to sanity-check the current setup β€” missing assets, a stale lib/white_label.g.dart, a missing flutter_native_splash dependency if splash_generate is on, etc.

5. Launcher Icons & Native Splash (per tenant) #

Launcher/notification icons and the native splash screen are generated by two well-established, purpose-built packages β€” icons_launcher and flutter_native_splash β€” not re-modeled by white_label_kit itself.

icons_launcher is a real dependency of this package, so dart run icons_launcher:create resolves for your app with nothing added to your own pubspec.yaml. flutter_native_splash can't be a dependency of this package the same way (it needs the Flutter SDK to resolve, which this package deliberately doesn't β€” see maybeGenerateNativeSplash's doc comment for the full reasoning). Add flutter_native_splash to your own app's pubspec.yaml (flutter pub add flutter_native_splash) if you want the splash generation below β€” icon generation needs no such step.

Opt-in auto-generation (recommended default): declare features: { icon_generate: true } / { splash_generate: true } for a tenant in white_label.yaml, and configure/build create icons_launcher-<id>.yaml / flutter_native_splash-<id>.yaml for you β€” derived from that tenant's assets.icon/assets.logo (icon) or assets.splash/assets.icon/assets.logo + theme.primary_color (splash) β€” only if the file doesn't already exist. Nothing to hand-author for the common case, and a file you've already customized is never touched or overwritten:

tenants:
  acme:
    features:
      icon_generate: true
      splash_generate: true

Both flags are off by default β€” a tenant that declares neither sees no change in behavior at all.

The auto-created icons_launcher-<id>.yaml includes an adaptive icon (Android 8.0+/API 26) by default β€” adaptive_foreground_image reuses the tenant's icon/logo, adaptive_background_color uses theme.primary_color (white if unset). A reasonable automatic default, not a substitute for a properly-padded, transparent foreground asset β€” hand-author the file with a dedicated foreground image for a polished result.

iOS storyboard registration is automatic β€” for the opt-in flag only. flutter_native_splash:create only writes ios/Runner/Base.lproj/LaunchScreen<Tenant>.storyboard to disk β€” Xcode never bundles a resource it doesn't know about, so a stock run of that command alone silently produces a splash screen that never ships. When splash_generate: true triggers a successful flutter_native_splash:create run, white_label_kit registers that storyboard into Runner.xcodeproj's Resources build phase for you right after (idempotent β€” safe to re-run, best-effort β€” a missing ruby/xcodeproj gem is reported as a warning in the output, never a crash).

Manual (full control) β€” skip the flags: hand-author icons_launcher-acme.yaml / flutter_native_splash-acme.yaml yourself using either package's full config reference (adaptive icon background/foreground, dark-mode variants, fullscreen, per-platform overrides, and everything else either supports), then run:

dart run icons_launcher:create --flavor acme
dart run flutter_native_splash:create --flavor acme

Going this route puts you outside the automatic registration above β€” you must still register the storyboard into Xcode yourself (ios/Runner.xcodeproj's Resources build phase) before it will actually appear in the built app.

6. Staging / Production (--env) #

environments: + --env switches runtime config (API URL and whatever else you put in custom:) for the SAME tenant/brand β€” it is not a second tenant. Icon, theme, bundle id, and app name all stay whatever the tenant already declares; only environment changes. See Configuration File below for the YAML shape.

staging/production below are just this README's example names β€” the key under environments: is an arbitrary string you choose, not a fixed/reserved keyword. Name it whatever matches your own release process (qa, uat, demo, beta, ...) and pass that exact name to --env.

dart run white_label_kit:generate  --tenant acme --env staging
dart run white_label_kit:configure --tenant acme --env staging
dart run white_label_kit:build     --tenant acme --env staging --platform android
dart run white_label_kit:run       --tenant acme --env staging

Omit --env anywhere above and the tenant's default environment: block is used β€” fully backward compatible, a white_label.yaml that never declares environments: needs no change. Passing an --env name the tenant never declared is a hard error (never a silent fallback to the default) β€” you can't accidentally ship "staging" with production's API URL baked in.

build/run/configure all (re)generate lib/white_label.g.dart for whichever tenant/environment they actually resolved to, every time they run β€” there's no separate "don't forget to regenerate" step to remember.

7. Monorepos & Melos Workspaces #

In a monorepo or Melos workspace where your Flutter app lives in a nested directory (e.g. apps/my_flutter_app):

# Configure native Android/iOS in apps/my_flutter_app and generate IDE files in monorepo root
dart run white_label_kit:configure --project-root apps/my_flutter_app --ide-root .

# Or run other commands targeting the nested Flutter project
dart run white_label_kit:generate --project-root apps/my_flutter_app --tenant acme
dart run white_label_kit:doctor   --project-root apps/my_flutter_app
dart run white_label_kit:validate --project-root apps/my_flutter_app
dart run white_label_kit:build    --project-root apps/my_flutter_app --tenant acme
  • --project-root <dir>: Sets the target Flutter project directory (where pubspec.yaml, android/, ios/, and white_label.yaml live).
  • --ide-root <dir>: (Optional) Sets the root directory where .vscode/ and .run/ IDE run configurations should be written (defaults to --project-root).
  • --config <path>: (Optional) Specifies a custom path to white_label.yaml.

πŸ–₯️ Running & Building Your App #

Launch the interactive runner:

dart run white_label_kit
╔══════════════════════════════════════════════════════════════════╗
β•‘              ✨ WHITE_LABEL_KIT RUNNER & BUILDER                 β•‘
β•‘          Automated Multi-Tenant Flutter CLI & Launcher           β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•

πŸ“Œ SELECT TENANT:
   [0] Acme App [acme] (Default)

Enter tenant number (default: acme): 0

⚑ SELECT ACTION:
   [1] ▢️  Run in Debug Mode (Simulator / Connected Device)
   [2] ⚑  Run in Release Mode (Device)
   [3] πŸš€  Build Release APK (Android)
   [4] πŸ“¦  Build Release AppBundle / AAB (Google Play Store)
   [5] 🍎  Build Release iOS (Simulator / Archive)
   [6] πŸ”§  Configure All Tenants (white_label_kit:configure)
   [7] βž•  Add New Tenant (white_label_kit:add-tenant)
   [8] ❌  Remove Tenant (white_label_kit:remove-tenant)
   [9] πŸ”  Analyze & Health Check (Flutter Analyze + Tests)
   [0] πŸšͺ  Exit

Option B: Flutter CLI Commands #

You can also run or build directly with standard Flutter commands β€” this bypasses white_label_kit's own build/run (so it does not regenerate lib/white_label.g.dart for you; run generate/configure first if you switched tenant or --env):

# Run tenant in debug mode
flutter run --flavor acme --dart-define=TENANT_ID=acme

# Build Android Release APK
flutter build apk --release --flavor acme --dart-define=TENANT_ID=acme

# Build Android Release AppBundle (Google Play)
flutter build appbundle --release --flavor acme --dart-define=TENANT_ID=acme

# Build iOS Release App
flutter build ios --release --flavor acme --dart-define=TENANT_ID=acme

Group tenant-specific logos and platform credentials under the root tenants/ folder:

my_flutter_app/
β”œβ”€β”€ white_label.yaml                # 🌟 Central configuration for all tenants
β”œβ”€β”€ tenants/                        # πŸ“‚ Assets grouped per tenant
β”‚   β”œβ”€β”€ acme/
β”‚   β”‚   β”œβ”€β”€ logo.png                # 🎨 App logo / icon asset
β”‚   β”‚   └── firebase/               # πŸ”’ Firebase credentials (optional)
β”‚   β”‚       β”œβ”€β”€ google-services.json
β”‚   β”‚       └── GoogleService-Info.plist
β”‚   β”‚
β”‚   └── beta/
β”‚       β”œβ”€β”€ logo.png
β”‚       └── firebase/
β”‚           β”œβ”€β”€ google-services.json
β”‚           └── GoogleService-Info.plist
β”‚
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ main.dart
β”‚   └── white_label.g.dart          # ⚑ Generated typed tenant constants
└── pubspec.yaml

βš™οΈ Configuration File (white_label.yaml) #

Define all tenant properties in white_label.yaml:

white_label:
  default_tenant: acme

  tenants:
    acme:
      name: "Acme App"
      version:
        name: "1.0.0"
        build_number: 1

      android:
        application_id: "com.example.acme"
        app_name: "Acme App"
        # version:                # optional β€” overrides the shared
        #   name: "1.0.0"         # `version:` above for Android only, if
        #   build_number: 1       # this platform's release cadence diverges

      ios:
        bundle_id: "com.example.acme"
        app_name: "Acme App"
        # version: { ... }        # same override shape as android.version

      theme:
        primary_color: "#1E88E5"
        secondary_color: "#FFC107"
        # brand_colors: { logo_accent: "#FF0000" }    # optional, arbitrary
        # feature_colors: { courses: "#00FF00" }      # keyed hex-color maps
        # section_colors: { header: "#0000FF" }       # for apps whose UI
        # gradient_colors: { start: "#111111" }       # needs more than one
        #                                              # primary/secondary

      environment:                            # the DEFAULT β€” used whenever
        api_base_url: "https://api.example.com"  # `--env` isn't passed
        # custom:                              # optional, arbitrary string
        #   sentry_dsn: "https://..."           # key-values for anything else
        #   cdn_url: "https://cdn.example.com"  # the build needs at runtime

      environments:                # optional β€” NAMED overrides of the
        staging:                   # `environment:` block above, selected
          api_base_url: "https://staging-api.example.com"  # via `--env`
          custom:                                        # (see Β§6 above).
            sentry_dsn: "https://staging-sentry.example.com"
        production:
          api_base_url: "https://api.example.com"
      # `staging`/`production` are just names picked for this example β€”
      # any key you write under `environments:` becomes a valid `--env`
      # value (e.g. `qa`, `uat`, `demo` all work equally). A tenant that
      # never declares `environments:` behaves exactly as before this
      # existed β€” the whole block is optional. Each named environment is
      # an independent override, NOT a patch on top of `environment:` β€”
      # declare everything that environment needs.

      features:
        enable_push_notifications: true
        enable_downloads: true
        # icon_generate: true      # see Β§5 above
        # splash_generate: true

      assets:
        logo: "tenants/acme/logo.png"
        # icon: "tenants/acme/icon.png"       # optional
        # splash: "tenants/acme/splash.png"   # optional

      firebase:
        google_services_json: "tenants/acme/firebase/google-services.json"
        google_service_info_plist: "tenants/acme/firebase/GoogleService-Info.plist"

πŸ“± Accessing Tenant Data in Flutter (Dart) #

Access your active tenant's branding, API endpoints, and feature flags anywhere in your Dart code:

dart run white_label_kit:generate compiles the current build's tenant (and, if --env was passed, that specific environment) into lib/white_label.g.dart as a single whiteLabelRuntime constant (a WhiteLabelRuntime) β€” never a map of every tenant, so no other tenant's data is ever compiled into a build that isn't theirs:

import 'package:flutter/material.dart';
import 'white_label.g.dart';

void main() {
  print('Tenant ID: ${whiteLabelRuntime.tenantId}');
  print('App Name: ${whiteLabelRuntime.tenantName}');
  print('Environment: $whiteLabelEnvironmentName');   // "" if --env wasn't used
  print('API URL: ${whiteLabelRuntime.environment.apiBaseUrl}');
  print('Sentry DSN: ${whiteLabelRuntime.environment.custom['sentry_dsn']}');
  print('Primary Color: ${whiteLabelRuntime.theme.primaryColorHex}');

  final hasPush = whiteLabelRuntime.isFeatureEnabled('enable_push_notifications');
  print('Push Notifications: $hasPush');

  runApp(const MyApp());
}

🧩 Optional: build_runner Integration #

You do not need build_runner for anything above β€” generate/ configure/build/run are plain, direct CLI commands, the same shape as flutter_native_splash:create. If your app already runs dart run build_runner build for freezed/json_serializable/ injectable_generator and you'd like that same command to also regenerate lib/white_label.g.dart, this package ships an optional builder for it (lib/builder.dart) β€” but it is not enabled automatically, and needs two things added to your own project's build.yaml before it does anything:

# your app's build.yaml
targets:
  $default:
    sources:
      - white_label.yaml   # lives at the project root, outside build_runner's default input set
      - lib/**             # default lib/** scan β€” add it explicitly
builders:
  white_label_kit:white_label_generator:
    enabled: true          # NOT auto-applied β€” must opt in explicitly

Why this isn't auto-applied: without the sources: override above, build_runner would activate the builder but it could never actually find its white_label.yaml input β€” while still treating lib/white_label.g.dart as an output it owns, and deleting it on the next build_runner build (the file generate/configure had already written correctly gets wiped with no warning). If you don't need build_runner to regenerate this file, don't add the build.yaml block above β€” generate/ configure are unaffected either way.


πŸ“– CLI Commands Reference #

Command Description
dart run white_label_kit Opens the interactive terminal runner & builder menu
dart run white_label_kit:init [--example] [--force] [--path <dir>] Creates a starter white_label.yaml file
dart run white_label_kit:add-tenant <id> "<Name>" <bundleId> [--logo <path>] [--default] Adds a new tenant and creates its asset directory
dart run white_label_kit:update-tenant <id> [options] Updates tenant configuration fields
dart run white_label_kit:remove-tenant <id> [--keep-assets] Removes the tenant's entry from white_label.yaml, deletes its tenants/<id>/ asset folder (unless --keep-assets), and cleans up its generated Android Gradle flavor, iOS Xcode build configs/scheme, and IDE run configurations
dart run white_label_kit:generate [--tenant <id>] [--env <name>] [--config <path>] (Re)generates lib/white_label.g.dart for one tenant/environment
dart run white_label_kit:configure [--tenant <id>] [--env <name>] [--platform android|ios|all] [--dry-run] [--skip-generate] Patches Android Gradle, iOS Xcode, IDE configs, icon/splash (if opted in), and regenerates lib/white_label.g.dart
dart run white_label_kit:build [--tenant <id>] [--env <name>] [--platform android|android-aab|ios|all] [--mode debug|release] [--dry-run] [--clean] [--stage-only] Stages tenant assets, regenerates lib/white_label.g.dart, and invokes the real flutter build. --mode release always adds --obfuscate --split-debug-info=build/outputs/symbols/<tenant>/<platform> (see Flutter's obfuscation guide) β€” not optional, so a release build can't accidentally ship un-obfuscated.
dart run white_label_kit:run [--tenant <id>] [--env <name>] Stages tenant assets and regenerates lib/white_label.g.dart for a debug run
dart run white_label_kit:validate Validates white_label.yaml syntax and asset paths
dart run white_label_kit:list Lists all declared tenants and the default tenant
dart run white_label_kit:doctor [id] [--all] [--json] [--strict] Performs a multi-tenant health check

πŸ”’ Security #

white_label.yaml and everything it generates (lib/white_label.g.dart, native Gradle/Xcode config) end up baked into the built binary β€” the same way any other compiled Flutter asset does. Anyone who unzips a shipped APK/IPA can read whiteLabelRuntime's data, including everything under environment.custom/environments.*.custom.

Never put in white_label.yaml: signing keys/certificates, private API secrets, database credentials, or anything else that would matter if extracted from the built app. environment/environment.custom is for public runtime config only (a base URL, a public DSN meant to be client-visible, a CDN URL) β€” not a place to smuggle a secret in because it was convenient. Firebase's google-services.json/ GoogleService-Info.plist (via firebase:) are the one exception this package handles directly, and only because Firebase itself designs those files to ship inside the client app.


🚫 What this does NOT do (yet) #

Deliberate scope boundaries, not oversights β€” flagged here instead of silently discovered later:

  • No per-environment theme/icon/splash. environments:/--env (Β§6) only ever changes environment (API URL + custom). Staging and production of the same tenant are expected to look identical; there is no environments.staging.theme or similar. If you need visually distinct staging builds, that's a real gap today, not a documented design choice β€” open an issue rather than hand-rolling around it.
  • environment/environments.*.custom are flat string maps, not arbitrary JSON. Same deliberate constraint as features (bool-only) β€” richer/nested structured runtime content isn't modeled here.
  • ASSETCATALOG_COMPILER_APPICON_NAME and LAUNCH_SCREEN_STORYBOARD_NAME (the two remaining iOS Xcode keys icons_launcher/flutter_native_splash themselves don't manage, beyond what generateIosConfig sets) are not touched by this package at all β€” see maybeGenerateNativeSplash's storyboard-registration note in Β§5 for the one exception (the storyboard file reference).
  • No CI/CD orchestration. This package configures native files and generates Dart code; it does not run or generate pipeline definitions (Bitbucket/GitHub Actions/etc.) β€” wire its CLI commands into whatever CI you already run.

🀝 Contributing #

Contributions, issues, and feature requests are welcome! Feel free to check the issues page.


πŸ“„ License #

This project is licensed under the MIT License β€” see the LICENSE file for details.

3
likes
160
points
432
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A complete multi-tenant white-label and flavor management toolkit for Flutter apps. Automatically configures Android Gradle and iOS Xcode schemes from a single YAML file.

Repository (GitHub)
View/report issues
Contributing

Topics

#white-label #flavors #multi-tenant #cli #build-tools

License

MIT (license)

Dependencies

build, icons_launcher, meta, path, yaml

More

Packages that depend on white_label_kit