Driver Lite

PR Checks workflow status

Deeper engineering docs — architecture overview, CI/CD runbook, and a full onboarding guide — live as Linear documents under the My Fuel Orders team (not tied to any single initiative, since this content outlives any one release).

Getting Started

Flutter SDK 3.44.8

A pre-commit hook mirrors the GitHub Actions PR checks (dart format, flutter analyze) locally, so failures surface before a push instead of after a PR round-trip. It's opt-in — activate it once per clone:

git config core.hooksPath .githooks

Skip it for a single commit with git commit --no-verify.


▶️ Running the App (Local Development)

The application supports two explicit environments:

  • Staging
  • Production

Each environment has:

  • Its own Dart entrypoint
  • Its own Android flavor
  • Shared application root and configuration

Dart Entry Points

Environment Entry File
Staging lib/presentation/app/flavors/main_staging.dart
Production lib/presentation/app/flavors/main_production.dart

Both entrypoints:

  • Load the shared root widget from:
    lib/presentation/app/app.dart
    
  • Configure environment-specific values via:
    lib/presentation/app/app_config.dart
    

Running via Flutter CLI

Staging

flutter run \
  --flavor staging \
  --target lib/presentation/app/flavors/main_staging.dart

Production

flutter run \
  --flavor production \
  --target lib/presentation/app/flavors/main_production.dart

Running via Android Studio / IntelliJ

The project includes predefined Run Configurations:

  • Staging
  • Production

Each configuration automatically sets:

  • The correct Android build flavor
  • The correct Dart entrypoint

Select the configuration from the Run dropdown and press ▶️.


Running Tests

Flutter unit tests

# Run all tests
flutter test

# Run a single test file
flutter test test/path/to/test.dart

Tests cover all application layers — infrastructure, data, logic, shared utilities, and presentation. The PR validation workflow runs the full suite automatically on every pull request.

Unit tests require no secrets, flavors, or platform setup — they run on any machine with Flutter installed.

Script tests

bash scripts/test_scripts.sh

Validates the behaviour of all build scripts (lib.sh, build.sh, smoke.sh, upload_symbols.sh) — argument validation, file-system guards, dry-run output, token guards, CI version resolution, and obfuscation behaviour. Runs as part of the PR validation workflow and requires no Flutter or platform setup.


Push Notifications

Push notifications are delivered via Firebase Cloud Messaging (FCM).

⚠️ Push notifications cannot be tested on the iOS simulator — APNs requires a physical device. The app will build and run on the simulator, but no notifications will be delivered.

Android notifications work on both emulators and physical devices.


Running & Building via Xcode (iOS)

For iOS, Xcode is the preferred tool for:

  • Archive generation
  • TestFlight uploads
  • App Store distribution
  • Signing and provisioning management

Steps

  1. Open:
    ios/Runner.xcworkspace
    
  2. Select the appropriate scheme:
    • Staging
    • Production
  3. Choose a build configuration:
    • Debug
    • Profile
    • Release
  4. Run, archive, or distribute using standard Xcode workflows

This workflow coexists intentionally with Flutter CLI and build.sh.


Release Builds (build.sh)

All CI-driven, release-grade Android and iOS builds are produced exclusively via:

scripts/build.sh

This script enforces a strict separation between:

  • Smoke builds – debug, unsigned, human-only
  • Release builds – signed, versioned, CI-driven

build.sh must never be used for debug or smoke builds.


Supported Platforms & Artifacts

  • Android
    • APK (apk)
    • App Bundle (appbundle, default)
  • iOS
    • IPA (ipa) for TestFlight / App Store distribution

Usage

bash ./scripts/build.sh <flavor> <platform> [artifact] [flags]

Positional Arguments

Argument Description
flavor staging or production
platform android or ios
artifact Android only: apk or appbundle (default AAB)

Flags

Flag Description
--obfuscate Enable Dart obfuscation (explicit opt-in)
--no-pub Skip dependency resolution (flutter pub get)
--dry-run Print resolved build commands without executing
--help Show usage information and exit

When to use --no-pub

--no-pub is intended primarily for CI release builds.

Use it when:

  • Dependencies were resolved earlier in the pipeline
  • Deterministic, faster builds are required
  • CI controls dependency state

Avoid using --no-pub locally unless dependencies are guaranteed up-to-date.


Examples

# Android AAB (default)
bash ./scripts/build.sh staging android

# Android APK
bash ./scripts/build.sh production android apk

# Production Android AAB with obfuscation
bash ./scripts/build.sh production android appbundle --obfuscate

# CI-style build skipping pub resolution
bash ./scripts/build.sh production android appbundle --no-pub

# Inspect build command only
bash ./scripts/build.sh production android appbundle --obfuscate --dry-run

Obfuscation & Symbol Upload

When --obfuscate is passed, build.sh:

  1. Passes --obfuscate --split-debug-info to Flutter to generate Dart debug symbols
  2. Calls scripts/upload_symbols.sh post-build to upload Dart symbols and the obfuscation map to Sentry

ProGuard/R8 mapping upload (Android) is handled automatically during the Gradle build via the Sentry Android Gradle Plugin — no extra step required.

APK builds skip obfuscation and symbol upload entirely. APKs are for internal testing where readable stack traces are more valuable.


Key Characteristics

  • Flavor-aware (staging, production)
  • Explicit entrypoint selection
  • Version name resolved from pubspec.yaml; build number uses BUILD_NUMBER + BUILD_NUMBER_OFFSET in CI, pubspec.yaml +N suffix locally
  • CI-only strict SemVer enforcement
  • Optional, explicit obfuscation with automatic symbol upload
  • Optional, explicit pub skipping
  • Safe defaults (Android defaults to appbundle)

Symbol Upload (upload_symbols.sh)

scripts/upload_symbols.sh is a standalone script that uploads Dart debug symbols and obfuscation maps to Sentry after an obfuscated release build.

It is called automatically by build.sh when --obfuscate is passed, but can also be run independently to re-upload symbols without rebuilding (e.g. after a failed upload in CI).

Usage

bash ./scripts/upload_symbols.sh <flavor> <platform> <dart_symbol_map> <build_path> [build_paths...] [--dry-run]

Arguments

Argument Description
flavor staging or production
platform android or ios
dart_symbol_map Path to the Dart obfuscation map JSON
build_path One or more paths to scan for Dart debug symbols

Options

Flag Description
--dry-run Print resolved commands without executing

Examples

# Android staging symbol upload (standalone)
bash ./scripts/upload_symbols.sh staging android \
  build/debug-info/android/obfuscation-map.json \
  build/debug-info/android

# iOS production symbol upload (standalone)
bash ./scripts/upload_symbols.sh production ios \
  build/debug-info/ios/obfuscation-map.json \
  build/debug-info/ios \
  build/ios

# Dry-run
bash ./scripts/upload_symbols.sh staging android \
  build/debug-info/android/obfuscation-map.json \
  build/debug-info/android \
  --dry-run

Required Environment Variables

Variable Used for
SENTRY_AUTH_TOKEN_STAGING Staging symbol uploads
SENTRY_AUTH_TOKEN_PROD Production symbol uploads

These must be set in your shell (locally) or as secure CI variables (Codemagic).


Smoke Builds (smoke.sh)

scripts/smoke.sh exists purely for local developer confidence.

It answers exactly one question:

“Does the app still build right now?”


Usage

bash ./scripts/smoke.sh [platform] [--help]

Arguments

Argument Description
(none) Build both Android and iOS
android Android debug APK only
ios iOS debug build (simulator, unsigned)

Options

Flag Description
--help Show usage and exit

Behavior

  • Debug mode only
  • Unsigned
  • Staging flavor only
  • Entrypoint:
    lib/presentation/app/flavors/main_staging.dart
    
  • No version enforcement
  • No obfuscation
  • No CI coupling

Examples

# Build both platforms
bash ./scripts/smoke.sh

# Android only
bash ./scripts/smoke.sh android

# iOS only
bash ./scripts/smoke.sh ios

Must NOT

  • Run in CI
  • Produce release artifacts
  • Use production entrypoints
  • Use signing or store credentials
  • Replace or duplicate build.sh

Release Process (CI/CD)

This project uses a branch-driven staging flow and a tag-driven production flow.

Branches

  • main
    • Always production-ready
    • Receives changes only via pull requests
    • Triggers smoke builds for Android and iOS on merge
  • release/staging
    • Dedicated delivery branch for internal testing
    • Triggers staging releases to Google Play Internal and TestFlight
    • Uses the merge commit message as release notes

Staging Releases

  1. Merge main into release/staging
  2. Write human-readable release notes in the merge commit message
  3. Push to release/staging
  4. CI builds and publishes:
    • Android → Play Internal
    • iOS → TestFlight

No tags are used for staging releases.

Production Releases

  1. Ensure main contains the approved code
  2. Create an annotated Git tag:
    git tag -a vX.Y.Z -m "Release title" -m "Detailed release notes"
    git push origin vX.Y.Z
    
  3. CI builds and publishes:
    • Android → Play Production
    • iOS → TestFlight / App Store

Production release notes are sourced exclusively from annotated tags.


Changelog Standards

This project follows the Keep a Changelog standard to ensure consistent, clear, and human-readable release notes.

Official reference:

Versioning

This project follows Semantic Versioning (SemVer):

MAJOR.MINOR.PATCH
  • MAJOR – Breaking changes
  • MINOR – Backward-compatible features
  • PATCH – Backward-compatible bug fixes

Reference:


Changelog Rules

All contributors must follow these rules when updating CHANGELOG.md:

  1. Every user-facing change must be documented.
  2. New changes go under the Unreleased section.
  3. Do not modify past release entries (except for typo fixes).
  4. Each release must include a release date in YYYY-MM-DD format.
  5. Entries must be written in clear, concise, imperative tense.

Allowed Change Categories

Only the following headings are permitted:

  • Added – New features or capabilities
  • Changed – Changes to existing functionality
  • Deprecated – Features marked for future removal
  • Removed – Features or files that were deleted
  • Fixed – Bug fixes
  • Security – Security-related changes or fixes

Do not introduce custom categories.


Example Changelog Entry

## [1.2.0] - 2026-02-01

### Added
- CI-based Android signing support using environment variables

### Changed
- Migrated Android build scripts to Kotlin DSL

### Fixed
- Release build failure when signing credentials were missing

Local Secrets & Signing

iOS Secrets Management (Local Development)

This project uses a local secrets file approach for managing sensitive iOS configuration values (such as API keys) during development. These values are never committed to source control.

Secrets file

Create a file named:

ios/Flutter/Secrets.xcconfig

This file is git-ignored and must be created locally by each developer.

Example contents (do not commit this file):

GMS_API_KEY=YOUR_IOS_GOOGLE_MAPS_API_KEY

Wiring secrets into iOS builds

Ensure Secrets.xcconfig is included by both:

Flutter/Debug.xcconfig
Flutter/Release.xcconfig
#include "Secrets.xcconfig"

Using the API key in iOS

The Google Maps API key is read from the build settings and injected via Info.plist:

<key>GMSApiKey</key>
<string>$(GMS_API_KEY)</string>

Android Secrets Management (Local Development)

Android builds use the same file-based secrets model as iOS.

Secrets are required for all Android builds, including debug and smoke builds.

If required secrets are missing, the build will fail immediately.


Secrets file

Create:

android/secrets.properties

This file is git-ignored and must be created locally.

Example (do not commit):

GMS_API_KEY=YOUR_ANDROID_GOOGLE_MAPS_API_KEY

⚠️ This file is mandatory. Android builds will fail if it is missing or incomplete.


Wiring secrets into Android builds

During the Gradle configuration phase, the build:

  • Loads android/secrets.properties
  • Verifies required secrets are present
  • Fails fast if any are missing
  • Injects values via manifest placeholders

This ensures:

  • No secrets in Git
  • Deterministic builds
  • Early failure instead of runtime crashes

Using the API key in Android

<meta-data
    android:name="com.google.android.geo.API_KEY"
    android:value="${GMS_API_KEY}" />

Required Android secrets

Key Description
GMS_API_KEY Google Maps API key (Android)

Sentry Auth Tokens (Local Development)

To upload debug symbols when building locally with --obfuscate, set the following environment variables in your shell profile (~/.zshrc or ~/.bashrc):

export SENTRY_AUTH_TOKEN_STAGING="your-staging-token"
export SENTRY_AUTH_TOKEN_PROD="your-production-token"

Then reload your shell:

source ~/.zshrc

These tokens are only required when using --obfuscate. Builds without obfuscation do not need them.

In CI, these are provided as secure environment variables in Codemagic under the sentry group.


CI behavior

In CI:

  • android/secrets.properties is generated at build time
  • Values come from secure CI variables
  • The file is written before any Android tooling runs

Local and CI behavior are intentionally identical.


🚀 Project Context Exporter (repomix)

This section explains how to use the repomix tool to generate a single, comprehensive context file of this project. This is useful for sharing, archiving, or feeding the project's structure and code to AI models.

1. Prerequisites

Before you begin, ensure you have the repomix command-line tool installed and up-to-date. For complete installation and usage instructions, please refer to the official GitHub repository.

2. Configuration Options

This project includes a single predefined repomix configuration file located at the project root.

  • repomix.config.json

    • What it does:
      • Exports only the application source code from lib/**.
      • Removes comments and empty lines.
      • Compresses the output for minimal size.
      • Excludes generated files (*.g.dart, *.freezed.dart), tests, IDE config, and build artifacts.
      • Runs Repomix security checks during export.
    • Use case:
      • Produces a clean, minimal, AI-optimized context file focused strictly on business logic and application structure.

3. How to Export

  1. Open your terminal in the root directory of this project.
  2. Choose the appropriate command below based on your needs.
  3. Replace "path/to/your/output.md" with your desired output file path and name.

Command to Export Library Code Only

repomix -o "path/to/your/project_context.md"

Libraries

data/models/network/error_response_model
data/models/user_management/auth_token_model
data/models/user_management/user_model
data/providers/auth_service
data/providers/firebase_remote_config_service
data/providers/jailbreak_root_detector_service
data/providers/user_service
data/repositories/auth_repository
data/repositories/i_jailbreak_root_detector
data/repositories/image_repository/image_picker_and_cropper_repository
data/repositories/image_repository/image_settings/cropper_settings
data/repositories/image_repository/image_settings/pick_image_settings
data/repositories/notification_repository
data/repositories/user_repository
data/secure_storage/secure_storage
design_system/theme/app_theme
design_system/theme/buttons/buttons
Barrel export for the design system button themes.
design_system/theme/buttons/dark_theme_buttons
design_system/theme/buttons/light_theme_buttons
design_system/theme/colors/colors
Barrel export for the design system color schemes.
design_system/theme/colors/dark_theme_colors
design_system/theme/colors/light_theme_colors
design_system/theme/inputs/dark_theme_inputs
design_system/theme/inputs/inputs
Barrel export for the design system input decoration themes.
design_system/theme/inputs/light_theme_inputs
design_system/theme/layouts/corner_radius
design_system/theme/layouts/spacing
design_system/theme/text/app_typography
infrastructure/di/injection
infrastructure/exceptions/app_exception
infrastructure/exceptions/app_version_exception
infrastructure/exceptions/exceptions
Exception types thrown across the app's networking, version-check, and security-check layers.
infrastructure/exceptions/external_exceptions
infrastructure/exceptions/network/network_app_exception
infrastructure/exceptions/network/network_exceptions
infrastructure/exceptions/security/jailbreak_app_exception
infrastructure/exceptions/security/jailbreak_exceptions
infrastructure/logging/app_logger
infrastructure/logging/sentry_app_logger
infrastructure/network/app_exception_mapping_interceptor
infrastructure/network/auth_failure_interceptor
infrastructure/network/auth_failure_notifier
infrastructure/network/auth_fresh_factory
infrastructure/network/base_url_resolver
infrastructure/network/certificate_pinning
infrastructure/network/dio_client_factory
infrastructure/network/null_params_interceptor
infrastructure/network/reporting_interceptor
infrastructure/network/retry_evaluator
infrastructure/network/token_refresher
infrastructure/network/token_storage_service
infrastructure/platform/url_launcher
logic/bloc/authentication/authentication_bloc
logic/cubit/connectivity/internet_cubit
logic/cubit/force_update/force_update_cubit
logic/cubit/force_update/force_update_types
logic/cubit/loading/loading_cubit
logic/cubit/startup/startup_cubit
logic/cubit/theme/theme_cubit
logic/debug/app_bloc_observer
logic/security/security_checker
logic/startup/startup_orchestrator
logic/startup/startup_task
logic/startup/startup_types
logic/startup/task/app_version_check_task
logic/startup/task/auth_check_task
logic/startup/task/connectivity_init_task
logic/startup/task/jailbreak_check_task
logic/startup/task/remote_config_sync_task
presentation/app/app
presentation/app/app_config
presentation/app/flavors/main_production
presentation/app/flavors/main_staging
presentation/onboarding/onboarding_view
presentation/router/app_router
presentation/screens/authentication/login/login_page
presentation/screens/home/home_page
presentation/screens/jailbreak/jailbreak_view
presentation/screens/splash/force_update_dialog
presentation/screens/splash/splash_view
presentation/widgets/layouts/loading_screen_builder
presentation/widgets/loaders/bottom_loader
presentation/widgets/loaders/full_screen_loader
presentation/widgets/loaders/loader
presentation/widgets/loaders/loading_overlay
shared/config/iterable_modifier
shared/constants/app/app_environment
shared/constants/app/authentication_status
shared/constants/app/connection_type
shared/constants/app/remote_config_keys
shared/constants/app/security_check_type
shared/constants/app/security_constants
shared/constants/strings/asset_strings
shared/constants/strings/strings
shared/input_formatters/formatters
shared/input_formatters/prevent_spaces_formatter
shared/utils/extensions
shared/utils/remote_config_asset_loader
shared/utils/toast_service
shared/utils/value_wrapper