force_updater 1.0.2 copy "force_updater: ^1.0.2" to clipboard
force_updater: ^1.0.2 copied to clipboard

Smart app version manager for Flutter. Add force update walls, soft update banners, and maintenance screens in 2 lines of code. Features 6 premium Material 3 UI themes.

example/lib/main.dart

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

/// ---------------------------------------------------------------------------
/// FORCE_UPDATER INTERACTIVE PLAYGROUND & EXAMPLE
/// ---------------------------------------------------------------------------
///
/// This example showcases how to implement and test the `force_updater` package.
/// It provides a dynamic interactive interface letting you test all 5 premium
/// visual styles, switch between inline simulator configs, and inspect how
/// the updater behaves in real-time.
///
/// To test the package:
/// 1. Run the local mock API server: `dart example_api.dart`
/// 2. Launch this app on your Emulator or Web browser.
/// 3. Toggle styles and configuration settings below to see immediate updates!
/// ---------------------------------------------------------------------------

// Global Notifiers to make the example highly interactive in real-time!
final ValueNotifier<UpdateDialogStyle> activeStyleNotifier =
    ValueNotifier<UpdateDialogStyle>(UpdateDialogStyle.glassmorphism);

final ValueNotifier<bool> isUsingLocalMockApiNotifier = ValueNotifier<bool>(
  true,
);

void main() {
  runApp(const ExampleApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ForceUpdater Premium Showcase',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: Colors.deepPurple,
        brightness: Brightness.light,
      ),
      darkTheme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: Colors.deepPurple,
        brightness: Brightness.dark,
      ),
      // We wrap the entire home view with ValueListenableBuilders so that
      // changes to the style or the config instantly rebuild the ForceUpdater!
      home: ValueListenableBuilder<UpdateDialogStyle>(
        valueListenable: activeStyleNotifier,
        builder: (context, activeStyle, _) {
          return ValueListenableBuilder<bool>(
            valueListenable: isUsingLocalMockApiNotifier,
            builder: (context, useLocalApi, _) {
              return ForceUpdater(
                // 1. Current App Version
                // We set the current app version to v4.0.0 (or v1.0.0) so we can trigger updates
                // whenever the latest version is greater than this current version.
                currentVersion: '4.0.0',

                // 2. Background Check Interval
                // We poll the backend every 3 seconds so you can see live hot updates
                // immediately when you change options in the 'example_api.dart' server terminal.
                checkInterval: const Duration(seconds: 3),

                // 3. UI Styling
                // Set the style dynamically based on the user's dropdown choice!
                dialogStyle: activeStyle,

                // 4. Custom UI Notification Override (100% Flexibity!)
                // - By default, this is true. ForceUpdater displays one of the 5 premium dialogs.
                // - Set this to false to suppress the default dialogs and implement your own custom UI
                //   (e.g., custom bottom sheets, overlays, or snackbars) inside the onVersionResolved callback below!
                showDefaultDialog: true,

                // 5. Update Strategy Configuration
                // Switch dynamically between the local Mock API server and a hardcoded inline simulator!
                config: useLocalApi
                    ? UpdateConfig.remote('http://10.0.2.2:3000/version.json')
                    : UpdateConfig.inline(
                        minVersion:
                            '1.5.0', // Users below v1.5.0 get locked forcefully
                        latestVersion:
                            '2.0.0', // Users between 1.5.0 and 2.0.0 get optional updates
                        forceUpdate: false, // Compulsory update toggle
                        maintenance:
                            false, // Global system maintenance screen toggle
                        changelog:
                            '• Beautiful new interactive dashboard\n• Toggle between 5 custom visual styles on the fly\n• Fixed emulator network routing issues\n• Added smooth transitions and rich micro-animations',
                        storeUrlAndroid: 'https://play.google.com/store',
                        storeUrlIos: 'https://apps.apple.com',
                      ),

                // Let's print logs to console for better debugging insight:
                onVersionResolved: (info) {
                  debugPrint(
                    '[ForceUpdater Playground] Version check successful: ${info.latestVersion}',
                  );

                  // 💡 CUSTOM UI IMPLEMENTATION PLACE:
                  // If you set `showDefaultDialog: false`, this is the exact place where you write
                  // your own custom notification layout! For example:
                  //
                  // if (Semver.isLessThan('1.0.0', info.minVersion)) {
                  //   showMyFullyCustomBlockingWall(context);
                  // } else if (Semver.isLessThan('1.0.0', info.latestVersion)) {
                  //   showMyCustomBannerOverlay(context);
                  // }
                },
                onError: (err) {
                  debugPrint('[ForceUpdater Playground] Error occurred: $err');
                },

                child: const HomePage(),
              );
            },
          );
        },
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final cs = theme.colorScheme;

    return Scaffold(
      backgroundColor: cs.surface,
      appBar: AppBar(
        title: const Text(
          'ForceUpdater Showcase',
          style: TextStyle(fontWeight: FontWeight.bold),
        ),
        centerTitle: true,
        elevation: 0,
        backgroundColor: Colors.transparent,
      ),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              // Welcome Card
              Container(
                padding: const EdgeInsets.all(20),
                decoration: BoxDecoration(
                  gradient: LinearGradient(
                    colors: [
                      cs.primaryContainer,
                      cs.secondaryContainer.withValues(alpha: 0.5),
                    ],
                    begin: Alignment.topLeft,
                    end: Alignment.bottomRight,
                  ),
                  borderRadius: BorderRadius.circular(24),
                  border: Border.all(color: cs.primary.withValues(alpha: 0.1)),
                ),
                child: Column(
                  children: [
                    CircleAvatar(
                      radius: 36,
                      backgroundColor: cs.primary,
                      child: const Icon(
                        Icons.rocket_launch_rounded,
                        size: 36,
                        color: Colors.white,
                      ),
                    ),
                    const SizedBox(height: 16),
                    Text(
                      'Interactive Playground',
                      style: theme.textTheme.titleLarge?.copyWith(
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 8),
                    Text(
                      'Experience premium update overlays and responsive dialog templates tailored for standard, minimalist, and luxury branding.',
                      textAlign: TextAlign.center,
                      style: theme.textTheme.bodyMedium?.copyWith(
                        color: cs.onSurfaceVariant,
                        height: 1.45,
                      ),
                    ),
                  ],
                ),
              ),
              const SizedBox(height: 24),

              // UI Style Selection Card
              Card(
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(20),
                ),
                elevation: 0,
                color: cs.surfaceContainerLow,
                child: Padding(
                  padding: const EdgeInsets.all(20),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Row(
                        children: [
                          Icon(Icons.palette_outlined, color: cs.primary),
                          const SizedBox(width: 10),
                          Text(
                            'Choose Dialog Style',
                            style: theme.textTheme.titleMedium?.copyWith(
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                        ],
                      ),
                      const SizedBox(height: 6),
                      Text(
                        'Switch styles below to instantly reload the update dialog in that specific layout.',
                        style: theme.textTheme.bodySmall?.copyWith(
                          color: cs.onSurfaceVariant,
                        ),
                      ),
                      const SizedBox(height: 16),

                      // Custom Dropdown for selecting one of the 5 premium dialog styles
                      ValueListenableBuilder<UpdateDialogStyle>(
                        valueListenable: activeStyleNotifier,
                        builder: (context, activeStyle, _) {
                          return Container(
                            padding: const EdgeInsets.symmetric(horizontal: 16),
                            decoration: BoxDecoration(
                              color: cs.surface,
                              borderRadius: BorderRadius.circular(12),
                              border: Border.all(color: cs.outlineVariant),
                            ),
                            child: DropdownButtonHideUnderline(
                              child: DropdownButton<UpdateDialogStyle>(
                                value: activeStyle,
                                isExpanded: true,
                                icon: const Icon(
                                  Icons.keyboard_arrow_down_rounded,
                                ),
                                items: const [
                                  DropdownMenuItem(
                                    value: UpdateDialogStyle.glassmorphism,
                                    child: Text(
                                      '🧪 Glassmorphism (Frosted Glow)',
                                    ),
                                  ),
                                  DropdownMenuItem(
                                    value: UpdateDialogStyle.classic,
                                    child: Text(
                                      '🏛️ Classic (Standard Material 3)',
                                    ),
                                  ),
                                  DropdownMenuItem(
                                    value: UpdateDialogStyle.minimalist,
                                    child: Text(
                                      '📐 Minimalist (Sophisticated Outlines)',
                                    ),
                                  ),
                                  DropdownMenuItem(
                                    value: UpdateDialogStyle.gradient,
                                    child: Text(
                                      '🎨 Gradient Card (Vibrant Colors)',
                                    ),
                                  ),
                                  DropdownMenuItem(
                                    value: UpdateDialogStyle.bottomSheet,
                                    child: Text(
                                      '📱 Slide-up BottomSheet (Sleek Compact)',
                                    ),
                                  ),
                                ],
                                onChanged: (val) {
                                  if (val != null) {
                                    activeStyleNotifier.value = val;
                                  }
                                },
                              ),
                            ),
                          );
                        },
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 16),

              // Configuration Controller Card
              Card(
                shape: RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(20),
                ),
                elevation: 0,
                color: cs.surfaceContainerLow,
                child: Padding(
                  padding: const EdgeInsets.all(20),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Row(
                        children: [
                          Icon(Icons.settings_outlined, color: cs.primary),
                          const SizedBox(width: 10),
                          Text(
                            'Configuration Method',
                            style: theme.textTheme.titleMedium?.copyWith(
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                        ],
                      ),
                      const SizedBox(height: 6),
                      Text(
                        'Change the data source for version checks. Use Mock API server or test locally without a backend!',
                        style: theme.textTheme.bodySmall?.copyWith(
                          color: cs.onSurfaceVariant,
                        ),
                      ),
                      const SizedBox(height: 16),

                      ValueListenableBuilder<bool>(
                        valueListenable: isUsingLocalMockApiNotifier,
                        builder: (context, useLocalApi, _) {
                          return Column(
                            crossAxisAlignment: CrossAxisAlignment.stretch,
                            children: [
                              SegmentedButton<bool>(
                                segments: const [
                                  ButtonSegment<bool>(
                                    value: true,
                                    label: Text('Mock API Server'),
                                    icon: Icon(Icons.dns_rounded),
                                  ),
                                  ButtonSegment<bool>(
                                    value: false,
                                    label: Text('Inline Simulator'),
                                    icon: Icon(Icons.offline_bolt_rounded),
                                  ),
                                ],
                                selected: {useLocalApi},
                                onSelectionChanged: (newSelection) {
                                  isUsingLocalMockApiNotifier.value =
                                      newSelection.first;
                                },
                              ),
                              const SizedBox(height: 16),
                              Container(
                                padding: const EdgeInsets.all(12),
                                decoration: BoxDecoration(
                                  color: cs.surface,
                                  borderRadius: BorderRadius.circular(12),
                                  border: Border.all(
                                    color: cs.outlineVariant.withValues(
                                      alpha: 0.5,
                                    ),
                                  ),
                                ),
                                child: Row(
                                  children: [
                                    Icon(
                                      useLocalApi
                                          ? Icons.link_rounded
                                          : Icons.info_outline_rounded,
                                      size: 18,
                                      color: cs.primary,
                                    ),
                                    const SizedBox(width: 8),
                                    Expanded(
                                      child: Text(
                                        useLocalApi
                                            ? 'Target: http://10.0.2.2:3000/version.json (Android) / localhost:3000 (Web/iOS)'
                                            : 'Simulating update check using local hardcoded parameters (min v1.5.0, latest v2.0.0).',
                                        style: theme.textTheme.bodySmall
                                            ?.copyWith(
                                              color: cs.onSurfaceVariant,
                                              height: 1.35,
                                            ),
                                      ),
                                    ),
                                  ],
                                ),
                              ),
                            ],
                          );
                        },
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 24),

              // Quick instructions
              Text(
                'How to test updates:',
                style: theme.textTheme.titleSmall?.copyWith(
                  fontWeight: FontWeight.bold,
                ),
              ),
              const SizedBox(height: 12),
              _StepTile(
                step: '1',
                title: 'Start local server',
                subtitle:
                    'Run "dart example_api.dart" in the root directory to start the test endpoint.',
              ),
              _StepTile(
                step: '2',
                title: 'Configure device target',
                subtitle:
                    'Android emulator resolves localhost as 10.0.2.2. Web/iOS use localhost. We support both automatically!',
              ),
              _StepTile(
                step: '3',
                title: 'Perform simulated hot updates',
                subtitle:
                    'Press [2] in server terminal to trigger force updates, [3] to toggle soft alerts, or [1] for maintenance.',
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class _StepTile extends StatelessWidget {
  final String step;
  final String title;
  final String subtitle;

  const _StepTile({
    required this.step,
    required this.title,
    required this.subtitle,
  });

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    final cs = theme.colorScheme;

    return Padding(
      padding: const EdgeInsets.only(bottom: 12),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          CircleAvatar(
            radius: 12,
            backgroundColor: cs.secondaryContainer,
            child: Text(
              step,
              style: TextStyle(
                fontSize: 11,
                fontWeight: FontWeight.bold,
                color: cs.onSecondaryContainer,
              ),
            ),
          ),
          const SizedBox(width: 12),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  title,
                  style: theme.textTheme.bodyMedium?.copyWith(
                    fontWeight: FontWeight.bold,
                  ),
                ),
                const SizedBox(height: 2),
                Text(
                  subtitle,
                  style: theme.textTheme.bodySmall?.copyWith(
                    color: cs.onSurfaceVariant,
                    height: 1.35,
                  ),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
2
likes
150
points
143
downloads
screenshot

Documentation

API reference

Publisher

verified publisheryashdodani.me

Weekly Downloads

Smart app version manager for Flutter. Add force update walls, soft update banners, and maintenance screens in 2 lines of code. Features 6 premium Material 3 UI themes.

Homepage

Topics

#update #force-update #version-check #maintenance #app-update

License

MIT (license)

Dependencies

flutter

More

Packages that depend on force_updater