adaptive_app_icon 0.0.1 copy "adaptive_app_icon: ^0.0.1" to clipboard
adaptive_app_icon: ^0.0.1 copied to clipboard

Switch your app's home-screen icon at runtime between pre-bundled variants, with a config-driven codegen system and a drop-in icon gallery widget.

example/lib/main.dart

import 'dart:io' show Platform;

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

import 'app_icons.g.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  // Registers the generated icon config (appIcons + androidIconComponents).
  initDynamicAppIcon();
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'adaptive_app_icon example',
      theme: ThemeData(
        colorSchemeSeed: Colors.indigo,
        useMaterial3: true,
      ),
      home: const IconSettingsScreen(),
    );
  }
}

/// A settings screen that lets the user pick the app icon.
///
/// Demonstrates cold-start restoration: the gallery is seeded from
/// [DynamicAppIcon.getCachedIcon] so the correct icon is highlighted
/// immediately, without waiting for the async native lookup.
class IconSettingsScreen extends StatefulWidget {
  const IconSettingsScreen({super.key});

  @override
  State<IconSettingsScreen> createState() => _IconSettingsScreenState();
}

class _IconSettingsScreenState extends State<IconSettingsScreen> {
  /// Seeded fast from the local cache, then reconciled with the real native
  /// state once it resolves.
  String? _selectedName;
  bool _supported = false;
  bool _loading = true;

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

  Future<void> _restore() async {
    // 1. Fast path: cached selection (survives cold start, no native call).
    try {
      final cached = await DynamicAppIcon.getCachedIcon();
      if (mounted) setState(() => _selectedName = cached);
    } catch (_) {
      // No cache yet / storage unavailable - fall through to the truth path.
    }

    // 2. Truth path: ask the platform + capability check.
    bool supported = false;
    String? current = _selectedName;
    try {
      supported = await DynamicAppIcon.isSupported();
      current = await DynamicAppIcon.getCurrentIcon();
    } catch (_) {
      // Platform unavailable (e.g. unsupported device) - keep the cached view.
    }
    if (!mounted) return;
    setState(() {
      _supported = supported;
      _selectedName = current;
      _loading = false;
    });
  }

  /// On Android, warn that the app will close (there's no reliable way to
  /// auto-relaunch). On iOS the system shows its own alert, so we don't add one.
  Future<bool> _confirmChange(AppIconAsset icon) async {
    if (!Platform.isAndroid) return true;
    final result = await showDialog<bool>(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('Switch to "${icon.label}"?'),
        content: const Text(
          'Android has to close the app to change its icon. '
          'The app will close now — reopen it to finish.',
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context, false),
            child: const Text('Cancel'),
          ),
          FilledButton(
            onPressed: () => Navigator.pop(context, true),
            child: const Text('Change & close'),
          ),
        ],
      ),
    );
    return result ?? false;
  }

  void _onChanged(AppIconAsset icon) {
    setState(() => _selectedName = icon.name);
    ScaffoldMessenger.of(context)
      ..hideCurrentSnackBar()
      ..showSnackBar(SnackBar(content: Text('Switched to "${icon.label}"')));
  }

  void _onError(AppIconAsset icon, Object error, StackTrace _) {
    ScaffoldMessenger.of(context)
      ..hideCurrentSnackBar()
      ..showSnackBar(
        SnackBar(
          content: Text('Could not switch to "${icon.label}": $error'),
          backgroundColor: Theme.of(context).colorScheme.error,
        ),
      );
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Scaffold(
      appBar: AppBar(title: const Text('App Icon')),
      body: _loading
          ? const Center(child: CircularProgressIndicator())
          : Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                Padding(
                  padding: const EdgeInsets.fromLTRB(16, 16, 16, 0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('Choose your icon', style: theme.textTheme.titleLarge),
                      const SizedBox(height: 4),
                      Text(
                        _supported
                            ? 'Tap a variant to switch your home-screen icon. '
                                  'On Android the app closes to apply the change '
                                  '(reopen it after); on iOS the system shows a '
                                  'confirmation alert.'
                            : 'Alternate icons are not supported on this device.',
                        style: theme.textTheme.bodyMedium?.copyWith(
                          color: theme.colorScheme.onSurfaceVariant,
                        ),
                      ),
                    ],
                  ),
                ),
                Expanded(
                  child: IconGallery(
                    icons: appIcons,
                    initialSelectedName: _selectedName,
                    onChanged: _onChanged,
                    onError: _onError,
                    // Android closes the app to apply the change, so warn first.
                    // (The seamless, no-close alternative is
                    // AndroidApplyMode.whenBackgrounded.)
                    confirmChange: _confirmChange,
                    androidApplyMode: AndroidApplyMode.immediately,
                  ),
                ),
              ],
            ),
    );
  }
}
2
likes
140
points
89
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Switch your app's home-screen icon at runtime between pre-bundled variants, with a config-driven codegen system and a drop-in icon gallery widget.

Repository (GitHub)
View/report issues

Topics

#icons #app-icon #launcher #ios #android

License

MIT (license)

Dependencies

flutter, path, shared_preferences, xml, yaml

More

Packages that depend on adaptive_app_icon

Packages that implement adaptive_app_icon