Docs Flutter Plugin

SK Auto-Updater

Flutter plugin for in-app updates

A fully configurable auto-update plugin for Flutter Android apps. Check for updates, download APKs, and trigger installation automatically.

Flutter Package Android Only MIT License
Download .md

Platform Support

Android

iOS

Web

macOS

Windows

Linux

This plugin only supports Android. Designed for sideloaded apps, internal/enterprise apps, and apps distributed outside the Google Play Store.

Requirements

This plugin requires a ReleaseHub backend to serve version information and APK files:

Hosted

Use releasehub.dev - managed service by Version Two

Self-hosted

Deploy your own ReleaseHub instance on your server

Important: ReleaseHub (either hosted or self-hosted) is required for this plugin to function. The plugin communicates with ReleaseHub's API to check for updates and download files.

Installation

Add to your pubspec.yaml:

dependencies:
  releasehub_updater: ^1.0.0

Then run:

$ flutter pub get

GetX Adapter (Optional)

If using the GetX adapter, ensure you have GetX in your dependencies:

dependencies:
  get: ^4.6.6

Android Configuration

Required Permissions

Add to your android/app/src/main/AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Required for downloading updates -->
    <uses-permission android:name="android.permission.INTERNET" />

    <!-- Required for installing APKs -->
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

    <!-- Required for Android 9 and below -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
        android:maxSdkVersion="28" />

    <application ...>
        <!-- Your activities and other components -->
    </application>
</manifest>

File Provider Configuration (Android 7+)

The open_filex package usually handles this automatically. If APK installation fails, add to your AndroidManifest.xml inside the <application> tag:

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="${applicationId}.fileProvider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths" />
</provider>

And create android/app/src/main/res/xml/file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_files" path="." />
    <external-files-path name="external_files_path" path="." />
    <cache-path name="cache" path="." />
</paths>

Quick Start

Simple Usage (Recommended)

The easiest way to use the plugin with built-in UI:

import 'package:releasehub_updater/autoupdater.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize the auto-updater with ReleaseHub
  await AutoUpdater.init(
    baseUrl: 'https://releases.example.com',  // your ReleaseHub server URL
    projectSlug: 'my-app',
    channel: 'stable',
  );

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // Required for dialogs to work
      navigatorKey: AutoUpdater.navigatorKey,
      scaffoldMessengerKey: AutoUpdater.scaffoldMessengerKey,
      home: HomeScreen(),
    );
  }
}

// Manual check from settings screen
ElevatedButton(
  onPressed: () => AutoUpdater.checkForUpdates(),
  child: Text('Check for Updates'),
)

Standalone Mode (No External Dependencies)

Uses Flutter's built-in ValueNotifier for reactive state:

import 'package:releasehub_updater/autoupdater_standalone.dart';

// Create the service
final updater = AutoUpdaterStandalone(
  config: AutoUpdaterConfig.releaseHub(
    baseUrl: 'https://releases.example.com',
    projectSlug: 'my-app',
    channel: 'stable',
  ),
  ui: AutoUpdaterDefaultUI.create(
    primaryColor: Colors.blue,
  ),
);

// Initialize (call once at app startup)
await updater.initialize();

// Use ValueListenableBuilder for reactive UI
ValueListenableBuilder<bool>(
  valueListenable: updater.isCheckingForUpdate,
  builder: (context, isChecking, child) {
    return isChecking
      ? CircularProgressIndicator()
      : ElevatedButton(
          onPressed: () => updater.checkForUpdate(
            silent: false,
            context: context,
          ),
          child: Text('Check for Updates'),
        );
  },
)

// Don't forget to dispose when done
updater.dispose();

GetX Integration

import 'package:releasehub_updater/autoupdater_getx.dart';

// Register the service
Get.put(
  AutoUpdaterGetxService(
    config: AutoUpdaterConfig.releaseHub(
      baseUrl: 'https://releases.example.com',
      projectSlug: 'my-app',
      channel: 'stable',
    ),
  ),
  permanent: true,
);

// Use in widgets
final updater = Get.find<AutoUpdaterGetxService>();
Obx(() => updater.isCheckingForUpdate.value
  ? CircularProgressIndicator()
  : Text('Version: ${updater.currentVersion?.version}')
)

Core Service (Maximum Control)

import 'package:releasehub_updater/autoupdater.dart';

final core = AutoUpdaterCore(
  config: AutoUpdaterConfig.releaseHub(
    baseUrl: 'https://releases.example.com',
    projectSlug: 'my-app',
  ),
);

await core.initialize();

final result = await core.checkForUpdate();

switch (result) {
  case UpdateAvailable(versionInfo: final info):
    print('Update available: ${info.displayVersion}');
    // Download and install manually
    final downloadResult = await core.downloadApk(
      info.apkUrl,
      info.displayVersion,
      onProgress: (progress) => print(progress.formattedProgress),
    );
  case NoUpdateAvailable():
    print('Already on latest version');
  case UpdateCheckError(message: final msg):
    print('Error: $msg');
  case UpdateCheckDisabled():
    print('Updates disabled');
}

Configuration

AutoUpdaterConfig

PropertyTypeDefaultDescription
baseUrlStringrequiredReleaseHub URL (hosted or self-hosted)
appIdStringrequiredProject slug in ReleaseHub
versionPathString'api/check'API path (auto-set in ReleaseHub mode)
environmentString'stable'Release channel name
releaseHubModebooltrueEnable ReleaseHub API format
checkOnStartupbooltrueAuto-check on initialization
startupDelayDuration3 secondsDelay before startup check
skipPermissionCheckboolfalseSkip Android permission dialogs
isDisabledboolfalseCompletely disable updates
apkFilenamePatternString'{appId}_update_{version}.apk'Downloaded APK filename
includeArchitecturebooltrueSend device arch to server
httpHeadersMap?nullCustom HTTP headers (for API auth)
connectionTimeoutDuration30 secondsHTTP request timeout
responseFieldsVersionResponseFields?nullCustom JSON field mapping for non-standard APIs
loggerFunction?nullCustom logging callback (String) => void
dialogAutoDismissDelayDuration?8 secondsAuto-dismiss update dialog after delay (null to disable)

Note: When using AutoUpdaterConfig.releaseHub(), the defaults are optimized for ReleaseHub. When using the base constructor directly, defaults differ: versionPath='version', environment='prod', releaseHubMode=false.

Project Visibility & Authentication

Public Projects (No Authentication)

await AutoUpdater.init(
  baseUrl: 'https://releases.example.com',
  projectSlug: 'my-public-app',
  channel: 'stable',
);

Private Projects (API Key Authentication)

await AutoUpdater.initWithConfig(
  config: AutoUpdaterConfig.releaseHub(
    baseUrl: 'https://releases.example.com',
    projectSlug: 'my-private-app',
    channel: 'stable',
    httpHeaders: {
      'Authorization': 'Bearer YOUR_API_KEY',
    },
  ),
);

Customizing UI

Custom Strings (Localization)

AutoUpdater.init(
  baseUrl: 'https://releases.example.com',
  projectSlug: 'my-app',
  strings: AutoUpdaterStrings(
    updateAvailable: 'Aktualizácia dostupná',
    download: 'Stiahnuť',
    later: 'Neskôr',
    noUpdateAvailable: 'Mate najnovsiu verziu',
  ),
);

Custom UI Callbacks (Standalone)

AutoUpdaterStandalone(
  config: myConfig,
  ui: AutoUpdaterStandaloneUI(
    onShowUpdateAvailable: (context, info, onDownload) {
      showDialog(
        context: context!,
        builder: (ctx) => MyCustomUpdateDialog(
          version: info.displayVersion,
          onDownload: () {
            Navigator.pop(ctx);
            onDownload();
          },
        ),
      );
    },
    onShowError: (context, title, message) {
      MyToast.showError('$title: $message');
    },
  ),
);

Architecture Detection

The plugin automatically detects the Android device's CPU architecture and:

  • Sends it as a query parameter (?arch=arm64-v8a) to ReleaseHub
  • Normalizes build numbers for Flutter split APK builds (--split-per-abi)

Supported Android Architectures

arm64-v8a

Most modern devices

armeabi-v7a

Older 32-bit ARM

x86_64

Emulators, Chromebooks

x86

Older emulators

Debug Mode

For troubleshooting update detection issues:

// Get debug info
print(AutoUpdater.getDebugInfo());

// Show debug dialog
AutoUpdater.showDebugDialog();

Output includes:

  • Current app version and build number
  • Device architecture
  • ReleaseHub URL being used
  • Raw vs normalized build numbers

Using Custom Backend (Non-ReleaseHub)

If you have your own backend, disable ReleaseHub mode:

AutoUpdaterConfig(
  baseUrl: 'https://your-server.com',
  appId: 'com.example.app',
  versionPath: 'version',
  environment: 'prod',
  releaseHubMode: false,  // Use standard mode
  responseFields: VersionResponseFields(
    version: 'app_version',
    build: 'build_number',
    apkUrl: 'download_url',
  ),
)

Custom Backend Response Format:

{
  "app_version": "1.2.0",
  "build_number": 42,
  "download_url": "https://your-server.com/app-1.2.0.apk"
}

Disabling in Development

To prevent update checks during development:

import 'package:flutter/foundation.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Only enable auto-updater in release builds
  if (!kDebugMode) {
    await AutoUpdater.init(
      baseUrl: 'https://releases.example.com',
      projectSlug: 'my-app',
    );
  }

  runApp(MyApp());
}