Docs / Branding & Customization
๐Ÿ  Home

Branding & Customization

Replace the logo, launcher icons, colors, languages and fonts of the admin panel and both mobile apps with your own brand.

Deep dives This page is the overview. Step-by-step guides live on their own pages: Customize the Admin Panel, Customize the Flutter Apps (API URL, colors, names โ€” with the exact code), and Payment Gateways.

Each app has one master artwork, assets/logo.png. The in-app brand mark and splash pick it up automatically; the platform launcher icons and the native Android splash bitmap are separate files that you regenerate after replacing the logo.

Replace assets/logo.png

Use a square, transparent PNG of at least 1024 px (the shipped file is exactly 1024ร—1024). No code changes are needed: the brand mark lib/screens/common/app_mark.dart (CargoAppMark) renders assets/logo.png on the splash, onboarding, login and offline screens, and lib/screens/auth/login_screen.dart references it directly.

Regenerate the Android icons

Launcher icons under android/app/src/main/res/mipmap-*/ic_launcher.png and the native splash bitmap mipmap-*/launch_image.png (centered by drawable/launch_background.xml and drawable-v21/launch_background.xml):

Density folderic_launcher.pnglaunch_image.png
mipmap-mdpi48ร—48160ร—160
mipmap-hdpi72ร—72240ร—240
mipmap-xhdpi96ร—96320ร—320
mipmap-xxhdpi144ร—144480ร—480
mipmap-xxxhdpi192ร—192640ร—640

Regenerate the iOS icons

In ios/Runner/Assets.xcassets/AppIcon.appiconset/, replace every Icon-App-*.png at the pixel size its Contents.json entry declares (for example Icon-App-40x40@2x.png is 80ร—80, Icon-App-1024x1024@1x.png is 1024ร—1024). The splash set LaunchImage.imageset holds LaunchImage.png (480ร—480), @2x (960ร—960) and @3x (1440ร—1440).

Regenerate the web & Windows icons (user app only)

Web: web/favicon.png (64ร—64) plus web/icons/Icon-192.png, Icon-512.png and the two Icon-maskable-*.png files referenced by web/manifest.json. Windows desktop: windows/runner/resources/app_icon.ico.

Or run the script below

Save it as regen_icons.py in the app folder (cargoproapp/) and run it once โ€” it square-pads the logo and writes every file listed above at the exact size.

pip install Pillow
python regen_icons.py

Replace assets/logo.png

Same rule: a square, transparent PNG of at least 1024 px. The login screen lib/screens/login_screen.dart renders assets/logo.png directly, so it updates without any code change.

Regenerate the Android icons

Identical layout to the user app: android/app/src/main/res/mipmap-*/ic_launcher.png at 48/72/96/144/192 px and mipmap-*/launch_image.png at 160/240/320/480/640 px for the native splash (drawable/launch_background.xml and drawable-v21/launch_background.xml center it).

Regenerate the iOS & web icons

iOS: replace the Icon-App-*.png files in ios/Runner/Assets.xcassets/AppIcon.appiconset/ per its Contents.json, and LaunchImage.png / @2x / @3x in LaunchImage.imageset. Web: web/favicon.png (64ร—64) and web/icons/Icon-192.png, Icon-512.png plus the maskable variants. The driver app has no Windows target, so there is no .ico to update.

Or run the script below

Save it as regen_icons.py in cargoprodriver/ and run it โ€” the Windows step is skipped automatically because the folder does not exist.

pip install Pillow
python regen_icons.py

The icon regeneration script

One script covers both apps โ€” it square-pads assets/logo.png, resizes it and saves every launcher, splash and favicon file at its exact path and size. iOS icon sizes are read straight from Contents.json, so the script stays correct even if the asset catalog changes.

# regen_icons.py -- regenerate all app icons from assets/logo.png.
# Run from inside the app folder (cargoproapp/ or cargoprodriver/):
#   pip install Pillow
#   python regen_icons.py
import json
import os

from PIL import Image

APP = os.path.abspath(os.path.dirname(__file__))
logo = Image.open(os.path.join(APP, "assets", "logo.png")).convert("RGBA")

# Square-pad: center the artwork on a transparent square canvas.
side = max(logo.size)
mark = Image.new("RGBA", (side, side), (0, 0, 0, 0))
mark.paste(logo, ((side - logo.width) // 2, (side - logo.height) // 2), logo)

def save(size, rel):
    path = os.path.join(APP, rel)
    os.makedirs(os.path.dirname(path), exist_ok=True)
    mark.resize((size, size), Image.LANCZOS).save(path)

# Android launcher icons + native splash bitmap.
DENSITIES = {  # density: (ic_launcher px, launch_image px)
    "mdpi": (48, 160), "hdpi": (72, 240), "xhdpi": (96, 320),
    "xxhdpi": (144, 480), "xxxhdpi": (192, 640),
}
for dpi, (launcher, splash) in DENSITIES.items():
    base = "android/app/src/main/res/mipmap-" + dpi
    save(launcher, base + "/ic_launcher.png")
    save(splash, base + "/launch_image.png")

# iOS AppIcon set -- pixel sizes derived from Contents.json.
xc = "ios/Runner/Assets.xcassets/AppIcon.appiconset"
with open(os.path.join(APP, xc, "Contents.json"), encoding="utf-8") as fh:
    for entry in json.load(fh)["images"]:
        px = round(float(entry["size"].split("x")[0]) * int(entry["scale"].rstrip("x")))
        save(px, xc + "/" + entry["filename"])

# iOS launch image (480 px at 1x, then 2x and 3x variants).
li = "ios/Runner/Assets.xcassets/LaunchImage.imageset"
for suffix, px in [("", 480), ("@2x", 960), ("@3x", 1440)]:
    save(px, li + "/LaunchImage" + suffix + ".png")

# Web favicon + PWA icons from web/manifest.json.
save(64, "web/favicon.png")
for px in (192, 512):
    save(px, "web/icons/Icon-%d.png" % px)
    save(px, "web/icons/Icon-maskable-%d.png" % px)

# Windows desktop icon (user app only; skipped for the driver app).
if os.path.isdir(os.path.join(APP, "windows")):
    mark.resize((256, 256), Image.LANCZOS).save(
        os.path.join(APP, "windows/runner/resources/app_icon.ico"),
        sizes=[(16, 16), (32, 32), (48, 48), (64, 64), (256, 256)])

print("Done -- all launcher, splash and favicon images regenerated.")
Tip iOS renders icons with transparency as a black background โ€” if your logo is mostly transparent, paste it onto a solid brand-colored square before saving the iOS entries.

The admin panel's logo, favicon and site name are uploaded in the admin panel itself โ€” no file editing or redeploy needed. The full walkthrough (including where the files are stored and their fallbacks) lives on Customize the Admin Panel → Logo & favicon; the storage mechanics are in Admin Tools → Logo & favicon.

Colors & theme

All three products share the brand green #00D084. Each product defines it in exactly one place:

ProductWhere the colors liveGuide
User appcargoproapp/lib/core/theme.dart โ€” CargoColors.light / CargoColors.darkCustomize the Flutter Apps → Brand colors
Driver appcargoprodriver/lib/core/theme.dart โ€” same structureCustomize the Flutter Apps → Brand colors
Admin panelcargoproadmin/resources/css/app.css โ€” the Tailwind @theme brand scaleCustomize the Admin Panel → Colors

The per-product pages show the exact code and the values to change.

Languages

English, Arabic and Spanish ship with the product. The admin panel and the user app use different translation systems.

Admin panel (en / ar / es)

Translations live in cargoproadmin/resources/lang/en.json, ar.json and es.json. Each file maps the English source text to its translation (2,259 keys per locale), and Blade templates look strings up with __('Company / Site Name'). To change or add wording, edit the JSON files โ€” keep the key exactly as the English text used in code.

Each admin user switches language from the header's language menu (English / Espaรฑol / ุงู„ุนุฑุจูŠุฉ). It calls GET /locale/{locale} (LocaleController::switch), which validates the locale against LocalizationServiceProvider (supports en, es, ar) and stores it in the session as app_locale โ€” the choice is per session, per browser.

User app (Flutter gen-l10n)

The user app compiles its strings with Flutter's gen-l10n (configured in cargoproapp/l10n.yaml, output to lib/l10n/gen/). Source of truth: 19 part files in lib/l10n/parts/ (one per feature, e.g. auth.json, ship.json, ui.json), each holding the en/ar/es values per key:

{
  "prefix": "auth",
  "strings": {
    "authSignIn": { "en": "Sign in", "ar": "ุชุณุฌูŠู„ ุงู„ุฏุฎูˆู„", "es": "Iniciar sesiรณn" }
  }
}

After editing any part file, regenerate from the cargoproapp/ folder:

dart run tool/merge_l10n.dart   # parts -> app_en.arb / app_ar.arb / app_es.arb
flutter gen-l10n                # ARB -> lib/l10n/gen/ AppLocalizations

The merge tool enforces unique, lowerCamelCase keys that start with the file's prefix, and falls back to English (with a warning) when an ar/es value is missing โ€” unless the admin panel's own lang file already translates the exact English string, in which case it seeds from there.

flowchart LR
    A["lib/l10n/parts/*.json"] -->|"dart run tool/merge_l10n.dart"| B["app_en.arb, app_ar.arb, app_es.arb"]
    B -->|"flutter gen-l10n"| C["AppLocalizations in lib/l10n/gen/"]
    C -->|"SettingsProvider.locale"| D["App UI with RTL for Arabic"]
      

Inside the app, users pick the language under Profile โ†’ Settings โ†’ Preferences โ†’ Language. SettingsProvider.setLanguageCode persists the choice in SharedPreferences; the supported codes are defined in kSupportedLanguageCodes in lib/providers/settings_provider.dart. Arabic layouts flip to RTL automatically โ€” MaterialApp is built with the provider's locale and the global material/widgets/cupertino localization delegates, so no extra wiring is needed.

Adding a brand-new language (user app) A new locale is not just a new file. You need: 1) the locale entries in every part file in lib/l10n/parts/ โ€” missing values silently fall back to English; 2) the locale added in tool/merge_l10n.dart โ€” both the locales list and the output map that writes the app_<locale>.arb files are hard-coded to en/ar/es; 3) the code added to kSupportedLanguageCodes in lib/providers/settings_provider.dart and to the _languageNames map in lib/screens/profile/settings_screen.dart; then run the two commands above.
Driver app The driver app is English-only โ€” it has no localization wiring, so there are no translation files to edit there.

Fonts

Both apps ship Nunito as static weight files in assets/fonts/ (Nunito-400.ttf through Nunito-900.ttf, plus the OFL.txt license) and register them in pubspec.yaml:

  fonts:
    - family: Nunito
      fonts:
        - asset: assets/fonts/Nunito-400.ttf
          weight: 400
        - asset: assets/fonts/Nunito-500.ttf
          weight: 500
        - asset: assets/fonts/Nunito-600.ttf
          weight: 600
        - asset: assets/fonts/Nunito-700.ttf
          weight: 700
        - asset: assets/fonts/Nunito-800.ttf
          weight: 800
        - asset: assets/fonts/Nunito-900.ttf
          weight: 900

The fastest way to swap the typeface is to keep the Nunito family name and overwrite the six .ttf files with your font's weights (400โ€“900) โ€” the themes (fontFamily: 'Nunito' in AppTheme/DriverTheme) keep working. To use a real new family name instead, update the family: line in both apps' pubspec.yaml, the file names, and the fontFamily references in lib/core/theme.dart.

Admin panel font The admin panel loads Outfit from Google Fonts in resources/views/layouts/admin.blade.php; the stack is defined by the --font-sans token in resources/css/app.css. Swap the Google Fonts <link> and the token together, then npm run build.

Rename the application id / bundle id

The shipped Android ids are com.devtrixx.cargouser and com.devtrixx.cargodriver (iOS bundle ids match). If you publish under your own brand, rename them before registering the apps in Firebase โ€” the package name is locked on first registration. The step-by-step walkthrough (Gradle, Kotlin package, iOS project) is now part of Customize the Flutter Apps → Application ID.