Driver Lite
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
Git Hooks (optional but recommended)
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
- Open:
ios/Runner.xcworkspace - Select the appropriate scheme:
StagingProduction
- Choose a build configuration:
- Debug
- Profile
- Release
- 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.shmust never be used for debug or smoke builds.
Supported Platforms & Artifacts
- Android
- APK (
apk) - App Bundle (
appbundle, default)
- APK (
- iOS
- IPA (
ipa) for TestFlight / App Store distribution
- IPA (
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:
- Passes
--obfuscate --split-debug-infoto Flutter to generate Dart debug symbols - Calls
scripts/upload_symbols.shpost-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 usesBUILD_NUMBER + BUILD_NUMBER_OFFSETin CI,pubspec.yaml+Nsuffix 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
- Merge
mainintorelease/staging - Write human-readable release notes in the merge commit message
- Push to
release/staging - CI builds and publishes:
- Android → Play Internal
- iOS → TestFlight
No tags are used for staging releases.
Production Releases
- Ensure
maincontains the approved code - Create an annotated Git tag:
git tag -a vX.Y.Z -m "Release title" -m "Detailed release notes" git push origin vX.Y.Z - 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:
- Every user-facing change must be documented.
- New changes go under the
Unreleasedsection. - Do not modify past release entries (except for typo fixes).
- Each release must include a release date in
YYYY-MM-DDformat. - 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.propertiesis 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.
- Official Documentation: https://github.com/yamadashy/repomix
- Installation:
npm install -g repomix
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.
- Exports only the application source code from
- Use case:
- Produces a clean, minimal, AI-optimized context file focused strictly on business logic and application structure.
- What it does:
3. How to Export
- Open your terminal in the root directory of this project.
- Choose the appropriate command below based on your needs.
- 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
- Barrel export for the design system button themes.
- 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/legal/legal_type
- 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