adaptive_app_icon 0.0.1
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 withandroidApplyMode:AndroidApplyModeBehavior 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. immediatelyApplied now; Android closes the app and the user reopens it. Pair it with IconGallery.confirmChangeto warn the user first.iOS ignores
androidApplyModeentirely.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 useimmediatelyand 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/CFBundleAlternateIconsintoios/Runner/Info.plist. - Injects
<activity-alias>blocks intoandroid/app/src/main/AndroidManifest.xml(one enabled default, the rest disabled), wrapped inadaptive_app_icon:begin/endmarkers so re-runs stay idempotent. - Generates
lib/app_icons.g.dart— a typedList<AppIconAsset>plus the Android component map and aninitDynamicAppIcon()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.
3. Register the config and drop in the gallery #
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) insideios/Runner/, then added to the Runner target's Copy Bundle Resources build phase (openRunner.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_assetmust exist as a launcher resource underandroid/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 theLAUNCHERintent-filter from yourMainActivity, 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
CFBundleIconsentry. For dedicated iPad alternate icons add aCFBundleIcons~ipadentry with 152/167 px assets. - Android package assumption. Generated component names use your module's
namespace. If yourapplicationIddiffers from yournamespace, 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.