/// Represents a row in the ``app_versions`` table on the server. The /// `AppUpdateService` fetches the most recent entry and compares it against the /// running application's build number. class AppVersion { /// Version string, e.g. `1.2.3` or `2026.03.12`. Stored as text in /// the database so semantic versions are allowed. final String versionCode; /// If the device is running a build number that is *strictly less* than this /// value the update is considered "forced" and the user cannot continue /// using the existing install. Also a string. final String minVersionRequired; /// A publicly‑accessible URL pointing at an APK stored in Supabase Storage. final String downloadUrl; /// Markdown/plaintext release notes that will be shown in the dialog. final String releaseNotes; AppVersion({ required this.versionCode, required this.minVersionRequired, required this.downloadUrl, required this.releaseNotes, }); factory AppVersion.fromMap(Map map) { return AppVersion( versionCode: map['version_code']?.toString() ?? '', minVersionRequired: map['min_version_required']?.toString() ?? '', downloadUrl: map['download_url'] as String, releaseNotes: map['release_notes'] as String? ?? '', ); } } /// Helper type returned by ``AppUpdateService.checkForUpdate`` so callers can /// decide how to present the UI. class AppUpdateInfo { /// The version that was returned from ``app_versions``. ``null`` when the /// table was empty (should not happen in normal operation). final AppVersion? latestVersion; /// Current build number / version string. May be empty on non-Android /// platforms since checkForUpdate is skipped there. final String currentBuildNumber; /// ``true`` when ``currentBuildNumber < latestVersion.versionCode``. final bool isUpdateAvailable; /// ``true`` when ``currentBuildNumber < latestVersion.minVersionRequired`` /// (regardless of whether there is a newer release available). final bool isForceUpdate; AppUpdateInfo({ required this.currentBuildNumber, required this.latestVersion, required this.isUpdateAvailable, required this.isForceUpdate, }); }