Version Check Updater

A simple and customizable Flutter package to handle version update notifications by comparing directly with App Store and Google Play Store.

Features

  • βœ… Automatic detection of new versions in stores
  • πŸ“± Customizable native UI for iOS and Android
  • πŸ”„ Smart cache system to avoid excessive checks
  • 🎨 Fully customizable (texts, styles, behavior)
  • πŸš€ Easy implementation with just a few lines of code
  • πŸ”§ Custom checker support for reliable Android updates

Important Notice for Android

⚠️ Android version checking is prone to failure due to the nature of web scraping from Google Play Store. Google frequently changes their HTML structure and serves different content based on various factors. To address this limitation, we've added supplementary alternatives:

  1. Custom Checker (Recommended) - Implement your own backend for 100% reliability
  2. Test Mode - For development and testing
  3. Multiple Fallback Strategies - APKMirror and other sources as backup

Installation

Add this to your pubspec.yaml file:

dependencies:
  version_check_updater: ^0.0.1

Platform Support

Platform Reliability Method
iOS βœ… Excellent Official iTunes API
Android ⚠️ Unreliable Web scraping (see alternatives)

Basic Usage

1. Initial Setup

import 'package:version_check_updater/version_check_updater.dart';

// Create instance with configuration
final versionChecker = VersionCheckUpdater(
  config: UpdateConfig(
    androidId: 'com.yourapp.android', // Your Android package ID
    iosId: 'com.yourapp.ios',         // Your iOS bundle ID
    showReleaseNotes: true,
    forceUpdate: false,
    checkInterval: const Duration(hours: 24),
  ),
);

2. Check for Updates Manually

// Check if updates are available
final versionInfo = await versionChecker.checkForUpdate();

if (versionInfo?.isUpdateAvailable ?? false) {
  print('New version available: ${versionInfo!.storeVersion}');
}

3. Show Update Dialog Automatically

// In your main widget
@override
void initState() {
  super.initState();
  
  WidgetsBinding.instance.addPostFrameCallback((_) {
    versionChecker.showUpdateDialog(
      context,
      onLater: () {
        // Callback when user chooses "Later"
        print('User postponed the update');
      },
    );
  });
}

Customization

Dialog Configuration

UpdateConfig(
  dialogConfig: UpdateDialogConfig(
    title: 'New version available πŸŽ‰',
    updateButtonText: 'Update now',
    laterButtonText: 'Remind me later',
    showAppIcon: true,
    titleStyle: TextStyle(
      fontSize: 20,
      fontWeight: FontWeight.bold,
    ),
    updateButtonStyle: ElevatedButton.styleFrom(
      backgroundColor: Colors.green,
    ),
  ),
)

Advanced Configuration

UpdateConfig(
  // Store IDs
  androidId: 'com.example.app',
  iosId: 'com.example.app',
  
  // Country for search (affects regional availability)
  countryCode: 'US',
  
  // Show version release notes
  showReleaseNotes: true,
  
  // Force update (doesn't allow closing the dialog)
  forceUpdate: false,
  
  // Interval between automatic checks
  checkInterval: const Duration(hours: 24),
  
  // Dialog visual configuration
  dialogConfig: UpdateDialogConfig(
    // ... custom configuration
  ),
)

Complete Example

class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late final VersionCheckUpdater _versionChecker;

  @override
  void initState() {
    super.initState();
    
    _versionChecker = VersionCheckUpdater(
      config: UpdateConfig(
        androidId: 'com.myapp.android',
        iosId: 'com.myapp.ios',
        showReleaseNotes: true,
        dialogConfig: UpdateDialogConfig(
          title: 'New version available!',
          updateButtonText: 'Update',
          laterButtonText: 'Later',
        ),
      ),
    );
    
    // Check on startup
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _versionChecker.showUpdateDialog(context);
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: ElevatedButton(
            onPressed: () {
              _versionChecker.checkAndShowUpdateDialog(context);
            },
            child: Text('Check for updates'),
          ),
        ),
      ),
    );
  }
}

Models

VersionInfo

class VersionInfo {
  final String currentVersion;
  final String storeVersion;
  final String? releaseNotes;
  final String? storeUrl;
  final bool isUpdateAvailable;
  final bool isForceUpdate;
}

UpdateConfig

class UpdateConfig {
  final String? androidId;
  final String? iosId;
  final String? countryCode;
  final bool showReleaseNotes;
  final bool forceUpdate;
  final Duration checkInterval;
  final UpdateDialogConfig? dialogConfig;
}

Platform Behavior

iOS (App Store) βœ…

  • Highly reliable - Uses official iTunes Search API
  • Gets version information directly from Apple
  • Includes release notes
  • Direct URL to App Store

Android (Google Play Store) ⚠️

  • Prone to failure - No official API available
  • Uses web scraping which is fragile and unreliable
  • Google frequently changes HTML structure
  • May serve different content based on region/device
  • We provide multiple alternatives to mitigate this issue

Important Considerations

General

  • Requires internet connection to check versions
  • Checks are cached according to the configured checkInterval
  • Make sure to use the correct app identifiers (Bundle ID for iOS, Package Name for Android)

Android Limitations ⚠️

Since Google Play Store doesn't provide an official API for version checking, Android version detection is inherently unreliable. We've implemented several strategies to improve reliability:

  1. Primary Method: APKMirror scraping
  2. Fallback Method: Play Store HTML parsing
  3. Alternative Solutions: Custom checkers, test mode

Strong Recommendation: For production apps, implement a custom checker with your own backend for Android. This is the only way to guarantee reliable version checking.

Android Solutions

Due to the unreliable nature of Android version checking, we provide these supplementary alternatives:

Create your own backend-based checker for 100% reliability:

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:version_check_updater/version_check_updater.dart';

class MyCustomVersionChecker implements StoreVersionChecker {
  final String apiUrl;
  
  MyCustomVersionChecker({required this.apiUrl});
  
  @override
  Future<VersionInfo?> checkForUpdate({
    required String currentVersion,
    required String packageId,
    String? countryCode,
  }) async {
    try {
      // Call your backend API
      final response = await http.get(
        Uri.parse('$apiUrl/version?packageId=$packageId&platform=${Platform.operatingSystem}'),
      );
      
      if (response.statusCode == 200) {
        final data = json.decode(response.body);
        return VersionInfo(
          currentVersion: currentVersion,
          storeVersion: data['version'],
          releaseNotes: data['releaseNotes'],
          storeUrl: data['storeUrl'],
          isUpdateAvailable: _isVersionGreater(data['version'], currentVersion),
          isForceUpdate: data['forceUpdate'] ?? false,
        );
      }
    } catch (e) {
      debugPrint('Custom version check error: $e');
    }
    return null;
  }
  
  bool _isVersionGreater(String storeVersion, String currentVersion) {
    // Version comparison logic
    try {
      final storeParts = storeVersion.split('.').map(int.parse).toList();
      final currentParts = currentVersion.split('.').map(int.parse).toList();
      
      for (int i = 0; i < storeParts.length || i < currentParts.length; i++) {
        final storePart = i < storeParts.length ? storeParts[i] : 0;
        final currentPart = i < currentParts.length ? currentParts[i] : 0;
        
        if (storePart > currentPart) return true;
        if (storePart < currentPart) return false;
      }
      return false;
    } catch (e) {
      return false;
    }
  }
}

Use the custom checker:

void main() {
  // Set custom checker for Android only
  if (Platform.isAndroid) {
    VersionCheckUpdater.setCustomChecker(
      MyCustomVersionChecker(
        apiUrl: 'https://your-api.com',
      ),
    );
  }
  
  runApp(MyApp());
}

Backend Examples: GitHub JSON, Firebase Functions, Supabase, or any REST API. See the BACKEND_SOLUTION.md file for detailed examples.

2. Test Mode (For Development)

Enable test mode to simulate updates during development:

// Enable test mode with mock version
VersionCheckUpdater.enableTestMode(mockStoreVersion: '2.0.0');

// Disable test mode
VersionCheckUpdater.disableTestMode();

3. Default Fallback Strategies

If you don't implement a custom checker, the package will attempt these methods in order:

  1. APKMirror scraping (more reliable than Play Store)
  2. Play Store HTML parsing (prone to failure)
  3. Alternative endpoints

Note: These methods are unreliable and should not be used in production apps.

License

This project is licensed under the MIT License - see the LICENSE file for details.