Skip to main content

app_hub_upgrader 1.0.1 copy "app_hub_upgrader: ^1.0.1" to clipboard
app_hub_upgrader: ^1.0.1 copied to clipboard

A Flutter package for checking app version updates from API and displaying customizable update dialogs.

App Hub Upgrader #

pub package License GitHub

A Flutter package for checking app version updates from an API and displaying a customizable update dialog to users.

Table of Contents #

Features #

  • ✅ Check for app updates from API
  • ✅ Automatic version detection using package_info_plus
  • ✅ Customizable update dialog with changelog display
  • ✅ Support for forced updates
  • ✅ Platform-specific version checking (Android/iOS)
  • ✅ Easy to integrate and use

Installation #

Add this to your package's pubspec.yaml file:

dependencies:
  app_hub_upgrader: ^1.0.1

Then run:

flutter pub get

Note: When using git, make sure to specify a tag (e.g., v1.0.1) for stable versions, or use a branch name for development versions.

Usage #

Quick Start #

Step 1: Initialize AppHubUpgrader (Recommended)

Initialize once in your main() function:

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

  // Initialize AppHubUpgrader with global parameters
  AppHubUpgrader.initialize(
    appID: 'your-app-id',
    navigatorKey: navigatorKey,
    useProduction: true,
  );

  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: navigatorKey,
      title: 'My App',
      home: MyHomePage(),
    );
  }
}

Step 2: Use AppHubUpgrader

After initialization, create instances without parameters:

// Create instance using initialized values
final upgrader = AppHubUpgrader();

// Check for updates (no context needed!)
await upgrader.checkForUpdate();

Alternative: Without Initialization

You can also use AppHubUpgrader without calling initialize():

final upgrader = AppHubUpgrader(
  appID: 'your-app-id',
  navigatorKey: MyApp.navigatorKey, // Recommended
  useProduction: true,
);

await upgrader.checkForUpdate();

That's it! The package will automatically check for updates and show a dialog if a new version is available.

Initialization #

The AppHubUpgrader.initialize() method allows you to set global parameters once, then create instances without passing them each time.

Benefits of Initialization

  • Cleaner code: No need to pass appID and navigatorKey every time
  • Centralized configuration: Manage all settings in one place
  • Easier maintenance: Update configuration in one location

Initialize in main()

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

  // Initialize once with all parameters
  AppHubUpgrader.initialize(
    appID: 'your-app-id',
    navigatorKey: MyApp.navigatorKey,
    useProduction: true,
    dialogConfig: UpdateDialogConfig(
      title: 'Update Available',
      updateButtonText: 'Update Now',
      laterButtonText: 'Maybe Later',
    ),
  );

  runApp(MyApp());
}

Use After Initialization

After calling initialize(), you can create instances without parameters:

// Simple - uses all initialized values
final upgrader = AppHubUpgrader();
await upgrader.checkForUpdate();

// Override specific values if needed
final devUpgrader = AppHubUpgrader(useProduction: false);

Check Initialization Status

if (AppHubUpgrader.isInitialized) {
  print('App ID: ${AppHubUpgrader.initializedAppID}');
}

Step-by-Step Integration #

Step 1: Initialize in Your App's Entry Point

Initialize AppHubUpgrader in your main() function:

import 'package:app_hub_upgrader/app_hub_upgrader.dart';
import 'package:flutter/material.dart';

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

  // Initialize AppHubUpgrader
  AppHubUpgrader.initialize(
    appID: 'your-app-id', // Get this from your backend/API
    navigatorKey: MyApp.navigatorKey,
    useProduction: true,
  );

  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // Global navigator key for showing dialogs
  static final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      navigatorKey: navigatorKey,
      title: 'My App',
      home: const MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key});

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late AppHubUpgrader _upgrader;

  @override
  void initState() {
    super.initState();

    // Create instance using initialized values (no parameters needed!)
    _upgrader = AppHubUpgrader();

    // Check for updates after the first frame is rendered
    WidgetsBinding.instance.addPostFrameCallback((_) {
      _checkForUpdates();
    });
  }

  Future<void> _checkForUpdates() async {
    try {
      await _upgrader.checkForUpdate();
    } catch (e) {
      // Handle errors silently or show a message
      debugPrint('Update check failed: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('My App')),
      body: const Center(
        child: Text('Welcome to My App'),
      ),
    );
  }
}

Step 2: Customize the Update Dialog (Optional)

You can customize the appearance and behavior of the update dialog:

final upgrader = AppHubUpgrader(
  appID: 'your-app-id',
  context: context,
  useProduction: true,
  dialogConfig: UpdateDialogConfig(
    // Customize dialog text
    title: 'New Version Available!',
    updateButtonText: 'Update Now',
    laterButtonText: 'Maybe Later',

    // Customize colors
    primaryColor: Colors.blue,
    backgroundColor: Colors.white,

    // Customize icon
    customIcon: const Icon(
      Icons.cloud_download,
      size: 64,
      color: Colors.blue,
    ),

    // Add callbacks
    onUpdate: () {
      debugPrint('User clicked update button');
      // You can add analytics tracking here
    },
    onLater: () {
      debugPrint('User clicked later button');
      // You can add analytics tracking here
    },
  ),
);

await upgrader.checkForUpdate();

Common Use Cases #

Initialize in main() and check for updates when the app starts:

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

  AppHubUpgrader.initialize(
    appID: 'your-app-id',
    navigatorKey: MyApp.navigatorKey,
  );

  runApp(MyApp());
}

@override
void initState() {
  super.initState();
  final upgrader = AppHubUpgrader(); // Uses initialized values

  WidgetsBinding.instance.addPostFrameCallback((_) {
    upgrader.checkForUpdate();
  });
}

Use Case 2: Check on Button Tap

Allow users to manually check for updates:

ElevatedButton(
  onPressed: () async {
    final upgrader = AppHubUpgrader(); // Uses initialized values
    await upgrader.checkForUpdate();
  },
  child: const Text('Check for Updates'),
)

Use Case 3: Check Periodically

Check for updates at regular intervals:

Timer.periodic(const Duration(hours: 24), (timer) async {
  final upgrader = AppHubUpgrader(); // Uses initialized values
  await upgrader.checkForUpdate();
});

Use Case 4: Check Without Auto-Dialog

Check for updates and handle the result manually:

final upgrader = AppHubUpgrader(); // Uses initialized values

// Check without showing dialog automatically
final updateInfo = await upgrader.checkForUpdate(
  checkOnly: true, // Don't show dialog automatically
);

if (updateInfo?.updateAvailable == true) {
  // Custom logic based on update info
  if (updateInfo!.isForced) {
    // Show custom forced update UI
    await upgrader.showUpdateDialog(updateInfo); // context optional when using navigatorKey
  } else {
    // Show custom optional update UI
    _showCustomUpdateNotification(updateInfo);
  }
} else {
  // No update available
  if (mounted) {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('You are using the latest version!')),
    );
  }
}

Use Case 5: Manual Version and Platform Specification

Override automatic detection:

final upgrader = AppHubUpgrader(); // Uses initialized values

final updateInfo = await upgrader.checkForUpdate(
  currentVersion: '1.0.0', // Override auto-detected version
  platform: 'android',     // Override auto-detected platform
);

Advanced Configuration #

Using Development API

Switch to development API for testing:

// Option 1: Override in instance
final upgrader = AppHubUpgrader(
  useProduction: false, // Override initialized value
);

// Option 2: Initialize with development API
AppHubUpgrader.initialize(
  appID: 'your-app-id',
  navigatorKey: MyApp.navigatorKey,
  useProduction: false, // Use development API
);

Error Handling

Handle errors gracefully:

try {
  await upgrader.checkForUpdate();
} on AppException catch (e) {
  // Handle API errors
  debugPrint('Update check failed: ${e.message} (${e.statusCode})');

  if (e.statusCode == 404) {
    // No update endpoint found
  } else if (e.statusCode >= 500) {
    // Server error
  }
} catch (e) {
  // Handle other errors
  debugPrint('Unexpected error: $e');
}

Using UpdateDialog Independently

You can use the UpdateDialog widget directly if you have your own update checking logic:

// After getting UpdateInfo from your own API call
showDialog(
  context: context,
  barrierDismissible: !updateInfo.isForced,
  builder: (context) => UpdateDialog(
    updateInfo: updateInfo,
    title: 'Update Required',
    updateButtonText: 'Update Now',
    laterButtonText: 'Later',
    primaryColor: Colors.blue,
  ),
);

API Response Format #

The package expects the API to return a response in the following format:

{
  "status_code": 200,
  "is_success": true,
  "message": "Success",
  "data": {
    "updateAvailable": true,
    "latestVersion": "1.2.0",
    "currentVersion": "1.1.0",
    "isForced": false,
    "downloadURL": "https://play.google.com/store/apps/details?id=com.example.app",
    "changelog": {
      "fixes": [
        "Fixed bug in login screen",
        "Improved performance",
        "Added new features"
      ]
    },
    "releaseDate": "2024-01-15"
  }
}

The package will automatically fall back to these defaults if:

  • .env file is not found
  • AppHubUpgrader.initialize() is not called
  • Environment variables are not set

Alternative: Modify api_config.dart #

You can also modify the default URLs directly in lib/config/api_config.dart if you prefer not to use .env files.

API Parameters #

The package sends the following query parameters to the API:

  • app_id: Your application ID
  • current_version: Current app version (auto-detected or manually provided)
  • platform: Platform name (android or ios, auto-detected)

UpdateDialog Widget #

The UpdateDialog widget can be used independently if needed. The isForced property is automatically read from updateInfo.isForced:

showDialog(
  context: context,
  barrierDismissible: !updateInfo.isForced, // Prevent dismissal for forced updates
  builder: (context) => UpdateDialog(
    updateInfo: updateInfo, // isForced is read from here
    title: 'Update Required',
    updateButtonText: 'Update Now',
    laterButtonText: 'Later',
    primaryColor: Colors.blue,
  ),
);

UpdateDialogConfig Properties #

Property Type Description
title String? Custom title for the dialog
updateButtonText String? Text for the update button
laterButtonText String? Text for the later button (hidden if forced update)
onUpdate VoidCallback? Callback when update button is clicked
onLater VoidCallback? Callback when later button is clicked
primaryColor Color? Primary color for buttons and icon
backgroundColor Color? Background color of the dialog
titleStyle TextStyle? Custom style for the title
contentStyle TextStyle? Custom style for content text
customIcon Widget? Custom icon widget

Models #

UpdateInfo #

Contains information about available updates:

class UpdateInfo {
  final bool updateAvailable;
  final String? latestVersion;
  final String? currentVersion;
  final bool isForced;
  final String? downloadURL;
  final Changelog? changelog;
  final String? releaseDate;
}

Changelog #

Contains changelog information:

class Changelog {
  final List<String> fixes;
}

Error Handling #

The package throws AppException when API calls fail. You can catch and handle these:

try {
  await upgrader.checkForUpdate();
} on AppException catch (e) {
  print('Error: ${e.message} (Status: ${e.statusCode})');
} catch (e) {
  print('Unexpected error: $e');
}

Forced Updates #

When isForced is true:

  • The dialog cannot be dismissed by tapping outside
  • The "Later" button is hidden
  • Users must update to continue using the app

Platform Support #

  • ✅ Android
  • ✅ iOS

Dependencies #

This package depends on the following:

  • http: ^1.6.0 - For making API requests
  • package_info_plus: ^9.0.0 - For getting app version information
  • url_launcher: ^6.3.1 - For opening download URLs

All dependencies are automatically installed when you add this package to your project.

Example #

See the example folder for a complete working example.

Versioning #

This package follows Semantic Versioning. The current version is 1.0.1.

For version history, see CHANGELOG.md.

License #

See the LICENSE file for details.

Contributing #

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request
1
likes
120
points
39
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

A Flutter package for checking app version updates from API and displaying customizable update dialogs.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, http, package_info_plus, url_launcher

More

Packages that depend on app_hub_upgrader