From 180a0a5b01983198a966e46d768611a2951598e9 Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Fri, 8 Jul 2022 18:07:38 +0530 Subject: [PATCH 01/24] style(analysis): formatted --- analysis_options.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 98c6b5e3f6..514cdc971d 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -13,12 +13,12 @@ analyzer: # Please see https://github.com/flutter/flutter/pull/24528 for details. sdk_version_async_exported_from_core: ignore exclude: - - 'bin/cache/**' + - "bin/cache/**" # the following two are relative to the stocks example and the flutter package respectively # see https://github.com/dart-lang/sdk/issues/28463 - - 'lib/i18n/stock_messages_*.dart' - - 'lib/src/http/**' - - 'example/**' + - "lib/i18n/stock_messages_*.dart" + - "lib/src/http/**" + - "example/**" linter: rules: From 8b925d00b1451d8326375a526db5161155288782 Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Fri, 8 Jul 2022 18:08:59 +0530 Subject: [PATCH 02/24] chore(lints): disabled `sort_constructors_first` --- analysis_options.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 514cdc971d..3d2c15e727 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -128,7 +128,7 @@ linter: # - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml - recursive_getters - slash_for_doc_comments - - sort_constructors_first + # - sort_constructors_first: vscode's command "dart:sort members" does not follow this - sort_pub_dependencies - sort_unnamed_constructors_first # - super_goes_last # no longer needed w/ Dart 2 From 2db23d443b3a4fdd0b7964e884f8c36ccbce68cc Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Fri, 8 Jul 2022 18:09:28 +0530 Subject: [PATCH 03/24] chore(vscode): add vscode workspace config --- flutter_launcher_icons.code-workspace | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 flutter_launcher_icons.code-workspace diff --git a/flutter_launcher_icons.code-workspace b/flutter_launcher_icons.code-workspace new file mode 100644 index 0000000000..b743fb6b85 --- /dev/null +++ b/flutter_launcher_icons.code-workspace @@ -0,0 +1,24 @@ +{ + "folders": [ + { + "name": "Flutter Launcher Icons", + "path": "." + } + ], + "settings": { + "[dart]": { + "editor.formatOnSave": true, + // "editor.formatOnType": true, + "editor.rulers": [ + 120 + ], + "editor.selectionHighlight": false, + "editor.suggest.snippetsPreventQuickSuggestions": false, + "editor.suggestSelection": "first", + "editor.tabCompletion": "onlySnippets", + "editor.wordBasedSuggestions": false, + "files.insertFinalNewline": true + }, + "dart.lineLength": 120, + } +} \ No newline at end of file From 9087923b636364b4888f3fad913d49e6aa3a8133 Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Fri, 8 Jul 2022 19:13:24 +0530 Subject: [PATCH 04/24] vendor(deps): added new dependencies checked_yaml,json_annotation for typesafe configs, cli_util for logging --- pubspec.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pubspec.yaml b/pubspec.yaml index 349a811dac..6b2e7cf825 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,10 @@ homepage: https://github.com/fluttercommunity/flutter_launcher_icons dependencies: args: ^2.2.0 + checked_yaml: ^2.0.1 + cli_util: ^0.3.5 image: ^3.0.2 + json_annotation: ^4.5.0 path: ^1.8.0 yaml: ^3.1.0 @@ -19,4 +22,5 @@ dev_dependencies: # allows us to get version number from pubspec yaml which we can pass to Sentry # https://pub.dev/packages/build_version build_version: ^2.1.1 + json_serializable: ^6.2.0 test: ^1.21.1 From 3a5cbfffde02efa1bcde8a0f0e5bf32116eb712a Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Fri, 8 Jul 2022 19:26:49 +0530 Subject: [PATCH 05/24] feat(web): constants for web platform --- lib/constants.dart | 36 ++++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/lib/constants.dart b/lib/constants.dart index cb22b10227..cdc07a32cd 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -1,35 +1,39 @@ -String androidResFolder(String? flavor) => - "android/app/src/${flavor ?? 'main'}/res/"; -String androidColorsFile(String? flavor) => - "android/app/src/${flavor ?? 'main'}/res/values/colors.xml"; +import 'package:path/path.dart' as path; + +String androidResFolder(String? flavor) => "android/app/src/${flavor ?? 'main'}/res/"; +String androidColorsFile(String? flavor) => "android/app/src/${flavor ?? 'main'}/res/values/colors.xml"; const String androidManifestFile = 'android/app/src/main/AndroidManifest.xml'; const String androidGradleFile = 'android/app/build.gradle'; const String androidLocalPropertiesFile = 'android/local.properties'; const String androidFileName = 'ic_launcher.png'; const String androidAdaptiveForegroundFileName = 'ic_launcher_foreground.png'; const String androidAdaptiveBackgroundFileName = 'ic_launcher_background.png'; -String androidAdaptiveXmlFolder(String? flavor) => - androidResFolder(flavor) + 'mipmap-anydpi-v26/'; +String androidAdaptiveXmlFolder(String? flavor) => androidResFolder(flavor) + 'mipmap-anydpi-v26/'; const String androidDefaultIconName = 'ic_launcher'; -const String iosDefaultIconFolder = - 'ios/Runner/Assets.xcassets/AppIcon.appiconset/'; +const String iosDefaultIconFolder = 'ios/Runner/Assets.xcassets/AppIcon.appiconset/'; const String iosAssetFolder = 'ios/Runner/Assets.xcassets/'; const String iosConfigFile = 'ios/Runner.xcodeproj/project.pbxproj'; const String iosDefaultIconName = 'Icon-App'; +// web +const int kFaviconSize = 16; +String webDirPath = path.join('web'); +String webIconsDirPath = path.join(webDirPath, 'icons'); +String webManifestFilePath = path.join(webDirPath, 'manifest.json'); +// todo: support for other images formats +String webFaviconFilePath = path.join(webDirPath, 'favicon.png'); +String webIndexFilePath = path.join(webDirPath, 'index.html'); +String pubspecFilePath = path.join('pubspec.yaml'); + const String errorMissingImagePath = 'Missing "image_path" or "image_path_android" + "image_path_ios" within configuration'; -const String errorMissingPlatform = - 'No platform specified within config to generate icons for.'; -const String errorMissingRegularAndroid = - 'Adaptive icon config found but no regular Android config. ' +const String errorMissingPlatform = 'No platform specified within config to generate icons for.'; +const String errorMissingRegularAndroid = 'Adaptive icon config found but no regular Android config. ' 'Below API 26 the regular Android config is required'; -const String errorMissingMinSdk = - 'Cannot not find minSdk from android/app/build.gradle or android/local.properties' +const String errorMissingMinSdk = 'Cannot not find minSdk from android/app/build.gradle or android/local.properties' 'Specify minSdk in either android/app/build.gradle or android/local.properties'; -const String errorIncorrectIconName = - 'The icon name must contain only lowercase a-z, 0-9, or underscore: ' +const String errorIncorrectIconName = 'The icon name must contain only lowercase a-z, 0-9, or underscore: ' 'E.g. "ic_my_new_icon"'; String introMessage(String currentVersion) => ''' From e597498be4a1b12646edfbb6f5d4df66b7af8b93 Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Fri, 8 Jul 2022 19:28:13 +0530 Subject: [PATCH 06/24] feat(logger): added logger --- lib/logger.dart | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 lib/logger.dart diff --git a/lib/logger.dart b/lib/logger.dart new file mode 100644 index 0000000000..61ae2956fb --- /dev/null +++ b/lib/logger.dart @@ -0,0 +1,33 @@ +import 'package:cli_util/cli_logging.dart'; + +export 'package:cli_util/cli_logging.dart' show Progress; + +/// Flutter Launcher Icons Logger +class FLILogger { + late Logger _logger; + + /// Returns true if this is a verbose logger + final bool isVerbose; + + /// Gives access to internal logger + Logger get rawLogger => _logger; + + /// Creates a instance of [FLILogger]. + /// In case [isVerbose] is `true`, + /// it logs all the [verbose] logs to console + FLILogger(this.isVerbose) { + final ansi = Ansi(Ansi.terminalSupportsAnsi); + _logger = isVerbose ? Logger.verbose(ansi: ansi) : Logger.standard(ansi: ansi); + } + + /// Logs error messages + void error(Object? message) => _logger.stderr('⚠️' + message.toString()); + + /// prings to console if [isVerbose] is true + void verbose(Object? message) => _logger.trace(message.toString()); + + void info(Object? message) => _logger.stdout(message.toString()); + + /// Shows progress in console + Progress progress(String message) => _logger.progress(message); +} From 4a49ff8344db741bf55008207c44d5ed1521c836 Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Fri, 8 Jul 2022 21:49:11 +0530 Subject: [PATCH 07/24] feat(web): support for web icons --- lib/abs/icon_generator.dart | 75 ++++++++++++ lib/custom_exceptions.dart | 10 ++ lib/flutter_launcher_icons_config.dart | 145 ++++++++++++++++++++++ lib/flutter_launcher_icons_config.g.dart | 78 ++++++++++++ lib/main.dart | 84 +++++++++---- lib/utils.dart | 37 ++++++ lib/web/web_icon_generator.dart | 148 +++++++++++++++++++++++ lib/web/web_template.dart | 31 +++++ 8 files changed, 583 insertions(+), 25 deletions(-) create mode 100644 lib/abs/icon_generator.dart create mode 100644 lib/flutter_launcher_icons_config.dart create mode 100644 lib/flutter_launcher_icons_config.g.dart create mode 100644 lib/web/web_icon_generator.dart create mode 100644 lib/web/web_template.dart diff --git a/lib/abs/icon_generator.dart b/lib/abs/icon_generator.dart new file mode 100644 index 0000000000..dcefb01e5c --- /dev/null +++ b/lib/abs/icon_generator.dart @@ -0,0 +1,75 @@ +import 'dart:io'; + +import 'package:flutter_launcher_icons/flutter_launcher_icons_config.dart'; +import 'package:flutter_launcher_icons/logger.dart'; + +///A base class to generate icons +abstract class IconGenerator { + final IconGeneratorContext context; + final String platformName; + + IconGenerator(this.context, this.platformName); + + /// Creates icons for this platform. + void createIcons(); + + /// Should return `true` if this platform + /// has all the requirments to create icons. + /// This runs before to [createIcons] + bool validateRequirments(); +} + +/// Provides easy access to user arguments and configuration +class IconGeneratorContext { + /// Contains configuration from configuration file + final FlutterLauncherIconsConfig config; + final FLILogger logger; + final String? flavor; + + IconGeneratorContext({required this.config, this.flavor, required this.logger}); + + /// Shortcut for `config.webConfig` + WebConfig? get webConfig => config.webConfig; +} + +/// Generates Icon for given platforms +void generateIconsFor({ + required FlutterLauncherIconsConfig config, + required String? flavor, + required FLILogger logger, + required List Function(IconGeneratorContext context) platforms, +}) { + try { + final platformList = platforms(IconGeneratorContext(config: config, logger: logger, flavor: flavor)); + if (platformList.isEmpty) { + // ? maybe we can print help + logger.info('No platform provided'); + } + + for (final platform in platformList) { + final progress = logger.progress('Creating Icons for ${platform.platformName}'); + logger.verbose('Validating platform requirments for ${platform.platformName}'); + // in case a platform throws an exception it should not effect other platforms + try { + if (!platform.validateRequirments()) { + logger.error('Requirments failed for platform ${platform.platformName}. Skipped'); + progress.cancel(); + continue; + } + platform.createIcons(); + progress.finish(message: 'done', showTiming: true); + } catch (e) { + progress.cancel(); + continue; + } + } + } catch (e, st) { + // todo: better error handling + // stacktrace should only print when verbose is turned on + // else a normal help line + logger + ..error(e.toString()) + ..verbose(st); + exit(1); + } +} diff --git a/lib/custom_exceptions.dart b/lib/custom_exceptions.dart index 3a297b0cd2..4ade63ab05 100644 --- a/lib/custom_exceptions.dart +++ b/lib/custom_exceptions.dart @@ -39,3 +39,13 @@ class NoDecoderForImageFormatException implements Exception { return generateError(this, message); } } + +class FileNotFoundException implements Exception { + const FileNotFoundException(this.fileName); + + final String fileName; + @override + String toString() { + return generateError(this, '$fileName file not found'); + } +} diff --git a/lib/flutter_launcher_icons_config.dart b/lib/flutter_launcher_icons_config.dart new file mode 100644 index 0000000000..75e6417e50 --- /dev/null +++ b/lib/flutter_launcher_icons_config.dart @@ -0,0 +1,145 @@ +import 'dart:io'; + +import 'package:checked_yaml/checked_yaml.dart' as yaml; +import 'package:json_annotation/json_annotation.dart'; + +import 'constants.dart' as constants; +import 'custom_exceptions.dart'; +import 'utils.dart' as utils; + +part 'flutter_launcher_icons_config.g.dart'; + +@JsonSerializable( + anyMap: true, + checked: true, +) +class FlutterLauncherIconsConfig { + /// Generic imagepath + @JsonKey(name: 'image_path') + final String? imagePath; + + /// Returns true or path if android config is enabled + final dynamic android; // path or bool + + /// Returns true or path if ios config is enabled + final dynamic ios; // path or bool + + /// Image path specific to android + @JsonKey(name: 'image_path_android') + final String? imagePathAndroid; + + /// Image path specific to ios + @JsonKey(name: 'image_path_ios') + final String? imagePathIOS; + + /// 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; + + /// Web platform config + @JsonKey(name: 'web') + final WebConfig? webConfig; + + const FlutterLauncherIconsConfig({ + this.imagePath, + this.android = false, + this.ios = false, + this.imagePathAndroid, + this.imagePathIOS, + this.adaptiveIconForeground, + this.adaptiveIconBackground, + this.webConfig, + }); + + factory FlutterLauncherIconsConfig.fromJson(Map json) => _$FlutterLauncherIconsConfigFromJson(json); + + /// Loads flutter launcher icons configs from given [filePath] + static FlutterLauncherIconsConfig? loadConfigFromPath(String filePath) { + final configFile = File(filePath); + if (!configFile.existsSync()) { + return null; + } + final configContent = configFile.readAsStringSync(); + try { + return yaml.checkedYamlDecode( + configContent, + (json) => FlutterLauncherIconsConfig.fromJson(json!['flutter_icons']), + ); + } on yaml.ParsedYamlException catch (e) { + throw InvalidConfigException(e.formattedMessage); + } catch (e) { + rethrow; + } + } + + /// Loads flutter launcher icons config from `pubspec.yaml` file + static FlutterLauncherIconsConfig? loadConfigFromPubSpec() { + try { + final pubspecFile = File(constants.pubspecFilePath); + if (!pubspecFile.existsSync()) { + return null; + } + final pubspecContent = pubspecFile.readAsStringSync(); + return yaml.checkedYamlDecode( + pubspecContent, + (json) { + if (json!['flutter_icons'] == null) { + return null; + } + return FlutterLauncherIconsConfig.fromJson(json['flutter_icons']); + }, + ); + } on yaml.ParsedYamlException catch (e) { + throw InvalidConfigException(e.formattedMessage); + } catch (e) { + rethrow; + } + } + + static FlutterLauncherIconsConfig? loadConfigFromFlavor(String flavor) { + return FlutterLauncherIconsConfig.loadConfigFromPath(utils.flavorConfigFile(flavor)); + } + + Map toJson() => _$FlutterLauncherIconsConfigToJson(this); + + @override + String toString() => 'FlutterLauncherIconsConfig: ${toJson()}'; +} + +@JsonSerializable( + anyMap: true, + checked: true, +) +class WebConfig { + final bool generate; + + /// Image path for web + @JsonKey(name: 'image_path') + final String? imagePath; + + /// manifest.json's background_color + @JsonKey(name: 'background_color') + final String? backgroundColor; + + /// manifest.json's theme_color + @JsonKey(name: 'theme_color') + final String? themeColor; + + const WebConfig({ + this.generate = false, + this.imagePath, + this.backgroundColor, + this.themeColor, + }); + + factory WebConfig.fromJson(Map json) => _$WebConfigFromJson(json); + + Map toJson() => _$WebConfigToJson(this); + + @override + String toString() => 'WebConfig: ${toJson()}'; +} diff --git a/lib/flutter_launcher_icons_config.g.dart b/lib/flutter_launcher_icons_config.g.dart new file mode 100644 index 0000000000..c3d85f33a5 --- /dev/null +++ b/lib/flutter_launcher_icons_config.g.dart @@ -0,0 +1,78 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'flutter_launcher_icons_config.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +FlutterLauncherIconsConfig _$FlutterLauncherIconsConfigFromJson(Map json) => + $checkedCreate( + 'FlutterLauncherIconsConfig', + json, + ($checkedConvert) { + final val = FlutterLauncherIconsConfig( + imagePath: $checkedConvert('image_path', (v) => v as String?), + android: $checkedConvert('android', (v) => v ?? false), + ios: $checkedConvert('ios', (v) => v ?? false), + imagePathAndroid: + $checkedConvert('image_path_android', (v) => v as String?), + imagePathIOS: $checkedConvert('image_path_ios', (v) => v as String?), + adaptiveIconForeground: + $checkedConvert('adaptive_icon_foreground', (v) => v as String?), + adaptiveIconBackground: + $checkedConvert('adaptive_icon_background', (v) => v as String?), + webConfig: $checkedConvert( + 'web', (v) => v == null ? null : WebConfig.fromJson(v as Map)), + ); + return val; + }, + fieldKeyMap: const { + 'imagePath': 'image_path', + 'imagePathAndroid': 'image_path_android', + 'imagePathIOS': 'image_path_ios', + 'adaptiveIconForeground': 'adaptive_icon_foreground', + 'adaptiveIconBackground': 'adaptive_icon_background', + 'webConfig': 'web' + }, + ); + +Map _$FlutterLauncherIconsConfigToJson( + FlutterLauncherIconsConfig instance) => + { + 'image_path': instance.imagePath, + 'android': instance.android, + 'ios': instance.ios, + 'image_path_android': instance.imagePathAndroid, + 'image_path_ios': instance.imagePathIOS, + 'adaptive_icon_foreground': instance.adaptiveIconForeground, + 'adaptive_icon_background': instance.adaptiveIconBackground, + 'web': instance.webConfig, + }; + +WebConfig _$WebConfigFromJson(Map json) => $checkedCreate( + 'WebConfig', + json, + ($checkedConvert) { + final val = WebConfig( + generate: $checkedConvert('generate', (v) => v as bool? ?? false), + imagePath: $checkedConvert('image_path', (v) => v as String?), + backgroundColor: + $checkedConvert('background_color', (v) => v as String?), + themeColor: $checkedConvert('theme_color', (v) => v as String?), + ); + return val; + }, + fieldKeyMap: const { + 'imagePath': 'image_path', + 'backgroundColor': 'background_color', + 'themeColor': 'theme_color' + }, + ); + +Map _$WebConfigToJson(WebConfig instance) => { + 'generate': instance.generate, + 'image_path': instance.imagePath, + 'background_color': instance.backgroundColor, + 'theme_color': instance.themeColor, + }; diff --git a/lib/main.dart b/lib/main.dart index d7176af4c6..785bdb0abe 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,17 +1,25 @@ import 'dart:io'; import 'package:args/args.dart'; +import 'package:flutter_launcher_icons/abs/icon_generator.dart'; +import 'package:flutter_launcher_icons/flutter_launcher_icons_config.dart'; +import 'package:flutter_launcher_icons/logger.dart'; +import 'package:flutter_launcher_icons/web/web_icon_generator.dart'; import 'package:path/path.dart' as path; import 'package:yaml/yaml.dart'; import 'package:flutter_launcher_icons/android.dart' as android_launcher_icons; import 'package:flutter_launcher_icons/ios.dart' as ios_launcher_icons; import 'package:flutter_launcher_icons/constants.dart'; +import 'package:flutter_launcher_icons/constants.dart' as constants; import 'package:flutter_launcher_icons/custom_exceptions.dart'; const String fileOption = 'file'; const String helpFlag = 'help'; +const String verboseFlag = 'verbose'; const String defaultConfigFile = 'flutter_launcher_icons.yaml'; const String flavorConfigFilePattern = r'^flutter_launcher_icons-(.*).yaml$'; + +/// todo: remove this as it is moved to utils.dart String flavorConfigFile(String flavor) => 'flutter_launcher_icons-$flavor.yaml'; List getFlavors() { @@ -32,9 +40,13 @@ Future createIconsFromArguments(List arguments) async { final ArgParser parser = ArgParser(allowTrailingOptions: true); parser.addFlag(helpFlag, abbr: 'h', help: 'Usage help', negatable: false); // Make default null to differentiate when it is explicitly set - parser.addOption(fileOption, - abbr: 'f', help: 'Config file (default: $defaultConfigFile)'); + parser.addOption(fileOption, abbr: 'f', help: 'Path to config file', defaultsTo: defaultConfigFile); + parser.addFlag(verboseFlag, abbr: 'v', help: 'Verbose output', defaultsTo: false); final ArgResults argResults = parser.parse(arguments); + // creating logger based on -v flag + final logger = FLILogger(argResults[verboseFlag]); + + logger.verbose('Recieved args ${argResults.arguments}'); if (argResults[helpFlag]) { stdout.writeln('Generates icons for iOS and Android'); @@ -47,17 +59,23 @@ Future createIconsFromArguments(List arguments) async { final hasFlavors = flavors.isNotEmpty; // Load the config file - final Map? yamlConfig = - loadConfigFileFromArgResults(argResults, verbose: true); + final Map? yamlConfig = loadConfigFileFromArgResults(argResults, verbose: true); - if (yamlConfig == null) { - throw const NoConfigFoundException(); + // Load configs from given file(defaults to ./flutter_launcher_icons.yaml) or from ./pubspec.yaml + + final flutterLauncherIconsConfigs = FlutterLauncherIconsConfig.loadConfigFromPath(argResults[fileOption]) ?? + FlutterLauncherIconsConfig.loadConfigFromPubSpec(); + if (yamlConfig == null || flutterLauncherIconsConfigs == null) { + throw NoConfigFoundException( + 'No configuration found in $defaultConfigFile or in ${constants.pubspecFilePath}. ' + 'In case file exists in different directory use --file option', + ); } // Create icons if (!hasFlavors) { try { - createIconsFromConfig(yamlConfig); + await createIconsFromConfig(yamlConfig, flutterLauncherIconsConfigs, logger); print('\n✓ Successfully generated launcher icons'); } catch (e) { stderr.writeln('\n✕ Could not generate launcher icons'); @@ -68,9 +86,8 @@ Future createIconsFromArguments(List arguments) async { try { for (String flavor in flavors) { print('\nFlavor: $flavor'); - final Map yamlConfig = - loadConfigFile(flavorConfigFile(flavor), flavorConfigFile(flavor)); - await createIconsFromConfig(yamlConfig, flavor); + final Map yamlConfig = loadConfigFile(flavorConfigFile(flavor), flavorConfigFile(flavor)); + await createIconsFromConfig(yamlConfig, flutterLauncherIconsConfigs, logger, flavor); } print('\n✓ Successfully generated launcher icons for flavors'); } catch (e) { @@ -81,8 +98,12 @@ Future createIconsFromArguments(List arguments) async { } } -Future createIconsFromConfig(Map config, - [String? flavor]) async { +Future createIconsFromConfig( + Map config, + FlutterLauncherIconsConfig flutterConfigs, + FLILogger logger, [ + String? flavor, +]) async { if (!isImagePathInConfig(config)) { throw const InvalidConfigException(errorMissingImagePath); } @@ -95,9 +116,7 @@ Future createIconsFromConfig(Map config, if (minSdk == 0) { throw const InvalidConfigException(errorMissingMinSdk); } - if (minSdk < 26 && - hasAndroidAdaptiveConfig(config) && - !hasAndroidConfig(config)) { + if (minSdk < 26 && hasAndroidAdaptiveConfig(config) && !hasAndroidConfig(config)) { throw const InvalidConfigException(errorMissingRegularAndroid); } } @@ -111,10 +130,20 @@ Future createIconsFromConfig(Map config, if (isNeedingNewIOSIcon(config)) { ios_launcher_icons.createIcons(config, flavor); } + + // Generates Icons for given platform + generateIconsFor( + config: flutterConfigs, + logger: logger, + flavor: flavor, + platforms: (context) => [ + WebIconGenerator(context), + // todo: add other platforms + ], + ); } -Map? loadConfigFileFromArgResults(ArgResults argResults, - {bool verbose = false}) { +Map? loadConfigFileFromArgResults(ArgResults argResults, {bool verbose = false}) { final String? configFile = argResults[fileOption]; final String? fileOptionResult = argResults[fileOption]; @@ -176,13 +205,12 @@ Map loadConfigFile(String path, String? fileOptionResult) { bool isImagePathInConfig(Map flutterIconsConfig) { return flutterIconsConfig.containsKey('image_path') || - (flutterIconsConfig.containsKey('image_path_android') && - flutterIconsConfig.containsKey('image_path_ios')); + (flutterIconsConfig.containsKey('image_path_android') && flutterIconsConfig.containsKey('image_path_ios')) || + flutterIconsConfig.containsKey('web'); } bool hasPlatformConfig(Map flutterIconsConfig) { - return hasAndroidConfig(flutterIconsConfig) || - hasIOSConfig(flutterIconsConfig); + return hasAndroidConfig(flutterIconsConfig) || hasIOSConfig(flutterIconsConfig) || hasWebConfig(flutterIconsConfig); } bool hasAndroidConfig(Map flutterLauncherIcons) { @@ -190,8 +218,7 @@ bool hasAndroidConfig(Map flutterLauncherIcons) { } bool isNeedingNewAndroidIcon(Map flutterLauncherIconsConfig) { - return hasAndroidConfig(flutterLauncherIconsConfig) && - flutterLauncherIconsConfig['android'] != false; + return hasAndroidConfig(flutterLauncherIconsConfig) && flutterLauncherIconsConfig['android'] != false; } bool hasAndroidAdaptiveConfig(Map flutterLauncherIconsConfig) { @@ -205,6 +232,13 @@ bool hasIOSConfig(Map flutterLauncherIconsConfig) { } bool isNeedingNewIOSIcon(Map flutterLauncherIconsConfig) { - return hasIOSConfig(flutterLauncherIconsConfig) && - flutterLauncherIconsConfig['ios'] != false; + return hasIOSConfig(flutterLauncherIconsConfig) && flutterLauncherIconsConfig['ios'] != false; +} + +bool hasWebConfig(Map flutterLauncherIconsConfig) { + return flutterLauncherIconsConfig.containsKey('web'); +} + +bool isNeddingNewWebIcons(Map flutterLauncherIconsConfig) { + return hasWebConfig(flutterLauncherIconsConfig) && flutterLauncherIconsConfig['web'] != false; } diff --git a/lib/utils.dart b/lib/utils.dart index 71491bfad5..d87e9d0601 100644 --- a/lib/utils.dart +++ b/lib/utils.dart @@ -1,6 +1,8 @@ +import 'dart:convert'; import 'dart:io'; import 'package:image/image.dart'; +import 'package:path/path.dart' as path; import 'custom_exceptions.dart'; @@ -38,3 +40,38 @@ Image? decodeImageFile(String filePath) { } return image; } + +/// Creates [File] in the given [filePath] if not exists +File createFileIfNotExist(String filePath) { + final file = File(path.joinAll(path.split(filePath))); + if (!file.existsSync()) { + file.createSync(recursive: true); + } + return file; +} + +/// Creates [Directory] in the given [dirPath] if not exists +Directory createDirIfNotExist(String dirPath) { + final dir = Directory(path.joinAll(path.split(dirPath))); + if (!dir.existsSync()) { + dir.createSync(recursive: true); + } + return dir; +} + +/// Returns a prettified json string +String prettifyJsonEncode(Object? map) => JsonEncoder.withIndent(' ' * 2).convert(map); + +/// Check if give [File] or [Directory] exists at the give [paths], +/// if not returns the failed [FileSystemEntity] path +String? areFSEntiesExist(List paths) { + for (final path in paths) { + final fsType = FileSystemEntity.typeSync(path); + if (![FileSystemEntityType.directory, FileSystemEntityType.file].contains(fsType)) { + return path; + } + } + return null; +} + +String flavorConfigFile(String flavor) => 'flutter_launcher_icons-$flavor.yaml'; diff --git a/lib/web/web_icon_generator.dart b/lib/web/web_icon_generator.dart new file mode 100644 index 0000000000..6346c418e9 --- /dev/null +++ b/lib/web/web_icon_generator.dart @@ -0,0 +1,148 @@ +import 'dart:convert'; + +import 'package:image/image.dart'; +import 'package:path/path.dart' as path; + +import '../abs/icon_generator.dart'; +import '../constants.dart' as constants; +import '../custom_exceptions.dart'; +import '../utils.dart' as utils; +import 'web_template.dart'; + +final metaTagsTemplate = ( + String appleMobileWebAppTitle, + String appleMobileWebAppStatusBarStyle, { + bool shouldInsertFLIString = false, +}) => + ''' + + + + + + + + + + + + + +'''; + +/// Generates Web icons for flutter +class WebIconGenerator extends IconGenerator { + static const _webIconSizeTemplates = [ + WebIconTemplate(size: 192), + WebIconTemplate(size: 512), + WebIconTemplate(size: 192, maskable: true), + WebIconTemplate(size: 512, maskable: true), + ]; + + WebIconGenerator(IconGeneratorContext context) : super(context, 'Web'); + + @override + void createIcons() { + final imgFilePath = context.webConfig!.imagePath ?? context.config.imagePath!; + + context.logger.verbose('Decoding and loading image file at $imgFilePath...'); + final imgFile = utils.decodeImageFile(imgFilePath); + if (imgFile == null) { + context.logger.error('Image File not found at give path $imgFilePath...'); + throw FileNotFoundException(imgFilePath); + } + + // generate favicon in web/favicon.png + context.logger.verbose('Generating favicon from $imgFilePath...'); + _generateFavicon(imgFile); + + // generate icons in web/icons/ + context.logger.verbose('Generating icons from $imgFilePath...'); + _generateIcons(imgFile); + + // update manifest.json in web/mainfest.json + context.logger.verbose('Updating ${constants.webManifestFilePath}...'); + _updateManifestFile(); + + // todo: update index.html in web/index.html + // as we are using flutter default config we no need + // to update index.html for now + // _updateIndexFile(); + } + + @override + bool validateRequirments() { + // check if web config exists + context.logger.verbose('Checking webconfig...'); + final webConfig = context.webConfig; + if (webConfig == null || !webConfig.generate) { + context.logger.verbose('Web config is not provided or generate is false. Skipped...'); + return false; + } + if (webConfig.imagePath == null && context.config.imagePath == null) { + context.logger.verbose('Invalid config. Either provide web.imagePath or imagePath'); + return false; + } + + // verify web platform related files and directories exists + final entitesToCheck = [ + constants.webDirPath, + constants.webManifestFilePath, + constants.webIndexFilePath, + ]; + + // web platform related files must exist to continue + final failedEntityPath = utils.areFSEntiesExist(entitesToCheck); + if (failedEntityPath != null) { + context.logger.error('$failedEntityPath this file or folder is required to generate web icons'); + } + + return true; + } + + void _generateFavicon(Image image) { + final favIcon = utils.createResizedImage(constants.kFaviconSize, image); + final favIconFile = utils.createFileIfNotExist(constants.webFaviconFilePath); + favIconFile.writeAsBytesSync(encodePng(favIcon)); + } + + void _generateIcons(Image image) { + final iconsDir = utils.createDirIfNotExist(constants.webIconsDirPath); + // generate icons + for (final template in _webIconSizeTemplates) { + final resizedImg = utils.createResizedImage(template.size, image); + final iconFile = utils.createFileIfNotExist(path.join(iconsDir.path, template.iconFile)); + iconFile.writeAsBytesSync(encodePng(resizedImg)); + } + } + + // void _updateIndexFile() { + // todo + // final indexFile = File(constants.webIndexFilePath); + // if (!indexFile.existsSync()) { + // throw FileNotFoundException(constants.webFaviconFilePath); + // } + // } + + void _updateManifestFile() { + final manifestFile = utils.createFileIfNotExist(constants.webManifestFilePath); + final manifestConfig = jsonDecode(manifestFile.readAsStringSync()) as Map; + + // update background_color + if (context.webConfig?.backgroundColor != null) { + manifestConfig['background_color'] = context.webConfig?.backgroundColor; + } + + // update theme_color + if (context.webConfig?.themeColor != null) { + manifestConfig['theme_color'] = context.webConfig?.themeColor; + } + + // replace existing icons to eliminate conflicts + manifestConfig + ..remove('icons') + ..['icons'] = _webIconSizeTemplates.map>((e) => e.iconManifest).toList(); + + manifestFile.writeAsStringSync(utils.prettifyJsonEncode(manifestConfig)); + } +} diff --git a/lib/web/web_template.dart b/lib/web/web_template.dart new file mode 100644 index 0000000000..4d4b8ab1bd --- /dev/null +++ b/lib/web/web_template.dart @@ -0,0 +1,31 @@ +class WebIconTemplate { + const WebIconTemplate({ + required this.size, + this.maskable = false, + }); + + final int size; + final bool maskable; + + /// Icon file name + String get iconFile => 'Icon${maskable ? '-maskable' : ''}-$size.png'; + + /// Icon config for manifest.json + /// + /// ```json + /// { + /// "src": "icons/Icon-maskable-192.png", + /// "sizes": "192x192", + /// "type": "image/png", + /// "purpose": "maskable" + /// }, + /// ``` + Map get iconManifest { + return { + 'src': 'icons/$iconFile', + 'sizes': '${size}x$size', + 'type': 'image/png', + if (maskable) 'purpose': 'maskable', + }; + } +} From b2b89823e91cea5c014dda30829d88c7612a191b Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Sun, 10 Jul 2022 10:43:42 +0530 Subject: [PATCH 08/24] feat(cli-option): added --prefix option --prefix Generates icons in the given path. Used for testing in virtual directory. Only supports web platform for now --- lib/abs/icon_generator.dart | 16 +++++++++++-- lib/flutter_launcher_icons_config.dart | 13 ++++++----- lib/main.dart | 31 ++++++++++++++++++-------- lib/web/web_icon_generator.dart | 18 +++++++-------- 4 files changed, 52 insertions(+), 26 deletions(-) diff --git a/lib/abs/icon_generator.dart b/lib/abs/icon_generator.dart index dcefb01e5c..159d0528ed 100644 --- a/lib/abs/icon_generator.dart +++ b/lib/abs/icon_generator.dart @@ -25,8 +25,14 @@ class IconGeneratorContext { final FlutterLauncherIconsConfig config; final FLILogger logger; final String? flavor; + final String prefixPath; - IconGeneratorContext({required this.config, this.flavor, required this.logger}); + IconGeneratorContext({ + required this.config, + this.flavor, + required this.prefixPath, + required this.logger, + }); /// Shortcut for `config.webConfig` WebConfig? get webConfig => config.webConfig; @@ -36,11 +42,17 @@ class IconGeneratorContext { void generateIconsFor({ required FlutterLauncherIconsConfig config, required String? flavor, + required String prefixPath, required FLILogger logger, required List Function(IconGeneratorContext context) platforms, }) { try { - final platformList = platforms(IconGeneratorContext(config: config, logger: logger, flavor: flavor)); + final platformList = platforms(IconGeneratorContext( + config: config, + logger: logger, + prefixPath: prefixPath, + flavor: flavor, + )); if (platformList.isEmpty) { // ? maybe we can print help logger.info('No platform provided'); diff --git a/lib/flutter_launcher_icons_config.dart b/lib/flutter_launcher_icons_config.dart index 75e6417e50..b758795de9 100644 --- a/lib/flutter_launcher_icons_config.dart +++ b/lib/flutter_launcher_icons_config.dart @@ -2,6 +2,7 @@ import 'dart:io'; import 'package:checked_yaml/checked_yaml.dart' as yaml; import 'package:json_annotation/json_annotation.dart'; +import 'package:path/path.dart' as path; import 'constants.dart' as constants; import 'custom_exceptions.dart'; @@ -58,8 +59,8 @@ class FlutterLauncherIconsConfig { factory FlutterLauncherIconsConfig.fromJson(Map json) => _$FlutterLauncherIconsConfigFromJson(json); /// Loads flutter launcher icons configs from given [filePath] - static FlutterLauncherIconsConfig? loadConfigFromPath(String filePath) { - final configFile = File(filePath); + static FlutterLauncherIconsConfig? loadConfigFromPath(String filePath, String prefixPath) { + final configFile = File(path.join(prefixPath, filePath)); if (!configFile.existsSync()) { return null; } @@ -77,9 +78,9 @@ class FlutterLauncherIconsConfig { } /// Loads flutter launcher icons config from `pubspec.yaml` file - static FlutterLauncherIconsConfig? loadConfigFromPubSpec() { + static FlutterLauncherIconsConfig? loadConfigFromPubSpec(String prefix) { try { - final pubspecFile = File(constants.pubspecFilePath); + final pubspecFile = File(path.join(prefix, constants.pubspecFilePath)); if (!pubspecFile.existsSync()) { return null; } @@ -100,8 +101,8 @@ class FlutterLauncherIconsConfig { } } - static FlutterLauncherIconsConfig? loadConfigFromFlavor(String flavor) { - return FlutterLauncherIconsConfig.loadConfigFromPath(utils.flavorConfigFile(flavor)); + static FlutterLauncherIconsConfig? loadConfigFromFlavor(String flavor, String prefixPath) { + return FlutterLauncherIconsConfig.loadConfigFromPath(utils.flavorConfigFile(flavor), prefixPath); } Map toJson() => _$FlutterLauncherIconsConfigToJson(this); diff --git a/lib/main.dart b/lib/main.dart index 785bdb0abe..01f82fbc72 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,6 +16,7 @@ import 'package:flutter_launcher_icons/custom_exceptions.dart'; const String fileOption = 'file'; const String helpFlag = 'help'; const String verboseFlag = 'verbose'; +const String prefixOption = 'prefix'; const String defaultConfigFile = 'flutter_launcher_icons.yaml'; const String flavorConfigFilePattern = r'^flutter_launcher_icons-(.*).yaml$'; @@ -38,10 +39,18 @@ List getFlavors() { Future createIconsFromArguments(List arguments) async { final ArgParser parser = ArgParser(allowTrailingOptions: true); - parser.addFlag(helpFlag, abbr: 'h', help: 'Usage help', negatable: false); - // Make default null to differentiate when it is explicitly set - parser.addOption(fileOption, abbr: 'f', help: 'Path to config file', defaultsTo: defaultConfigFile); - parser.addFlag(verboseFlag, abbr: 'v', help: 'Verbose output', defaultsTo: false); + parser + ..addFlag(helpFlag, abbr: 'h', help: 'Usage help', negatable: false) + // Make default null to differentiate when it is explicitly set + ..addOption(fileOption, abbr: 'f', help: 'Path to config file', defaultsTo: defaultConfigFile) + ..addFlag(verboseFlag, abbr: 'v', help: 'Verbose output', defaultsTo: false) + ..addOption( + prefixOption, + abbr: 'p', + help: 'Generates config in the given path. Only Supports web platform', + defaultsTo: '.', + ); + final ArgResults argResults = parser.parse(arguments); // creating logger based on -v flag final logger = FLILogger(argResults[verboseFlag]); @@ -60,11 +69,13 @@ Future createIconsFromArguments(List arguments) async { // Load the config file final Map? yamlConfig = loadConfigFileFromArgResults(argResults, verbose: true); + final String prefixPath = argResults[prefixOption]; // Load configs from given file(defaults to ./flutter_launcher_icons.yaml) or from ./pubspec.yaml - final flutterLauncherIconsConfigs = FlutterLauncherIconsConfig.loadConfigFromPath(argResults[fileOption]) ?? - FlutterLauncherIconsConfig.loadConfigFromPubSpec(); + final flutterLauncherIconsConfigs = + FlutterLauncherIconsConfig.loadConfigFromPath(argResults[fileOption], prefixPath) ?? + FlutterLauncherIconsConfig.loadConfigFromPubSpec(prefixPath); if (yamlConfig == null || flutterLauncherIconsConfigs == null) { throw NoConfigFoundException( 'No configuration found in $defaultConfigFile or in ${constants.pubspecFilePath}. ' @@ -75,7 +86,7 @@ Future createIconsFromArguments(List arguments) async { // Create icons if (!hasFlavors) { try { - await createIconsFromConfig(yamlConfig, flutterLauncherIconsConfigs, logger); + await createIconsFromConfig(yamlConfig, flutterLauncherIconsConfigs, logger, prefixPath); print('\n✓ Successfully generated launcher icons'); } catch (e) { stderr.writeln('\n✕ Could not generate launcher icons'); @@ -87,7 +98,7 @@ Future createIconsFromArguments(List arguments) async { for (String flavor in flavors) { print('\nFlavor: $flavor'); final Map yamlConfig = loadConfigFile(flavorConfigFile(flavor), flavorConfigFile(flavor)); - await createIconsFromConfig(yamlConfig, flutterLauncherIconsConfigs, logger, flavor); + await createIconsFromConfig(yamlConfig, flutterLauncherIconsConfigs, logger, prefixPath, flavor); } print('\n✓ Successfully generated launcher icons for flavors'); } catch (e) { @@ -101,7 +112,8 @@ Future createIconsFromArguments(List arguments) async { Future createIconsFromConfig( Map config, FlutterLauncherIconsConfig flutterConfigs, - FLILogger logger, [ + FLILogger logger, + String prefixPath, [ String? flavor, ]) async { if (!isImagePathInConfig(config)) { @@ -135,6 +147,7 @@ Future createIconsFromConfig( generateIconsFor( config: flutterConfigs, logger: logger, + prefixPath: prefixPath, flavor: flavor, platforms: (context) => [ WebIconGenerator(context), diff --git a/lib/web/web_icon_generator.dart b/lib/web/web_icon_generator.dart index 6346c418e9..0324d151e3 100644 --- a/lib/web/web_icon_generator.dart +++ b/lib/web/web_icon_generator.dart @@ -43,7 +43,7 @@ class WebIconGenerator extends IconGenerator { @override void createIcons() { - final imgFilePath = context.webConfig!.imagePath ?? context.config.imagePath!; + final imgFilePath = path.join(context.prefixPath, context.webConfig!.imagePath ?? context.config.imagePath!); context.logger.verbose('Decoding and loading image file at $imgFilePath...'); final imgFile = utils.decodeImageFile(imgFilePath); @@ -61,7 +61,7 @@ class WebIconGenerator extends IconGenerator { _generateIcons(imgFile); // update manifest.json in web/mainfest.json - context.logger.verbose('Updating ${constants.webManifestFilePath}...'); + context.logger.verbose('Updating ${path.join(context.prefixPath, constants.webManifestFilePath)}...'); _updateManifestFile(); // todo: update index.html in web/index.html @@ -86,9 +86,9 @@ class WebIconGenerator extends IconGenerator { // verify web platform related files and directories exists final entitesToCheck = [ - constants.webDirPath, - constants.webManifestFilePath, - constants.webIndexFilePath, + path.join(context.prefixPath, constants.webDirPath), + path.join(context.prefixPath, constants.webManifestFilePath), + path.join(context.prefixPath, constants.webIndexFilePath), ]; // web platform related files must exist to continue @@ -102,16 +102,16 @@ class WebIconGenerator extends IconGenerator { void _generateFavicon(Image image) { final favIcon = utils.createResizedImage(constants.kFaviconSize, image); - final favIconFile = utils.createFileIfNotExist(constants.webFaviconFilePath); + final favIconFile = utils.createFileIfNotExist(path.join(context.prefixPath, constants.webFaviconFilePath)); favIconFile.writeAsBytesSync(encodePng(favIcon)); } void _generateIcons(Image image) { - final iconsDir = utils.createDirIfNotExist(constants.webIconsDirPath); + final iconsDir = utils.createDirIfNotExist(path.join(context.prefixPath, constants.webIconsDirPath)); // generate icons for (final template in _webIconSizeTemplates) { final resizedImg = utils.createResizedImage(template.size, image); - final iconFile = utils.createFileIfNotExist(path.join(iconsDir.path, template.iconFile)); + final iconFile = utils.createFileIfNotExist(path.join(context.prefixPath, iconsDir.path, template.iconFile)); iconFile.writeAsBytesSync(encodePng(resizedImg)); } } @@ -125,7 +125,7 @@ class WebIconGenerator extends IconGenerator { // } void _updateManifestFile() { - final manifestFile = utils.createFileIfNotExist(constants.webManifestFilePath); + final manifestFile = utils.createFileIfNotExist(path.join(context.prefixPath, constants.webManifestFilePath)); final manifestConfig = jsonDecode(manifestFile.readAsStringSync()) as Map; // update background_color From 9a1f8281c97b8b68dacda3a9984fe614f73d5b85 Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Sun, 10 Jul 2022 12:09:25 +0530 Subject: [PATCH 09/24] fix(config): type error when invalid config is passed --- lib/flutter_launcher_icons_config.dart | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/flutter_launcher_icons_config.dart b/lib/flutter_launcher_icons_config.dart index b758795de9..59df952b0f 100644 --- a/lib/flutter_launcher_icons_config.dart +++ b/lib/flutter_launcher_icons_config.dart @@ -66,9 +66,15 @@ class FlutterLauncherIconsConfig { } final configContent = configFile.readAsStringSync(); try { - return yaml.checkedYamlDecode( + return yaml.checkedYamlDecode( configContent, - (json) => FlutterLauncherIconsConfig.fromJson(json!['flutter_icons']), + (json) { + // todo: add support for new scheme https://github.com/fluttercommunity/flutter_launcher_icons/issues/373 + return json == null || json['flutter_icons'] == null + ? null + : FlutterLauncherIconsConfig.fromJson(json['flutter_icons']); + }, + allowNull: true, ); } on yaml.ParsedYamlException catch (e) { throw InvalidConfigException(e.formattedMessage); @@ -88,11 +94,12 @@ class FlutterLauncherIconsConfig { return yaml.checkedYamlDecode( pubspecContent, (json) { - if (json!['flutter_icons'] == null) { - return null; - } - return FlutterLauncherIconsConfig.fromJson(json['flutter_icons']); + // todo: add support for new scheme https://github.com/fluttercommunity/flutter_launcher_icons/issues/373 + return json == null || json['flutter_icons'] == null + ? null + : FlutterLauncherIconsConfig.fromJson(json['flutter_icons']); }, + allowNull: true, ); } on yaml.ParsedYamlException catch (e) { throw InvalidConfigException(e.formattedMessage); From 542e7fe3a0af1b915e9ba468859d440e5f485276 Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Sun, 10 Jul 2022 12:53:05 +0530 Subject: [PATCH 10/24] vendor(deps): added test dependencies --- pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pubspec.yaml b/pubspec.yaml index 6b2e7cf825..2d8bb60686 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -24,3 +24,4 @@ dev_dependencies: build_version: ^2.1.1 json_serializable: ^6.2.0 test: ^1.21.1 + test_descriptor: ^2.0.0 From d3c061d572cfff14cb2d18462b04bc98d097ae84 Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Sun, 10 Jul 2022 12:54:04 +0530 Subject: [PATCH 11/24] test(fli_config): added tests for fli config --- test/flutter_launcher_icons_config_test.dart | 152 +++++++++++ test/templates.dart | 249 +++++++++++++++++++ 2 files changed, 401 insertions(+) create mode 100644 test/flutter_launcher_icons_config_test.dart create mode 100644 test/templates.dart diff --git a/test/flutter_launcher_icons_config_test.dart b/test/flutter_launcher_icons_config_test.dart new file mode 100644 index 0000000000..a73e414410 --- /dev/null +++ b/test/flutter_launcher_icons_config_test.dart @@ -0,0 +1,152 @@ +import 'package:flutter_launcher_icons/custom_exceptions.dart'; +import 'package:flutter_launcher_icons/flutter_launcher_icons_config.dart'; +import 'package:path/path.dart' as path; +import 'package:test/test.dart'; +import 'package:test_descriptor/test_descriptor.dart' as d; + +import './templates.dart' as templates; + +void main() { + group('FlutterLauncherIconsConfig', () { + late String prefixPath; + setUpAll(() { + prefixPath = path.join(d.sandbox, 'fli_test'); + }); + group('#loadConfigFromPath', () { + setUpAll(() async { + await d.dir('fli_test', [ + d.file('flutter_launcher_icons.yaml', templates.fliConfigTemplate), + d.file('invalid_fli_config.yaml', templates.invlaidfliConfigTemplate), + ]).create(); + }); + test('should return valid configs', () { + final configs = FlutterLauncherIconsConfig.loadConfigFromPath('flutter_launcher_icons.yaml', prefixPath); + expect(configs, isNotNull); + // android configs + expect(configs!.android, isTrue); + expect(configs.imagePath, isNotNull); + expect(configs.imagePathAndroid, isNotNull); + expect(configs.adaptiveIconBackground, isNotNull); + expect(configs.adaptiveIconForeground, isNotNull); + // ios configs + expect(configs.ios, isTrue); + expect(configs.imagePathIOS, isNotNull); + // web configs + expect(configs.webConfig, isNotNull); + expect(configs.webConfig!.generate, isTrue); + expect(configs.webConfig!.backgroundColor, isNotNull); + expect(configs.webConfig!.imagePath, isNotNull); + expect(configs.webConfig!.themeColor, isNotNull); + expect( + configs.webConfig!.toJson(), + equals({ + 'generate': true, + 'image_path': 'app_icon.png', + 'background_color': '#0175C2', + 'theme_color': '#0175C2', + }), + ); + }); + + test('should return null when invalid filePath is given', () { + final configs = FlutterLauncherIconsConfig.loadConfigFromPath('file_that_dont_exist.yaml', prefixPath); + expect(configs, isNull); + }); + + test('should throw InvalidConfigException when config is invalid', () { + expect( + () => FlutterLauncherIconsConfig.loadConfigFromPath('invalid_fli_config.yaml', prefixPath), + throwsA(isA()), + ); + }); + }); + + group('#loadConfigFromPubSpec', () { + setUpAll(() async { + await d.dir('fli_test', [ + d.file('pubspec.yaml', templates.pubspecTemplate), + d.file('flutter_launcher_icons.yaml', templates.fliConfigTemplate), + d.file('invalid_fli_config.yaml', templates.invlaidfliConfigTemplate), + ]).create(); + }); + test('should return valid configs', () { + final configs = FlutterLauncherIconsConfig.loadConfigFromPubSpec(prefixPath); + expect(configs, isNotNull); + // android configs + expect(configs!.android, isTrue); + expect(configs.imagePath, isNotNull); + expect(configs.imagePathAndroid, isNotNull); + expect(configs.adaptiveIconBackground, isNotNull); + expect(configs.adaptiveIconForeground, isNotNull); + // ios configs + expect(configs.ios, isTrue); + expect(configs.imagePathIOS, isNotNull); + // web configs + expect(configs.webConfig, isNotNull); + expect(configs.webConfig!.generate, isTrue); + expect(configs.webConfig!.backgroundColor, isNotNull); + expect(configs.webConfig!.imagePath, isNotNull); + expect(configs.webConfig!.themeColor, isNotNull); + expect( + configs.webConfig!.toJson(), + equals({ + 'generate': true, + 'image_path': 'app_icon.png', + 'background_color': '#0175C2', + 'theme_color': '#0175C2', + }), + ); + }); + + group('should throw', () { + setUp(() async { + await d.dir('fli_test', [ + d.file('pubspec.yaml', templates.invalidPubspecTemplate), + d.file('flutter_launcher_icons.yaml', templates.fliConfigTemplate), + d.file('invalid_fli_config.yaml', templates.invlaidfliConfigTemplate), + ]).create(); + }); + test('InvalidConfigException when config is invalid', () { + expect( + () => FlutterLauncherIconsConfig.loadConfigFromPubSpec(prefixPath), + throwsA(isA()), + ); + }); + }); + }); + group('#loadConfigFromFlavor', () { + setUpAll(() async { + await d.dir('fli_test', [ + d.file('flutter_launcher_icons-development.yaml', templates.flavorFLIConfigTemplate), + ]).create(); + }); + test('should return valid config', () { + final configs = FlutterLauncherIconsConfig.loadConfigFromFlavor('development', prefixPath); + expect(configs, isNotNull); + expect(configs!.android, isTrue); + expect(configs.imagePath, isNotNull); + expect(configs.imagePathAndroid, isNotNull); + expect(configs.adaptiveIconBackground, isNotNull); + expect(configs.adaptiveIconForeground, isNotNull); + // ios configs + expect(configs.ios, isTrue); + expect(configs.imagePathIOS, isNotNull); + // web configs + expect(configs.webConfig, isNotNull); + expect(configs.webConfig!.generate, isTrue); + expect(configs.webConfig!.backgroundColor, isNotNull); + expect(configs.webConfig!.imagePath, isNotNull); + expect(configs.webConfig!.themeColor, isNotNull); + expect( + configs.webConfig!.toJson(), + equals({ + 'generate': true, + 'image_path': 'app_icon.png', + 'background_color': '#0175C2', + 'theme_color': '#0175C2', + }), + ); + }); + }); + }); +} diff --git a/test/templates.dart b/test/templates.dart new file mode 100644 index 0000000000..c50cde7875 --- /dev/null +++ b/test/templates.dart @@ -0,0 +1,249 @@ +const fliConfigTemplate = r''' +flutter_icons: + android: true + ios: true + image_path: "assets/images/icon-128x128.png" + image_path_android: "assets/images/icon-710x599-android.png" + image_path_ios: "assets/images/icon-1024x1024.png" + adaptive_icon_background: "assets/images/christmas-background.png" + adaptive_icon_foreground: "assets/images/icon-foreground-432x432.png" + web: + generate: true + image_path: "app_icon.png" # filepath + background_color: "#0175C2" # hex_color + theme_color: "#0175C2" # hex_color + apple_mobile_web_app_title: "demo" + apple_mobile_web_app_status_bar_style: "hex_color" +'''; + +const flavorFLIConfigTemplate = fliConfigTemplate; + +const invlaidfliConfigTemplate = r''' +# flutter_icons +android: true +ios: true +image_path: "assets/images/icon-128x128.png" + ad +image_path_android: "assets/images/icon-710x599-android.png" +image_path_ios: "assets/images/icon-1024x1024.png" +adaptive_icon_background: "assets/images/christmas-background.png" +adaptive_icon_foreground: "assets/images/icon-foreground-432x432.png" +web: + generate: true + image_path: "app_icon.png" # filepath + background_color: "#0175C2" # hex_color + theme_color: "#0175C2" # hex_color + apple_mobile_web_app_title: "demo" + apple_mobile_web_app_status_bar_style: "hex_color" +'''; + +const pubspecTemplate = r''' +name: demo +description: A new Flutter project. +publish_to: 'none' +version: 1.0.0+1 + +environment: + sdk: '>=2.18.0-44.1.beta <3.0.0' + +dependencies: + flutter: + sdk: flutter + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + flutter_launcher_icons: + path: C:/Users/asus/projects/flutter_launcher_icons + +flutter: + uses-material-design: true + assets: + - images/a_dot_burr.jpeg + - images/a_dot_ham.jpeg + fonts: + - family: Schyler + fonts: + - asset: fonts/Schyler-Regular.ttf + - asset: fonts/Schyler-Italic.ttf + style: italic + - family: Trajan Pro + fonts: + - asset: fonts/TrajanPro.ttf + - asset: fonts/TrajanPro_Bold.ttf + weight: 700 + +flutter_icons: + android: true + ios: true + image_path: "assets/images/icon-128x128.png" + image_path_android: "assets/images/icon-710x599-android.png" + image_path_ios: "assets/images/icon-1024x1024.png" + adaptive_icon_background: "assets/images/christmas-background.png" + adaptive_icon_foreground: "assets/images/icon-foreground-432x432.png" + web: + generate: true + image_path: "app_icon.png" # filepath + background_color: "#0175C2" # hex_color + theme_color: "#0175C2" # hex_color + apple_mobile_web_app_title: "demo" + apple_mobile_web_app_status_bar_style: "hex_color" +'''; + +const invalidPubspecTemplate = r''' +name: demo +description: A new Flutter project. +publish_to: 'none' +version: 1.0.0+1 + +environment: + sdk: '>=2.18.0-44.1.beta <3.0.0' + +dependencies: + flutter: + sdk: flutter + cupertino_icons: ^1.0.2 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^2.0.0 + flutter_launcher_icons: + path: C:/Users/asus/projects/flutter_launcher_icons + +flutter: + uses-material-design: true + assets: + - images/a_dot_burr.jpeg + - images/a_dot_ham.jpeg + fonts: + - family: Schyler + fonts: + - asset: fonts/Schyler-Regular.ttf + - asset: fonts/Schyler-Italic.ttf + style: italic + - family: Trajan Pro + fonts: + - asset: fonts/TrajanPro.ttf + - asset: fonts/TrajanPro_Bold.ttf + weight: 700 + +flutter_icons: + android: true + invalid_indented_key_key + ios: true + image_path: "assets/images/icon-128x128.png" + image_path_android: "assets/images/icon-710x599-android.png" + image_path_ios: "assets/images/icon-1024x1024.png" + adaptive_icon_background: "assets/images/christmas-background.png" + adaptive_icon_foreground: "assets/images/icon-foreground-432x432.png" + web: + generate: true + image_path: "app_icon.png" # filepath + background_color: "#0175C2" # hex_color + theme_color: "#0175C2" # hex_color + apple_mobile_web_app_title: "demo" + apple_mobile_web_app_status_bar_style: "hex_color" +'''; + +const webManifestTemplate = r''' +{ + "name": "demo", + "short_name": "demo", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} +'''; + +const webIndexTemplate = r''' + + + + + + + + + + + + + + + + + + + + demo + + + + + + + + + + + +'''; From c2aaaa81e861183e0eb6ab68aa59fa95100da08b Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Sun, 10 Jul 2022 14:39:17 +0530 Subject: [PATCH 12/24] test(utils): added tests for utils --- test/utils_test.dart | 133 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 test/utils_test.dart diff --git a/test/utils_test.dart b/test/utils_test.dart new file mode 100644 index 0000000000..1ff60f5e17 --- /dev/null +++ b/test/utils_test.dart @@ -0,0 +1,133 @@ +import 'package:flutter_launcher_icons/utils.dart' as utils; +import 'package:path/path.dart' as path; +import 'package:test/test.dart'; +import 'package:test_descriptor/test_descriptor.dart' as d; + +void main() { + group('#areFSEntitesExist', () { + late String prefixPath; + setUp(() async { + prefixPath = path.join(d.sandbox, 'fli_test'); + await d.dir('fli_test', [ + d.file('file1.txt', 'contents1'), + d.dir('dir1'), + ]).create(); + }); + + test('should return null when entites exists', () async { + expect( + utils.areFSEntiesExist([ + path.join(prefixPath, 'file1.txt'), + path.join(prefixPath, 'dir1'), + ]), + isNull, + ); + }); + + test('should return the file path that does not exist', () { + final result = utils.areFSEntiesExist([ + path.join(prefixPath, 'dir1'), + path.join(prefixPath, 'file_that_does_not_exist.txt'), + ]); + expect(result, isNotNull); + expect(result, equals(path.join(prefixPath, 'file_that_does_not_exist.txt'))); + }); + + test('should return the dir path that does not exist', () { + final result = utils.areFSEntiesExist([ + path.join(prefixPath, 'dir_that_does_not_exist'), + path.join(prefixPath, 'file.txt'), + ]); + expect(result, isNotNull); + expect(result, equals(path.join(prefixPath, 'dir_that_does_not_exist'))); + }); + + test('should return the first entity path that does not exist', () { + final result = utils.areFSEntiesExist([ + path.join(prefixPath, 'dir_that_does_not_exist'), + path.join(prefixPath, 'file_that_dodes_not_exist.txt'), + ]); + expect(result, isNotNull); + expect(result, equals(path.join(prefixPath, 'dir_that_does_not_exist'))); + }); + }); + + group('#createDirIfNotExist', () { + setUpAll(() async { + await d.dir('fli_test', [ + d.dir('dir_exists'), + ]).create(); + }); + test('should create directory if it does not exist', () async { + await expectLater( + d.dir('fli_test', [d.dir('dir_that_does_not_exist')]).validate(), + throwsException, + ); + final result = utils.createDirIfNotExist(path.join(d.sandbox, 'fli_test', 'dir_that_does_not_exist')); + expect(result.existsSync(), isTrue); + await expectLater( + d.dir('fli_test', [d.dir('dir_that_does_not_exist')]).validate(), + completes, + ); + }); + test('should return dir if it exist', () async { + await expectLater( + d.dir('fli_test', [d.dir('dir_exists')]).validate(), + completes, + ); + final result = utils.createDirIfNotExist(path.join(d.sandbox, 'fli_test', 'dir_exists')); + expect(result.existsSync(), isTrue); + await expectLater( + d.dir('fli_test', [d.dir('dir_exists')]).validate(), + completes, + ); + }); + }); + + group('#createFileIfNotExist', () { + setUpAll(() async { + await d.dir('fli_test', [ + d.file('file_exists.txt'), + ]).create(); + }); + test('should create file if it does not exist', () async { + await expectLater( + d.dir('fli_test', [d.file('file_that_does_not_exist.txt')]).validate(), + throwsException, + ); + final result = utils.createFileIfNotExist(path.join(d.sandbox, 'fli_test', 'file_that_does_not_exist.txt')); + expect(result.existsSync(), isTrue); + await expectLater( + d.dir('fli_test', [d.file('file_that_does_not_exist.txt')]).validate(), + completes, + ); + }); + test('should return file if it exist', () async { + await expectLater( + d.dir('fli_test', [d.file('file_exists.txt')]).validate(), + completes, + ); + final result = utils.createFileIfNotExist(path.join(d.sandbox, 'fli_test', 'file_exists.txt')); + expect(result.existsSync(), isTrue); + await expectLater( + d.dir('fli_test', [d.file('file_exists.txt')]).validate(), + completes, + ); + }); + }); + + group('#prettifyJsonEncode', () { + test('should return prettiffed json string 2 indents', () { + const expectedValue = r''' +{ + "key1": "value1", + "key2": "value2" +}'''; + final result = utils.prettifyJsonEncode({ + 'key1': 'value1', + 'key2': 'value2', + }); + expect(result, equals(expectedValue)); + }); + }); +} From bae9be291d734216b272ebd83ea48a771a4a62e3 Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Sun, 10 Jul 2022 14:39:58 +0530 Subject: [PATCH 13/24] test(web): added tests for web icon templates --- test/web/web_template_test.dart | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 test/web/web_template_test.dart diff --git a/test/web/web_template_test.dart b/test/web/web_template_test.dart new file mode 100644 index 0000000000..84351893ea --- /dev/null +++ b/test/web/web_template_test.dart @@ -0,0 +1,30 @@ +import 'package:flutter_launcher_icons/web/web_template.dart'; +import 'package:test/test.dart'; + +void main() { + group('WebTemplate', () { + late WebIconTemplate icTemplate; + late WebIconTemplate icMaskableTemplate; + + setUp(() { + icTemplate = const WebIconTemplate(size: 512); + icMaskableTemplate = const WebIconTemplate(size: 512, maskable: true); + }); + + test('.iconFile should return valid file name', () async { + expect(icTemplate.iconFile, equals('Icon-512.png')); + expect(icMaskableTemplate.iconFile, equals('Icon-maskable-512.png')); + }); + + test('.iconManifest should return valid manifest config', () { + expect( + icTemplate.iconManifest, + equals({'src': 'icons/Icon-512.png', 'sizes': '512x512', 'type': 'image/png'}), + ); + expect( + icMaskableTemplate.iconManifest, + equals({'src': 'icons/Icon-maskable-512.png', 'sizes': '512x512', 'type': 'image/png', 'purpose': 'maskable'}), + ); + }); + }); +} From a769629e63b7b99b51a9923382cf400255338434 Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Sun, 10 Jul 2022 17:05:56 +0530 Subject: [PATCH 14/24] fix(logging): added verbose logging to platform failure --- lib/abs/icon_generator.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/abs/icon_generator.dart b/lib/abs/icon_generator.dart index 159d0528ed..9bd17d8042 100644 --- a/lib/abs/icon_generator.dart +++ b/lib/abs/icon_generator.dart @@ -70,8 +70,11 @@ void generateIconsFor({ } platform.createIcons(); progress.finish(message: 'done', showTiming: true); - } catch (e) { + } catch (e, st) { progress.cancel(); + logger + ..error(e.toString()) + ..verbose(st); continue; } } From 12da70f156e3b63d95d2d6cc1635786e3510d231 Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Sun, 10 Jul 2022 20:29:06 +0530 Subject: [PATCH 15/24] vendor(deps): added mokito for tests --- pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pubspec.yaml b/pubspec.yaml index 2d8bb60686..dee2aaf911 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -23,5 +23,6 @@ dev_dependencies: # https://pub.dev/packages/build_version build_version: ^2.1.1 json_serializable: ^6.2.0 + mockito: ^5.2.0 test: ^1.21.1 test_descriptor: ^2.0.0 From 3e2578e34362140241dd4d2b92b786285dfc8bf7 Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Sun, 10 Jul 2022 20:29:17 +0530 Subject: [PATCH 16/24] test(icon_generator): added tests for IconGenerator --- test/abs/icon_generator_test.dart | 72 +++++++++++++++++++++++++ test/abs/icon_generator_test.mocks.dart | 61 +++++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 test/abs/icon_generator_test.dart create mode 100644 test/abs/icon_generator_test.mocks.dart diff --git a/test/abs/icon_generator_test.dart b/test/abs/icon_generator_test.dart new file mode 100644 index 0000000000..296acada6a --- /dev/null +++ b/test/abs/icon_generator_test.dart @@ -0,0 +1,72 @@ +import 'package:flutter_launcher_icons/abs/icon_generator.dart'; +import 'package:flutter_launcher_icons/flutter_launcher_icons_config.dart'; +import 'package:flutter_launcher_icons/logger.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; +import 'package:path/path.dart' as path; +import 'package:test/test.dart'; +import 'package:test_descriptor/test_descriptor.dart' as d; + +import 'icon_generator_test.mocks.dart'; + +@GenerateMocks([FlutterLauncherIconsConfig, IconGenerator]) +void main() { + group('#generateIconsFor', () { + late String prefixPath; + late FLILogger logger; + late IconGenerator mockGenerator; + late FlutterLauncherIconsConfig mockFLIConfig; + setUp(() async { + prefixPath = path.join(d.sandbox, 'fli_test'); + mockFLIConfig = MockFlutterLauncherIconsConfig(); + logger = FLILogger(false); + mockGenerator = MockIconGenerator(); + when(mockGenerator.platformName).thenReturn('Mock'); + when(mockGenerator.context).thenReturn(IconGeneratorContext( + config: mockFLIConfig, + prefixPath: prefixPath, + logger: logger, + )); + }); + test('should execute createIcons() when validateRequiremnts() returns true', () { + when(mockGenerator.validateRequirments()).thenReturn(true); + generateIconsFor( + config: mockFLIConfig, + flavor: null, + prefixPath: prefixPath, + logger: logger, + platforms: (context) => [mockGenerator], + ); + verify(mockGenerator.validateRequirments()).called(equals(1)); + verify(mockGenerator.createIcons()).called(equals(1)); + }); + + test('should not execute createIcons() when validateRequiremnts() returns false', () { + when(mockGenerator.validateRequirments()).thenReturn(false); + generateIconsFor( + config: mockFLIConfig, + flavor: null, + prefixPath: prefixPath, + logger: logger, + platforms: (context) => [mockGenerator], + ); + verify(mockGenerator.validateRequirments()).called(equals(1)); + verifyNever(mockGenerator.createIcons()); + }); + + test('should skip platform if any exception occured', () { + when(mockGenerator.validateRequirments()).thenReturn(true); + when(mockGenerator.createIcons()).thenThrow(Exception('should-skip-platform')); + generateIconsFor( + config: mockFLIConfig, + flavor: null, + prefixPath: prefixPath, + logger: logger, + platforms: (context) => [mockGenerator], + ); + verify(mockGenerator.validateRequirments()).called(equals(1)); + verify(mockGenerator.createIcons()).called(equals(1)); + expect(() => mockGenerator.createIcons(), throwsException); + }); + }); +} diff --git a/test/abs/icon_generator_test.mocks.dart b/test/abs/icon_generator_test.mocks.dart new file mode 100644 index 0000000000..eca08ac033 --- /dev/null +++ b/test/abs/icon_generator_test.mocks.dart @@ -0,0 +1,61 @@ +// Mocks generated by Mockito 5.2.0 from annotations +// in flutter_launcher_icons/test/abs/icon_generator_test.dart. +// Do not manually edit this file. + +import 'package:flutter_launcher_icons/abs/icon_generator.dart' as _i2; +import 'package:flutter_launcher_icons/flutter_launcher_icons_config.dart' + as _i3; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types + +class _FakeIconGeneratorContext_0 extends _i1.Fake + implements _i2.IconGeneratorContext {} + +/// A class which mocks [FlutterLauncherIconsConfig]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockFlutterLauncherIconsConfig extends _i1.Mock + implements _i3.FlutterLauncherIconsConfig { + MockFlutterLauncherIconsConfig() { + _i1.throwOnMissingStub(this); + } + + @override + Map toJson() => + (super.noSuchMethod(Invocation.method(#toJson, []), + returnValue: {}) as Map); +} + +/// A class which mocks [IconGenerator]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockIconGenerator extends _i1.Mock implements _i2.IconGenerator { + MockIconGenerator() { + _i1.throwOnMissingStub(this); + } + + @override + _i2.IconGeneratorContext get context => (super.noSuchMethod( + Invocation.getter(#context), + returnValue: _FakeIconGeneratorContext_0()) as _i2.IconGeneratorContext); + @override + String get platformName => + (super.noSuchMethod(Invocation.getter(#platformName), returnValue: '') + as String); + @override + void createIcons() => super.noSuchMethod(Invocation.method(#createIcons, []), + returnValueForMissingStub: null); + @override + bool validateRequirments() => + (super.noSuchMethod(Invocation.method(#validateRequirments, []), + returnValue: false) as bool); +} From 96a54a563caf9a9c96f6aa798fc5551ce490656c Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Sun, 10 Jul 2022 20:36:49 +0530 Subject: [PATCH 17/24] test(web): added tests for web icon generator --- test/assets/app_icon.png | Bin 0 -> 1274 bytes test/templates.dart | 11 +++++ test/web/web_icon_generator_test.dart | 67 ++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 test/assets/app_icon.png create mode 100644 test/web/web_icon_generator_test.dart diff --git a/test/assets/app_icon.png b/test/assets/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c61b320e338ac3b3322b7b589ab6885607aa548a GIT binary patch literal 1274 zcmV6)Cx5y#ZF2vedv9X>K;P~? z_kQ_)a&qqJjRjkb{{sKpv6+1X1jqIkzUS=KG1sn+=hcq!#}C&-(GvqlgmiMf(s7*H z3$TK3^?SR9fEKNtot`8nQEUcz7QpcJan1t;)dv(I;ed0N_W8|AX-rp}L zEX@226OBo;n!}zL7?2wlW(6)b8`7$AzS(qgRmpjVD{Y3xG=?^8mc}%O1vXP-8bg#e zTVopC8?FY8X><)-EgIA4rgSxFOruM~)uu5G^T5@pF%46tt5stfW(`-f#xzU7fmtSl-9PX2l~5jpyD z_rrx>rj*`@)NEYRSZlN2z5m9WVou1(JxA6oUHoU4?60G0)0l?&*V2Z@6`Jk->`GLt z#!8zlA^S5dM{g;Ooq>Jr?H^taTk3g3HKtL#mM*8al*aYXb_RT>E=R2Ou2$X(wDBA) zoU+TS)tH9K!-JH@q}jI9-$Y5+jWHC4)}k?u>J5O>f+V#VTduUIAYpx{hQl`ZKNl4) z8{pd}9lErr&>9w6cRYyR>!}Cpax^YChE&rmG^WvQBYrcD%TX>gD~)M%S!tCtmJ^nc z6oC1Zh@ALn8LV|SE{B!nU?^(b_R;8s&E)HFt*WRSe#LY>}P0hB52i`gl zYn_Ajy+N-@%j#+7pZet6P~A@X&(Mh**RV8u2b`S;EF8r6&ml~l8Gw*sx)r3$q$T{h zl#HD`)W5d8_@kWTS8h!qK0O1@=G>@&YYOzC`<)TiO*5agO5`4{q#sNL&$(WFrOVo| z-M85>d5y1)BR)9^I2!}%teTTz~a7i-2hno?QY`UyS;to zKGTWFFyhfLV6~IBm@0rLi!34mdGrpBaShF?f8 Date: Sun, 10 Jul 2022 20:38:01 +0530 Subject: [PATCH 18/24] test: added common entrypoint to all tests This can be used to generate code coverage reports --- test/all_tests.dart | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 test/all_tests.dart diff --git a/test/all_tests.dart b/test/all_tests.dart new file mode 100644 index 0000000000..2ae13005cf --- /dev/null +++ b/test/all_tests.dart @@ -0,0 +1,25 @@ +import 'package:test/test.dart'; + +import 'abs/icon_generator_test.dart' as icon_generator_test; +import 'android_test.dart' as android_test; +import 'flutter_launcher_icons_config_test.dart' as fli_config; +import 'main_test.dart' as main_test; +import 'utils_test.dart' as utils_test; +import 'web/web_template_test.dart' as web_template_test; +import 'web/web_icon_generator_test.dart' as web_icon_gen_test; + +void main() { + group('Flutter launcher icons', () { + // others + utils_test.main(); + fli_config.main(); + icon_generator_test.main(); + + main_test.main(); + // android + android_test.main(); + // web + web_template_test.main(); + web_icon_gen_test.main(); + }); +} From 4fac23997a52f70debbf0dff1e7c6fbb79b4ad55 Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Wed, 20 Jul 2022 11:46:03 +0530 Subject: [PATCH 19/24] style: add missing space Co-authored-by: Mark O'Sullivan --- lib/abs/icon_generator.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/abs/icon_generator.dart b/lib/abs/icon_generator.dart index 9bd17d8042..83e4721dc5 100644 --- a/lib/abs/icon_generator.dart +++ b/lib/abs/icon_generator.dart @@ -3,7 +3,7 @@ import 'dart:io'; import 'package:flutter_launcher_icons/flutter_launcher_icons_config.dart'; import 'package:flutter_launcher_icons/logger.dart'; -///A base class to generate icons +/// A base class to generate icons abstract class IconGenerator { final IconGeneratorContext context; final String platformName; From 4aa41626f222133d60675f797c82f8689bba9334 Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Wed, 20 Jul 2022 15:31:13 +0530 Subject: [PATCH 20/24] fix(generator): fixed typo in a method --- lib/abs/icon_generator.dart | 4 ++-- lib/web/web_icon_generator.dart | 2 +- test/abs/icon_generator_test.dart | 12 +++++----- test/abs/icon_generator_test.mocks.dart | 30 +++++++++---------------- test/web/web_icon_generator_test.dart | 2 +- 5 files changed, 21 insertions(+), 29 deletions(-) diff --git a/lib/abs/icon_generator.dart b/lib/abs/icon_generator.dart index 83e4721dc5..4d48f6265a 100644 --- a/lib/abs/icon_generator.dart +++ b/lib/abs/icon_generator.dart @@ -16,7 +16,7 @@ abstract class IconGenerator { /// Should return `true` if this platform /// has all the requirments to create icons. /// This runs before to [createIcons] - bool validateRequirments(); + bool validateRequirements(); } /// Provides easy access to user arguments and configuration @@ -63,7 +63,7 @@ void generateIconsFor({ logger.verbose('Validating platform requirments for ${platform.platformName}'); // in case a platform throws an exception it should not effect other platforms try { - if (!platform.validateRequirments()) { + if (!platform.validateRequirements()) { logger.error('Requirments failed for platform ${platform.platformName}. Skipped'); progress.cancel(); continue; diff --git a/lib/web/web_icon_generator.dart b/lib/web/web_icon_generator.dart index 0324d151e3..76706db09c 100644 --- a/lib/web/web_icon_generator.dart +++ b/lib/web/web_icon_generator.dart @@ -71,7 +71,7 @@ class WebIconGenerator extends IconGenerator { } @override - bool validateRequirments() { + bool validateRequirements() { // check if web config exists context.logger.verbose('Checking webconfig...'); final webConfig = context.webConfig; diff --git a/test/abs/icon_generator_test.dart b/test/abs/icon_generator_test.dart index 296acada6a..931a488cea 100644 --- a/test/abs/icon_generator_test.dart +++ b/test/abs/icon_generator_test.dart @@ -29,7 +29,7 @@ void main() { )); }); test('should execute createIcons() when validateRequiremnts() returns true', () { - when(mockGenerator.validateRequirments()).thenReturn(true); + when(mockGenerator.validateRequirements()).thenReturn(true); generateIconsFor( config: mockFLIConfig, flavor: null, @@ -37,12 +37,12 @@ void main() { logger: logger, platforms: (context) => [mockGenerator], ); - verify(mockGenerator.validateRequirments()).called(equals(1)); + verify(mockGenerator.validateRequirements()).called(equals(1)); verify(mockGenerator.createIcons()).called(equals(1)); }); test('should not execute createIcons() when validateRequiremnts() returns false', () { - when(mockGenerator.validateRequirments()).thenReturn(false); + when(mockGenerator.validateRequirements()).thenReturn(false); generateIconsFor( config: mockFLIConfig, flavor: null, @@ -50,12 +50,12 @@ void main() { logger: logger, platforms: (context) => [mockGenerator], ); - verify(mockGenerator.validateRequirments()).called(equals(1)); + verify(mockGenerator.validateRequirements()).called(equals(1)); verifyNever(mockGenerator.createIcons()); }); test('should skip platform if any exception occured', () { - when(mockGenerator.validateRequirments()).thenReturn(true); + when(mockGenerator.validateRequirements()).thenReturn(true); when(mockGenerator.createIcons()).thenThrow(Exception('should-skip-platform')); generateIconsFor( config: mockFLIConfig, @@ -64,7 +64,7 @@ void main() { logger: logger, platforms: (context) => [mockGenerator], ); - verify(mockGenerator.validateRequirments()).called(equals(1)); + verify(mockGenerator.validateRequirements()).called(equals(1)); verify(mockGenerator.createIcons()).called(equals(1)); expect(() => mockGenerator.createIcons(), throwsException); }); diff --git a/test/abs/icon_generator_test.mocks.dart b/test/abs/icon_generator_test.mocks.dart index eca08ac033..e75828592b 100644 --- a/test/abs/icon_generator_test.mocks.dart +++ b/test/abs/icon_generator_test.mocks.dart @@ -3,8 +3,7 @@ // Do not manually edit this file. import 'package:flutter_launcher_icons/abs/icon_generator.dart' as _i2; -import 'package:flutter_launcher_icons/flutter_launcher_icons_config.dart' - as _i3; +import 'package:flutter_launcher_icons/flutter_launcher_icons_config.dart' as _i3; import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: type=lint @@ -17,22 +16,19 @@ import 'package:mockito/mockito.dart' as _i1; // ignore_for_file: unnecessary_parenthesis // ignore_for_file: camel_case_types -class _FakeIconGeneratorContext_0 extends _i1.Fake - implements _i2.IconGeneratorContext {} +class _FakeIconGeneratorContext_0 extends _i1.Fake implements _i2.IconGeneratorContext {} /// A class which mocks [FlutterLauncherIconsConfig]. /// /// See the documentation for Mockito's code generation for more information. -class MockFlutterLauncherIconsConfig extends _i1.Mock - implements _i3.FlutterLauncherIconsConfig { +class MockFlutterLauncherIconsConfig extends _i1.Mock implements _i3.FlutterLauncherIconsConfig { MockFlutterLauncherIconsConfig() { _i1.throwOnMissingStub(this); } @override Map toJson() => - (super.noSuchMethod(Invocation.method(#toJson, []), - returnValue: {}) as Map); + (super.noSuchMethod(Invocation.method(#toJson, []), returnValue: {}) as Map); } /// A class which mocks [IconGenerator]. @@ -44,18 +40,14 @@ class MockIconGenerator extends _i1.Mock implements _i2.IconGenerator { } @override - _i2.IconGeneratorContext get context => (super.noSuchMethod( - Invocation.getter(#context), - returnValue: _FakeIconGeneratorContext_0()) as _i2.IconGeneratorContext); + _i2.IconGeneratorContext get context => + (super.noSuchMethod(Invocation.getter(#context), returnValue: _FakeIconGeneratorContext_0()) + as _i2.IconGeneratorContext); @override - String get platformName => - (super.noSuchMethod(Invocation.getter(#platformName), returnValue: '') - as String); + String get platformName => (super.noSuchMethod(Invocation.getter(#platformName), returnValue: '') as String); @override - void createIcons() => super.noSuchMethod(Invocation.method(#createIcons, []), - returnValueForMissingStub: null); + void createIcons() => super.noSuchMethod(Invocation.method(#createIcons, []), returnValueForMissingStub: null); @override - bool validateRequirments() => - (super.noSuchMethod(Invocation.method(#validateRequirments, []), - returnValue: false) as bool); + bool validateRequirements() => + (super.noSuchMethod(Invocation.method(#validateRequirments, []), returnValue: false) as bool); } diff --git a/test/web/web_icon_generator_test.dart b/test/web/web_icon_generator_test.dart index 28548c2e09..644e2e170a 100644 --- a/test/web/web_icon_generator_test.dart +++ b/test/web/web_icon_generator_test.dart @@ -39,7 +39,7 @@ void main() { // end to end test test('should generate valid icons', () async { - expect(generator.validateRequirments(), isTrue); + expect(generator.validateRequirements(), isTrue); generator.createIcons(); await expectLater( d.dir('fli_test', [ From af107158e65ff920b8ddeb0dd2f7f49bcb836348 Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Wed, 20 Jul 2022 16:09:53 +0530 Subject: [PATCH 21/24] fix(lint): filxed lint warnings for `public_member_api_docs` --- analysis_options.yaml | 1 + lib/abs/icon_generator.dart | 19 +++++++++++++++++++ lib/constants.dart | 16 ++++++++++++++++ lib/custom_exceptions.dart | 4 ++++ lib/flutter_launcher_icons_config.dart | 10 ++++++++++ lib/logger.dart | 3 ++- lib/web/web_icon_generator.dart | 5 +++++ lib/web/web_template.dart | 13 ++++++++++--- 8 files changed, 67 insertions(+), 4 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 3d2c15e727..b2746c9048 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -157,3 +157,4 @@ linter: # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review - valid_regexps # - void_checks # not yet tested + - public_member_api_docs diff --git a/lib/abs/icon_generator.dart b/lib/abs/icon_generator.dart index 4d48f6265a..ae0a484737 100644 --- a/lib/abs/icon_generator.dart +++ b/lib/abs/icon_generator.dart @@ -5,9 +5,21 @@ import 'package:flutter_launcher_icons/logger.dart'; /// A base class to generate icons abstract class IconGenerator { + /// Contains config final IconGeneratorContext context; + + /// Name of the platform this [IconGenerator] is created for. final String platformName; + /// Creates a instance of [IconGenerator]. + /// + /// A [context] is created and provided by [generateIconsFor], + /// [platformName] takes the name of the platform that this [IconGenerator] + /// is implemented for + /// + /// Also Refer + /// - [WebIconGenerator] generate icons for web + /// - [generateIconFor] generates icons for given platform IconGenerator(this.context, this.platformName); /// Creates icons for this platform. @@ -23,10 +35,17 @@ abstract class IconGenerator { class IconGeneratorContext { /// Contains configuration from configuration file final FlutterLauncherIconsConfig config; + + /// A logger final FLILogger logger; + + /// Value of `--flavor` flag final String? flavor; + + /// Value of `--prefix` flag final String prefixPath; + /// Creates an instance of [IconGeneratorContext] IconGeneratorContext({ required this.config, this.flavor, diff --git a/lib/constants.dart b/lib/constants.dart index cdc07a32cd..638249f4cf 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -1,7 +1,11 @@ import 'package:path/path.dart' as path; +/// Relative path to android resource folder String androidResFolder(String? flavor) => "android/app/src/${flavor ?? 'main'}/res/"; + +/// Relative path to android colors.xml file String androidColorsFile(String? flavor) => "android/app/src/${flavor ?? 'main'}/res/values/colors.xml"; + const String androidManifestFile = 'android/app/src/main/AndroidManifest.xml'; const String androidGradleFile = 'android/app/build.gradle'; const String androidLocalPropertiesFile = 'android/local.properties'; @@ -17,13 +21,25 @@ const String iosConfigFile = 'ios/Runner.xcodeproj/project.pbxproj'; const String iosDefaultIconName = 'Icon-App'; // web +/// favicon.ico size const int kFaviconSize = 16; + +/// Relative web direcotry path String webDirPath = path.join('web'); + +/// Relative web icons directory path String webIconsDirPath = path.join(webDirPath, 'icons'); + +/// Relative web manifest.json file path String webManifestFilePath = path.join(webDirPath, 'manifest.json'); // todo: support for other images formats +/// Relative favicon.png path String webFaviconFilePath = path.join(webDirPath, 'favicon.png'); + +/// Relative index.html file path String webIndexFilePath = path.join(webDirPath, 'index.html'); + +/// Relative pubspec.yaml path String pubspecFilePath = path.join('pubspec.yaml'); const String errorMissingImagePath = diff --git a/lib/custom_exceptions.dart b/lib/custom_exceptions.dart index 4ade63ab05..82cbe2b746 100644 --- a/lib/custom_exceptions.dart +++ b/lib/custom_exceptions.dart @@ -40,10 +40,14 @@ class NoDecoderForImageFormatException implements Exception { } } +/// A exception to throw when given [fileName] is not found class FileNotFoundException implements Exception { + /// Creates a instance of [FileNotFoundException]. const FileNotFoundException(this.fileName); + /// Name of the file final String fileName; + @override String toString() { return generateError(this, '$fileName file not found'); diff --git a/lib/flutter_launcher_icons_config.dart b/lib/flutter_launcher_icons_config.dart index 59df952b0f..1f5d0bc895 100644 --- a/lib/flutter_launcher_icons_config.dart +++ b/lib/flutter_launcher_icons_config.dart @@ -10,6 +10,7 @@ import 'utils.dart' as utils; part 'flutter_launcher_icons_config.g.dart'; +/// A Config parsed from flutter_launcher_config.yaml @JsonSerializable( anyMap: true, checked: true, @@ -45,6 +46,7 @@ class FlutterLauncherIconsConfig { @JsonKey(name: 'web') final WebConfig? webConfig; + /// Creates an instance of [FlutterLauncherIconsConfig] const FlutterLauncherIconsConfig({ this.imagePath, this.android = false, @@ -56,6 +58,7 @@ class FlutterLauncherIconsConfig { this.webConfig, }); + /// Creates [FlutterLauncherIconsConfig] icons from [json] factory FlutterLauncherIconsConfig.fromJson(Map json) => _$FlutterLauncherIconsConfigFromJson(json); /// Loads flutter launcher icons configs from given [filePath] @@ -108,21 +111,25 @@ class FlutterLauncherIconsConfig { } } + /// Creates [FlutterLauncherIconsConfig] for given [flavor] and [prefixPath] static FlutterLauncherIconsConfig? loadConfigFromFlavor(String flavor, String prefixPath) { return FlutterLauncherIconsConfig.loadConfigFromPath(utils.flavorConfigFile(flavor), prefixPath); } + /// Converts config to [Map] Map toJson() => _$FlutterLauncherIconsConfigToJson(this); @override String toString() => 'FlutterLauncherIconsConfig: ${toJson()}'; } +/// Parse `web` config from `flutter_launcher_icons.yaml` @JsonSerializable( anyMap: true, checked: true, ) class WebConfig { + /// Specifies weather to generate icons for web final bool generate; /// Image path for web @@ -137,6 +144,7 @@ class WebConfig { @JsonKey(name: 'theme_color') final String? themeColor; + /// Creates an instance of [WebConfig] const WebConfig({ this.generate = false, this.imagePath, @@ -144,8 +152,10 @@ class WebConfig { this.themeColor, }); + /// Creates [WebConfig] from [json] factory WebConfig.fromJson(Map json) => _$WebConfigFromJson(json); + /// Creates [Map] from [WebConfig] Map toJson() => _$WebConfigToJson(this); @override diff --git a/lib/logger.dart b/lib/logger.dart index 61ae2956fb..22cf2f0334 100644 --- a/lib/logger.dart +++ b/lib/logger.dart @@ -23,9 +23,10 @@ class FLILogger { /// Logs error messages void error(Object? message) => _logger.stderr('⚠️' + message.toString()); - /// prings to console if [isVerbose] is true + /// Prints to console if [isVerbose] is true void verbose(Object? message) => _logger.trace(message.toString()); + /// Prints to console void info(Object? message) => _logger.stdout(message.toString()); /// Shows progress in console diff --git a/lib/web/web_icon_generator.dart b/lib/web/web_icon_generator.dart index 76706db09c..abb5f05ccb 100644 --- a/lib/web/web_icon_generator.dart +++ b/lib/web/web_icon_generator.dart @@ -9,6 +9,8 @@ import '../custom_exceptions.dart'; import '../utils.dart' as utils; import 'web_template.dart'; +// This is not yet implemented +// ignore: public_member_api_docs final metaTagsTemplate = ( String appleMobileWebAppTitle, String appleMobileWebAppStatusBarStyle, { @@ -39,6 +41,9 @@ class WebIconGenerator extends IconGenerator { WebIconTemplate(size: 512, maskable: true), ]; + /// Creates an instance of [WebIconGenerator]. + /// + /// WebIconGenerator(IconGeneratorContext context) : super(context, 'Web'); @override diff --git a/lib/web/web_template.dart b/lib/web/web_template.dart index 4d4b8ab1bd..485578043f 100644 --- a/lib/web/web_template.dart +++ b/lib/web/web_template.dart @@ -1,12 +1,19 @@ +/// A Icon Template for Web class WebIconTemplate { + /// Size of the web icon + final int size; + + /// Support for maskable icon + /// + /// Refer to https://web.dev/maskable-icon/ + final bool maskable; + + /// Creates an instance of [WebIconTemplate]. const WebIconTemplate({ required this.size, this.maskable = false, }); - final int size; - final bool maskable; - /// Icon file name String get iconFile => 'Icon${maskable ? '-maskable' : ''}-$size.png'; From 610fcb88ce4533782e991a232283ddd3b364c89f Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Wed, 20 Jul 2022 16:12:44 +0530 Subject: [PATCH 22/24] fix(lint): filxed lint warnings for `require_trailing_commas` --- analysis_options.yaml | 2 ++ test/abs/icon_generator_test.dart | 12 +++++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index b2746c9048..6df18fa29e 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -19,6 +19,7 @@ analyzer: - "lib/i18n/stock_messages_*.dart" - "lib/src/http/**" - "example/**" + - "**/*.g.dart" linter: rules: @@ -158,3 +159,4 @@ linter: - valid_regexps # - void_checks # not yet tested - public_member_api_docs + - require_trailing_commas diff --git a/test/abs/icon_generator_test.dart b/test/abs/icon_generator_test.dart index 931a488cea..6edc3f4f44 100644 --- a/test/abs/icon_generator_test.dart +++ b/test/abs/icon_generator_test.dart @@ -22,11 +22,13 @@ void main() { logger = FLILogger(false); mockGenerator = MockIconGenerator(); when(mockGenerator.platformName).thenReturn('Mock'); - when(mockGenerator.context).thenReturn(IconGeneratorContext( - config: mockFLIConfig, - prefixPath: prefixPath, - logger: logger, - )); + when(mockGenerator.context).thenReturn( + IconGeneratorContext( + config: mockFLIConfig, + prefixPath: prefixPath, + logger: logger, + ), + ); }); test('should execute createIcons() when validateRequiremnts() returns true', () { when(mockGenerator.validateRequirements()).thenReturn(true); From 6713bd479bcac7d48037d9b25a8eee6dec44502a Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Wed, 20 Jul 2022 16:14:04 +0530 Subject: [PATCH 23/24] style: sorted imports --- lib/main.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 01f82fbc72..5cffcc4bfb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,16 +2,16 @@ import 'dart:io'; import 'package:args/args.dart'; import 'package:flutter_launcher_icons/abs/icon_generator.dart'; +import 'package:flutter_launcher_icons/android.dart' as android_launcher_icons; +import 'package:flutter_launcher_icons/constants.dart' as constants; +import 'package:flutter_launcher_icons/constants.dart'; +import 'package:flutter_launcher_icons/custom_exceptions.dart'; import 'package:flutter_launcher_icons/flutter_launcher_icons_config.dart'; +import 'package:flutter_launcher_icons/ios.dart' as ios_launcher_icons; import 'package:flutter_launcher_icons/logger.dart'; import 'package:flutter_launcher_icons/web/web_icon_generator.dart'; import 'package:path/path.dart' as path; import 'package:yaml/yaml.dart'; -import 'package:flutter_launcher_icons/android.dart' as android_launcher_icons; -import 'package:flutter_launcher_icons/ios.dart' as ios_launcher_icons; -import 'package:flutter_launcher_icons/constants.dart'; -import 'package:flutter_launcher_icons/constants.dart' as constants; -import 'package:flutter_launcher_icons/custom_exceptions.dart'; const String fileOption = 'file'; const String helpFlag = 'help'; @@ -248,10 +248,12 @@ bool isNeedingNewIOSIcon(Map flutterLauncherIconsConfig) { return hasIOSConfig(flutterLauncherIconsConfig) && flutterLauncherIconsConfig['ios'] != false; } +/// Checks if the [flutterLauncherIconsConfig] contains web configs bool hasWebConfig(Map flutterLauncherIconsConfig) { return flutterLauncherIconsConfig.containsKey('web'); } +/// Checks if we should generate icons for web platform bool isNeddingNewWebIcons(Map flutterLauncherIconsConfig) { return hasWebConfig(flutterLauncherIconsConfig) && flutterLauncherIconsConfig['web'] != false; } From 3a5822ee40d0eeee1b9e02b02d3feefdfe5dcd21 Mon Sep 17 00:00:00 2001 From: Ratakondala Arun Date: Wed, 20 Jul 2022 16:19:48 +0530 Subject: [PATCH 24/24] style: sort variable order --- lib/abs/icon_generator.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/abs/icon_generator.dart b/lib/abs/icon_generator.dart index ae0a484737..1576ddb939 100644 --- a/lib/abs/icon_generator.dart +++ b/lib/abs/icon_generator.dart @@ -39,18 +39,18 @@ class IconGeneratorContext { /// A logger final FLILogger logger; - /// Value of `--flavor` flag - final String? flavor; - /// Value of `--prefix` flag final String prefixPath; + /// Value of `--flavor` flag + final String? flavor; + /// Creates an instance of [IconGeneratorContext] IconGeneratorContext({ required this.config, - this.flavor, - required this.prefixPath, required this.logger, + required this.prefixPath, + this.flavor, }); /// Shortcut for `config.webConfig`