{"id":32270222,"url":"https://github.com/playx-flutter/playx_version_update","last_synced_at":"2026-02-23T14:33:00.172Z","repository":{"id":174717280,"uuid":"652332574","full_name":"playx-flutter/playx_version_update","owner":"playx-flutter","description":"Easily show material update dialog in Android or Cupertino dialog in IOS with support for Google play in app updates.","archived":false,"fork":false,"pushed_at":"2025-11-18T18:20:12.000Z","size":1536,"stargazers_count":5,"open_issues_count":0,"forks_count":5,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-18T20:07:53.804Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Dart","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/playx-flutter.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-06-11T20:14:13.000Z","updated_at":"2025-11-18T18:20:17.000Z","dependencies_parsed_at":null,"dependency_job_id":"a01a1086-0ef2-4bfb-826d-1ba7720d1b25","html_url":"https://github.com/playx-flutter/playx_version_update","commit_stats":null,"previous_names":["playx-flutter/playx_version_update"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/playx-flutter/playx_version_update","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playx-flutter%2Fplayx_version_update","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playx-flutter%2Fplayx_version_update/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playx-flutter%2Fplayx_version_update/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playx-flutter%2Fplayx_version_update/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/playx-flutter","download_url":"https://codeload.github.com/playx-flutter/playx_version_update/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/playx-flutter%2Fplayx_version_update/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29745593,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-23T07:44:07.782Z","status":"ssl_error","status_checked_at":"2026-02-23T07:44:07.432Z","response_time":90,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-22T22:33:56.299Z","updated_at":"2026-02-23T14:33:00.166Z","avatar_url":"https://github.com/playx-flutter.png","language":"Dart","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n\n# Playx Version Update\n\n[![pub package](https://img.shields.io/pub/v/playx_version_update.svg?color=1284C5)](https://pub.dev/packages/playx_version_update)\n\n**Playx Version Update** empowers you to deliver a **flawless update experience** for your Flutter app users. Seamlessly integrate **native in-app updates for Android** (immediate or flexible), and present **beautifully customizable Flutter UI for iOS**. Detect new versions intelligently, enforce minimum app versions, and keep your users on the latest, greatest version of your app with minimal effort.\n\n\n----------\n\n\n-   **Intelligent Update Detection:** Automatically finds new versions on Google Play (Android) and Apple App Store (iOS).\n\n-   **Native Android In-App Updates:**\n\n    -   **Immediate:** Full-screen, mandatory updates for critical fixes.\n\n    -   **Flexible:** Background downloads for non-critical updates, with user-controlled installation.\n\n-   **Customizable Cross-Platform UI:** Display tailored Flutter dialogs or full-screen update pages for iOS or any custom needs.\n\n-   **Comprehensive Configuration:** Control version comparisons, set minimum required versions, and override force update status. Specify store IDs, country, and language for precise lookups.\n\n-   **Flexible UI Customization:** Personalize titles, descriptions, buttons, colors, text styles, and even add custom widgets. Choose between dialogs or full-screen pages, with control over display types and button actions.\n\n-   **Detailed Version Info:** Access new version number, release notes, force update status, and direct store URL.\n\n-   **Minimum Version Enforcement:** Trigger forced updates by adding a `[Minimum Version :X.Y.Z]` tag to your store description.\n\n-   **Robust Error Handling:** Specific error types for network, installation, cancellation, and platform issues.\n\n## 💻 Installation\n\nAdd `playx_version_update` to your `pubspec.yaml` dependencies:\n\nYAML\n\n```\ndependencies:\n  playx_version_update: ^1.0.1 # Use the latest stable version\n\n```\n\nThen, run `flutter pub get` to fetch the package.\n\n----------\n\n## 🛠️ Requirements\n\n\u003e **Note:** This package currently supports **Android** and **iOS** platforms only. Support for other platforms may be added in future updates.\n\n-   **Flutter:** `\u003e=3.27.0`\n\n-   **Dart:** `\u003e=3.6.0 \u003c4.0.0`\n\n-   **Android:**\n\n    -   `compileSdkVersion`: `36`\n\n    -   `minSdkVersion`: `23`\n\n    -   Java `JVM target`: `17`\n\n\n----------\n\n## 🚀 Usage\n\n`Playx Version Update` offers multiple ways to handle app updates, from simple dialogs to full custom UI experiences.\n\nAll update operations return a `PlayxVersionUpdateResult`, which allows you to easily handle both success data and specific error types.\n\n----------\n\n### 1. In-App Update Flow (`showInAppUpdateDialog`)\n\nInitiate platform-native in-app updates for Android (Flexible or Immediate) or display a customizable Flutter UI for iOS. This provides a more integrated user experience.\n\n\n  \u003cp float=\"left\" align=\"middle\"\u003e \u003cimg src=\"https://github.com/playx-flutter/playx_version_update/blob/main/screenshots/screenshot1.jpg?raw=true\" width=\"30%\" \u003e      \n  \u003cimg src=\"https://github.com/playx-flutter/playx_version_update/blob/main/screenshots/screenshot3.jpg?raw=true\" width=\"43%\" \u003e     \n \u003c/p\u003e\n\n```Dart\n\nFuture\u003cvoid\u003e showInAppUpdateFlow(BuildContext context) async {\n  final result = await PlayxVersionUpdate.showInAppUpdateDialog(\n    context: context,\n    // Specify the desired Android update flow type\n    type: PlayxAppUpdateType.flexible, // Or PlayxAppUpdateType.immediate\n\n    // PlayxUpdateOptions: Basic options for the version check for ios\n    iosOptions: const PlayxUpdateOptions(\n      iosBundleId: 'com.your_company.your_app_id',  // If not provided it will get it from app info\n    ),\n    // PlayxUpdateUIOptions: Customize iOS-specific Flutter UI (Android uses native UI)\n    iosUiOptions: PlayxUpdateUIOptions(\n      showReleaseNotes: true,\n      releaseNotesTitle: (info) =\u003e 'What\\'s New in ${info.newVersion}?',\n      // Customize how the iOS update UI behaves\n      displayType: PlayxUpdateDisplayType.pageOnForceUpdate, // Show full page for forced iOS updates\n      isDismissible: false, // Make the iOS update UI non-dismissible if forced\n      },\n    ),\n  );\n\n  result.when(\n    success: (isShown) {\n      if (isShown) {\n        print('In-app update dialog process initiated or no update needed.');\n      }\n    },\n    error: (error) {\n      print('Error during in-app update dialog: ${error.message}');\n      // Handle specific errors from PlayxVersionUpdateError hierarchy like PlayxInstallError\n    },\n  );\n}\n\n```\n**Important Note for Android In App Updates:** If you choose `PlayxAppUpdateType.flexible`, your app is responsible for monitoring the download status  and prompting the user to complete the installation once the update is downloaded.   Refer to the [\"Monitoring Flexible Updates\"](#monitoring-flexible-updates) and [\"Installing a Flexible Update\"]( #installing-a-flexible-update) sections for detailed instructions.\n\n### 2.  Simple Update Dialog (`showUpdateDialog`)\n\nQuickly inform users about new updates with a standard Material (Android) or Cupertino (iOS) dialog. This method uses Flutter-based UI and is cross-platform.\n\n\n   \u003cp float=\"left\" align=\"middle\"\u003e    \n \u003cimg src=\"https://github.com/playx-flutter/playx_version_update/blob/main/screenshots/screenshot3.jpg?raw=true\" width=\"45%\" \u003e     \n    \u003cimg src=\"https://github.com/playx-flutter/playx_version_update/blob/main/screenshots/screenshot6.jpg?raw=true\" width=\"30%\" \u003e \u003c/p\u003e\n\n```Dart\nFuture\u003cvoid\u003e showSimpleUpdateDialog(BuildContext context) async {\n  final result = await PlayxVersionUpdate.showUpdateDialog(\n    context: context,\n    // PlayxUpdateOptions: Configure the version check\n    options: const PlayxUpdateOptions(\n      androidPackageName: 'com.your_company.your_app_id', // Your Android package name\n      iosBundleId: 'com.your_company.your_app_id',      // Your iOS bundle ID\n      minVersion: '1.0.0', // Optional: Sets a minimum required version for a forced update\n    ),\n    // PlayxUpdateUIOptions: Customize the Flutter UI of the dialog\n    uiOptions: PlayxUpdateUIOptions(\n      title: (info) =\u003e 'A New Update is Available!', // Dynamic title\n      description: (info) =\u003e 'Version ${info.newVersion} is now available. '\n          'Please update to get the latest features and bug fixes.', // Dynamic description\n      showReleaseNotes: false, // Don't show release notes in this dialog\n      updateButtonText: 'Update Now!',\n      dismissButtonText: 'Not Now',\n      // You can also customize text styles, button styles, etc.\n    ),\n  );\n\n  result.when(\n    success: (isShowed) {\n      if (isShowed) {\n        print('Update dialog displayed successfully.');\n      } else {\n        print('Update dialog was not shown (e.g., no update available).');\n      }\n    },\n    error: (error) {\n      print('Failed to show update dialog: ${error.message}');\n      // Handle specific errors like NoInternetConnectionError, PlatformNotSupportedError etc.\n    },\n  );\n}\n\n```\n\n\n\n----------\n\n### 3. Custom UI with `checkVersion` \u0026 `PlayxUpdatePage`\n\nFor complete control over the update presentation, use `checkVersion` to get detailed update information and then display your own custom Flutter UI, such as the provided `PlayxUpdatePage`.\n\u003cp float=\"left\" align=\"middle\"\u003e    \n \u003cimg src=\"https://github.com/playx-flutter/playx_version_update/blob/main/screenshots/screenshot5.jpg?raw=true\" width=\"25%\"  \u003e \u003c/p\u003e\n\n```Dart\nFuture\u003cvoid\u003e checkForUpdateAndShowCustomUI(BuildContext context) async {\n  final result = await PlayxVersionUpdate.checkVersion(\n    // PlayxUpdateOptions: Full control over version comparison logic\n    options: PlayxUpdateOptions(\n      // Optional: Provide local app version (defaults to PackageInfo.fromPlatform())\n      localVersion: '1.0.0',\n      // Optional: Provide new app version (bypasses store lookup if provided)\n      newVersion: '1.1.0',\n      // Optional: Manually override force update status (e.g., forceUpdate: true)\n      // If null, it's calculated based on localVersion vs. minVersion\n      forceUpdate: true,\n      // Your app's store identifiers (essential for store lookups)\n      androidPackageName: 'com.your_company.your_app_id',\n      iosBundleId: 'com.your_company.your_app_id',\n      // Optional: Country and language for fetching store information\n      country: 'us',\n      language: 'en',\n    ),\n  );\n\n  result.when(\n    success: (info) {\n      // Use PlayxVersionUpdateInfo to decide how to present the update\n      if (info.canUpdate) {\n        Navigator.push(\n          context,\n          MaterialPageRoute\u003cvoid\u003e(\n            builder: (BuildContext context) =\u003e PlayxUpdatePage(\n              versionUpdateInfo: info, // Pass the update info to the page\n              // PlayxUpdateUIOptions: Customize the PlayxUpdatePage's look and feel\n              uiOptions: PlayxUpdateUIOptions(\n                showReleaseNotes: true,\n                showDismissButtonOnForceUpdate: false, // Don't show dismiss for forced updates\n                leading: Image.network('https://via.placeholder.com/150'), // Custom image at the top\n                title: (i) =\u003e \"It's Time to Update!\", // Dynamic title\n                description: (i) =\u003e\n                    'A new version of the app (${i.newVersion}) is available. '\n                    'Update now to enjoy the latest features and improvements.', // Dynamic description\n                // Custom text styling for various elements\n                titleTextStyle: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.blueAccent),\n                updateButtonTextStyle: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold),\n                // Custom button styling\n                updateButtonStyle: ElevatedButton.styleFrom(\n                  backgroundColor: Colors.blue.shade700,\n                  padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 15),\n                  shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),\n                ),\n                // Control dismiss behavior\n                isDismissible: !info.forceUpdate, // Make dismissible if not a forced update\n                // Callback when user dismisses the page (if dismissible)\n                onCancel: (info) =\u003e print('Update page dismissed for version ${info.newVersion}'),\n              ),\n            ),\n          ),\n        );\n      } else {\n        print('App is already up to date.');\n      }\n    },\n    error: (error) {\n      print('Error checking for update: ${error.message}');\n      // Handle various errors like NoInternetConnectionError, VersionFormatException, etc.\n    },\n  );\n}\n\n```\n\n----------\n\n### Minimum App Version\n\nYou can automatically determine if an update should be forced by embedding a minimum version string in your app's Google Play Store or Apple App Store description.\n\nSimply add `[Minimum Version :X.Y.Z]` to the end of your app's store description (e.g., `[Minimum Version :1.5.0]`). The package will parse this information and automatically update the `forceUpdate` value of `PlayxVersionUpdateInfo` returned by `checkVersion` accordingly.\n\n\n-----\n\n## Google Play In-App Updates (Android Only)\n\nThe `playx_version_update` package makes it super easy to integrate **Google Play's In-App Update** feature into your Android app. This means you can prompt users to update without them ever leaving your app or opening the Play Store separately\\! You get two main update options: **immediate** and **flexible**.\n\nWant to dive deeper into the Android SDK behind this? Check out the official Google documentation:\n\n* [In-App Updates Overview](https://developer.android.com/guide/playcore/in-app-updates)\n* [Kotlin/Java Implementation Guide](https://developer.android.com/guide/playcore/in-app-updates/kotlin-java)\n\n\n-----\n\n\n\n### Immediate Updates\n\n**Immediate updates** are full-screen experiences that **require** your user to update and restart the app to continue. Think of these as **mandatory updates** for critical fixes or security patches. Once the user agrees, Google Play handles the download and installation in the background, typically restarting your app when it's done.\n\n\u003cp float=\"left\" align=\"middle\"\u003e    \n      \u003cimg src=\"https://developer.android.com/static/images/app-bundle/immediate_flow.png\" width=\"70%\" \u003e     \n     \u003c/p\u003e\n\n#### Initiating an Immediate Update\n\n**Before starting an immediate update, it's a good practice to first check if a flexible update has already been downloaded and is waiting to be installed.** If so, prioritize installing that existing download to save user data and storage.\n\n```dart\nimport 'package:playx_version_update/playx_version_update.dart';\nimport 'package:flutter/material.dart';\n\nFuture\u003cvoid\u003e _startImmediateUpdateFlow() async {\n  print('Trying to start an immediate update...');\n\n  // Important: Always check if a flexible update is already downloaded.\n  final isUpdateNeedToBeInstalledResult = await PlayxVersionUpdate.isFlexibleUpdateNeedToBeInstalled();\n  isUpdateNeedToBeInstalledResult.when(\n    success: (isNeeded) {\n      if (isNeeded) {\n        print('A flexible update is already downloaded! Completing it now instead of starting immediate.');\n        PlayxVersionUpdate.completeFlexibleUpdate(); // Directly complete\n        return; \n      }\n    },\n    error: (error) =\u003e print('Error checking for pending flexible update before immediate: ${error.message}'),\n  );\n\n  final result = await PlayxVersionUpdate.startImmediateUpdate();\n\n  result.when(\n    success: (isSucceeded) {\n      print('Immediate update flow initiated successfully (user likely accepted).');\n    },\n    error: (error) {\n      print('An error happened during the immediate update: ${error.message}');\n      if (error is PlayxInAppUpdateCanceledError) {\n        print('The user said \"no\" or cancelled the update.');\n        // Decide what to do: Maybe show a reminder later, or block app use.\n      } else if (error is InstallNotAllowedError) {\n        print('Update blocked, maybe due to low storage or no internet.');\n      } else {\n        print('Unknown immediate update error: ${error.runtimeType}');\n      }\n    },\n  );\n}\n```\n\n#### What Happens When an Immediate Update Starts?\n\nWhen you kick off an immediate update and the user agrees, Google Play takes over, showing the download progress right over your app. If the user closes your app during this, the update usually keeps downloading and installing in the background.\n\nIf the user declines or cancels the update, Google Play's screen will close, and your app will be back in control. At this point, you'll need to decide your next move:\n\n* **Prompt again later:** Remind them on their next app launch.\n* **Show a message:** Explain why the update is important.\n* **Force restart:** If it's absolutely critical, you might have to prevent further app use until they update.\n\n-----\n\n### Flexible Updates\n\n**Flexible updates** download in the background, letting users continue to use your app without interruption. Once the download is done, you decide when to prompt the user to install it. This is perfect for **non-critical updates** like new features or minor bug fixes.\n\n\u003cp float=\"left\" align=\"middle\"\u003e    \n \u003cimg src=\"https://developer.android.com/static/images/app-bundle/flexible_flow.png\" width=\"70%\" \u003e     \n \u003c/p\u003e\n\n#### Initiating a Flexible Update\n\n**Before starting a new flexible update download, it's a good practice to first check if a flexible update has already been downloaded and is waiting to be installed.** If so, prioritize installing that existing download to save user data and storage.\n\n```dart\nimport 'package:playx_version_update/playx_version_update.dart';\nimport 'package:flutter/material.dart';\n\nFuture\u003cvoid\u003e _startFlexibleUpdateFlow(BuildContext context) async {\n  print('Trying to start a flexible update download...');\n\n  // Important: Always check if a flexible update is already downloaded.\n  final isUpdateNeedToBeInstalledResult = await PlayxVersionUpdate.isFlexibleUpdateNeedToBeInstalled();\n  isUpdateNeedToBeInstalledResult.when(\n    success: (isNeeded) {\n      if (isNeeded) {\n        print('A flexible update is already downloaded! Completing it now instead of starting a new download.');\n        _promptToCompleteFlexibleUpdate(context); \n        return; \n      }\n    },\n    error: (error) =\u003e print('Error checking for pending flexible update before new download: ${error.message}'),\n  );\n\n  final result = await PlayxVersionUpdate.startFlexibleUpdate();\n\n  result.when(\n    success: (isStarted) {\n      if (isStarted) {\n        print('Flexible update download started! We\\'ll monitor its progress.');\n        // Start listening to the download progress right away.\n        listenToFlexibleDownloadUpdates(context);\n      } else {\n        print('Flexible update didn\\'t start (user likely declined).');\n      }\n    },\n    error: (error) {\n      print('An error happened trying to start the flexible update: ${error.message}');\n      if (error is PlayxInAppUpdateCanceledError) {\n        print('The user cancelled the flexible update download.');\n      } else if (error is InstallNotAllowedError) {\n        print('Flexible update download not allowed due to device issues.');\n      }\n      // Handle other errors as needed.\n    },\n  );\n}\n```\n\n#### Monitoring Flexible Updates\n\nOnce a flexible update download begins, you'll want to show your user how it's going (maybe a progress bar\\!). You also need to know when it's completely downloaded and ready to install.\n\nUse the `listenToFlexibleDownloadUpdate` stream to keep an eye on things:\n\n```dart\nimport 'dart:async'; // For StreamSubscription\nimport 'package:playx_version_update/playx_version_update.dart';\nimport 'package:flutter/material.dart'; // For UI elements like SnackBar\n\nStreamSubscription? _downloadInfoStreamSubscription;\n\nvoid listenToFlexibleDownloadUpdates(BuildContext context) {\n  _downloadInfoStreamSubscription = PlayxVersionUpdate.listenToFlexibleDownloadUpdate().listen((info) {\n    if (info == null) {\n      print('No flexible update download active.');\n      return;\n    }\n\n    switch (info.status) {\n      case PlayxDownloadStatus.downloaded:\n        print('Flexible update downloaded! Ready to install.');\n        // The update is ready! Prompt the user to install it.\n        _promptToCompleteFlexibleUpdate(context);\n        _downloadInfoStreamSubscription?.cancel(); // Stop listening once it's downloaded.\n        break;\n      case PlayxDownloadStatus.downloading:\n        final progress = (info.bytesDownloaded / info.totalBytesToDownload) * 100;\n        print('Download progress: ${progress.toStringAsFixed(1)}%');\n        // Update your UI (e.g., a progress bar) with `info.bytesDownloaded` and `info.totalBytesToDownload`.\n        break;\n      case PlayxDownloadStatus.pending:\n        print('Flexible update download is waiting to start.');\n        break;\n      case PlayxDownloadStatus.failed:\n        print('Flexible update download failed.');\n        _downloadInfoStreamSubscription?.cancel();\n        // Inform the user and maybe offer a retry.\n        break;\n      case PlayxDownloadStatus.canceled:\n        print('Flexible update download cancelled.');\n        _downloadInfoStreamSubscription?.cancel();\n        break;\n      case PlayxDownloadStatus.installing:\n        print('Flexible update is installing...');\n        break;\n      case PlayxDownloadStatus.installed: // Added this case for completeness with new enum\n        print('Flexible update installed successfully.');\n        _downloadInfoStreamSubscription?.cancel();\n        break;\n      default: // Handles unknown or any new statuses\n        print('Flexible update status: ${info.status}');\n        break;\n    }\n  }, onError: (error) {\n    print('Error while monitoring download updates: $error');\n  });\n}\n\n// IMPORTANT: Always cancel your stream subscription when it's no longer needed\n// (e.g., in your widget's dispose method) to prevent memory leaks.\nvoid disposeDownloadSubscription() {\n  _downloadInfoStreamSubscription?.cancel();\n}\n```\n\n-----\n\n### Installing a Flexible Update\n\nOnce the flexible update is `downloaded` (you'll know from the stream above\\!), you need to tell the app to install it. Unlike immediate updates, Google Play won't automatically restart your app for flexible updates.\n\nWe strongly recommend you show a clear message or notification to your user, asking if they're ready to restart and install the update.\n\n```dart\nimport 'package:playx_version_update/playx_version_update.dart';\nimport 'package:flutter/material.dart';\n\n/// Call this function when a flexible update is downloaded and ready.\n/// It shows a SnackBar prompting the user to install.\nFuture\u003cvoid\u003e _promptToCompleteFlexibleUpdate(BuildContext context) async {\n  final snackBar = SnackBar(\n    content: const Text('A new update has finished downloading!'),\n    action: SnackBarAction(\n      label: 'Restart App',\n      onPressed: () async {\n        print('User tapped \"Restart App\" for flexible update.');\n        final result = await PlayxVersionUpdate.completeFlexibleUpdate();\n        result.when(\n          success: (isCompleted) {\n            print('Flexible update completion initiated: $isCompleted');\n            // If successful, your app will restart automatically.\n          },\n          error: (error) {\n            print('Failed to install flexible update: ${error.message}');\n            if (error is InstallApiNotAvailableError) {\n              print('In-app updates API not available on this device for installation.');\n            } else if (error is InstallNotAllowedError) {\n              print('Installation not allowed (e.g., low battery, no internet).');\n            }\n            ScaffoldMessenger.of(context).showSnackBar(\n              SnackBar(content: Text('Update failed: ${error.message}')),\n            );\n          },\n        );\n      }),\n    duration: const Duration(seconds: 10), // Give user some time to see it\n  );\n  ScaffoldMessenger.of(context).showSnackBar(snackBar);\n}\n```\n\n#### How Installation Works\n\n* If you call `PlayxVersionUpdate.completeFlexibleUpdate()` while your app is in the **foreground**, Google Play will show a full-screen screen that restarts your app to complete the installation. Your app will then restart normally.\n* If your app is in the **background** when you call it, the update will install silently without bothering the user.\n\n-----\n\n### Don't Forget Pending Updates on App Resume\\!\n\nIt's really important to check for any flexible updates that were downloaded but not yet installed every time your app comes back to the foreground. This makes sure users get the latest version and downloaded updates don't just sit there wasting space.\n\n```dart\nimport 'package:flutter/widgets.dart'; // For WidgetsBindingObserver\nimport 'package:playx_version_update/playx_version_update.dart';\nimport 'dart:io'; // For Platform.isAndroid\n\nclass MyApp extends StatefulWidget {\n  const MyApp({super.key});\n\n  @override\n  State\u003cMyApp\u003e createState() =\u003e _MyAppState();\n}\n\nclass _MyAppState extends State\u003cMyApp\u003e with WidgetsBindingObserver {\n  @override\n  void initState() {\n    super.initState();\n    WidgetsBinding.instance.addObserver(this); // Start listening to app lifecycle changes\n  }\n\n  @override\n  void dispose() {\n    WidgetsBinding.instance.removeObserver(this); // Stop listening when widget is removed\n    disposeDownloadSubscription(); // Make sure to clean up any download listeners!\n    super.dispose();\n  }\n\n  /// This gets called whenever the app changes its lifecycle state (e.g., goes to background, comes to foreground).\n  @override\n  void didChangeAppLifecycleState(AppLifecycleState state) {\n    // If the app is resuming (coming back to foreground)\n    if (state == AppLifecycleState.resumed) {\n      _checkIfPendingFlexibleUpdate();\n    }\n  }\n\n  /// Checks if a flexible update has been downloaded and is waiting to be installed.\n  Future\u003cvoid\u003e _checkIfPendingFlexibleUpdate() async {\n    if (Platform.isAndroid) {\n      final result = await PlayxVersionUpdate.isFlexibleUpdateNeedToBeInstalled();\n      result.when(\n        success: (isNeeded) {\n          if (isNeeded) {\n            print('A flexible update is ready to install on app resume!');\n            // You should prompt the user to install it here.\n            // For example, by showing a SnackBar or a persistent notification/banner.\n            if (mounted) { // Make sure the widget is still active before showing UI\n               // You'd need a way to get a valid BuildContext here, or use a GlobalKey for ScaffoldMessenger.\n               // For example: _promptToCompleteFlexibleUpdate(context);\n               print('Consider prompting the user to install the downloaded update.');\n            }\n          } else {\n            print('No pending flexible update to install.');\n          }\n        },\n        error: (error) =\u003e print('Error checking for pending flexible update: ${error.message}'),\n      );\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    // This is just a basic example for demonstration.\n    // Your actual app UI would go here.\n    return MaterialApp(\n      home: Scaffold(\n        appBar: AppBar(title: const Text('Playx Version Update')),\n        body: Center(\n          child: Column(\n            mainAxisAlignment: MainAxisAlignment.center,\n            children: [\n              ElevatedButton(\n                onPressed: () =\u003e _checkForAppUpdate(context),\n                child: const Text('Check for App Update'),\n              ),\n              const SizedBox(height: 20),\n              const Text('This area will show update status in the console.'),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n```\n\n-----\n### How to Check for Updates\n\nBefore you can offer an update, you need to know if one's even available\\! Here's how you check and gather useful details like how old the update is and its priority.\n\n```dart\nimport 'package:playx_version_update/playx_version_update.dart';\nimport 'package:flutter/material.dart';\nimport 'dart:io'; // Needed for Platform.isAndroid\n\nFuture\u003cvoid\u003e _checkForAppUpdate(BuildContext context) async {\n  // In-app updates are only for Android devices.\n  if (!Platform.isAndroid) {\n    print('In-app updates are only supported on Android.');\n    return;\n  }\n\n  // Check if an update is available at all.\n  final availabilityResult = await PlayxVersionUpdate.getUpdateAvailability();\n\n  availabilityResult.when(\n    success: (availability) async {\n      // Using the provided enum for clarity\n      if (availability == PlayxAppUpdateAvailability.available) {\n        print('Good news! An update is available.');\n\n        // Get how long the update has been available (staleness)\n        final stalenessResult = await PlayxVersionUpdate.getUpdateStalenessDays();\n        int stalenessDays = stalenessResult.when(\n          success: (days) =\u003e days,\n          error: (error) =\u003e -1, // Default if error\n        );\n        print('This update has been available for $stalenessDays days.');\n\n        // Get the priority of the update (0-5, 5 is highest)\n        final priorityResult = await PlayxVersionUpdate.getUpdatePriority();\n        int priority = priorityResult.when(\n          success: (p) =\u003e p,\n          error: (error) =\u003e 0, // Default if error\n        );\n        print('The update priority is: $priority.');\n\n        // Now, decide if you want an Immediate or Flexible update\n        // based on staleness, priority, or your app's specific rules.\n        if (priority \u003e= 4 || stalenessDays \u003e= 7) {\n          // This update is pretty important or old, let's go with immediate.\n          await _checkAndStartUpdate(PlayxAppUpdateType.immediate, context); // Pass context\n        } else {\n          // It's not super critical, a flexible update will do.\n          await _checkAndStartUpdate(PlayxAppUpdateType.flexible, context); // Pass context\n        }\n      } else if (availability == PlayxAppUpdateAvailability.notAvailable) {\n        print('No updates found. Your app is up to date!');\n      } else if (availability == PlayxAppUpdateAvailability.inProgress) {\n        print('An update is already in progress.');\n      } else { // PlayxAppUpdateAvailability.unknown\n        print('Could not determine update availability.');\n      }\n    },\n    error: (error) {\n      print('Failed to check for updates: ${error.message}');\n      // Handle network errors, Play Store not available, etc.\n    },\n  );\n}\n\n// Helper to check if update type is allowed and then start it\nFuture\u003cvoid\u003e _checkAndStartUpdate(PlayxAppUpdateType type, BuildContext context) async {\n  // Always check for already downloaded flexible updates first!\n  final isUpdateNeedToBeInstalledResult = await PlayxVersionUpdate.isFlexibleUpdateNeedToBeInstalled();\n  isUpdateNeedToBeInstalledResult.when(\n    success: (isNeeded) {\n      if (isNeeded) {\n        print('A flexible update is already downloaded! Prioritizing installation...');\n        _promptToCompleteFlexibleUpdate(context); // Use your existing prompt function\n        return; // Stop here, we're handling the existing download\n      }\n    },\n    error: (error) =\u003e print('Error checking for pending flexible update: ${error.message}'),\n  );\n\n  final isAllowed = await PlayxVersionUpdate.isUpdateAllowed(type: type);\n  isAllowed.when(\n    success: (allowed) {\n      if (allowed) {\n        if (type == PlayxAppUpdateType.immediate) {\n          print('Immediate update is allowed. Starting now...');\n          _startImmediateUpdateFlow();\n        } else {\n          print('Flexible update is allowed. Starting download...');\n          _startFlexibleUpdateFlow(context); // Pass context\n        }\n      } else {\n        print('${type == PlayxAppUpdateType.immediate ? \"Immediate\" : \"Flexible\"} update not allowed right now.');\n      }\n    },\n    error: (error) =\u003e print('Error checking update allowance: ${error.message}'),\n  );\n}\n```\n\n\n\n## ⚠️ Important Notice for Testing In-App Updates (Android)\n\nAndroid in-app updates **will only work if your app is installed directly from Google Play**. This means testing builds installed via Android Studio or other side-loading methods will **not** trigger in-app updates.\n\nTo successfully test in-app updates on Android, please ensure you follow these steps precisely:\n\n1.  **Publish to a Test Track:** Your app (an **older version**) must be published to at least an **Internal test track** in your Google Play Console.\n\n    -   **Internal App Sharing** is often a quicker alternative for rapid iteration testing, allowing you to share APKs/App Bundles directly without full track review cycles.\n\n2.  **Install via Play Store:** On your emulator or physical device, install this **older version** of your app _directly_ from the Google Play Store link generated by your chosen test track (or internal app sharing link). Ensure the Google account on the device is a **tester** for that track and has downloaded the app at least once from the Play Store.\n\n3.  **Upload a Newer Version:** Upload a **newer version** of your app (with an **incremented `versionCode` and `versionName`**) to the _same test track_ in the Google Play Console. This new version should contain your `playx_version_update` implementation.\n\n4.  **Wait for Processing:** Allow some time (it can vary from minutes to several hours) for Google Play to process and make the new version available to your test track. Google Play Services and Play Store caches can sometimes cause delays; clearing the Play Store cache or restarting the device might help.\n\n5.  **Test Update:** Now, when you open the **older version** of your app (the one you installed from the Play Store) on your emulator/device, it should detect the available update via the in-app update API.\n\n    -   For **Flexible Updates**, remember that your app needs to explicitly monitor the download status and then prompt the user to complete the installation once the update is downloaded. Refer to the [\"Monitoring Flexible Updates\"](#monitoring-flexible-updates) and [\"Installing a Flexible Update\"]( #installing-a-flexible-update) sections for detailed instructions. It's also crucial to check for **already downloaded flexible updates** that might be pending installation before initiating a new update flow.\n\n    -   For **Immediate Updates**, the full-screen UI should appear, requiring the user to update.\n\n\nFor detailed, official guidance on setting up your Android in-app update testing environment, please refer to the Android Developers documentation:\n\nhttps://developer.android.com/guide/playcore/in-app-updates/test\n\nFor a complete list of all possible errors, refer to the [PlayxVersionUpdateError API Reference](https://pub.dev/documentation/playx_version_update/latest/playx_version_update/PlayxVersionUpdateError-class.html).\n\n\n## 📄 Documentation \u0026 References\n\n-   [In-app updates](https://developer.android.com/guide/playcore/in-app-updates) - Official Google Play documentation on in-app updates.\n\n-   [playx_network](https://pub.dev/packages/playx_network) - The network package used internally for version checks.\n    \n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplayx-flutter%2Fplayx_version_update","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fplayx-flutter%2Fplayx_version_update","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fplayx-flutter%2Fplayx_version_update/lists"}