force_updater 1.1.0 copy "force_updater: ^1.1.0" to clipboard
force_updater: ^1.1.0 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
160
points
143
downloads

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