A single Flutter codebase that can run with one Customer Engagement Platform (CEP) baked in per run — WebEngage, CleverTap, or MoEngage. The CEP is chosen by running a setup script before flutter run. There is no runtime switch and no default: a project that hasn't had the script run will not compile.
This is an internal test app — its sole purpose is to validate the Digia engage SDK and its CEP plugin packages. It will never ship to the App Store or Play Store.
| Tool | Version | Notes |
|---|---|---|
| Flutter | ≥ 3.x | Install via flutter.dev; confirm with flutter doctor |
| Dart | ≥ 3.0 | Bundled with Flutter |
| Xcode | 16+ | iOS builds; install Command Line Tools and at least one iOS simulator |
| CocoaPods | ≥ 1.14 | iOS native deps — brew install cocoapods |
| Android Studio | latest | Android SDK + an emulator; set ANDROID_HOME |
| JDK | 17 | Required by the Android Gradle build |
Run flutter doctor after installing — fix anything it flags before continuing.
# 1. Create your credentials file from the template
cp cep.env.example cep.env
# Open cep.env and fill in:
# DIGIA_API_KEY — your Digia API key
# DIGIA_ENVIRONMENT — production | sandbox (use sandbox for local testing)
# ... plus the active CEP's credential block (see cep.env for the full list)
# 2. Run the setup script for your chosen CEP
./scripts/cep.sh webengage # or: clevertap | moengage
# 3. Start the app
flutter runThe script installs pub packages and generates everything the native build needs. You do not run flutter pub get manually — the script does it.
Re-run the script with a different CEP name, then start the app again:
./scripts/cep.sh clevertap
flutter runThe script rewrites the pubspec, native init files, and all generated config in one shot. No manual edits needed.
Confirm the build is what you think: the app name on the device reads MediHub (CleverTap) / (MoEngage) / (WebEngage) — the script sets this per CEP.
Everything that varies per developer or per run — which Digia environment, and the active CEP's credentials — lives in one file: cep.env. It is gitignored; never commit it.
# ── Common ──
DIGIA_API_KEY=digia_xxxx
DIGIA_ENVIRONMENT=sandbox # production | sandbox
# ── CleverTap (fill in when using clevertap) ──
CLEVERTAP_ACCOUNT_ID=XXX-XXX-XXXX
CLEVERTAP_ACCOUNT_TOKEN=XXX-XXXThe script is the only thing you run to switch CEPs. It generates every file that varies by CEP, injects credentials from cep.env, and runs flutter pub get.
| Step | What it generates |
|---|---|
| 1 | pubspec.yaml — base deps + the active CEP's packages |
| 2 | lib/cep/active_cep.g.dart — points to the right adapter class |
| 3 | android/cep.properties — app name + active CEP for Gradle |
| 4 | ios/Flutter/cep_generated.xcconfig — app name for Xcode |
| 5 | analysis_options.yaml — linter config with inactive adapters excluded |
| 6–8 | Credential files for Dart, Android, and iOS |
| 9 | lib/config/cep_secrets.g.dart — CepSecrets class with API keys as constants |
| 10 | android/.../MainApplication.kt — native Android SDK init |
| 11 | ios/Runner/AppDelegate.swift — native iOS SDK init |
| 12 | flutter pub get |
Every screen and service talks to a generic CepAdapter interface — never to WebEngage/CleverTap/MoEngage directly. Each CEP's adapter lives in lib/cep/<cep>_adapter.dart. The generated active_cep.g.dart barrel points to exactly one of them; the others are excluded from the analyzer by analysis_options.yaml.
cep.env (DIGIA_ENVIRONMENT + CEP credentials)
│
▼
./scripts/cep.sh <cep>
│
├── pubspec.yaml ← only the active CEP's packages are installed
├── active_cep.g.dart ← createActiveAdapter() returns e.g. CleverTapAdapter()
├── cep_secrets.g.dart ← CepSecrets.digiaApiKey, .moengageAppId, etc.
├── MainApplication.kt ← Android SDK init for the active CEP
└── AppDelegate.swift ← iOS SDK init for the active CEP
│
▼
flutter run
│
▼
DigiaService.initialize()
→ adapter.initNativeSdk()
→ Digia.initialize(environment: CepSecrets.digiaEnvironment)
→ Digia.register(adapter.makeDigiaPlugin())
→ adapter.identify(user)
| Layer | File | What it does |
|---|---|---|
| Dart packages | pubspec.yaml (generated) |
Only the active CEP's packages are in the dep tree |
| Analyzer | analysis_options.yaml (generated) |
Inactive adapter files are excluded — they won't compile |
| Native | MainApplication.kt / AppDelegate.swift (generated) |
Only the active CEP's native SDK is initialized |
| File | Purpose |
|---|---|
cep.env |
Your local credentials and environment selection (gitignored) |
cep.env.example |
Template — update when adding a new credential key |
pubspec.base.yaml |
Base Flutter dependencies (common to all CEPs) |
analysis_options.base.yaml |
Base linter config (CEP excludes are appended by the script) |
tool/cep/webengage.deps.yaml |
WebEngage-specific pub packages |
tool/cep/clevertap.deps.yaml |
CleverTap-specific pub packages |
tool/cep/moengage.deps.yaml |
MoEngage-specific pub packages |
lib/cep/cep_adapter.dart |
CepAdapter interface + CepUser model |
lib/cep/webengage_adapter.dart |
WebEngage adapter implementation |
lib/cep/clevertap_adapter.dart |
CleverTap adapter implementation |
lib/cep/moengage_adapter.dart |
MoEngage adapter implementation |
scripts/cep.sh |
The setup script itself |
| File | Regenerated by |
|---|---|
pubspec.yaml |
scripts/cep.sh step 1 |
pubspec.lock |
flutter pub get (also gitignored — internal app) |
lib/cep/active_cep.g.dart |
scripts/cep.sh step 2 |
lib/config/cep_secrets.g.dart |
scripts/cep.sh step 9 |
analysis_options.yaml |
scripts/cep.sh step 5 |
android/cep.properties |
scripts/cep.sh step 3 |
android/cep_secrets.properties |
scripts/cep.sh step 7 |
android/app/src/main/kotlin/.../MainApplication.kt |
scripts/cep.sh step 10 |
ios/Flutter/cep_generated.xcconfig |
scripts/cep.sh step 4 |
ios/Flutter/Secrets.xcconfig |
scripts/cep.sh step 8 |
ios/Runner/AppDelegate.swift |
scripts/cep.sh step 11 |
Rule of thumb: if a value is in cep.env, that is the only place to change it. Never hardcode a credential anywhere in source.
Credentials needed in cep.env: WEBENGAGE_LICENSE_CODE, WEBENGAGE_ENVIRONMENT (in / us / eu / sg — match your WE account region)
- Android:
WebengageInitializer.initialize()in generatedMainApplication.kt - iOS:
WebEngage.sharedInstance().application(…)in generatedAppDelegate.swift
Credentials needed in cep.env: CLEVERTAP_ACCOUNT_ID, CLEVERTAP_ACCOUNT_TOKEN, and optionally CLEVERTAP_ACCOUNT_REGION (eu1 / in1 / sg1 / us1; leave blank for the default region)
- Android: auto-initializes from
AndroidManifest.xmlmeta-data (values injected by Gradle fromcep_secrets.properties) - iOS:
CleverTap.autoIntegrate()reads fromInfo.plist(values come fromSecrets.xcconfig)
Credentials needed in cep.env: MOENGAGE_WORKSPACE_ID
- Android:
MoEFlutterHelper.getInstance().initialise(…)in generatedMainApplication.kt - iOS:
MoEngage.sharedInstance.initializeDefaultSDK(sdkConfig:launchOptions:)in generatedAppDelegate.swift
When you're changing the Digia SDK itself, point the app at a local copy instead of the published pub version. The Digia packages live in sibling repos:
| pub package | local source path (relative to this app) |
|---|---|
digia_engage |
../../digia_engage/flutter/ |
digia_webengage_plugin |
../../digia_webengage_plugin/flutter/ |
digia_engage_clevertap |
../../digia_engage_clevertap/flutter/ |
digia_moengage_plugin |
../../digia_moengage_plugin/flutter/ |
Create a pubspec_overrides.yaml in the project root (it is gitignored):
# pubspec_overrides.yaml — never commit this file
dependency_overrides:
digia_engage:
path: ../../digia_engage/flutterThen run flutter pub get. Delete the file to revert.
Replace the version constraint with a path: reference directly in pubspec.base.yaml or the active CEP's .deps.yaml, then re-run ./scripts/cep.sh <cep>:
digia_engage:
path: ../../digia_engage/flutterRevert by restoring the version constraint and re-running the script.
medihub_flutter/
├── scripts/
│ └── cep.sh ← run this to switch CEPs
├── tool/cep/
│ ├── webengage.deps.yaml ← WebEngage pub packages
│ ├── clevertap.deps.yaml ← CleverTap pub packages
│ └── moengage.deps.yaml ← MoEngage pub packages
├── lib/
│ ├── cep/
│ │ ├── cep_adapter.dart ← CepAdapter interface + CepUser model
│ │ ├── webengage_adapter.dart ← WebEngage implementation
│ │ ├── clevertap_adapter.dart ← CleverTap implementation
│ │ ├── moengage_adapter.dart ← MoEngage implementation
│ │ ├── cep.dart ← getCepAdapter() factory
│ │ └── active_cep.g.dart ← GENERATED — do not edit
│ ├── config/
│ │ └── cep_secrets.g.dart ← GENERATED — do not edit
│ ├── core/services/
│ │ └── digia_service.dart ← bootstraps Digia + the active adapter
│ └── views/ ← app screens
├── pubspec.base.yaml ← committed dep source; script generates pubspec.yaml
├── analysis_options.base.yaml ← committed linter source; script generates analysis_options.yaml
├── cep.env.example ← template — copy to cep.env and fill in
└── cep.env ← GITIGNORED — your local credentials
cep.envis the control panel. SetDIGIA_ENVIRONMENT=sandboxfor local testing. Fill in the active CEP's credential block.- Always run the script before
flutter run../scripts/cep.sh <cep>handles pubspec, credentials, native files, andpub getin one shot. Never runflutter pub getby itself on a fresh clone. - Confirm your build: the device/simulator app name reads
MediHub (CleverTap)etc. If it still shows the old CEP, the script wasn't re-run. - Don't edit generated files.
pubspec.yaml,active_cep.g.dart,cep_secrets.g.dart,MainApplication.kt,AppDelegate.swift, and the xcconfig/properties files are all overwritten on the next script run. Edit the source files listed in §4 instead. flutter analyzewith 0 issues is the bar. The script excludes inactive adapter files from the analyzer so only the active CEP's imports need to resolve.cep.envis never committed. Don't paste credentials into source files. Share them out-of-band.- Switching CEP is one command.
./scripts/cep.sh moengage→flutter run. Nothing else.