Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow web favicon to be built from a different source image file #519

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ Shown below is the full list of attributes which you can specify within your Flu
be used to fill out the background of the adaptive icon.
- `adaptive_icon_foreground`: The image asset which will be used for the icon foreground of the adaptive icon
*Note: Adaptive Icons will only be generated when both adaptive_icon_background and adaptive_icon_foreground are specified. (the image_path is not automatically taken as foreground)*
- `adaptive_icon_monochrome`: The image asset which will be used for the icon
foreground of the Android 13+ themed icon. For more information see [Android Adaptive Icons](https://developer.android.com/develop/ui/views/launch/icon_design_adaptive#user-theming)

### IOS

Expand All @@ -119,22 +121,23 @@ be used to fill out the background of the adaptive icon.
### Web

- `web`: Add web related configs
- `generate`: Specifies weather to generate icons for this platform or not
- `generate`: Specifies whether to generate icons for this platform or not
- `image_path`: Path to web icon.png
- `image_path_favicon`: The location of the icon image file used to generate the favicon (optional - if not defined then falls back to using the web image_path then the global image_path)
- `background_color`: Updates *background_color* in `web/manifest.json`
- `theme_color`: Updates *theme_color* in `web/manifest.json`

### Windows

- `windows`: Add Windows related configs
- `generate`: Specifies weather to generate icons for Windows platform or not
- `generate`: Specifies whether to generate icons for Windows platform or not
- `image_path`: Path to web icon.png
- `icon_size`: Windows app icon size. Icon size should be within this constrains *48<=icon_size<=256, defaults to 48*

### MacOS

- `macos`: Add MacOS related configs
- `generate`: Specifies weather to generate icons for MacOS platform or not
- `generate`: Specifies whether to generate icons for MacOS platform or not
- `image_path`: Path to macos icon.png file

*Note: iOS icons should [fill the entire image](https://stackoverflow.com/questions/26014461/black-border-on-my-ios-icon) and not contain transparent borders.*
Expand Down
7 changes: 1 addition & 6 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ analyzer:
missing_return: warning
# allow having TODOs in the code
todo: ignore
# Ignore analyzer hints for updating pubspecs when using Future or
# Stream and not importing dart:async
# Please see https://github.com/flutter/flutter/pull/24528 for details.
sdk_version_async_exported_from_core: ignore
exclude:
- "bin/cache/**"
# the following two are relative to the stocks example and the flutter package respectively
Expand Down Expand Up @@ -55,10 +51,9 @@ linter:
- flutter_style_todos
- hash_and_equals
- implementation_imports
- iterable_contains_unrelated_type
- collection_methods_unrelated_type
- library_names
- library_prefixes
- list_remove_unrelated_type
- no_adjacent_strings_in_list
- no_duplicate_case_values
- non_constant_identifier_names
Expand Down
2 changes: 2 additions & 0 deletions bin/generate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ flutter_launcher_icons:
min_sdk_android: 21 # android min sdk min:16, default 21
# adaptive_icon_background: "assets/icon/background.png"
# adaptive_icon_foreground: "assets/icon/foreground.png"
# adaptive_icon_monochrome: "assets/icon/monochrome.png"

ios: true
# image_path_ios: "assets/icon/icon.png"
Expand All @@ -99,6 +100,7 @@ flutter_launcher_icons:
web:
generate: true
image_path: "path/to/image.png"
# image_path_favicon: "assets/icon/icon-favicon.png"
background_color: "#hexcode"
theme_color: "#hexcode"

Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions example/default_example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ flutter_launcher_icons:
ios: true # can specify file name here e.g. "My-Launcher-Icon"
adaptive_icon_background: "assets/images/christmas-background.png" # only available for Android 8.0 devices and above
adaptive_icon_foreground: "assets/images/icon-foreground-432x432.png" # only available for Android 8.0 devices and above
adaptive_icon_monochrome: "assets/images/icon-monochrome-432x432.png" # only available for Android 13 devices and above
min_sdk_android: 21 # android min sdk min:16, default 21
remove_alpha_ios: true
background_color_ios: "#ffffff"
Expand Down
115 changes: 73 additions & 42 deletions lib/android.dart
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,83 @@ void createAdaptiveIcons(
flavor,
);
} else {
createAdaptiveIconMipmapXmlFile(config, flavor);
updateColorsXmlFile(backgroundConfig, flavor);
}
}

void createAdaptiveMonochromeIcons(
Config config,
String? flavor,
) {
utils.printStatus('Creating adaptive monochrome icons Android');

// Retrieve the necessary Flutter Launcher Icons configuration from the pubspec.yaml file
final String? monochromeImagePath = config.adaptiveIconMonochrome;
if (monochromeImagePath == null) {
throw const InvalidConfigException(errorMissingImagePath);
}
final Image? monochromeImage = utils.decodeImageFile(monochromeImagePath);
if (monochromeImage == null) {
return;
}

// Create adaptive icon monochrome images
for (AndroidIconTemplate androidIcon in adaptiveForegroundIcons) {
overwriteExistingIcons(
androidIcon,
monochromeImage,
constants.androidAdaptiveMonochromeFileName,
flavor,
);
}
}

void createMipmapXmlFile(
Config config,
String? flavor,
) {
utils.printStatus('Creating mipmap xml file Android');

String xmlContent = '';

if (config.hasAndroidAdaptiveConfig) {
if (isAdaptiveIconConfigPngFile(config.adaptiveIconBackground!)) {
xmlContent +=
' <background android:drawable="@drawable/ic_launcher_background"/>\n';
} else {
xmlContent +=
' <background android:drawable="@color/ic_launcher_background"/>\n';
}

xmlContent +=
' <foreground android:drawable="@drawable/ic_launcher_foreground"/>\n';
}

if (config.hasAndroidAdaptiveMonochromeConfig) {
xmlContent +=
' <monochrome android:drawable="@drawable/ic_launcher_monochrome"/>\n';
}

late File mipmapXmlFile;
if (config.isCustomAndroidFile) {
mipmapXmlFile = File(
constants.androidAdaptiveXmlFolder(flavor) + config.android + '.xml',
);
} else {
mipmapXmlFile = File(
constants.androidAdaptiveXmlFolder(flavor) +
constants.androidDefaultIconName +
'.xml',
);
}

mipmapXmlFile.create(recursive: true).then((File adaptiveIconFile) {
adaptiveIconFile.writeAsString(
xml_template.mipmapXmlFile.replaceAll('{{CONTENT}}', xmlContent),
);
});
}

/// Retrieves the colors.xml file for the project.
///
/// If the colors.xml file is found, it is updated with a new color item for the
Expand All @@ -151,29 +223,6 @@ void updateColorsXmlFile(String backgroundConfig, String? flavor) {
}
}

/// Creates the xml file required for the adaptive launcher icon
/// FILE LOCATED HERE: res/mipmap-anydpi/{icon-name-from-yaml-config}.xml
void createAdaptiveIconMipmapXmlFile(
Config config,
String? flavor,
) {
if (config.isCustomAndroidFile) {
File(
constants.androidAdaptiveXmlFolder(flavor) + config.android + '.xml',
).create(recursive: true).then((File adaptiveIcon) {
adaptiveIcon.writeAsString(xml_template.icLauncherXml);
});
} else {
File(
constants.androidAdaptiveXmlFolder(flavor) +
constants.androidDefaultIconName +
'.xml',
).create(recursive: true).then((File adaptiveIcon) {
adaptiveIcon.writeAsString(xml_template.icLauncherXml);
});
}
}

/// creates adaptive background using png image
void _createAdaptiveBackgrounds(
Config config,
Expand All @@ -196,24 +245,6 @@ void _createAdaptiveBackgrounds(
flavor,
);
}

// Creates the xml file required for the adaptive launcher icon
// FILE LOCATED HERE: res/mipmap-anydpi/{icon-name-from-yaml-config}.xml
if (config.isCustomAndroidFile) {
File(
constants.androidAdaptiveXmlFolder(flavor) + config.android + '.xml',
).create(recursive: true).then((File adaptiveIcon) {
adaptiveIcon.writeAsString(xml_template.icLauncherDrawableBackgroundXml);
});
} else {
File(
constants.androidAdaptiveXmlFolder(flavor) +
constants.androidDefaultIconName +
'.xml',
).create(recursive: true).then((File adaptiveIcon) {
adaptiveIcon.writeAsString(xml_template.icLauncherDrawableBackgroundXml);
});
}
}

/// Creates a colors.xml file if it was missing from android/app/src/main/res/values/colors.xml
Expand Down
12 changes: 11 additions & 1 deletion lib/config/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class Config {
this.imagePathIOS,
this.adaptiveIconForeground,
this.adaptiveIconBackground,
this.adaptiveIconMonochrome,
this.minSdkAndroid = constants.androidDefaultAndroidMinSDK,
this.removeAlphaIOS = false,
this.backgroundColorIOS = '#ffffff',
Expand Down Expand Up @@ -116,14 +117,18 @@ class Config {
@JsonKey(name: 'image_path_ios')
final String? imagePathIOS;

/// android adaptive icon foreground image
/// android adaptive_icon_foreground image
@JsonKey(name: 'adaptive_icon_foreground')
final String? adaptiveIconForeground;

/// android adaptive_icon_background image
@JsonKey(name: 'adaptive_icon_background')
final String? adaptiveIconBackground;

/// android adaptive_icon_background image
@JsonKey(name: 'adaptive_icon_monochrome')
final String? adaptiveIconMonochrome;

/// Android min_sdk_android
@JsonKey(name: 'min_sdk_android')
final int minSdkAndroid;
Expand Down Expand Up @@ -157,6 +162,11 @@ class Config {
adaptiveIconForeground != null &&
adaptiveIconBackground != null;

/// whether or not there is configuration for monochrome icons for android
bool get hasAndroidAdaptiveMonochromeConfig {
return isNeedingNewAndroidIcon && adaptiveIconMonochrome != null;
}

/// Checks if contains any platform config
bool get hasPlatformConfig {
return ios != false ||
Expand Down
4 changes: 4 additions & 0 deletions lib/config/config.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions lib/config/web_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ class WebConfig {
@JsonKey(name: 'image_path')
final String? imagePath;

/// Override image path for favicon
@JsonKey(name: 'image_path_favicon')
final String? imagePathFavicon;

/// manifest.json's background_color
@JsonKey(name: 'background_color')
final String? backgroundColor;
Expand All @@ -27,6 +31,7 @@ class WebConfig {
const WebConfig({
this.generate = false,
this.imagePath,
this.imagePathFavicon,
this.backgroundColor,
this.themeColor,
});
Expand Down
4 changes: 4 additions & 0 deletions lib/config/web_config.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions lib/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const int androidDefaultAndroidMinSDK = 21;
const String androidFileName = 'ic_launcher.png';
const String androidAdaptiveForegroundFileName = 'ic_launcher_foreground.png';
const String androidAdaptiveBackgroundFileName = 'ic_launcher_background.png';
const String androidAdaptiveMonochromeFileName = 'ic_launcher_monochrome.png';
String androidAdaptiveXmlFolder(String? flavor) =>
androidResFolder(flavor) + 'mipmap-anydpi-v26/';
const String androidDefaultIconName = 'ic_launcher';
Expand Down
6 changes: 3 additions & 3 deletions lib/ios.dart
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ void modifyContentsFile(String newIconName) {
String generateContentsFileAsString(String newIconName) {
final Map<String, dynamic> contentJson = <String, dynamic>{
'images': createImageList(newIconName),
'info': ContentsInfoObject(version: 1, author: 'xcode').toJson()
'info': ContentsInfoObject(version: 1, author: 'xcode').toJson(),
};
return json.encode(contentJson);
}
Expand All @@ -220,7 +220,7 @@ class ContentsImageObject {
'size': size,
'idiom': idiom,
'filename': filename,
'scale': scale
'scale': scale,
};
}
}
Expand Down Expand Up @@ -390,7 +390,7 @@ List<Map<String, String>> createImageList(String fileNamePrefix) {
idiom: 'ios-marketing',
filename: '[email protected]',
scale: '1x',
).toJson()
).toJson(),
];
return imageList;
}
Expand Down
12 changes: 12 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,18 @@ Future<void> createIconsFromConfig(
if (flutterConfigs.hasAndroidAdaptiveConfig) {
android_launcher_icons.createAdaptiveIcons(flutterConfigs, flavor);
}
if (flutterConfigs.hasAndroidAdaptiveMonochromeConfig) {
android_launcher_icons.createAdaptiveMonochromeIcons(
flutterConfigs,
flavor,
);
}
if (flutterConfigs.isNeedingNewAndroidIcon) {
android_launcher_icons.createMipmapXmlFile(
flutterConfigs,
flavor,
);
}
if (flutterConfigs.isNeedingNewIOSIcon) {
ios_launcher_icons.createIcons(flutterConfigs, flavor);
}
Expand Down
Loading