white_label_kit 0.0.5 copy "white_label_kit: ^0.0.5" to clipboard
white_label_kit: ^0.0.5 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.


๐Ÿ–ฅ๏ธ 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
295
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