Environment Management and Device Conditions (OtaEnvironment)
In tk_ota_push, environment management covers two key facets:
- Runtime Physical Condition Inspection (
lib/src/environment/ota_environment.dart): network connectivity type (OtaConnectivityType), battery level (OtaDeviceConditions), and deferred background scheduling (OtaBackgroundScheduler). - Application Environment Resolution (
dev,staging,prod,uat) and Runtime Track Switching (enableQaMode()/disableQaMode()andFlutterProject.deduceEnvironmentName).
1. Physical Conditions: Network & Battery (ota_environment.dart)
When a patch is flagged for silent download (forceUpdate: false), the SDK inspects physical device conditions via OtaEnvironmentMonitor.checkConditions() before starting the download.
Connectivity Types (OtaConnectivityType)
enum OtaConnectivityType {
wifi,
ethernet,
mobile,
none,
}
Device Physical Conditions (OtaDeviceConditions)
The OtaDeviceConditions class provides:
connectivity(OtaConnectivityType): active connection type.batteryPercentage(int, default100): remaining battery percentage.isCharging(bool, defaultfalse): whether the device is connected to a power source.isUnmeteredWifi(bool): computed getter returningtrueifconnectivity == OtaConnectivityType.wifi || connectivity == OtaConnectivityType.ethernet.isOnline(bool): computed getter returningtrueifconnectivity != OtaConnectivityType.none.
Silent Mode Decision Rules (TkOtaPush.syncWithRemoteConfig)
In lib/src/tk_ota_push_sdk.dart, a silent update (forceUpdate: false) executes immediately if and only if all three criteria are satisfied:
conditions.isOnline == truewifiOk:!requireWifiForSilentUpdate || conditions.isUnmeteredWifibatteryOk:conditions.isCharging || conditions.batteryPercentage >= minBatteryPercentageForSilentUpdate
If any of these conditions fails (e.g., user is on 3G/4G cellular data while requireWifiForSilentUpdate: true, or battery is at 15% without a charger while minBatteryPercentageForSilentUpdate: 20):
- No immediate OTA network call is performed (
networkCallPerformed: false). - The SDK enqueues
_backgroundScheduler.scheduleSilentUpdateTask(...)using unique keytk_ota_silent_patch_${remoteConfig.targetReleaseVersion}_${remoteConfig.latestPatchNumber}. syncWithRemoteConfigreturnsOtaUpdateExecutionMode.deferredToBackgroundTask.
Connecting connectivity_plus and battery_plus to OtaEnvironmentMonitor
The SDK provides DefaultOtaEnvironmentMonitor (which defaults to wifi and 100% battery so no native plugins are forced). To connect your real production hardware monitors:
import 'package:tk_ota_push/tk_ota_push.dart';
class ProductionEnvironmentMonitor implements OtaEnvironmentMonitor {
Future<OtaDeviceConditions> checkConditions() async {
// Example integration with native device plugins
return const OtaDeviceConditions(
connectivity: OtaConnectivityType.wifi,
batteryPercentage: 75,
isCharging: false,
);
}
}
Connecting workmanager to OtaBackgroundScheduler
Similarly, replace InMemoryOtaBackgroundScheduler with an implementation delegating to workmanager:
import 'package:tk_ota_push/tk_ota_push.dart';
class WorkmanagerBackgroundScheduler implements OtaBackgroundScheduler {
Future<void> scheduleSilentUpdateTask({
required String taskUniqueName,
required bool requireWifi,
required bool requireBatteryNotLow,
}) async {
// Register deferred background worker with Android WorkManager / iOS BGTaskScheduler
}
}
2. Application Environments (dev, staging, prod) and URL Resolution
Resolving base_url per Environment
The OTA server URL (base_url) is determined according to the following precedence order:
- At CLI build and publish time:
--server <url>flagbase_urlkey inshorebird.yamlOTA_SERVER_URLenvironment variable- Fallback default:
http://localhost:8080
- At mobile runtime in the Rust engine:
- Reads the embedded
base_urlkey fromflutter_assets/shorebird.yaml. - When a phone queries
POST {base_url}/api/v1/patches/check, the TkushPatch Server backend (ShorebirdUpdaterController.resolveClientReachableUrl) inspectsX-Forwarded-Host,Host, andX-Forwarded-Protoheaders to rewrite download URLs pointing tolocalhostor127.0.0.1into the actual client-reachable endpoint.
- Reads the embedded
Automatic Environment Name Deduction (deduceEnvironmentName)
During multi-environment builds (dev, staging, prod, uat), FlutterProject.deduceEnvironmentName (lib/src/cli/project.dart) automatically infers the active target by evaluating:
- Explicit
--env <name>flag (e.g.,--env staging). --flavor <name>flag (e.g.,--flavor dev).--dart-definearguments matchingENV=...,ENVIRONMENT=..., orFLAVOR=....- Target entrypoint file
--targetmatchingmain_<env>.dartormain-<env>.dart(e.g.,-t lib/main_dev.dartinfersdev). - File name in
--dart-define-from-file(e.g.,env/dev.jsonor.env.staginginfersdevorstaging).
3. Dynamic Runtime Track Switching (stable / staging)
Within a single compiled release, the TkOtaPush instance listens by default to UpdateTrack.stable. You can dynamically switch a device to UpdateTrack.staging (e.g., from a hidden QA menu) without recompiling the app:
final ota = TkOtaPush.instance;
// Switch to UpdateTrack.staging
ota.enableQaMode();
print(ota.track.name); // "staging"
// Check and download the latest patch on the staging channel
final updated = await ota.checkAndUpdate();
// Revert to the public UpdateTrack.stable channel
ota.disableQaMode();
print(ota.track.name); // "stable"