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.

adaptive_app_icon #

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

iOS UIApplication.setAlternateIconName(_:) (iOS 10.3+)
Android <activity-alias> toggling via PackageManager.setComponentEnabledSetting

Important

This package only switches between icons that are already bundled in the binary at build time. Neither iOS nor Android can download a new icon at runtime — every variant must ship inside the app. Adding a new icon later requires a new build and store submission.


How it works (and its limits) #

  • iOS shows an unavoidable system confirmation alert every time the icon changes. This is enforced by the OS and cannot be suppressed. If the user cancels, the switch fails and the gallery selection rolls back automatically.

  • Android enables exactly one <activity-alias> at a time (the main activity is never toggled). Because Android finishes the current task when the launcher component it was launched through is disabled, a switch can't happen silently mid-session. Choose how it lands with androidApplyMode:

    AndroidApplyMode Behavior
    whenBackgrounded (default) Applied when the app is next backgrounded — the app never visibly closes; the icon is already updated by the time the user is on the home screen.
    immediately Applied now; Android closes the app and the user reopens it. Pair it with IconGallery.confirmChange to warn the user first.

    iOS ignores androidApplyMode entirely.

    Why no auto-relaunch? Android 10+ blocks background activity starts, so an app cannot reliably relaunch itself after being closed. Prefer whenBackgrounded (no close at all), or use immediately and tell the user to reopen the app.


Quick start (3 steps) #

1. Declare your icons in adaptive_app_icon.yaml #

Create this file at your project root:

adaptive_app_icon:
  icons:
    # The first entry (or one with `default: true`) is the primary icon.
    - name: default
      ios_asset: AppIcon          # Asset-catalog name of your primary icon
      android_asset: ic_launcher  # mipmap/drawable resource name
      preview: assets/icons/default_preview.png
      label: "Classic"

    - name: neon
      ios_asset: AppIcon-Neon     # Loose PNG base name in ios/Runner
      android_asset: ic_launcher_neon
      preview: assets/icons/neon_preview.png
      label: "Neon"

2. Run the codegen #

dart run adaptive_app_icon:generate_icon_config

This automatically:

  • Injects CFBundleIcons / CFBundleAlternateIcons into ios/Runner/Info.plist.
  • Injects <activity-alias> blocks into android/app/src/main/AndroidManifest.xml (one enabled default, the rest disabled), wrapped in adaptive_app_icon:begin/end markers so re-runs stay idempotent.
  • Generates lib/app_icons.g.dart — a typed List<AppIconAsset> plus the Android component map and an initDynamicAppIcon() helper. No raw strings downstream.
  • Validates that the referenced icon assets actually exist and prints clear errors (not silent failures) if any are missing. Warns if you declare more than ~10 icons (binary-size consideration).

Re-run it any time you change the YAML.

import 'package:adaptive_app_icon/adaptive_app_icon.dart';
import 'app_icons.g.dart'; // generated

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  initDynamicAppIcon(); // register the generated config
  runApp(const MyApp());
}

// ...on a settings screen:
FutureBuilder<String?>(
  future: DynamicAppIcon.getCachedIcon(), // avoids a cold-start flash
  builder: (context, snapshot) => IconGallery(
    icons: DynamicAppIcon.icons,
    initialSelectedName: snapshot.data,
    onChanged: (icon) => debugPrint('Switched to ${icon.label}'),
  ),
);

Or call the API directly:

await DynamicAppIcon.setIcon('neon');       // switch
await DynamicAppIcon.setIcon(null);         // back to primary
final current = await DynamicAppIcon.getCurrentIcon(); // 'neon' or null
final ok = await DynamicAppIcon.isSupported();

What the codegen handles vs. what you must do manually #

Handled automatically You must do manually
Info.plist icon entries Create the actual icon image files (the codegen never draws art)
AndroidManifest.xml aliases (incl. moving the launcher off MainActivity) iOS: add the loose alternate PNGs to the Xcode target's Copy Bundle Resources
Typed Dart config Android: place mipmap resources for each android_asset at every density
Asset-existence validation Provide preview PNGs for the gallery

Creating the icon files #

  • iOS. Your primary icon lives in the asset catalog (Assets.xcassets/AppIcon.appiconset) as usual. Each alternate icon must be added as loose PNG files named <ios_asset>@2x.png (120×120) and <ios_asset>@3x.png (180×180) inside ios/Runner/, then added to the Runner target's Copy Bundle Resources build phase (open Runner.xcworkspace → Runner target → Build Phases → drag the files in). The codegen validates the files exist but does not edit the Xcode project, so this one-time step is manual. If you skip it the icon turns blank/white — the file simply isn't in the bundle.

    iOS icons must be opaque and full-bleed — no transparency and no pre-rounded corners. iOS applies its own icon mask; an image with an alpha channel renders as a colorless/black square.

  • Android. Each android_asset must exist as a launcher resource under android/app/src/main/res/mipmap-*/ for the usual densities (mdpi→xxxhdpi). Adaptive icons (mipmap-anydpi-v26/*.xml) work too. The codegen turns every icon into an <activity-alias> and removes the LAUNCHER intent-filter from your MainActivity, so the currently-running activity is never disabled during a switch (doing so crashes the app).

The preview thumbnails (preview:) are ordinary Flutter assets you control — use flat PNGs, not the platform icon files, because real launcher icons carry OS-specific masking/sizing that won't render consistently in-app.


Known limitations & review notes #

Note

Android notification icons are separate. The small icon shown in the status bar / notifications is set independently of the launcher icon and will not follow an icon switch. Update it via your notification code if needed.

Warning

iOS App Store review. Every bundled icon variant is visible to reviewers at submission time (they appear in the "App Icons" section). Icons cannot be added over-the-air — introducing a new variant later requires a new build and a new review.

Other notes:

  • iOS always shows a confirmation alert on switch; there is no API to suppress it.
  • iPad. This package writes the iPhone CFBundleIcons entry. For dedicated iPad alternate icons add a CFBundleIcons~ipad entry with 152/167 px assets.
  • Android package assumption. Generated component names use your module's namespace. If your applicationId differs from your namespace, verify the alias names resolve on-device.

API reference #

DynamicAppIcon #

Member Description
initialize({icons, androidComponents}) Register the generated config. Call once at startup (via initDynamicAppIcon()).
setIcon(String? name, {AndroidApplyMode androidApplyMode}) Activate name, or the primary icon when null. See the apply-mode table above.
getCurrentIcon() Active icon name, or null for the primary icon.
isSupported() Whether switching is available (platform supports it and >1 icon configured).
getCachedIcon() Last selection from shared_preferences — seed cold-start UI with this.
icons / defaultIconName The registered icon list / the default icon's name.

Failures throw a PlatformException with code UNSUPPORTED, BAD_ARGS, or SET_FAILED.

IconGallery #

A GridView that renders preview thumbnails, checkmarks the active icon, and performs an optimistic switch that rolls back if setIcon throws (e.g. the user cancels iOS's alert). Provide itemBuilder to fully restyle each tile without forking the widget.


Regenerating the native bridge #

The Dart⇄Swift⇄Kotlin bindings are generated by Pigeon from pigeons/messages.dart:

dart run pigeon --input pigeons/messages.dart

Example #

See example/ for a working app with three icon variants, the gallery wired onto a settings screen, and cold-start state restoration.

adaptive_app_icon #

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