flutter_chen_updater 1.2.0 copy "flutter_chen_updater: ^1.2.0" to clipboard
flutter_chen_updater: ^1.2.0 copied to clipboard

A comprehensive Flutter app update library supporting background downloads and iOS App Store redirects.

Flutter Chen Updater #

Language: English | 中文

A powerful Flutter in-app update plugin that supports Android APK automatic download, installation, and iOS App Store redirection.

✨ Features #

  • Cross-platform Support: Android APK auto-update, iOS App Store redirection
  • Smart Download: Support for resumable downloads, file verification, multiple fallback options
  • Permission Management: Automatic handling of Android installation permissions and storage permissions
  • Force Update: Support for optional and mandatory update modes
  • Progress Monitoring: Real-time download progress callbacks
  • File Verification: MD5 file integrity validation
  • Lifecycle Management: Smart handling of app foreground/background switching
  • Custom UI: Support for custom update dialogs

Preview #

Preview

📦 Installation #

Add the dependency to your pubspec.yaml file:

dependencies:
  flutter_chen_updater: ^1.0.0

Then run:

flutter pub get

⚙️ Permission Configuration #

Android #

Add necessary permissions in android/app/src/main/AndroidManifest.xml:

<!-- Internet permission -->
<uses-permission android:name="android.permission.INTERNET" />

<!-- Storage permissions (Android 9 and below) -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" 
    android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" 
    android:maxSdkVersion="28" />

<!-- Install permission -->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

<!-- Network state permission -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

🚀 Basic Usage #

1. Simple Update Check #

import 'package:flutter_chen_updater/flutter_chen_updater.dart';

void checkForUpdate() {
  final updateInfo = UpdateInfo(
    version: '1.1.0',
    downloadUrl: 'https://example.com/app-v1.1.0.apk',
    iosUrl: 'https://apps.apple.com/app/id123456789',
    description: '1. Fixed known issues\n2. Improved user experience\n3. Added new features',
    isForceUpdate: false,
  );

  Updater.checkAndUpdate(
    context,
    updateInfo,
    onAlreadyLatest: () => print('Already latest version'),
    onConfirm: () => print('User confirmed update'),
    onCancel: () => print('User cancelled update'),
  );
}

2. Update with Progress Monitoring #

void checkForUpdateWithProgress() {
  final updateInfo = UpdateInfo(
    version: '1.1.0',
    downloadUrl: 'https://example.com/app-v1.1.0.apk',
    description: 'Update description',
    fileHash: 'abc123def456', // Optional: MD5 file verification
    hashAlgorithm: 'md5',
    fileSize: 15 * 1024 * 1024, // 15MB
  );

  Updater.checkAndUpdate(
    context,
    updateInfo,
    onProgress: (progress) {
      print('Download progress: ${(progress.progress * 100).toStringAsFixed(1)}%');
      print('Downloaded: ${progress.downloaded} / ${progress.total}');
    },
    onConfirm: () => print('Started download update'),
  );
}

3. Force Update Example #

void forceUpdate() {
  final updateInfo = UpdateInfo(
    version: '2.1.0',
    description: 'Important security update, please upgrade immediately',
    downloadUrl: 'https://example.com/app-v2.1.0.apk',
    isForceUpdate: true, // Force update, user cannot cancel
  );

  Updater.checkAndUpdate(context, updateInfo);
}

4. Custom Dialog #

4.1 Using dialogBuilder for Customization

Future<bool> customDialog(BuildContext context, UpdateInfo updateInfo) {
  return showDialog<bool>(
    context: context,
    barrierDismissible: !updateInfo.isForceUpdate,
    builder: (context) => AlertDialog(
      title: Text('New Version ${updateInfo.version} Available'),
      content: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(updateInfo.description),
          if (updateInfo.fileSize != null)
            Padding(
              padding: const EdgeInsets.only(top: 8.0),
              child: Text('Size: ${(updateInfo.fileSize! / 1024 / 1024).toStringAsFixed(1)}MB'),
            ),
        ],
      ),
      actions: [
        if (!updateInfo.isForceUpdate)
          TextButton(
            onPressed: () => Navigator.pop(context, false),
            child: const Text('Later'),
          ),
        ElevatedButton(
          onPressed: () => Navigator.pop(context, true),
          child: const Text('Update Now'),
        ),
      ],
    ),
  ) ?? false;
}

void checkWithCustomDialog() {
  final updateInfo = UpdateInfo(
    version: '1.1.0',
    downloadUrl: 'https://example.com/app.apk',
    description: 'Important update description',
  );

  Updater.checkAndUpdate(
    context,
    updateInfo,
    dialogBuilder: customDialog,
  );
}

4.2 Using UpdateDialog for Flexible Configuration

void checkWithFlexibleDialog() {
  final updateInfo = UpdateInfo(
    version: '1.2.0',
    downloadUrl: 'https://example.com/app.apk',
    description: '• Fixed important security vulnerabilities\n• Optimized startup speed\n• Added night mode',
    fileSize: 25 * 1024 * 1024, // 25MB
  );

  showDialog<bool>(
    context: context,
    barrierDismissible: !updateInfo.isForceUpdate,
    builder: (context) => UpdateDialog(
      updateInfo: updateInfo,
      // Custom title
      title: Container(
        padding: const EdgeInsets.all(16),
        decoration: BoxDecoration(
          gradient: LinearGradient(
            colors: [Colors.blue, Colors.purple],
          ),
        ),
        child: Row(
          children: [
            Icon(Icons.system_update, color: Colors.white),
            const SizedBox(width: 8),
            Text(
              'Important Update V${updateInfo.version}',
              style: const TextStyle(
                color: Colors.white,
                fontWeight: FontWeight.bold,
              ),
            ),
          ],
        ),
      ),
      // Custom content
      content: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            padding: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  'Update Content',
                  style: Theme.of(context).textTheme.titleMedium?.copyWith(
                    fontWeight: FontWeight.bold,
                  ),
                ),
                const SizedBox(height: 8),
                Text(updateInfo.description),
                const SizedBox(height: 12),
                if (updateInfo.fileSize != null)
                  Container(
                    padding: const EdgeInsets.symmetric(
                      horizontal: 12,
                      vertical: 6,
                    ),
                    decoration: BoxDecoration(
                      color: Colors.grey.shade100,
                      borderRadius: BorderRadius.circular(16),
                    ),
                    child: Text(
                      'Package size: ${(updateInfo.fileSize! / 1024 / 1024).toStringAsFixed(1)}MB',
                      style: Theme.of(context).textTheme.bodySmall,
                    ),
                  ),
              ],
            ),
          ),
        ],
      ),
      // Custom footer
      footer: Container(
        padding: const EdgeInsets.all(16),
        child: Row(
          children: [
            if (!updateInfo.isForceUpdate) ...[
              Expanded(
                child: OutlinedButton(
                  onPressed: () => Navigator.pop(context, false),
                  child: const Text('Remind Later'),
                ),
              ),
              const SizedBox(width: 12),
            ],
            Expanded(
              flex: updateInfo.isForceUpdate ? 1 : 1,
              child: ElevatedButton.icon(
                onPressed: () => Navigator.pop(context, true),
                icon: const Icon(Icons.download),
                label: const Text('Update Now'),
              ),
            ),
          ],
        ),
      ),
    ),
  );
}

5. Download Only Function #

void downloadOnly() {
  final updateInfo = UpdateInfo(
    version: '1.1.0',
    downloadUrl: 'https://example.com/app.apk',
    description: 'Update package',
  );

  Updater.download(updateInfo).listen((progress) {
    if (progress.isCompleted && progress.filePath != null) {
      print('Download completed: ${progress.filePath}');
      // Install later
      Updater.installApk(
        progress.filePath!,
        onSuccess: () => print('Installation successful'),
        onError: (error) => print('Installation failed: $error'),
      );
    } else if (progress.isFailed) {
      print('Download failed: ${progress.error}');
    }
  });
}

6. Version Comparison #

void checkVersionUpdate() {
  final currentVersion = '1.0.0';
  final newVersion = '1.1.0';
  
  final needUpdate = Updater.needUpdate(currentVersion, newVersion);
  print('Need update: $needUpdate');
}

📚 API Documentation #

UpdateInfo Class #

Update information configuration class:

class UpdateInfo {
  final String version;           // New version number (required)
  final String? downloadUrl;      // Android APK download link
  final String? iosUrl;          // iOS App Store link
  final String description;       // Update description (required)
  final bool isForceUpdate;      // Whether to force update
  final String? fileHash;        // File hash value (optional)
  final String? hashAlgorithm;   // Hash algorithm (default: 'md5')
  final int? fileSize;           // File size (bytes)
  final Map<String, dynamic>? extra; // Extra data
}

Updater Class #

Main updater class:

Static Methods

  • checkAndUpdate() - Check and update application
  • download() - Pure download method (no auto-installation)
  • installApk() - Install APK file
  • needUpdate() - Version comparison
  • cancelDownload() - Cancel download
  • dispose() - Clean up resources

DownloadProgress Class #

Download progress information:

class DownloadProgress {
  final int downloaded;          // Downloaded bytes
  final int total;              // Total bytes
  final double progress;        // Progress (0.0 - 1.0)
  final DownloadStatus status;  // Download status
  final String? error;          // Error message
  final String? filePath;       // File path
}

DownloadStatus Enum #

enum DownloadStatus {
  idle,         // Idle
  downloading,  // Downloading
  paused,       // Paused
  completed,    // Completed
  failed,       // Failed
  cancelled,    // Cancelled
  installing    // Installing
}

🔧 Advanced Features #

File Verification #

The plugin supports MD5 file integrity verification:

final updateInfo = UpdateInfo(
  version: '1.1.0',
  downloadUrl: 'https://example.com/app.apk',
  description: 'Security update',
  fileHash: 'a1b2c3d4e5f6...',
  hashAlgorithm: 'md5',
);

Permission Handling #

The plugin automatically handles the following permissions:

  1. Storage permission (Android 9 and below)
  2. Installation permission (Android 8+)
  3. Network permission

For missing permissions, the plugin will:

  • Automatically request permissions
  • Guide users to system settings page
  • Monitor app lifecycle and auto-retry

Lifecycle Management #

The plugin intelligently handles app foreground/background switching:

  • Auto-retry installation when user returns from permission settings
  • Keep background download tasks active
  • Auto-cleanup resources when app exits

💡 Error Handling #

The plugin provides comprehensive error handling mechanisms:

Updater.checkAndUpdate(
  context,
  updateInfo,
  onProgress: (progress) {
    if (progress.isFailed) {
      // Handle download failure
      showDialog(
        context: context,
        builder: (_) => AlertDialog(
          title: const Text('Download Failed'),
          content: Text(progress.error ?? 'Unknown error'),
          actions: [
            TextButton(
              onPressed: () => Navigator.pop(context),
              child: const Text('OK'),
            ),
          ],
        ),
      );
    }
  },
);

🚦 Best Practices #

  1. Version Check: Recommend checking for updates on app startup
  2. Network Check: Check network status before checking for updates
  3. User Experience: Avoid showing update prompts during critical operations
  4. Resource Cleanup: Call Updater.dispose() when app exits
  5. Error Logging: Log error information during update process for debugging

❓ FAQ #

Q: What to do if Android installation fails? #

A: Check if "Install unknown apps" permission is granted. The plugin will automatically guide users to settings.

Q: What to do if download speed is slow? #

A: The plugin uses Android system download manager and HTTP fallback options to ensure optimal download experience.

Q: How to support incremental updates? #

A: Current version doesn't support incremental updates. Recommend using file verification to ensure integrity.

Q: How does iOS implement automatic updates? #

A: iOS can only redirect to App Store due to system restrictions, cannot implement APK-style automatic updates.

⚠️ Important Notes #

  1. Android Permissions:

    • Android 6.0+ requires runtime storage permission requests
    • Android 8.0+ requires permission to install from unknown sources
    • Android 10+ uses scoped storage, no storage permission needed
  2. File Security:

    • Recommend using HTTPS download links
    • Strongly recommend setting fileHash for file integrity verification
    • Downloaded APK files will be automatically validated
  3. User Experience:

    • Avoid showing update prompts during important user operations
    • Use force updates cautiously, only for security updates
    • Provide clear update descriptions and file size information

📄 License #

MIT License

2
likes
150
points
280
downloads

Publisher

unverified uploader

Weekly Downloads

A comprehensive Flutter app update library supporting background downloads and iOS App Store redirects.

Repository (GitHub)

Topics

#updater

Documentation

API reference

License

MIT (license)

Dependencies

crypto, flutter, path_provider, permission_handler, url_launcher

More

Packages that depend on flutter_chen_updater

Packages that implement flutter_chen_updater