├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── cd-pub-deploy-published-release.yml │ └── ci-open-pr.yml ├── .gitignore ├── .pubignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── bin ├── flutter_launcher_icons.dart ├── generate.dart └── main.dart ├── example ├── default_example │ ├── .metadata │ ├── README.md │ ├── assets │ │ └── images │ │ │ ├── background-test.png │ │ │ ├── christmas-background.png │ │ │ ├── ic_launcher.png │ │ │ ├── icon-1024x1024.png │ │ │ ├── icon-128x128.png │ │ │ ├── icon-710x599-android.png │ │ │ ├── icon-710x599-ios.png │ │ │ ├── icon-710x599.png │ │ │ ├── icon-foreground-432x432.png │ │ │ └── icon-monochrome-432x432.png │ └── pubspec.yaml ├── example.md └── flavors │ ├── .metadata │ ├── README.md │ ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── development │ │ │ └── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── integration │ │ │ └── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── com │ │ │ │ │ └── flutter │ │ │ │ │ └── icons │ │ │ │ │ ├── example_with_flavors │ │ │ │ │ └── MainActivity.kt │ │ │ │ │ └── flavors │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ ├── production │ │ │ └── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ │ └── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── flavors_android.iml │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle │ ├── assets │ └── launcher_icon │ │ ├── demo-icon-dev.png │ │ ├── demo-icon-int.png │ │ └── demo-icon.png │ ├── flavors.iml │ ├── flutter_launcher_icons-development.yaml │ ├── flutter_launcher_icons-integration.yaml │ ├── flutter_launcher_icons-production.yaml │ └── pubspec.yaml ├── flutter_launcher_icons.code-workspace ├── lib ├── abs │ └── icon_generator.dart ├── android.dart ├── config │ ├── config.dart │ ├── config.g.dart │ ├── macos_config.dart │ ├── macos_config.g.dart │ ├── web_config.dart │ ├── web_config.g.dart │ ├── windows_config.dart │ └── windows_config.g.dart ├── constants.dart ├── custom_exceptions.dart ├── ios.dart ├── logger.dart ├── macos │ ├── macos_icon_generator.dart │ └── macos_icon_template.dart ├── main.dart ├── pubspec_parser.dart ├── src │ └── version.dart ├── utils.dart ├── web │ ├── web_icon_generator.dart │ └── web_template.dart ├── windows │ └── windows_icon_generator.dart └── xml_templates.dart ├── pubspec.yaml └── test ├── abs ├── icon_generator_test.dart └── icon_generator_test.mocks.dart ├── all_tests.dart ├── android_test.dart ├── assets └── app_icon.png ├── config └── test_pubspec.yaml ├── config_test.dart ├── macos ├── macos_icon_generator_test.dart ├── macos_icon_generator_test.mocks.dart └── macos_icon_template_test.dart ├── main_test.dart ├── package_version_test.dart ├── templates.dart ├── utils_test.dart ├── web ├── web_icon_generator_test.dart └── web_template_test.dart └── windows ├── windows_icon_generator_test.dart └── windows_icon_generator_test.mocks.dart /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us fix any issues you are having 4 | title: '' 5 | labels: 'bug' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### :information_source: Info 11 | 12 | 13 | 14 | 15 | ### :speech_balloon: Description 16 | 17 | 18 | 19 | 20 | ### :scroll: Pubspec.yaml 21 | 22 | 23 | ```yaml 24 | flutter_launcher_icons: 25 | android: 26 | ios: 27 | image_path: 28 | ``` 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea to improve Flutter Launcher Icons 4 | title: '' 5 | labels: improvement 6 | assignees: '' 7 | 8 | --- 9 | 10 | ### :speech_balloon: Description 11 | 12 | 13 | -------------------------------------------------------------------------------- /.github/workflows/cd-pub-deploy-published-release.yml: -------------------------------------------------------------------------------- 1 | # Deploy new version to pub.dev after a new GitHub release has been published 2 | name: pub.dev auto deployment 3 | 4 | on: 5 | release: 6 | types: [published] 7 | 8 | # Allows you to run this workflow manually from the Actions tab 9 | workflow_dispatch: 10 | 11 | jobs: 12 | deploy: 13 | runs-on: ubuntu-latest 14 | steps: 15 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 16 | - name: Checkout repo 17 | uses: actions/checkout@v2 18 | - name: Run a one-line script 19 | uses: k-paxian/dart-package-publisher@v1.5.1 20 | with: 21 | accessToken: ${{ secrets.PUB_ACCESS_TOKEN }} 22 | refreshToken: ${{ secrets.PUB_REFRESH_TOKEN}} -------------------------------------------------------------------------------- /.github/workflows/ci-open-pr.yml: -------------------------------------------------------------------------------- 1 | # Runs all the unit tests and the analyzer for any PR opened to merge into master branch. 2 | # 3 | # This helps ensure we avoid breaking existing functionality and stick to the rules 4 | # defined in the analysis_options.yaml file 5 | name: run tests & analyzer for new pr 6 | 7 | on: 8 | pull_request: 9 | branches: [ master ] 10 | 11 | # allows for this workflow to be run manually from the Actions tab 12 | workflow_dispatch: 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v2 19 | - uses: dart-lang/setup-dart@v1.3 20 | - name: get dependencies 21 | run: dart pub get 22 | - name: run tests 23 | run: dart test test/all_tests.dart 24 | - name: run analyzer 25 | run: dart analyze 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Files and directories created by pub 2 | .packages 3 | .dart_tool/ 4 | .pub/ 5 | .idea/ 6 | build/ 7 | 8 | # Remove the following pattern if you wish to check in your lock file 9 | pubspec.lock 10 | 11 | # Directory created by dartdoc 12 | doc/api/ 13 | 14 | .DS_Store -------------------------------------------------------------------------------- /.pubignore: -------------------------------------------------------------------------------- 1 | # test directory 2 | /test/* 3 | 4 | # other 5 | /flutter_launcher_icons.code-workspace 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: dart 2 | 3 | dart: 4 | - dev 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.14.3 (17th January 2025) 4 | 5 | - Android: Avoids creating mipmap file used by adaptive and monochrome icons if no config exist for both [#601](https://github.com/fluttercommunity/flutter_launcher_icons/pull/601) 6 | 7 | ## 0.14.2 (5th December 2024) 8 | 9 | - iOS: Fixed issue where dark and tinted icons were placed into the wrong directory [#597](https://github.com/fluttercommunity/flutter_launcher_icons/pull/597) 10 | 11 | ## 0.14.1 (24th September 2024) 12 | 13 | - Fixed README 14 | 15 | ## 0.14.0 (21st September 2024) 16 | 17 | - Android: Support for monochrome icons [#497](https://github.com/fluttercommunity/flutter_launcher_icons/pull/497) 18 | 19 | **Before** 20 | 21 | 22 | 23 | **Now** 24 | 25 | 26 | 27 | - Android: Ability to set inset for adaptive icon foreground and monochrome icon [#563](https://github.com/fluttercommunity/flutter_launcher_icons/pull/563) 28 | - iOS: Dark and Tinted icons for iOS 18+ [#569](https://github.com/fluttercommunity/flutter_launcher_icons/pull/569) 29 | 30 | ## 0.13.1 (15th April 2023) 31 | 32 | - Can now use `flutter_launcher_icons` instead of `flutter_icons` [#478](https://github.com/fluttercommunity/flutter_launcher_icons/pull/478) 33 | - Can use command `flutter pub run flutter_launcher_icons:generate` to automatically generate config file [#475](https://github.com/fluttercommunity/flutter_launcher_icons/pull/475) 34 | 35 | 36 | ## 0.13.0 (7th April 2023) 37 | 38 | - Fix remove alpha for iOS [#464](https://github.com/fluttercommunity/flutter_launcher_icons/pull/464) 39 | - Updating code style [#472](https://github.com/fluttercommunity/flutter_launcher_icons/pull/472) 40 | - Updated out of bounds dependency [#473](https://github.com/fluttercommunity/flutter_launcher_icons/pull/473) 41 | 42 | ## 0.12.0 (24th February 2023) 43 | 44 | - Updated image package and other packages [#447](https://github.com/fluttercommunity/flutter_launcher_icons/pull/447) 45 | 46 | ## 0.11.0 (27th September 2022) 47 | 48 | - Support for Macos Icons [#407](https://github.com/fluttercommunity/flutter_launcher_icons/pull/407) 49 | - Cli-improvement [#400](https://github.com/fluttercommunity/flutter_launcher_icons/pull/400) 50 | - Add `repository` and `issue_tracker` [#411](https://github.com/fluttercommunity/flutter_launcher_icons/pull/411) (thanks to [@patelpathik](https://github.com/patelpathik)) 51 | - Fix indent in web/manifest.json [#407](https://github.com/fluttercommunity/flutter_launcher_icons/pull/407) 52 | - Fix the icons 50 and 57 in `contents.json` [#412](https://github.com/fluttercommunity/flutter_launcher_icons/pull/412) (thanks to [@adnanjpg](https://github.com/adnanjpg)) 53 | - Fix typos [#405](https://github.com/fluttercommunity/flutter_launcher_icons/pull/405) (thanks to [@edwardmp](https://github.com/edwardmp)) 54 | - Added newline to EOF [#325](https://github.com/fluttercommunity/flutter_launcher_icons/pull/325) (thanks to [@sandersaelmans](https://github.com/sandersaelmans)) 55 | 56 | ## 0.10.0 (2nd August 2022) 57 | 58 | - Support for Web Icons [#374](https://github.com/fluttercommunity/flutter_launcher_icons/pull/374) 59 | - Support for Windows Icons [#382](https://github.com/fluttercommunity/flutter_launcher_icons/pull/382) 60 | - Added missing IOS icon sizes [#298](https://github.com/fluttercommunity/flutter_launcher_icons/pull/298) 61 | - Added `min_sdk_android` option [#392](https://github.com/fluttercommunity/flutter_launcher_icons/pull/392) 62 | - Added documentation for `remove_alpha_ios` [#392](https://github.com/fluttercommunity/flutter_launcher_icons/pull/392) 63 | - Fixed issue with loading config from `pubspec.yaml` [#398](https://github.com/fluttercommunity/flutter_launcher_icons/pull/398) (thanks to [@p-mazhnik](https://github.com/p-mazhnik)) 64 | 65 | ## 0.9.3 (6th June 2022) 66 | 67 | - Fixes to make sure it works for Flutter v2.8 (thanks to @RatakondalaArun) 68 | - Fixed issue with incorrect version being shown 69 | 70 | ## 0.9.2 (22nd August 2021) 71 | 72 | - Fixed issue where success message printed even when exception occured (thanks to @happy-san) 73 | 74 | ## 0.9.1 (25th July 2021) 75 | 76 | - Upgrade args dependency to ^2.1.1 (thanks to @PiN73 and @comlaterra) 77 | - Upgraded `image` and `test` dependencies 78 | 79 | ## 0.9.0 (28th Feb 2021) 80 | 81 | - Null-safety support added (thanks to @SteveAlexander) 82 | - Added option to remove alpha channel for iOS icons (thanks to @SimonIT) 83 | 84 | ## 0.8.1 (2nd Oct 2020) 85 | 86 | - Fixed flavor support on windows (@slightfoot) 87 | 88 | ## 0.8.0 (12th Sept 2020) 89 | 90 | - Added flavours support (thanks to @sestegra & @jorgecoca) 91 | - Removed unassigned iOS icons (thanks to @melvinsalas) 92 | - Fixing formatting (thanks to @mreichelt) 93 | 94 | ## 0.7.5 (24th April 2020) 95 | 96 | - Fixed issue where new lines were added to Android manifest (thanks to @mreichelt) 97 | - Improvements to code quality and general tidying up (thanks to @connectety) 98 | - Fixed Android example project not running (needed to be migrated to AndroidX) 99 | 100 | ## 0.7.4 (28th Oct 2019) 101 | 102 | - Worked on suggestions from [pub.dev](https://pub.dev/packages/flutter_launcher_icons#-analysis-tab-) 103 | 104 | ## 0.7.3 (3rd Sept 2019) 105 | 106 | - Lot of refactoring and improving code quality (thanks to @connectety) 107 | - Added correct App Store icon settings (thanks to @richgoldmd) 108 | 109 | ## 0.7.2 (25th May 2019) 110 | 111 | - Reverted back using old interpolation method 112 | 113 | ## 0.7.1 (24th May 2019) 114 | 115 | - Fixed issue with image dependency not working on latest version of Flutter (thanks to @sboutet06) 116 | - Fixed iOS icon sizes which were incorrect (thanks to @sestegra) 117 | - Removed dart_config git dependency and replaced with yaml dependency 118 | - Refactoring of code 119 | 120 | ## 0.7.0 (22nd November 2018) 121 | 122 | - Now ensuring that the Android file name is valid - An error will be thrown if it doesn't meet the criteria 123 | - Fixed issue where there was a git diff when there was no change 124 | - Fixed issue where iOS icon would be generated when it shouldn't be 125 | - Added support for drawables to be used for adaptive icon backgrounds 126 | - Added support for Flutter Launcher Icons to be able to run with it's own config file (no longer necessary to add to pubspec.yaml) 127 | 128 | ## 0.6.1 (26th August 2018) 129 | 130 | - Upgraded test package 131 | - Due to issue with dart_config not working with Dart 2.1.0, now using forked version of dart_config which contains fixes from both @v3rm0n and @SPodjasek 132 | 133 | ## 0.6.0 (8th August 2018) 134 | 135 | - Moved the package to [Flutter Community](https://github.com/fluttercommunity/community) 136 | 137 | ## 0.5.2 (19th June 2018) 138 | 139 | - Previous release didn't fix adaptive icons, just prevented the error message from appearing. This should hopefully fix it! 140 | 141 | ## 0.5.1 (18th June 2018) 142 | 143 | - Fix for adaptive icons 144 | 145 | ## 0.5.0 (12th June 2018) 146 | 147 | - [Android] Support for adaptive icons added (Suggestion #23) 148 | 149 | ## 0.4.0 (9th June 2018) 150 | 151 | - Now possible to generate icons for each platform with different image paths (one for iOS icon and a separate one for Android) 152 | 153 | ## 0.3.3 (28th May 2018) 154 | 155 | - Upgraded dart image package dependency to 2.0.0 (issue #26) 156 | 157 | ## 0.3.2 (2nd May 2018) 158 | 159 | - Bug fixing 160 | 161 | ## 0.3.1 (1st May 2018) 162 | 163 | - Bug fixing 164 | 165 | ## 0.3.0 (1st May 2018) 166 | 167 | - Fixed issue where icons produced weren't the correct size (Due to images not with a 1:1 aspect r ation) 168 | - Improved quality of smaller icons produced (Thanks to PR #17 - Thank you!) 169 | - Updated console printed messages to keep them consistent 170 | - Added example folder to GitHub project 171 | 172 | ## 0.2.1 (25th April 2018) 173 | 174 | - Added extra iOS icon size (1024x1024) 175 | - Fixed iOS default icon name (Thanks to PR #15 - Thank you!) 176 | - Fixed issue #10 where creation of the icons was failing due to the target folder not existing 177 | 178 | ## 0.2.0 (18th January 2018) 179 | 180 | - Ability to create new launcher icons without replacing the old ones added (#6) 181 | - Fixed issue with launcher icons for iOS not correctly being set 182 | 183 | ## 0.0.5 184 | 185 | - Quick Fix on if statement 186 | 187 | ## 0.0.4 188 | 189 | - Fixing strong mode error 190 | 191 | ## 0.0.3 192 | 193 | - Adding flutter as a dependency so its listed as a flutter package. 194 | 195 | ## 0.0.2 196 | 197 | - Fix Doc typo 198 | 199 | ## 0.0.1 200 | 201 | - Initial version, Resizes Icon to Android sizes only. 202 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Mark O'Sullivan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Launcher Icons 2 | 3 | [![Flutter Community: flutter_launcher_icons](https://fluttercommunity.dev/_github/header/flutter_launcher_icons)](https://github.com/fluttercommunity/community) 4 | 5 | [![pub package](https://img.shields.io/pub/v/flutter_launcher_icons.svg)](https://pub.dartlang.org/packages/flutter_launcher_icons) 6 | 7 | A command-line tool which simplifies the task of updating your Flutter app's launcher icon. Fully flexible, allowing you to choose what platform you wish to update the launcher icon for and if you want, the option to keep your old launcher icon in case you want to revert back sometime in the future. 8 | 9 | ## :book: Guide 10 | 11 | ### 1. Setup the config file 12 | 13 | Run the following command to create a new config automatically: 14 | 15 | ```shell 16 | dart run flutter_launcher_icons:generate 17 | ``` 18 | 19 | This will create a new file called `flutter_launcher_icons.yaml` in your `flutter` project's root directory. 20 | 21 | If you want to override the default location or name of the config file, use the `-f` flag: 22 | 23 | ```shell 24 | dart run flutter_launcher_icons:generate -f 25 | ``` 26 | 27 | To override an existing config file, use the `-o` flag: 28 | 29 | ```shell 30 | dart run flutter_launcher_icons:generate -o 31 | ``` 32 | 33 | OR 34 | 35 | Add your Flutter Launcher Icons configuration to your `pubspec.yaml`. 36 | An example is shown below. More complex examples [can be found in the example projects](https://github.com/fluttercommunity/flutter_launcher_icons/tree/master/example). 37 | 38 | ```yaml 39 | dev_dependencies: 40 | flutter_launcher_icons: "^0.14.3" 41 | 42 | flutter_launcher_icons: 43 | android: "launcher_icon" 44 | ios: true 45 | image_path: "assets/icon/icon.png" 46 | min_sdk_android: 21 # android min sdk min:16, default 21 47 | web: 48 | generate: true 49 | image_path: "path/to/image.png" 50 | background_color: "#hexcode" 51 | theme_color: "#hexcode" 52 | windows: 53 | generate: true 54 | image_path: "path/to/image.png" 55 | icon_size: 48 # min:48, max:256, default: 48 56 | macos: 57 | generate: true 58 | image_path: "path/to/image.png" 59 | ``` 60 | 61 | ### 2. Run the package 62 | 63 | After setting up the configuration, all that is left to do is run the package. 64 | 65 | ```shell 66 | flutter pub get 67 | dart run flutter_launcher_icons 68 | ``` 69 | 70 | If you name your configuration file something other than `flutter_launcher_icons.yaml` or `pubspec.yaml` you will need to specify 71 | the name of the file when running the package. 72 | 73 | ```shell 74 | flutter pub get 75 | dart run flutter_launcher_icons -f 76 | ``` 77 | 78 | Note: If you are not using the existing `pubspec.yaml` ensure that your config file is located in the same directory as it. 79 | 80 | If you encounter any issues [please report them here](https://github.com/fluttercommunity/flutter_launcher_icons/issues). 81 | 82 | In the above configuration, the package is setup to replace the existing launcher icons in both the Android and iOS project 83 | with the icon located in the image path specified above and given the name "launcher_icon" in the Android project and "Example-Icon" in the iOS project. 84 | 85 | ## :mag: Attributes 86 | 87 | Shown below is the full list of attributes which you can specify within your Flutter Launcher Icons configuration. 88 | 89 | ### Global 90 | 91 | - `image_path`: The location of the icon image file which you want to use as the app launcher icon. 92 | 93 | ### Android 94 | 95 | - `android` 96 | - `true`: Override the default existing Flutter launcher icon for the platform specified 97 | - `false`: Ignore making launcher icons for this platform 98 | - `icon/path/here.png`: This will generate a new launcher icons for the platform with the name you specify, without removing the old default existing Flutter launcher icon. 99 | - `image_path`: The location of the icon image file which you want to use as the app launcher icon 100 | - `image_path_android`: The location of the icon image file specific for Android platform (optional - if not defined then the image_path is used) 101 | - `min_sdk_android`: Specify android min sdk value 102 | **The next two attributes are only used when generating Android launcher icon** 103 | 104 | - `adaptive_icon_background`: The color (E.g. `"#ffffff"`) or image asset (E.g. `"assets/images/christmas-background.png"`) which will 105 | be used to fill out the background of the adaptive icon. 106 | - `adaptive_icon_foreground`: The image asset which will be used for the icon foreground of the adaptive icon 107 | *Note: Adaptive Icons will only be generated when both adaptive_icon_background and adaptive_icon_foreground are specified. (the image_path is not automatically taken as foreground)* 108 | - `adaptive_icon_foreground_inset`: This is used to add padding to the icon when applying an adaptive icon. The default value is `16`. 109 | - `adaptive_icon_monochrome`: The image asset which will be used for the icon 110 | foreground of the Android 13+ themed icon. For more information see [Android Adaptive Icons](https://developer.android.com/develop/ui/views/launch/icon_design_adaptive#user-theming) 111 | 112 | ### IOS 113 | 114 | - `ios` 115 | - `true`: Override the default existing Flutter launcher icon for the platform specified 116 | - `false`: Ignore making launcher icons for this platform 117 | - `icon/path/here.png`: This will generate a new launcher icons for the platform with the name you specify, without removing the old default existing Flutter launcher icon. 118 | - `image_path_ios`: The location of the icon image file specific for iOS platform (optional - if not defined then the image_path is used) 119 | - `remove_alpha_ios`: Removes alpha channel for IOS icons 120 | - `image_path_ios_dark_transparent`: The location of the dark mode icon image file specific for iOS 18+ platform. *Note: Apple recommends this icon to be transparent. For more information see [Apple Human Interface Guidelines for App Icons](https://developer.apple.com/design/human-interface-guidelines/app-icons#iOS-iPadOS)* 121 | - `image_path_ios_tinted_grayscale`: The location of the tinted mode icon image file specific for iOS 18+ platform. *Note: This icon should be an grayscale image. Use `desaturate_tinted_to_grayscale_ios: true` to automatically desaturate the image provided here.* 122 | - `desaturate_tinted_to_grayscale_ios`: Automatically desaturates tinted mode icon image to grayscale, *defaults to false* 123 | - `background_color_ios`: The color (in the format "#RRGGBB") to be used as the background when removing the alpha channel. It is used only when the `remove_alpha_ios` property is set to true. (optional - if not defined then `#ffffff` is used) 124 | 125 | ### Web 126 | 127 | - `web`: Add web related configs 128 | - `generate`: Specifies whether to generate icons for this platform or not 129 | - `image_path`: Path to web icon.png 130 | - `background_color`: Updates *background_color* in `web/manifest.json` 131 | - `theme_color`: Updates *theme_color* in `web/manifest.json` 132 | 133 | ### Windows 134 | 135 | - `windows`: Add Windows related configs 136 | - `generate`: Specifies whether to generate icons for Windows platform or not 137 | - `image_path`: Path to windows icon.png 138 | - `icon_size`: Windows app icon size. Icon size should be within this constrains *48<=icon_size<=256, defaults to 48* 139 | 140 | ### MacOS 141 | 142 | - `macos`: Add MacOS related configs 143 | - `generate`: Specifies whether to generate icons for MacOS platform or not 144 | - `image_path`: Path to macos icon.png file 145 | 146 | *Note: iOS icons should [fill the entire image](https://stackoverflow.com/questions/26014461/black-border-on-my-ios-icon) and not contain transparent borders.* 147 | 148 | ## Flavor support 149 | 150 | Create a Flutter Launcher Icons configuration file for your flavor. The config file is called `flutter_launcher_icons-.yaml` by replacing `` by the name of your desired flavor. 151 | 152 | The configuration file format is the same. 153 | 154 | An example project with flavor support enabled [has been added to the examples](https://github.com/fluttercommunity/flutter_launcher_icons/tree/master/example/flavors). 155 | 156 | ## :question: Troubleshooting 157 | 158 | Listed a couple common issues with solutions for them 159 | 160 | ### Generated icon color is different from the original icon 161 | 162 | Caused by an update to the image dependency which is used by Flutter Launcher Icons. 163 | 164 | ```txt 165 | Use #AARRGGBB for colors instead of #AABBGGRR, to be compatible with Flutter image class. 166 | ``` 167 | 168 | [Related issue](https://github.com/fluttercommunity/flutter_launcher_icons/issues/98) 169 | 170 | ### Image foreground is too big / too small 171 | 172 | For best results try and use a foreground image which has padding much like [the one in the example](https://github.com/fluttercommunity/flutter_launcher_icons/blob/master/example/default_example/assets/images/icon-foreground-432x432.png). 173 | 174 | [Related issue](https://github.com/fluttercommunity/flutter_launcher_icons/issues/96) 175 | 176 | ### Dependency incompatible 177 | 178 | You may receive a message similar to the following 179 | 180 | ```log 181 | Because flutter_launcher_icons >=0.9.0 depends on args 2.0.0 and flutter_native_splash 1.2.0 depends on args ^2.1.1, flutter_launcher_icons >=0.9.0 is incompatible with flutter_native_splash 1.2.0. 182 | And because no versions of flutter_native_splash match >1.2.0 <2.0.0, flutter_launcher_icons >=0.9.0 is incompatible with flutter_native_splash ^1.2.0. 183 | So, because enstack depends on both flutter_native_splash ^1.2.0 and flutter_launcher_icons ^0.9.0, version solving failed. 184 | pub get failed (1; So, because enstack depends on both flutter_native_splash ^1.2.0 and flutter_launcher_icons ^0.9.0, version solving failed.) 185 | ``` 186 | 187 | For a quick fix, you can temporarily override all references to a dependency: [See here for an example](https://github.com/fluttercommunity/flutter_launcher_icons/issues/262#issuecomment-879872076). 188 | 189 | ## :eyes: Example 190 | 191 | [![Video Example](https://i.imgur.com/R28hqdz.png)](https://www.youtube.com/watch?v=RjNAxwcP3Tc) 192 | 193 | Note: This is showing a very old version (v0.0.5) 194 | 195 | ### Special thanks 196 | 197 | - Thanks to Brendan Duncan for the underlying [image package](https://pub.dev/packages/image) to transform the icons. 198 | - Big thank you to all the contributors to the project. Every PR / reported issue is greatly appreciated! 199 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | analyzer: 2 | strong-mode: 3 | implicit-dynamic: false 4 | errors: 5 | # treat missing required parameters as a warning (not a hint) 6 | missing_required_param: warning 7 | # treat missing returns as a warning (not a hint) 8 | missing_return: warning 9 | # allow having TODOs in the code 10 | todo: ignore 11 | exclude: 12 | - "bin/cache/**" 13 | # the following two are relative to the stocks example and the flutter package respectively 14 | # see https://github.com/dart-lang/sdk/issues/28463 15 | - "lib/i18n/stock_messages_*.dart" 16 | - "lib/src/http/**" 17 | - "example/**" 18 | - "**/*.g.dart" 19 | - "lib/src/version.dart" 20 | 21 | linter: 22 | rules: 23 | # See all available options here 24 | # https://github.com/dart-lang/linter/blob/master/example/all.yaml 25 | - always_declare_return_types 26 | - always_put_control_body_on_new_line 27 | 28 | - always_require_non_null_named_parameters 29 | - annotate_overrides 30 | - avoid_empty_else 31 | - avoid_field_initializers_in_const_classes 32 | - avoid_function_literals_in_foreach_calls 33 | - avoid_init_to_null 34 | - avoid_null_checks_in_equality_operators 35 | - avoid_relative_lib_imports 36 | - avoid_renaming_method_parameters 37 | - avoid_return_types_on_setters 38 | - avoid_returning_null_for_void 39 | - avoid_slow_async_io 40 | - avoid_types_as_parameter_names 41 | - avoid_unused_constructor_parameters 42 | - avoid_void_async 43 | - await_only_futures 44 | - camel_case_types 45 | - cancel_subscriptions 46 | - control_flow_in_finally 47 | - directives_ordering 48 | - empty_catches 49 | - empty_constructor_bodies 50 | - empty_statements 51 | - flutter_style_todos 52 | - hash_and_equals 53 | - implementation_imports 54 | - collection_methods_unrelated_type 55 | - library_names 56 | - library_prefixes 57 | - no_adjacent_strings_in_list 58 | - no_duplicate_case_values 59 | - non_constant_identifier_names 60 | - overridden_fields 61 | - package_api_docs 62 | - package_names 63 | - package_prefixed_library_names 64 | - prefer_adjacent_string_concatenation 65 | - prefer_asserts_in_initializer_lists 66 | - prefer_collection_literals 67 | - prefer_conditional_assignment 68 | - prefer_const_constructors 69 | - prefer_const_constructors_in_immutables 70 | - prefer_const_declarations 71 | - prefer_const_literals_to_create_immutables 72 | - prefer_contains 73 | - prefer_final_fields 74 | - prefer_final_locals 75 | - prefer_foreach 76 | - prefer_generic_function_type_aliases 77 | - prefer_initializing_formals 78 | - prefer_is_empty 79 | - prefer_is_not_empty 80 | - prefer_iterable_whereType 81 | - prefer_single_quotes 82 | - prefer_typing_uninitialized_variables 83 | - prefer_void_to_null 84 | - recursive_getters 85 | - slash_for_doc_comments 86 | - sort_pub_dependencies 87 | - sort_unnamed_constructors_first 88 | - test_types_in_equals 89 | - throw_in_finally 90 | - type_init_formals 91 | - unnecessary_brace_in_string_interps 92 | - unnecessary_const 93 | - unnecessary_getters_setters 94 | - unnecessary_new 95 | - unnecessary_null_aware_assignments 96 | - unnecessary_null_in_if_null_operators 97 | - unnecessary_overrides 98 | - unnecessary_parenthesis 99 | - unnecessary_statements 100 | - unnecessary_this 101 | - unrelated_type_equality_checks 102 | - use_rethrow_when_possible 103 | - valid_regexps 104 | - public_member_api_docs 105 | - require_trailing_commas 106 | -------------------------------------------------------------------------------- /bin/flutter_launcher_icons.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_launcher_icons/constants.dart'; 2 | import 'package:flutter_launcher_icons/main.dart' as flutter_launcher_icons; 3 | import 'package:flutter_launcher_icons/src/version.dart'; 4 | 5 | void main(List arguments) { 6 | print(introMessage(packageVersion)); 7 | flutter_launcher_icons.createIconsFromArguments(arguments); 8 | } 9 | -------------------------------------------------------------------------------- /bin/generate.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:args/args.dart'; 4 | 5 | import 'package:flutter_launcher_icons/constants.dart'; 6 | import 'package:flutter_launcher_icons/src/version.dart'; 7 | 8 | const _defaultConfigFileName = './flutter_launcher_icons.yaml'; 9 | 10 | /// The function will be called from command line 11 | /// using the following command: 12 | /// ```sh 13 | /// flutter pub run flutter_launcher_icons:generate 14 | /// ``` 15 | /// 16 | /// Calling this function will generate a flutter_launcher_icons.yaml file 17 | /// with a default config template. 18 | /// 19 | /// This command can take 2 optional arguments: 20 | /// - --override: This will override the current `flutter_launcher_icons.yaml` 21 | /// file if it exists, if not provided, the file will not be overridden and 22 | /// a message will be printed to the console. 23 | /// 24 | /// - --fileName: This flag will take a file name as an argument and 25 | /// will generate the config format in that file instead of the default 26 | /// `flutter_launcher_icons.yaml` file, if not provided, 27 | /// the default file will be used. 28 | void main(List arguments) { 29 | print(introMessage(packageVersion)); 30 | 31 | final parser = ArgParser() 32 | ..addFlag('override', abbr: 'o', defaultsTo: false) 33 | ..addOption( 34 | 'fileName', 35 | abbr: 'f', 36 | defaultsTo: _defaultConfigFileName, 37 | ); 38 | 39 | final results = parser.parse(arguments); 40 | final override = results['override'] as bool; 41 | final fileName = results['fileName'] as String; 42 | 43 | // Check if fileName is valid and has a .yaml extension 44 | if (!fileName.endsWith('.yaml')) { 45 | print('Invalid file name, please provide a valid file name'); 46 | return; 47 | } 48 | 49 | final file = File(fileName); 50 | if (file.existsSync()) { 51 | if (override) { 52 | print('File already exists, overriding...'); 53 | _generateConfigFile(file); 54 | } else { 55 | print( 56 | 'File already exists, use --override flag to override the file, or use --fileName flag to use a different file name', 57 | ); 58 | } 59 | } else { 60 | try { 61 | file.createSync(recursive: true); 62 | _generateConfigFile(file); 63 | } on Exception catch (e) { 64 | print('Error creating file: $e'); 65 | } 66 | } 67 | } 68 | 69 | void _generateConfigFile(File configFile) { 70 | try { 71 | configFile.writeAsStringSync(_configFileTemplate); 72 | 73 | print('\nConfig file generated successfully 🎉'); 74 | print( 75 | 'You can now use this new config file by using the command below:\n\n' 76 | 'flutter pub run flutter_launcher_icons' 77 | '${configFile.path == _defaultConfigFileName ? '' : ' -f ${configFile.path}'}\n', 78 | ); 79 | } on Exception catch (e) { 80 | print('Error generating config file: $e'); 81 | } 82 | } 83 | 84 | const _configFileTemplate = ''' 85 | # flutter pub run flutter_launcher_icons 86 | flutter_launcher_icons: 87 | image_path: "assets/icon/icon.png" 88 | 89 | android: "launcher_icon" 90 | # image_path_android: "assets/icon/icon.png" 91 | min_sdk_android: 21 # android min sdk min:16, default 21 92 | # adaptive_icon_background: "assets/icon/background.png" 93 | # adaptive_icon_foreground: "assets/icon/foreground.png" 94 | # adaptive_icon_monochrome: "assets/icon/monochrome.png" 95 | 96 | ios: true 97 | # image_path_ios: "assets/icon/icon.png" 98 | remove_alpha_channel_ios: true 99 | # image_path_ios_dark_transparent: "assets/icon/icon_dark.png" 100 | # image_path_ios_tinted_grayscale: "assets/icon/icon_tinted.png" 101 | # desaturate_tinted_to_grayscale_ios: true 102 | 103 | web: 104 | generate: true 105 | image_path: "path/to/image.png" 106 | background_color: "#hexcode" 107 | theme_color: "#hexcode" 108 | 109 | windows: 110 | generate: true 111 | image_path: "path/to/image.png" 112 | icon_size: 48 # min:48, max:256, default: 48 113 | 114 | macos: 115 | generate: true 116 | image_path: "path/to/image.png" 117 | '''; 118 | -------------------------------------------------------------------------------- /bin/main.dart: -------------------------------------------------------------------------------- 1 | import 'flutter_launcher_icons.dart' as flutter_launcher_icons; 2 | 3 | void main(List arguments) { 4 | print( 5 | 'This command is deprecated and replaced with "flutter pub run flutter_launcher_icons"', 6 | ); 7 | flutter_launcher_icons.main(arguments); 8 | } 9 | -------------------------------------------------------------------------------- /example/default_example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled. 5 | 6 | version: 7 | revision: f72efea43c3013323d1b95cff571f3c1caa37583 8 | channel: stable 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: f72efea43c3013323d1b95cff571f3c1caa37583 17 | base_revision: f72efea43c3013323d1b95cff571f3c1caa37583 18 | - platform: android 19 | create_revision: f72efea43c3013323d1b95cff571f3c1caa37583 20 | base_revision: f72efea43c3013323d1b95cff571f3c1caa37583 21 | - platform: ios 22 | create_revision: f72efea43c3013323d1b95cff571f3c1caa37583 23 | base_revision: f72efea43c3013323d1b95cff571f3c1caa37583 24 | - platform: linux 25 | create_revision: f72efea43c3013323d1b95cff571f3c1caa37583 26 | base_revision: f72efea43c3013323d1b95cff571f3c1caa37583 27 | - platform: macos 28 | create_revision: f72efea43c3013323d1b95cff571f3c1caa37583 29 | base_revision: f72efea43c3013323d1b95cff571f3c1caa37583 30 | - platform: web 31 | create_revision: f72efea43c3013323d1b95cff571f3c1caa37583 32 | base_revision: f72efea43c3013323d1b95cff571f3c1caa37583 33 | - platform: windows 34 | create_revision: f72efea43c3013323d1b95cff571f3c1caa37583 35 | base_revision: f72efea43c3013323d1b95cff571f3c1caa37583 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /example/default_example/README.md: -------------------------------------------------------------------------------- 1 | # flutter_launcher_icons 2 | 3 | A new example Flutter project to quickly test flutter_launcher_icons. 4 | 5 | Before being able to run this example you need to navigate to this directory and run the following command 6 | 7 | ``` 8 | flutter create . 9 | ``` 10 | -------------------------------------------------------------------------------- /example/default_example/assets/images/background-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/default_example/assets/images/background-test.png -------------------------------------------------------------------------------- /example/default_example/assets/images/christmas-background.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/default_example/assets/images/christmas-background.png -------------------------------------------------------------------------------- /example/default_example/assets/images/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/default_example/assets/images/ic_launcher.png -------------------------------------------------------------------------------- /example/default_example/assets/images/icon-1024x1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/default_example/assets/images/icon-1024x1024.png -------------------------------------------------------------------------------- /example/default_example/assets/images/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/default_example/assets/images/icon-128x128.png -------------------------------------------------------------------------------- /example/default_example/assets/images/icon-710x599-android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/default_example/assets/images/icon-710x599-android.png -------------------------------------------------------------------------------- /example/default_example/assets/images/icon-710x599-ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/default_example/assets/images/icon-710x599-ios.png -------------------------------------------------------------------------------- /example/default_example/assets/images/icon-710x599.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/default_example/assets/images/icon-710x599.png -------------------------------------------------------------------------------- /example/default_example/assets/images/icon-foreground-432x432.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/default_example/assets/images/icon-foreground-432x432.png -------------------------------------------------------------------------------- /example/default_example/assets/images/icon-monochrome-432x432.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/default_example/assets/images/icon-monochrome-432x432.png -------------------------------------------------------------------------------- /example/default_example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: A new Flutter project to quickly test flutter_launcher_icons. 3 | 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.17.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | cupertino_icons: ^1.0.3 13 | flutter_launcher_icons: 14 | path: ../.. 15 | 16 | flutter_launcher_icons: 17 | # image_path: "assets/images/icon-128x128.png" 18 | image_path_android: "assets/images/icon-710x599-android.png" 19 | image_path_ios: "assets/images/icon-1024x1024.png" 20 | android: true # can specify file name here e.g. "ic_launcher" 21 | ios: true # can specify file name here e.g. "My-Launcher-Icon" 22 | adaptive_icon_background: "assets/images/christmas-background.png" # only available for Android 8.0 devices and above 23 | adaptive_icon_foreground: "assets/images/icon-foreground-432x432.png" # only available for Android 8.0 devices and above 24 | adaptive_icon_foreground_inset: 16 # only available for Android 8.0 devices and above 25 | adaptive_icon_monochrome: "assets/images/icon-monochrome-432x432.png" # only available for Android 13 devices and above 26 | min_sdk_android: 21 # android min sdk min:16, default 21 27 | remove_alpha_ios: true 28 | background_color_ios: "#ffffff" 29 | web: 30 | generate: true 31 | image_path: "assets/images/icon-1024x1024.png" 32 | background_color: "#hexcode" 33 | theme_color: "#hexcode" 34 | windows: 35 | generate: true 36 | image_path: "assets/images/icon-1024x1024.png" 37 | icon_size: 48 # min:48, max:256, default: 48 38 | macos: 39 | generate: true 40 | image_path: "assets/images/icon-1024x1024.png" 41 | 42 | dev_dependencies: 43 | flutter_test: 44 | sdk: flutter 45 | 46 | flutter: 47 | uses-material-design: true 48 | -------------------------------------------------------------------------------- /example/example.md: -------------------------------------------------------------------------------- 1 | # flutter launcher icons examples 2 | 3 | - [default_example](https://github.com/fluttercommunity/flutter_launcher_icons/tree/master/example/default_example) 4 | 5 | - [flavors_example](https://github.com/fluttercommunity/flutter_launcher_icons/tree/master/example/flavors) 6 | -------------------------------------------------------------------------------- /example/flavors/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: f18b9281c2280c2646aa3d4348715ed5bb9446c8 8 | channel: beta 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/flavors/README.md: -------------------------------------------------------------------------------- 1 | # example_with_flavors 2 | 3 | A new Flutter project to showcase how to use different icons 4 | 5 | ## How to run this project 6 | 7 | Before being able to run this example you need to navigate to this directory and run the following command 8 | 9 | ``` 10 | flutter create . 11 | ``` 12 | 13 | This project has the following flavors: 14 | 15 | - production: `flutter run --flavor production` 16 | - development: `flutter run --flavor development` 17 | - integration: `flutter run --flavor integration` 18 | -------------------------------------------------------------------------------- /example/flavors/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /example/flavors/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 28 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | applicationId "com.flutter.icons.example_with_flavors" 41 | minSdkVersion 23 42 | targetSdkVersion 28 43 | versionCode flutterVersionCode.toInteger() 44 | versionName flutterVersionName 45 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 46 | } 47 | 48 | flavorDimensions "default" 49 | productFlavors { 50 | production { 51 | dimension "default" 52 | applicationIdSuffix "" 53 | manifestPlaceholders = [appName: "example_with_flavors"] 54 | } 55 | development { 56 | dimension "default" 57 | applicationIdSuffix ".dev" 58 | manifestPlaceholders = [appName: "[DEV] example_with_flavors"] 59 | } 60 | integration { 61 | dimension "default" 62 | applicationIdSuffix ".int" 63 | manifestPlaceholders = [appName: "[INT] example_with_flavors"] 64 | } 65 | } 66 | 67 | buildTypes { 68 | release { 69 | // TODO: Add your own signing config for the release build. 70 | // Signing with the debug keys for now, so `flutter run --release` works. 71 | signingConfig signingConfigs.debug 72 | } 73 | } 74 | } 75 | 76 | flutter { 77 | source '../..' 78 | } 79 | 80 | dependencies { 81 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 82 | testImplementation 'junit:junit:4.12' 83 | androidTestImplementation 'androidx.test:runner:1.1.1' 84 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 85 | } 86 | -------------------------------------------------------------------------------- /example/flavors/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/flavors/android/app/src/development/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/development/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/development/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/development/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/development/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/development/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/development/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/development/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/development/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/development/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/integration/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/integration/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/integration/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/integration/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/integration/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/integration/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/integration/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/integration/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/integration/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/integration/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 12 | 19 | 20 | 21 | 22 | 23 | 24 | 26 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /example/flavors/android/app/src/main/kotlin/com/flutter/icons/example_with_flavors/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.flutter.icons.example_with_flavors 2 | 3 | import androidx.annotation.NonNull; 4 | import io.flutter.embedding.android.FlutterActivity 5 | import io.flutter.embedding.engine.FlutterEngine 6 | import io.flutter.plugins.GeneratedPluginRegistrant 7 | 8 | class MainActivity: FlutterActivity() { 9 | override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { 10 | GeneratedPluginRegistrant.registerWith(flutterEngine); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /example/flavors/android/app/src/main/kotlin/com/flutter/icons/flavors/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.flutter.icons.flavors 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/flavors/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/flavors/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/flavors/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/flavors/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /example/flavors/android/app/src/production/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/production/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/production/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/production/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/production/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/production/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/production/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/production/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/production/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/android/app/src/production/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/flavors/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/flavors/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /example/flavors/android/flavors_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /example/flavors/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /example/flavors/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /example/flavors/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /example/flavors/assets/launcher_icon/demo-icon-dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/assets/launcher_icon/demo-icon-dev.png -------------------------------------------------------------------------------- /example/flavors/assets/launcher_icon/demo-icon-int.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/assets/launcher_icon/demo-icon-int.png -------------------------------------------------------------------------------- /example/flavors/assets/launcher_icon/demo-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/example/flavors/assets/launcher_icon/demo-icon.png -------------------------------------------------------------------------------- /example/flavors/flavors.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /example/flavors/flutter_launcher_icons-development.yaml: -------------------------------------------------------------------------------- 1 | flutter_launcher_icons: 2 | android: true 3 | ios: true 4 | image_path: "assets/launcher_icon/demo-icon-dev.png" 5 | -------------------------------------------------------------------------------- /example/flavors/flutter_launcher_icons-integration.yaml: -------------------------------------------------------------------------------- 1 | flutter_launcher_icons: 2 | android: true 3 | ios: true 4 | image_path: "assets/launcher_icon/demo-icon-int.png" 5 | -------------------------------------------------------------------------------- /example/flavors/flutter_launcher_icons-production.yaml: -------------------------------------------------------------------------------- 1 | flutter_launcher_icons: 2 | android: true 3 | ios: true 4 | image_path: "assets/launcher_icon/demo-icon.png" -------------------------------------------------------------------------------- /example/flavors/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example_with_flavors 2 | description: A new Flutter project. 3 | version: 1.0.0+1 4 | 5 | environment: 6 | sdk: ">=2.12.0 <3.0.0" 7 | 8 | dependencies: 9 | flutter: 10 | sdk: flutter 11 | flutter_launcher_icons: 12 | path: ../.. 13 | 14 | flutter: 15 | uses-material-design: true 16 | -------------------------------------------------------------------------------- /flutter_launcher_icons.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "name": "Flutter Launcher Icons", 5 | "path": "." 6 | } 7 | ], 8 | "settings": { 9 | "[dart]": { 10 | "editor.formatOnSave": true, 11 | // "editor.formatOnType": true, 12 | "editor.rulers": [ 13 | 120 14 | ], 15 | "editor.selectionHighlight": false, 16 | "editor.suggest.snippetsPreventQuickSuggestions": false, 17 | "editor.suggestSelection": "first", 18 | "editor.tabCompletion": "onlySnippets", 19 | "editor.wordBasedSuggestions": false, 20 | "files.insertFinalNewline": true 21 | }, 22 | "dart.lineLength": 120, 23 | } 24 | } -------------------------------------------------------------------------------- /lib/abs/icon_generator.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter_launcher_icons/config/config.dart'; 4 | import 'package:flutter_launcher_icons/config/macos_config.dart'; 5 | import 'package:flutter_launcher_icons/config/web_config.dart'; 6 | import 'package:flutter_launcher_icons/config/windows_config.dart'; 7 | import 'package:flutter_launcher_icons/logger.dart'; 8 | 9 | /// A base class to generate icons 10 | abstract class IconGenerator { 11 | /// Contains config 12 | final IconGeneratorContext context; 13 | 14 | /// Name of the platform this [IconGenerator] is created for. 15 | final String platformName; 16 | 17 | /// Creates a instance of [IconGenerator]. 18 | /// 19 | /// A [context] is created and provided by [generateIconsFor], 20 | /// [platformName] takes the name of the platform that this [IconGenerator] 21 | /// is implemented for 22 | /// 23 | /// Also Refer 24 | /// - [WebIconGenerator] generate icons for web 25 | /// - [generateIconFor] generates icons for given platform 26 | IconGenerator(this.context, this.platformName); 27 | 28 | /// Creates icons for this platform. 29 | void createIcons(); 30 | 31 | /// Should return `true` if this platform 32 | /// has all the requirements to create icons. 33 | /// This runs before to [createIcons] 34 | bool validateRequirements(); 35 | } 36 | 37 | /// Provides easy access to user arguments and configuration 38 | class IconGeneratorContext { 39 | /// Contains configuration from configuration file 40 | final Config config; 41 | 42 | /// A logger 43 | final FLILogger logger; 44 | 45 | /// Value of `--prefix` flag 46 | final String prefixPath; 47 | 48 | /// Value of `--flavor` flag 49 | final String? flavor; 50 | 51 | /// Creates an instance of [IconGeneratorContext] 52 | IconGeneratorContext({ 53 | required this.config, 54 | required this.logger, 55 | required this.prefixPath, 56 | this.flavor, 57 | }); 58 | 59 | /// Shortcut for `config.webConfig` 60 | WebConfig? get webConfig => config.webConfig; 61 | 62 | /// Shortcut for `config.windowsConfig` 63 | WindowsConfig? get windowsConfig => config.windowsConfig; 64 | 65 | /// Shortcut for `config.macOSConfig` 66 | MacOSConfig? get macOSConfig => config.macOSConfig; 67 | } 68 | 69 | /// Generates Icon for given platforms 70 | void generateIconsFor({ 71 | required Config config, 72 | required String? flavor, 73 | required String prefixPath, 74 | required FLILogger logger, 75 | required List Function(IconGeneratorContext context) platforms, 76 | }) { 77 | try { 78 | final platformList = platforms( 79 | IconGeneratorContext( 80 | config: config, 81 | logger: logger, 82 | prefixPath: prefixPath, 83 | flavor: flavor, 84 | ), 85 | ); 86 | if (platformList.isEmpty) { 87 | // ? maybe we can print help 88 | logger.info('No platform provided'); 89 | } 90 | 91 | for (final platform in platformList) { 92 | final progress = 93 | logger.progress('Creating Icons for ${platform.platformName}'); 94 | logger.verbose( 95 | 'Validating platform requirements for ${platform.platformName}', 96 | ); 97 | // in case a platform throws an exception it should not effect other platforms 98 | try { 99 | if (!platform.validateRequirements()) { 100 | logger.error( 101 | 'Requirements failed for platform ${platform.platformName}. Skipped', 102 | ); 103 | progress.cancel(); 104 | continue; 105 | } 106 | platform.createIcons(); 107 | progress.finish(message: 'done', showTiming: true); 108 | } catch (e, st) { 109 | progress.cancel(); 110 | logger 111 | ..error(e.toString()) 112 | ..verbose(st); 113 | continue; 114 | } 115 | } 116 | } catch (e, st) { 117 | // TODO(RatakondalaArun): better error handling 118 | // stacktrace should only print when verbose is turned on 119 | // else a normal help line 120 | logger 121 | ..error(e.toString()) 122 | ..verbose(st); 123 | exit(1); 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /lib/config/config.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:checked_yaml/checked_yaml.dart' as yaml; 4 | import 'package:flutter_launcher_icons/config/macos_config.dart'; 5 | import 'package:flutter_launcher_icons/config/web_config.dart'; 6 | import 'package:flutter_launcher_icons/config/windows_config.dart'; 7 | import 'package:flutter_launcher_icons/constants.dart' as constants; 8 | import 'package:flutter_launcher_icons/custom_exceptions.dart'; 9 | import 'package:flutter_launcher_icons/utils.dart' as utils; 10 | import 'package:json_annotation/json_annotation.dart'; 11 | import 'package:path/path.dart' as path; 12 | 13 | part 'config.g.dart'; 14 | 15 | /// A model representing the flutter_launcher_icons configuration 16 | @JsonSerializable( 17 | anyMap: true, 18 | checked: true, 19 | ) 20 | class Config { 21 | /// Creates an instance of [Config] 22 | const Config({ 23 | this.imagePath, 24 | this.android = false, 25 | this.ios = false, 26 | this.imagePathAndroid, 27 | this.imagePathIOS, 28 | this.imagePathIOSDarkTransparent, 29 | this.imagePathIOSTintedGrayscale, 30 | this.adaptiveIconForeground, 31 | this.adaptiveIconForegroundInset = 16, 32 | this.adaptiveIconBackground, 33 | this.adaptiveIconMonochrome, 34 | this.minSdkAndroid = constants.androidDefaultAndroidMinSDK, 35 | this.removeAlphaIOS = false, 36 | this.desaturateTintedToGrayscaleIOS = false, 37 | this.backgroundColorIOS = '#ffffff', 38 | this.webConfig, 39 | this.windowsConfig, 40 | this.macOSConfig, 41 | }); 42 | 43 | /// Creates [Config] for given [flavor] and [prefixPath] 44 | static Config? loadConfigFromFlavor( 45 | String flavor, 46 | String prefixPath, 47 | ) { 48 | return _getConfigFromPubspecYaml( 49 | prefix: prefixPath, 50 | pathToPubspecYamlFile: utils.flavorConfigFile(flavor), 51 | ); 52 | } 53 | 54 | /// Loads flutter launcher icons configs from given [filePath] 55 | static Config? loadConfigFromPath(String filePath, String prefixPath) { 56 | return _getConfigFromPubspecYaml( 57 | prefix: prefixPath, 58 | pathToPubspecYamlFile: filePath, 59 | ); 60 | } 61 | 62 | /// Loads flutter launcher icons config from `pubspec.yaml` file 63 | static Config? loadConfigFromPubSpec(String prefix) { 64 | return _getConfigFromPubspecYaml( 65 | prefix: prefix, 66 | pathToPubspecYamlFile: constants.pubspecFilePath, 67 | ); 68 | } 69 | 70 | static Config? _getConfigFromPubspecYaml({ 71 | required String pathToPubspecYamlFile, 72 | required String prefix, 73 | }) { 74 | final configFile = File(path.join(prefix, pathToPubspecYamlFile)); 75 | if (!configFile.existsSync()) { 76 | return null; 77 | } 78 | final configContent = configFile.readAsStringSync(); 79 | try { 80 | return yaml.checkedYamlDecode( 81 | configContent, 82 | (Map? json) { 83 | if (json != null) { 84 | // if we have flutter_icons configuration ... 85 | if (json['flutter_icons'] != null) { 86 | stderr.writeln('\n⚠ Warning: flutter_icons has been deprecated ' 87 | 'please use flutter_launcher_icons instead in your yaml files'); 88 | return Config.fromJson(json['flutter_icons']); 89 | } 90 | // if we have flutter_launcher_icons configuration ... 91 | if (json['flutter_launcher_icons'] != null) { 92 | return Config.fromJson(json['flutter_launcher_icons']); 93 | } 94 | } 95 | return null; 96 | }, 97 | allowNull: true, 98 | ); 99 | } on yaml.ParsedYamlException catch (e) { 100 | throw InvalidConfigException(e.formattedMessage); 101 | } catch (e) { 102 | rethrow; 103 | } 104 | } 105 | 106 | /// Generic image_path 107 | @JsonKey(name: 'image_path') 108 | final String? imagePath; 109 | 110 | /// Returns true or path if android config is enabled 111 | final dynamic android; // path or bool 112 | 113 | /// Returns true or path if ios config is enabled 114 | final dynamic ios; // path or bool 115 | 116 | /// Image path specific to android 117 | @JsonKey(name: 'image_path_android') 118 | final String? imagePathAndroid; 119 | 120 | /// Image path specific to ios 121 | @JsonKey(name: 'image_path_ios') 122 | final String? imagePathIOS; 123 | 124 | /// IOS image_path_ios_dark_transparent 125 | @JsonKey(name: 'image_path_ios_dark_transparent') 126 | final String? imagePathIOSDarkTransparent; 127 | 128 | /// IOS image_path_ios_tinted_grayscale 129 | @JsonKey(name: 'image_path_ios_tinted_grayscale') 130 | final String? imagePathIOSTintedGrayscale; 131 | 132 | /// android adaptive_icon_foreground image 133 | @JsonKey(name: 'adaptive_icon_foreground') 134 | final String? adaptiveIconForeground; 135 | 136 | /// android adaptive_icon_foreground inset 137 | @JsonKey(name: 'adaptive_icon_foreground_inset') 138 | final int adaptiveIconForegroundInset; 139 | 140 | /// android adaptive_icon_background image 141 | @JsonKey(name: 'adaptive_icon_background') 142 | final String? adaptiveIconBackground; 143 | 144 | /// android adaptive_icon_background image 145 | @JsonKey(name: 'adaptive_icon_monochrome') 146 | final String? adaptiveIconMonochrome; 147 | 148 | /// Android min_sdk_android 149 | @JsonKey(name: 'min_sdk_android') 150 | final int minSdkAndroid; 151 | 152 | /// IOS remove_alpha_ios 153 | @JsonKey(name: 'remove_alpha_ios') 154 | final bool removeAlphaIOS; 155 | 156 | /// IOS desaturate_tinted_to_grayscale 157 | @JsonKey(name: 'desaturate_tinted_to_grayscale_ios') 158 | final bool desaturateTintedToGrayscaleIOS; 159 | 160 | /// IOS background_color_ios 161 | @JsonKey(name: 'background_color_ios') 162 | final String backgroundColorIOS; 163 | 164 | /// Web platform config 165 | @JsonKey(name: 'web') 166 | final WebConfig? webConfig; 167 | 168 | /// Windows platform config 169 | @JsonKey(name: 'windows') 170 | final WindowsConfig? windowsConfig; 171 | 172 | /// MacOS platform config 173 | @JsonKey(name: 'macos') 174 | final MacOSConfig? macOSConfig; 175 | 176 | /// Creates [Config] icons from [json] 177 | factory Config.fromJson(Map json) => _$ConfigFromJson(json); 178 | 179 | /// whether or not there is configuration for adaptive icons for android 180 | bool get hasAndroidAdaptiveConfig => 181 | isNeedingNewAndroidIcon && 182 | adaptiveIconForeground != null && 183 | adaptiveIconBackground != null; 184 | 185 | /// whether or not there is configuration for monochrome icons for android 186 | bool get hasAndroidAdaptiveMonochromeConfig { 187 | return isNeedingNewAndroidIcon && adaptiveIconMonochrome != null; 188 | } 189 | 190 | /// Checks if contains any platform config 191 | bool get hasPlatformConfig { 192 | return ios != false || 193 | android != false || 194 | webConfig != null || 195 | windowsConfig != null || 196 | macOSConfig != null; 197 | } 198 | 199 | /// Whether or not configuration for generating Web icons exist 200 | bool get hasWebConfig => webConfig != null; 201 | 202 | /// Whether or not configuration for generating Windows icons exist 203 | bool get hasWindowsConfig => windowsConfig != null; 204 | 205 | /// Whether or not configuration for generating MacOS icons exists 206 | bool get hasMacOSConfig => macOSConfig != null; 207 | 208 | /// Check to see if specified Android config is a string or bool 209 | /// String - Generate new launcher icon with the string specified 210 | /// bool - override the default flutter project icon 211 | bool get isCustomAndroidFile => android is String; 212 | 213 | /// if we are needing a new Android icon 214 | bool get isNeedingNewAndroidIcon => android != false; 215 | 216 | /// if we are needing a new iOS icon 217 | bool get isNeedingNewIOSIcon => ios != false; 218 | 219 | /// Method for the retrieval of the Android icon path 220 | /// If image_path_android is found, this will be prioritised over the image_path 221 | /// value. 222 | String? getImagePathAndroid() => imagePathAndroid ?? imagePath; 223 | 224 | // TODO(RatakondalaArun): refactor after Android & iOS configs will be refactored to the new schema 225 | // https://github.com/fluttercommunity/flutter_launcher_icons/issues/394 226 | /// get the image path for IOS 227 | String? getImagePathIOS() => imagePathIOS ?? imagePath; 228 | 229 | /// Converts config to [Map] 230 | Map toJson() => _$ConfigToJson(this); 231 | 232 | @override 233 | String toString() => 'FlutterLauncherIconsConfig: ${toJson()}'; 234 | } 235 | -------------------------------------------------------------------------------- /lib/config/config.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'config.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Config _$ConfigFromJson(Map json) => $checkedCreate( 10 | 'Config', 11 | json, 12 | ($checkedConvert) { 13 | final val = Config( 14 | imagePath: $checkedConvert('image_path', (v) => v as String?), 15 | android: $checkedConvert('android', (v) => v ?? false), 16 | ios: $checkedConvert('ios', (v) => v ?? false), 17 | imagePathAndroid: 18 | $checkedConvert('image_path_android', (v) => v as String?), 19 | imagePathIOS: $checkedConvert('image_path_ios', (v) => v as String?), 20 | imagePathIOSDarkTransparent: $checkedConvert( 21 | 'image_path_ios_dark_transparent', (v) => v as String?), 22 | imagePathIOSTintedGrayscale: $checkedConvert( 23 | 'image_path_ios_tinted_grayscale', (v) => v as String?), 24 | adaptiveIconForeground: 25 | $checkedConvert('adaptive_icon_foreground', (v) => v as String?), 26 | adaptiveIconForegroundInset: $checkedConvert( 27 | 'adaptive_icon_foreground_inset', 28 | (v) => (v as num?)?.toInt() ?? 16), 29 | adaptiveIconBackground: 30 | $checkedConvert('adaptive_icon_background', (v) => v as String?), 31 | adaptiveIconMonochrome: 32 | $checkedConvert('adaptive_icon_monochrome', (v) => v as String?), 33 | minSdkAndroid: $checkedConvert( 34 | 'min_sdk_android', 35 | (v) => 36 | (v as num?)?.toInt() ?? 37 | constants.androidDefaultAndroidMinSDK), 38 | removeAlphaIOS: 39 | $checkedConvert('remove_alpha_ios', (v) => v as bool? ?? false), 40 | desaturateTintedToGrayscaleIOS: $checkedConvert( 41 | 'desaturate_tinted_to_grayscale_ios', (v) => v as bool? ?? false), 42 | backgroundColorIOS: $checkedConvert( 43 | 'background_color_ios', (v) => v as String? ?? '#ffffff'), 44 | webConfig: $checkedConvert( 45 | 'web', (v) => v == null ? null : WebConfig.fromJson(v as Map)), 46 | windowsConfig: $checkedConvert('windows', 47 | (v) => v == null ? null : WindowsConfig.fromJson(v as Map)), 48 | macOSConfig: $checkedConvert('macos', 49 | (v) => v == null ? null : MacOSConfig.fromJson(v as Map)), 50 | ); 51 | return val; 52 | }, 53 | fieldKeyMap: const { 54 | 'imagePath': 'image_path', 55 | 'imagePathAndroid': 'image_path_android', 56 | 'imagePathIOS': 'image_path_ios', 57 | 'imagePathIOSDarkTransparent': 'image_path_ios_dark_transparent', 58 | 'imagePathIOSTintedGrayscale': 'image_path_ios_tinted_grayscale', 59 | 'adaptiveIconForeground': 'adaptive_icon_foreground', 60 | 'adaptiveIconForegroundInset': 'adaptive_icon_foreground_inset', 61 | 'adaptiveIconBackground': 'adaptive_icon_background', 62 | 'adaptiveIconMonochrome': 'adaptive_icon_monochrome', 63 | 'minSdkAndroid': 'min_sdk_android', 64 | 'removeAlphaIOS': 'remove_alpha_ios', 65 | 'desaturateTintedToGrayscaleIOS': 'desaturate_tinted_to_grayscale_ios', 66 | 'backgroundColorIOS': 'background_color_ios', 67 | 'webConfig': 'web', 68 | 'windowsConfig': 'windows', 69 | 'macOSConfig': 'macos' 70 | }, 71 | ); 72 | 73 | Map _$ConfigToJson(Config instance) => { 74 | 'image_path': instance.imagePath, 75 | 'android': instance.android, 76 | 'ios': instance.ios, 77 | 'image_path_android': instance.imagePathAndroid, 78 | 'image_path_ios': instance.imagePathIOS, 79 | 'image_path_ios_dark_transparent': instance.imagePathIOSDarkTransparent, 80 | 'image_path_ios_tinted_grayscale': instance.imagePathIOSTintedGrayscale, 81 | 'adaptive_icon_foreground': instance.adaptiveIconForeground, 82 | 'adaptive_icon_foreground_inset': instance.adaptiveIconForegroundInset, 83 | 'adaptive_icon_background': instance.adaptiveIconBackground, 84 | 'adaptive_icon_monochrome': instance.adaptiveIconMonochrome, 85 | 'min_sdk_android': instance.minSdkAndroid, 86 | 'remove_alpha_ios': instance.removeAlphaIOS, 87 | 'desaturate_tinted_to_grayscale_ios': 88 | instance.desaturateTintedToGrayscaleIOS, 89 | 'background_color_ios': instance.backgroundColorIOS, 90 | 'web': instance.webConfig, 91 | 'windows': instance.windowsConfig, 92 | 'macos': instance.macOSConfig, 93 | }; 94 | -------------------------------------------------------------------------------- /lib/config/macos_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'macos_config.g.dart'; 4 | 5 | /// The flutter_launcher_icons configuration set for MacOS 6 | @JsonSerializable( 7 | anyMap: true, 8 | checked: true, 9 | ) 10 | class MacOSConfig { 11 | /// Specifies weather to generate icons for macos 12 | @JsonKey() 13 | final bool generate; 14 | 15 | /// Image path for macos 16 | @JsonKey(name: 'image_path') 17 | final String? imagePath; 18 | 19 | /// Creates a instance of [MacOSConfig] 20 | const MacOSConfig({ 21 | this.generate = false, 22 | this.imagePath, 23 | }); 24 | 25 | /// Creates [WebConfig] from [json] 26 | factory MacOSConfig.fromJson(Map json) => _$MacOSConfigFromJson(json); 27 | 28 | /// Creates [Map] from [WebConfig] 29 | Map toJson() => _$MacOSConfigToJson(this); 30 | 31 | @override 32 | String toString() => '$runtimeType: ${toJson()}'; 33 | } 34 | -------------------------------------------------------------------------------- /lib/config/macos_config.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'macos_config.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | MacOSConfig _$MacOSConfigFromJson(Map json) => $checkedCreate( 10 | 'MacOSConfig', 11 | json, 12 | ($checkedConvert) { 13 | final val = MacOSConfig( 14 | generate: $checkedConvert('generate', (v) => v as bool? ?? false), 15 | imagePath: $checkedConvert('image_path', (v) => v as String?), 16 | ); 17 | return val; 18 | }, 19 | fieldKeyMap: const {'imagePath': 'image_path'}, 20 | ); 21 | 22 | Map _$MacOSConfigToJson(MacOSConfig instance) => 23 | { 24 | 'generate': instance.generate, 25 | 'image_path': instance.imagePath, 26 | }; 27 | -------------------------------------------------------------------------------- /lib/config/web_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'web_config.g.dart'; 4 | 5 | /// The flutter_launcher_icons configuration set for Web 6 | @JsonSerializable( 7 | anyMap: true, 8 | checked: true, 9 | ) 10 | class WebConfig { 11 | /// Specifies weather to generate icons for web 12 | final bool generate; 13 | 14 | /// Image path for web 15 | @JsonKey(name: 'image_path') 16 | final String? imagePath; 17 | 18 | /// manifest.json's background_color 19 | @JsonKey(name: 'background_color') 20 | final String? backgroundColor; 21 | 22 | /// manifest.json's theme_color 23 | @JsonKey(name: 'theme_color') 24 | final String? themeColor; 25 | 26 | /// Creates an instance of [WebConfig] 27 | const WebConfig({ 28 | this.generate = false, 29 | this.imagePath, 30 | this.backgroundColor, 31 | this.themeColor, 32 | }); 33 | 34 | /// Creates [WebConfig] from [json] 35 | factory WebConfig.fromJson(Map json) => _$WebConfigFromJson(json); 36 | 37 | /// Creates [Map] from [WebConfig] 38 | Map toJson() => _$WebConfigToJson(this); 39 | 40 | @override 41 | String toString() => 'WebConfig: ${toJson()}'; 42 | } 43 | -------------------------------------------------------------------------------- /lib/config/web_config.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'web_config.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | WebConfig _$WebConfigFromJson(Map json) => $checkedCreate( 10 | 'WebConfig', 11 | json, 12 | ($checkedConvert) { 13 | final val = WebConfig( 14 | generate: $checkedConvert('generate', (v) => v as bool? ?? false), 15 | imagePath: $checkedConvert('image_path', (v) => v as String?), 16 | backgroundColor: 17 | $checkedConvert('background_color', (v) => v as String?), 18 | themeColor: $checkedConvert('theme_color', (v) => v as String?), 19 | ); 20 | return val; 21 | }, 22 | fieldKeyMap: const { 23 | 'imagePath': 'image_path', 24 | 'backgroundColor': 'background_color', 25 | 'themeColor': 'theme_color' 26 | }, 27 | ); 28 | 29 | Map _$WebConfigToJson(WebConfig instance) => { 30 | 'generate': instance.generate, 31 | 'image_path': instance.imagePath, 32 | 'background_color': instance.backgroundColor, 33 | 'theme_color': instance.themeColor, 34 | }; 35 | -------------------------------------------------------------------------------- /lib/config/windows_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:json_annotation/json_annotation.dart'; 2 | 3 | part 'windows_config.g.dart'; 4 | 5 | /// The flutter_launcher_icons configuration set for Windows 6 | @JsonSerializable( 7 | anyMap: true, 8 | checked: true, 9 | ) 10 | class WindowsConfig { 11 | /// Specifies weather to generate icons for web 12 | final bool generate; 13 | 14 | /// Image path for web 15 | @JsonKey(name: 'image_path') 16 | final String? imagePath; 17 | 18 | /// Size of the icon to generate 19 | @JsonKey(name: 'icon_size') 20 | final int? iconSize; 21 | 22 | /// Creates a instance of [WindowsConfig] 23 | const WindowsConfig({ 24 | this.generate = false, 25 | this.imagePath, 26 | this.iconSize, 27 | }); 28 | 29 | /// Creates [WindowsConfig] from [json] 30 | factory WindowsConfig.fromJson(Map json) => _$WindowsConfigFromJson(json); 31 | 32 | /// Creates [Map] from [WindowsConfig] 33 | Map toJson() => _$WindowsConfigToJson(this); 34 | 35 | @override 36 | String toString() => 'WindowsConfig: ${toJson()}'; 37 | } 38 | -------------------------------------------------------------------------------- /lib/config/windows_config.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'windows_config.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | WindowsConfig _$WindowsConfigFromJson(Map json) => $checkedCreate( 10 | 'WindowsConfig', 11 | json, 12 | ($checkedConvert) { 13 | final val = WindowsConfig( 14 | generate: $checkedConvert('generate', (v) => v as bool? ?? false), 15 | imagePath: $checkedConvert('image_path', (v) => v as String?), 16 | iconSize: $checkedConvert('icon_size', (v) => (v as num?)?.toInt()), 17 | ); 18 | return val; 19 | }, 20 | fieldKeyMap: const {'imagePath': 'image_path', 'iconSize': 'icon_size'}, 21 | ); 22 | 23 | Map _$WindowsConfigToJson(WindowsConfig instance) => 24 | { 25 | 'generate': instance.generate, 26 | 'image_path': instance.imagePath, 27 | 'icon_size': instance.iconSize, 28 | }; 29 | -------------------------------------------------------------------------------- /lib/constants.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | import 'package:path/path.dart' as path; 4 | 5 | /// Relative path to android resource folder 6 | String androidResFolder(String? flavor) => 7 | "android/app/src/${flavor ?? 'main'}/res/"; 8 | 9 | /// Relative path to android colors.xml file 10 | String androidColorsFile(String? flavor) => 11 | "android/app/src/${flavor ?? 'main'}/res/values/colors.xml"; 12 | 13 | const String androidManifestFile = 'android/app/src/main/AndroidManifest.xml'; 14 | const String androidGradleFile = 'android/app/build.gradle'; 15 | const String androidLocalPropertiesFile = 'android/local.properties'; 16 | 17 | /// Relative path to flutter.gradle from flutter sdk path 18 | const String androidFlutterGardlePath = 19 | 'packages/flutter_tools/gradle/flutter.gradle'; 20 | 21 | /// Default min_sdk value for android 22 | /// https://github.com/flutter/flutter/blob/master/packages/flutter_tools/gradle/flutter.gradle#L35-L37 23 | const int androidDefaultAndroidMinSDK = 21; 24 | const String androidFileName = 'ic_launcher.png'; 25 | const String androidAdaptiveForegroundFileName = 'ic_launcher_foreground.png'; 26 | const String androidAdaptiveBackgroundFileName = 'ic_launcher_background.png'; 27 | const String androidAdaptiveMonochromeFileName = 'ic_launcher_monochrome.png'; 28 | String androidAdaptiveXmlFolder(String? flavor) => 29 | androidResFolder(flavor) + 'mipmap-anydpi-v26/'; 30 | const String androidDefaultIconName = 'ic_launcher'; 31 | 32 | const String iosDefaultIconFolder = 33 | 'ios/Runner/Assets.xcassets/AppIcon.appiconset/'; 34 | const String iosAssetFolder = 'ios/Runner/Assets.xcassets/'; 35 | const String iosConfigFile = 'ios/Runner.xcodeproj/project.pbxproj'; 36 | const String iosDefaultIconName = 'Icon-App'; 37 | 38 | // web 39 | /// favicon.ico size 40 | const int kFaviconSize = 16; 41 | 42 | /// Relative web direcotry path 43 | String webDirPath = path.join('web'); 44 | 45 | /// Relative web icons directory path 46 | String webIconsDirPath = path.join(webDirPath, 'icons'); 47 | 48 | /// Relative web manifest.json file path 49 | String webManifestFilePath = path.join(webDirPath, 'manifest.json'); 50 | // TODO(RatakondalaArun): support for other images formats 51 | /// Relative favicon.png path 52 | String webFaviconFilePath = path.join(webDirPath, 'favicon.png'); 53 | 54 | /// Relative index.html file path 55 | String webIndexFilePath = path.join(webDirPath, 'index.html'); 56 | 57 | /// Relative pubspec.yaml path 58 | String pubspecFilePath = path.join('pubspec.yaml'); 59 | 60 | // Windows 61 | /// Relative path to windows directory 62 | String windowsDirPath = path.join('windows'); 63 | 64 | /// Relative path to windows resources directory 65 | String windowsResourcesDirPath = 66 | path.join(windowsDirPath, 'runner', 'resources'); 67 | 68 | /// Relative path to windows icon file path 69 | String windowsIconFilePath = path.join(windowsResourcesDirPath, 'app_icon.ico'); 70 | 71 | /// Default windows icon size for flutter 72 | /// 73 | const int windowsDefaultIconSize = 48; 74 | 75 | // MacOS 76 | 77 | /// Relative path to macos folder 78 | final macOSDirPath = path.join('macos'); 79 | 80 | /// Relative path to macos icons folder 81 | final macOSIconsDirPath = 82 | path.join(macOSDirPath, 'Runner', 'Assets.xcassets', 'AppIcon.appiconset'); 83 | 84 | /// Relative path to macos contents.json 85 | final macOSContentsFilePath = path.join(macOSIconsDirPath, 'Contents.json'); 86 | 87 | const String errorMissingImagePath = 88 | 'Missing "image_path" or "image_path_android" + "image_path_ios" within configuration'; 89 | const String errorMissingPlatform = 90 | 'No platform specified within config to generate icons for.'; 91 | const String errorMissingRegularAndroid = 92 | 'Adaptive icon config found but no regular Android config. ' 93 | 'Below API 26 the regular Android config is required'; 94 | const String errorMissingMinSdk = 95 | 'Cannot not find minSdk from android/app/build.gradle or android/local.properties' 96 | ' Specify minSdk in your flutter_launcher_config.yaml with "min_sdk_android"'; 97 | const String errorIncorrectIconName = 98 | 'The icon name must contain only lowercase a-z, 0-9, or underscore: ' 99 | 'E.g. "ic_my_new_icon"'; 100 | 101 | String introMessage(String currentVersion) => ''' 102 | ════════════════════════════════════════════ 103 | FLUTTER LAUNCHER ICONS (v$currentVersion) 104 | ════════════════════════════════════════════ 105 | '''; 106 | -------------------------------------------------------------------------------- /lib/custom_exceptions.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_launcher_icons/utils.dart'; 2 | 3 | /// Exception to be thrown whenever we have an invalid configuration 4 | class InvalidConfigException implements Exception { 5 | /// Constructs instance 6 | const InvalidConfigException([this.message]); 7 | 8 | /// Message for the exception 9 | final String? message; 10 | 11 | @override 12 | String toString() { 13 | return generateError(this, message); 14 | } 15 | } 16 | 17 | /// Exception to be thrown whenever using an invalid Android icon name 18 | class InvalidAndroidIconNameException implements Exception { 19 | /// Constructs instance of this exception 20 | const InvalidAndroidIconNameException([this.message]); 21 | 22 | /// Message for the exception 23 | final String? message; 24 | 25 | @override 26 | String toString() { 27 | return generateError(this, message); 28 | } 29 | } 30 | 31 | /// Exception to be thrown whenever no config is found 32 | class NoConfigFoundException implements Exception { 33 | /// Constructs instance of this exception 34 | const NoConfigFoundException([this.message]); 35 | 36 | /// Message for the exception 37 | final String? message; 38 | 39 | @override 40 | String toString() { 41 | return generateError(this, message); 42 | } 43 | } 44 | 45 | /// Exception to be thrown whenever there is no decoder for the image format 46 | class NoDecoderForImageFormatException implements Exception { 47 | /// Constructs instance of this exception 48 | const NoDecoderForImageFormatException([this.message]); 49 | 50 | /// Message for the exception 51 | final String? message; 52 | 53 | @override 54 | String toString() { 55 | return generateError(this, message); 56 | } 57 | } 58 | 59 | /// A exception to throw when given [fileName] is not found 60 | class FileNotFoundException implements Exception { 61 | /// Creates a instance of [FileNotFoundException]. 62 | const FileNotFoundException(this.fileName); 63 | 64 | /// Name of the file 65 | final String fileName; 66 | 67 | @override 68 | String toString() { 69 | return generateError(this, '$fileName file not found'); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:cli_util/cli_logging.dart'; 2 | 3 | export 'package:cli_util/cli_logging.dart' show Progress; 4 | 5 | /// Flutter Launcher Icons Logger 6 | class FLILogger { 7 | late Logger _logger; 8 | 9 | /// Returns true if this is a verbose logger 10 | final bool isVerbose; 11 | 12 | /// Gives access to internal logger 13 | Logger get rawLogger => _logger; 14 | 15 | /// Creates a instance of [FLILogger]. 16 | /// In case [isVerbose] is `true`, 17 | /// it logs all the [verbose] logs to console 18 | FLILogger(this.isVerbose) { 19 | final ansi = Ansi(Ansi.terminalSupportsAnsi); 20 | _logger = 21 | isVerbose ? Logger.verbose(ansi: ansi) : Logger.standard(ansi: ansi); 22 | } 23 | 24 | /// Logs error messages 25 | void error(Object? message) => _logger.stderr('⚠️' + message.toString()); 26 | 27 | /// Prints to console if [isVerbose] is true 28 | void verbose(Object? message) => _logger.trace(message.toString()); 29 | 30 | /// Prints to console 31 | void info(Object? message) => _logger.stdout(message.toString()); 32 | 33 | /// Shows progress in console 34 | Progress progress(String message) => _logger.progress(message); 35 | } 36 | -------------------------------------------------------------------------------- /lib/macos/macos_icon_generator.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:flutter_launcher_icons/abs/icon_generator.dart'; 5 | import 'package:flutter_launcher_icons/constants.dart' as constants; 6 | import 'package:flutter_launcher_icons/custom_exceptions.dart'; 7 | import 'package:flutter_launcher_icons/macos/macos_icon_template.dart'; 8 | import 'package:flutter_launcher_icons/utils.dart' as utils; 9 | import 'package:image/image.dart'; 10 | import 'package:path/path.dart' as path; 11 | 12 | /// A [IconGenerator] implementation for macos 13 | class MacOSIconGenerator extends IconGenerator { 14 | static const _iconSizeTemplates = [ 15 | MacOSIconTemplate(16, 1), 16 | MacOSIconTemplate(16, 2), 17 | MacOSIconTemplate(32, 1), 18 | MacOSIconTemplate(32, 2), 19 | MacOSIconTemplate(128, 1), 20 | MacOSIconTemplate(128, 2), 21 | MacOSIconTemplate(256, 1), 22 | MacOSIconTemplate(256, 2), 23 | MacOSIconTemplate(512, 1), 24 | MacOSIconTemplate(512, 2), 25 | ]; 26 | 27 | /// Creates a instance of [MacOSIconGenerator] 28 | MacOSIconGenerator(IconGeneratorContext context) : super(context, 'MacOS'); 29 | 30 | @override 31 | void createIcons() { 32 | final imgFilePath = path.join( 33 | context.prefixPath, 34 | context.config.macOSConfig!.imagePath ?? context.config.imagePath, 35 | ); 36 | 37 | context.logger 38 | .verbose('Decoding and loading image file at $imgFilePath...'); 39 | final imgFile = utils.decodeImageFile(imgFilePath); 40 | if (imgFile == null) { 41 | context.logger.error('Image File not found at give path $imgFilePath...'); 42 | throw FileNotFoundException(imgFilePath); 43 | } 44 | 45 | context.logger.verbose('Generating icons $imgFilePath...'); 46 | _generateIcons(imgFile); 47 | context.logger.verbose('Updating contents.json'); 48 | _updateContentsFile(); 49 | } 50 | 51 | @override 52 | bool validateRequirements() { 53 | context.logger.verbose('Checking $platformName config...'); 54 | final macOSConfig = context.macOSConfig; 55 | 56 | if (macOSConfig == null || !macOSConfig.generate) { 57 | context.logger 58 | ..verbose( 59 | '$platformName config is missing or "flutter_icons.macos.generate" is false. Skipped...', 60 | ) 61 | ..verbose(macOSConfig); 62 | return false; 63 | } 64 | 65 | if (macOSConfig.imagePath == null && context.config.imagePath == null) { 66 | context.logger 67 | ..verbose({ 68 | 'flutter_launcher_icons.macos.image_path': macOSConfig.imagePath, 69 | 'flutter_launcher_icons.image_path': context.config.imagePath, 70 | }) 71 | ..error( 72 | 'Missing image_path. Either provide "flutter_launcher_icons.macos.image_path" or "flutter_launcher_icons.image_path"', 73 | ); 74 | 75 | return false; 76 | } 77 | 78 | // this files and folders should exist to create macos icons 79 | final enitiesToCheck = [ 80 | path.join(context.prefixPath, constants.macOSDirPath), 81 | path.join(context.prefixPath, constants.macOSIconsDirPath), 82 | path.join(context.prefixPath, constants.macOSContentsFilePath), 83 | ]; 84 | 85 | final failedEntityPath = utils.areFSEntiesExist(enitiesToCheck); 86 | if (failedEntityPath != null) { 87 | context.logger.error( 88 | '$failedEntityPath this file or folder is required to generate $platformName icons', 89 | ); 90 | return false; 91 | } 92 | 93 | return true; 94 | } 95 | 96 | void _generateIcons(Image image) { 97 | final iconsDir = utils.createDirIfNotExist( 98 | path.join(context.prefixPath, constants.macOSIconsDirPath), 99 | ); 100 | 101 | for (final template in _iconSizeTemplates) { 102 | final resizedImg = utils.createResizedImage(template.scaledSize, image); 103 | final iconFile = utils.createFileIfNotExist( 104 | path.join(context.prefixPath, iconsDir.path, template.iconFile), 105 | ); 106 | iconFile.writeAsBytesSync(encodePng(resizedImg)); 107 | } 108 | } 109 | 110 | void _updateContentsFile() { 111 | final contentsFilePath = 112 | File(path.join(context.prefixPath, constants.macOSContentsFilePath)); 113 | final contentsConfig = 114 | jsonDecode(contentsFilePath.readAsStringSync()) as Map; 115 | contentsConfig 116 | ..remove('images') 117 | ..['images'] = _iconSizeTemplates 118 | .map>((e) => e.iconContent) 119 | .toList(); 120 | 121 | contentsFilePath 122 | .writeAsStringSync(utils.prettifyJsonEncode(contentsConfig)); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /lib/macos/macos_icon_template.dart: -------------------------------------------------------------------------------- 1 | /// A macOS icon template 2 | class MacOSIconTemplate { 3 | /// Icon size 4 | final int size; 5 | 6 | /// Icon scale 7 | final int scale; 8 | 9 | /// Creates an instance of [MacOSIconTemplate] 10 | /// 11 | const MacOSIconTemplate(this.size, this.scale); 12 | 13 | /// Icon content for contents.json' s images 14 | /// 15 | /// ```json 16 | /// { 17 | /// "size" : "16x16", 18 | /// "idiom" : "mac", 19 | /// "filename" : "app_icon_16.png", 20 | /// "scale" : "1x" 21 | /// } 22 | /// ``` 23 | Map get iconContent { 24 | return { 25 | 'size': '${size}x$size', 26 | 'idiom': 'mac', 27 | 'filename': iconFile, 28 | 'scale': '${scale}x', 29 | }; 30 | } 31 | 32 | /// Icon file name with extension 33 | /// 34 | /// `app_icon_16.png` 35 | String get iconFile => 'app_icon_$scaledSize.png'; 36 | 37 | /// Image size after computing scale 38 | int get scaledSize => size * scale; 39 | } 40 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | import 'dart:io'; 4 | 5 | import 'package:args/args.dart'; 6 | import 'package:flutter_launcher_icons/abs/icon_generator.dart'; 7 | import 'package:flutter_launcher_icons/android.dart' as android_launcher_icons; 8 | import 'package:flutter_launcher_icons/config/config.dart'; 9 | import 'package:flutter_launcher_icons/constants.dart' as constants; 10 | import 'package:flutter_launcher_icons/constants.dart'; 11 | import 'package:flutter_launcher_icons/custom_exceptions.dart'; 12 | import 'package:flutter_launcher_icons/ios.dart' as ios_launcher_icons; 13 | import 'package:flutter_launcher_icons/logger.dart'; 14 | import 'package:flutter_launcher_icons/macos/macos_icon_generator.dart'; 15 | import 'package:flutter_launcher_icons/web/web_icon_generator.dart'; 16 | import 'package:flutter_launcher_icons/windows/windows_icon_generator.dart'; 17 | import 'package:path/path.dart' as path; 18 | 19 | const String fileOption = 'file'; 20 | const String helpFlag = 'help'; 21 | const String verboseFlag = 'verbose'; 22 | const String prefixOption = 'prefix'; 23 | const String defaultConfigFile = 'flutter_launcher_icons.yaml'; 24 | const String flavorConfigFilePattern = r'^flutter_launcher_icons-(.*).yaml$'; 25 | 26 | List getFlavors() { 27 | final List flavors = []; 28 | for (var item in Directory('.').listSync()) { 29 | if (item is File) { 30 | final name = path.basename(item.path); 31 | final match = RegExp(flavorConfigFilePattern).firstMatch(name); 32 | if (match != null) { 33 | flavors.add(match.group(1)!); 34 | } 35 | } 36 | } 37 | return flavors; 38 | } 39 | 40 | Future createIconsFromArguments(List arguments) async { 41 | final ArgParser parser = ArgParser(allowTrailingOptions: true); 42 | parser 43 | ..addFlag(helpFlag, abbr: 'h', help: 'Usage help', negatable: false) 44 | // Make default null to differentiate when it is explicitly set 45 | ..addOption( 46 | fileOption, 47 | abbr: 'f', 48 | help: 'Path to config file', 49 | defaultsTo: defaultConfigFile, 50 | ) 51 | ..addFlag(verboseFlag, abbr: 'v', help: 'Verbose output', defaultsTo: false) 52 | ..addOption( 53 | prefixOption, 54 | abbr: 'p', 55 | help: 'Generates config in the given path. Only Supports web platform', 56 | defaultsTo: '.', 57 | ); 58 | 59 | final ArgResults argResults = parser.parse(arguments); 60 | // creating logger based on -v flag 61 | final logger = FLILogger(argResults[verboseFlag]); 62 | 63 | logger.verbose('Received args ${argResults.arguments}'); 64 | 65 | if (argResults[helpFlag]) { 66 | stdout.writeln('Generates icons for iOS and Android'); 67 | stdout.writeln(parser.usage); 68 | exit(0); 69 | } 70 | 71 | // Flavors management 72 | final flavors = getFlavors(); 73 | final hasFlavors = flavors.isNotEmpty; 74 | 75 | final String prefixPath = argResults[prefixOption]; 76 | 77 | // Create icons 78 | if (!hasFlavors) { 79 | // Load configs from given file(defaults to ./flutter_launcher_icons.yaml) or from ./pubspec.yaml 80 | 81 | final flutterLauncherIconsConfigs = 82 | loadConfigFileFromArgResults(argResults); 83 | if (flutterLauncherIconsConfigs == null) { 84 | throw NoConfigFoundException( 85 | 'No configuration found in $defaultConfigFile or in ${constants.pubspecFilePath}. ' 86 | 'In case file exists in different directory use --file option', 87 | ); 88 | } 89 | try { 90 | await createIconsFromConfig( 91 | flutterLauncherIconsConfigs, 92 | logger, 93 | prefixPath, 94 | ); 95 | print('\n✓ Successfully generated launcher icons'); 96 | } catch (e) { 97 | stderr.writeln('\n✕ Could not generate launcher icons'); 98 | stderr.writeln(e); 99 | exit(2); 100 | } 101 | } else { 102 | try { 103 | for (String flavor in flavors) { 104 | print('\nFlavor: $flavor'); 105 | final flutterLauncherIconsConfigs = 106 | Config.loadConfigFromFlavor(flavor, prefixPath); 107 | if (flutterLauncherIconsConfigs == null) { 108 | throw NoConfigFoundException( 109 | 'No configuration found for $flavor flavor.', 110 | ); 111 | } 112 | await createIconsFromConfig( 113 | flutterLauncherIconsConfigs, 114 | logger, 115 | prefixPath, 116 | flavor, 117 | ); 118 | } 119 | print('\n✓ Successfully generated launcher icons for flavors'); 120 | } catch (e) { 121 | stderr.writeln('\n✕ Could not generate launcher icons for flavors'); 122 | stderr.writeln(e); 123 | exit(2); 124 | } 125 | } 126 | } 127 | 128 | Future createIconsFromConfig( 129 | Config flutterConfigs, 130 | FLILogger logger, 131 | String prefixPath, [ 132 | String? flavor, 133 | ]) async { 134 | if (!flutterConfigs.hasPlatformConfig) { 135 | throw const InvalidConfigException(errorMissingPlatform); 136 | } 137 | 138 | if (flutterConfigs.isNeedingNewAndroidIcon) { 139 | android_launcher_icons.createDefaultIcons(flutterConfigs, flavor); 140 | } 141 | if (flutterConfigs.hasAndroidAdaptiveConfig) { 142 | android_launcher_icons.createAdaptiveIcons(flutterConfigs, flavor); 143 | } 144 | if (flutterConfigs.hasAndroidAdaptiveMonochromeConfig) { 145 | android_launcher_icons.createAdaptiveMonochromeIcons( 146 | flutterConfigs, 147 | flavor, 148 | ); 149 | } 150 | if (flutterConfigs.isNeedingNewAndroidIcon) { 151 | android_launcher_icons.createMipmapXmlFile( 152 | flutterConfigs, 153 | flavor, 154 | ); 155 | } 156 | if (flutterConfigs.isNeedingNewIOSIcon) { 157 | ios_launcher_icons.createIcons(flutterConfigs, flavor); 158 | } 159 | 160 | // Generates Icons for given platform 161 | generateIconsFor( 162 | config: flutterConfigs, 163 | logger: logger, 164 | prefixPath: prefixPath, 165 | flavor: flavor, 166 | platforms: (context) { 167 | final platforms = []; 168 | if (flutterConfigs.hasWebConfig) { 169 | platforms.add(WebIconGenerator(context)); 170 | } 171 | if (flutterConfigs.hasWindowsConfig) { 172 | platforms.add(WindowsIconGenerator(context)); 173 | } 174 | if (flutterConfigs.hasMacOSConfig) { 175 | platforms.add(MacOSIconGenerator(context)); 176 | } 177 | return platforms; 178 | }, 179 | ); 180 | } 181 | 182 | Config? loadConfigFileFromArgResults( 183 | ArgResults argResults, 184 | ) { 185 | final String prefixPath = argResults[prefixOption]; 186 | final flutterLauncherIconsConfigs = Config.loadConfigFromPath( 187 | argResults[fileOption], 188 | prefixPath, 189 | ) ?? 190 | Config.loadConfigFromPubSpec(prefixPath); 191 | return flutterLauncherIconsConfigs; 192 | } 193 | -------------------------------------------------------------------------------- /lib/pubspec_parser.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:yaml/yaml.dart'; 4 | 5 | /// helper class for parsing the contents of pubspec file 6 | class PubspecParser { 7 | /// ensures unnamed constructor cannot be used as this class should only have 8 | /// static methods 9 | PubspecParser._(); 10 | 11 | /// parses the pubspec located at [path] to map 12 | static Map fromPathToMap(String path) { 13 | final File file = File(path); 14 | final String yamlString = file.readAsStringSync(); 15 | final Map yamlMap = loadYaml(yamlString); 16 | return yamlMap; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/src/version.dart: -------------------------------------------------------------------------------- 1 | // Generated code. Do not modify. 2 | const packageVersion = '0.14.3'; 3 | -------------------------------------------------------------------------------- /lib/utils.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | import 'dart:convert'; 4 | import 'dart:io'; 5 | 6 | import 'package:image/image.dart'; 7 | import 'package:path/path.dart' as path; 8 | 9 | import 'custom_exceptions.dart'; 10 | 11 | Image createResizedImage(int iconSize, Image image) { 12 | if (image.width >= iconSize) { 13 | return copyResize( 14 | image, 15 | width: iconSize, 16 | height: iconSize, 17 | interpolation: Interpolation.average, 18 | ); 19 | } else { 20 | return copyResize( 21 | image, 22 | width: iconSize, 23 | height: iconSize, 24 | interpolation: Interpolation.linear, 25 | ); 26 | } 27 | } 28 | 29 | void printStatus(String message) { 30 | print('• $message'); 31 | } 32 | 33 | String generateError(Exception e, String? error) { 34 | final errorOutput = error == null ? '' : ' \n$error'; 35 | return '\n✗ ERROR: ${(e).runtimeType.toString()}$errorOutput'; 36 | } 37 | 38 | // TODO(RatakondalaArun): Remove nullable return type 39 | // this can never return null value since it already throws exception 40 | Image? decodeImageFile(String filePath) { 41 | final image = decodeImage(File(filePath).readAsBytesSync()); 42 | if (image == null) { 43 | throw NoDecoderForImageFormatException(filePath); 44 | } 45 | return image; 46 | } 47 | 48 | /// Creates [File] in the given [filePath] if not exists 49 | File createFileIfNotExist(String filePath) { 50 | final file = File(path.joinAll(path.split(filePath))); 51 | if (!file.existsSync()) { 52 | file.createSync(recursive: true); 53 | } 54 | return file; 55 | } 56 | 57 | /// Creates [Directory] in the given [dirPath] if not exists 58 | Directory createDirIfNotExist(String dirPath) { 59 | final dir = Directory(path.joinAll(path.split(dirPath))); 60 | if (!dir.existsSync()) { 61 | dir.createSync(recursive: true); 62 | } 63 | return dir; 64 | } 65 | 66 | /// Returns a prettified json string 67 | String prettifyJsonEncode(Object? map) => 68 | JsonEncoder.withIndent(' ' * 4).convert(map); 69 | 70 | /// Check if give [File] or [Directory] exists at the give [paths], 71 | /// if not returns the failed [FileSystemEntity] path 72 | String? areFSEntiesExist(List paths) { 73 | for (final path in paths) { 74 | final fsType = FileSystemEntity.typeSync(path); 75 | if (![FileSystemEntityType.directory, FileSystemEntityType.file] 76 | .contains(fsType)) { 77 | return path; 78 | } 79 | } 80 | return null; 81 | } 82 | 83 | String flavorConfigFile(String flavor) => 'flutter_launcher_icons-$flavor.yaml'; 84 | -------------------------------------------------------------------------------- /lib/web/web_icon_generator.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:image/image.dart'; 4 | import 'package:path/path.dart' as path; 5 | 6 | import '../abs/icon_generator.dart'; 7 | import '../constants.dart' as constants; 8 | import '../custom_exceptions.dart'; 9 | import '../utils.dart' as utils; 10 | import 'web_template.dart'; 11 | 12 | // This is not yet implemented 13 | // ignore: public_member_api_docs 14 | final metaTagsTemplate = ( 15 | String appleMobileWebAppTitle, 16 | String appleMobileWebAppStatusBarStyle, { 17 | bool shouldInsertFLIString = false, 18 | }) => 19 | ''' 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | '''; 34 | 35 | /// Generates Web icons for flutter 36 | class WebIconGenerator extends IconGenerator { 37 | static const _webIconSizeTemplates = [ 38 | WebIconTemplate(size: 192), 39 | WebIconTemplate(size: 512), 40 | WebIconTemplate(size: 192, maskable: true), 41 | WebIconTemplate(size: 512, maskable: true), 42 | ]; 43 | 44 | /// Creates an instance of [WebIconGenerator]. 45 | /// 46 | /// 47 | WebIconGenerator(IconGeneratorContext context) : super(context, 'Web'); 48 | 49 | @override 50 | void createIcons() { 51 | final imgFilePath = path.join( 52 | context.prefixPath, 53 | context.webConfig!.imagePath ?? context.config.imagePath!, 54 | ); 55 | 56 | context.logger 57 | .verbose('Decoding and loading image file at $imgFilePath...'); 58 | final imgFile = utils.decodeImageFile(imgFilePath); 59 | if (imgFile == null) { 60 | context.logger.error('Image File not found at give path $imgFilePath...'); 61 | throw FileNotFoundException(imgFilePath); 62 | } 63 | 64 | // generate favicon in web/favicon.png 65 | context.logger.verbose('Generating favicon from $imgFilePath...'); 66 | _generateFavicon(imgFile); 67 | 68 | // generate icons in web/icons/ 69 | context.logger.verbose('Generating icons from $imgFilePath...'); 70 | _generateIcons(imgFile); 71 | 72 | // update manifest.json in web/mainfest.json 73 | context.logger.verbose( 74 | 'Updating ${path.join(context.prefixPath, constants.webManifestFilePath)}...', 75 | ); 76 | _updateManifestFile(); 77 | 78 | // TODO(RatakondalaArun): update index.html in web/index.html 79 | // as we are using flutter default config we no need 80 | // to update index.html for now 81 | // _updateIndexFile(); 82 | } 83 | 84 | @override 85 | bool validateRequirements() { 86 | // check if web config exists 87 | context.logger.verbose('Checking webconfig...'); 88 | final webConfig = context.webConfig; 89 | if (webConfig == null || !webConfig.generate) { 90 | context.logger.verbose( 91 | 'Web config is not provided or generate is false. Skipped...', 92 | ); 93 | return false; 94 | } 95 | if (webConfig.imagePath == null && context.config.imagePath == null) { 96 | context.logger 97 | .verbose('Invalid config. Either provide web.imagePath or imagePath'); 98 | return false; 99 | } 100 | 101 | // verify web platform related files and directories exists 102 | final entitesToCheck = [ 103 | path.join(context.prefixPath, constants.webDirPath), 104 | path.join(context.prefixPath, constants.webManifestFilePath), 105 | path.join(context.prefixPath, constants.webIndexFilePath), 106 | ]; 107 | 108 | // web platform related files must exist to continue 109 | final failedEntityPath = utils.areFSEntiesExist(entitesToCheck); 110 | if (failedEntityPath != null) { 111 | context.logger.error( 112 | '$failedEntityPath this file or folder is required to generate web icons', 113 | ); 114 | } 115 | 116 | return true; 117 | } 118 | 119 | void _generateFavicon(Image image) { 120 | final favIcon = utils.createResizedImage(constants.kFaviconSize, image); 121 | final favIconFile = utils.createFileIfNotExist( 122 | path.join(context.prefixPath, constants.webFaviconFilePath), 123 | ); 124 | favIconFile.writeAsBytesSync(encodePng(favIcon)); 125 | } 126 | 127 | void _generateIcons(Image image) { 128 | final iconsDir = utils.createDirIfNotExist( 129 | path.join(context.prefixPath, constants.webIconsDirPath), 130 | ); 131 | // generate icons 132 | for (final template in _webIconSizeTemplates) { 133 | final resizedImg = utils.createResizedImage(template.size, image); 134 | final iconFile = utils.createFileIfNotExist( 135 | path.join(context.prefixPath, iconsDir.path, template.iconFile), 136 | ); 137 | iconFile.writeAsBytesSync(encodePng(resizedImg)); 138 | } 139 | } 140 | 141 | void _updateManifestFile() { 142 | final manifestFile = utils.createFileIfNotExist( 143 | path.join(context.prefixPath, constants.webManifestFilePath), 144 | ); 145 | final manifestConfig = 146 | jsonDecode(manifestFile.readAsStringSync()) as Map; 147 | 148 | // update background_color 149 | if (context.webConfig?.backgroundColor != null) { 150 | manifestConfig['background_color'] = context.webConfig?.backgroundColor; 151 | } 152 | 153 | // update theme_color 154 | if (context.webConfig?.themeColor != null) { 155 | manifestConfig['theme_color'] = context.webConfig?.themeColor; 156 | } 157 | 158 | // replace existing icons to eliminate conflicts 159 | manifestConfig 160 | ..remove('icons') 161 | ..['icons'] = _webIconSizeTemplates 162 | .map>((e) => e.iconManifest) 163 | .toList(); 164 | 165 | manifestFile.writeAsStringSync(utils.prettifyJsonEncode(manifestConfig)); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /lib/web/web_template.dart: -------------------------------------------------------------------------------- 1 | /// A Icon Template for Web 2 | class WebIconTemplate { 3 | /// Size of the web icon 4 | final int size; 5 | 6 | /// Support for maskable icon 7 | /// 8 | /// Refer to https://web.dev/maskable-icon/ 9 | final bool maskable; 10 | 11 | /// Creates an instance of [WebIconTemplate]. 12 | const WebIconTemplate({ 13 | required this.size, 14 | this.maskable = false, 15 | }); 16 | 17 | /// Icon file name 18 | String get iconFile => 'Icon${maskable ? '-maskable' : ''}-$size.png'; 19 | 20 | /// Icon config for manifest.json 21 | /// 22 | /// ```json 23 | /// { 24 | /// "src": "icons/Icon-maskable-192.png", 25 | /// "sizes": "192x192", 26 | /// "type": "image/png", 27 | /// "purpose": "maskable" 28 | /// }, 29 | /// ``` 30 | Map get iconManifest { 31 | return { 32 | 'src': 'icons/$iconFile', 33 | 'sizes': '${size}x$size', 34 | 'type': 'image/png', 35 | if (maskable) 'purpose': 'maskable', 36 | }; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/windows/windows_icon_generator.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_launcher_icons/abs/icon_generator.dart'; 2 | import 'package:flutter_launcher_icons/constants.dart' as constants; 3 | import 'package:flutter_launcher_icons/custom_exceptions.dart'; 4 | import 'package:flutter_launcher_icons/utils.dart' as utils; 5 | import 'package:image/image.dart'; 6 | import 'package:path/path.dart' as path; 7 | 8 | /// A Implementation of [IconGenerator] for Windows 9 | class WindowsIconGenerator extends IconGenerator { 10 | /// Creates a instance of [WindowsIconGenerator] 11 | WindowsIconGenerator(IconGeneratorContext context) 12 | : super(context, 'Windows'); 13 | 14 | @override 15 | void createIcons() { 16 | final imgFilePath = path.join( 17 | context.prefixPath, 18 | context.windowsConfig!.imagePath ?? context.config.imagePath, 19 | ); 20 | 21 | context.logger 22 | .verbose('Decoding and loading image file from $imgFilePath...'); 23 | final imgFile = utils.decodeImageFile(imgFilePath); 24 | // TODO(RatakondalaArun): remove null check 25 | // #utils.decodeImageFile never returns null instead it throws Exception 26 | if (imgFile == null) { 27 | context.logger 28 | .error('Image File not found at given path $imgFilePath...'); 29 | throw FileNotFoundException(imgFilePath); 30 | } 31 | 32 | context.logger.verbose('Generating icon from $imgFilePath...'); 33 | _generateIcon(imgFile); 34 | } 35 | 36 | @override 37 | bool validateRequirements() { 38 | context.logger.verbose('Validating windows config...'); 39 | final windowsConfig = context.windowsConfig; 40 | if (windowsConfig == null || !windowsConfig.generate) { 41 | context.logger.error( 42 | 'Windows config is not provided or windows.generate is false. Skipped...', 43 | ); 44 | return false; 45 | } 46 | 47 | if (windowsConfig.imagePath == null && context.config.imagePath == null) { 48 | context.logger.error( 49 | 'Invalid config. Either provide windows.image_path or image_path', 50 | ); 51 | return false; 52 | } 53 | 54 | // if icon_size is given it should be between 48<=icon_size<=256 55 | // because .ico only supports this size 56 | if (windowsConfig.iconSize != null && 57 | (windowsConfig.iconSize! < 48 || windowsConfig.iconSize! > 256)) { 58 | context.logger.error( 59 | 'Invalid windows.icon_size=${windowsConfig.iconSize}. Icon size should be between 48<=icon_size<=256', 60 | ); 61 | return false; 62 | } 63 | final entitesToCheck = [ 64 | path.join(context.prefixPath, constants.windowsDirPath), 65 | path.join( 66 | context.prefixPath, 67 | windowsConfig.imagePath ?? context.config.imagePath, 68 | ), 69 | ]; 70 | 71 | final failedEntityPath = utils.areFSEntiesExist(entitesToCheck); 72 | if (failedEntityPath != null) { 73 | context.logger.error( 74 | '$failedEntityPath this file or folder is required to generate web icons', 75 | ); 76 | return false; 77 | } 78 | 79 | return true; 80 | } 81 | 82 | void _generateIcon(Image image) { 83 | final favIcon = utils.createResizedImage( 84 | context.windowsConfig!.iconSize ?? constants.windowsDefaultIconSize, 85 | image, 86 | ); 87 | final favIconFile = utils.createFileIfNotExist( 88 | path.join(context.prefixPath, constants.windowsIconFilePath), 89 | ); 90 | favIconFile.writeAsBytesSync(encodeIco(favIcon)); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/xml_templates.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs 2 | 3 | const String mipmapXmlFile = ''' 4 | 5 | 6 | {{CONTENT}} 7 | '''; 8 | 9 | const String colorsXml = ''' 10 | 11 | 12 | #FF000000 13 | 14 | '''; 15 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_launcher_icons 2 | description: A package which simplifies the task of updating your Flutter app's launcher icon. 3 | version: 0.14.3 4 | maintainer: Mark O'Sullivan (@MarkOSullivan94) 5 | homepage: https://github.com/fluttercommunity/flutter_launcher_icons 6 | repository: https://github.com/fluttercommunity/flutter_launcher_icons/ 7 | issue_tracker: https://github.com/fluttercommunity/flutter_launcher_icons/issues 8 | 9 | dependencies: 10 | args: ^2.5.0 11 | checked_yaml: ^2.0.3 12 | cli_util: ^0.4.1 13 | image: ^4.2.0 14 | json_annotation: ^4.9.0 15 | path: ^1.9.0 16 | yaml: ^3.1.2 17 | 18 | environment: 19 | sdk: ">=3.0.0 <4.0.0" 20 | 21 | dev_dependencies: 22 | # Needed by build_version 23 | build_runner: ^2.4.12 24 | # https://pub.dev/packages/build_version 25 | build_version: ^2.1.1 26 | json_serializable: ^6.8.0 27 | mockito: ^5.4.4 28 | test: ^1.25.8 29 | test_descriptor: ^2.0.1 30 | -------------------------------------------------------------------------------- /test/abs/icon_generator_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_launcher_icons/abs/icon_generator.dart'; 2 | import 'package:flutter_launcher_icons/config/config.dart'; 3 | import 'package:flutter_launcher_icons/logger.dart'; 4 | import 'package:mockito/annotations.dart'; 5 | import 'package:mockito/mockito.dart'; 6 | import 'package:path/path.dart' as path; 7 | import 'package:test/test.dart'; 8 | import 'package:test_descriptor/test_descriptor.dart' as d; 9 | 10 | import 'icon_generator_test.mocks.dart'; 11 | 12 | @GenerateMocks([Config, IconGenerator]) 13 | void main() { 14 | group('#generateIconsFor', () { 15 | late String prefixPath; 16 | late FLILogger logger; 17 | late IconGenerator mockGenerator; 18 | late Config mockFLIConfig; 19 | setUp(() async { 20 | prefixPath = path.join(d.sandbox, 'fli_test'); 21 | mockFLIConfig = MockConfig(); 22 | logger = FLILogger(false); 23 | mockGenerator = MockIconGenerator(); 24 | when(mockGenerator.platformName).thenReturn('Mock'); 25 | when(mockGenerator.context).thenReturn( 26 | IconGeneratorContext( 27 | config: mockFLIConfig, 28 | prefixPath: prefixPath, 29 | logger: logger, 30 | ), 31 | ); 32 | }); 33 | test('should execute createIcons() when validateRequiremnts() returns true', 34 | () { 35 | when(mockGenerator.validateRequirements()).thenReturn(true); 36 | generateIconsFor( 37 | config: mockFLIConfig, 38 | flavor: null, 39 | prefixPath: prefixPath, 40 | logger: logger, 41 | platforms: (context) => [mockGenerator], 42 | ); 43 | verify(mockGenerator.validateRequirements()).called(equals(1)); 44 | verify(mockGenerator.createIcons()).called(equals(1)); 45 | }); 46 | 47 | test( 48 | 'should not execute createIcons() when validateRequiremnts() returns false', 49 | () { 50 | when(mockGenerator.validateRequirements()).thenReturn(false); 51 | generateIconsFor( 52 | config: mockFLIConfig, 53 | flavor: null, 54 | prefixPath: prefixPath, 55 | logger: logger, 56 | platforms: (context) => [mockGenerator], 57 | ); 58 | verify(mockGenerator.validateRequirements()).called(equals(1)); 59 | verifyNever(mockGenerator.createIcons()); 60 | }); 61 | 62 | test('should skip platform if any exception occurred', () { 63 | when(mockGenerator.validateRequirements()).thenReturn(true); 64 | when(mockGenerator.createIcons()) 65 | .thenThrow(Exception('should-skip-platform')); 66 | generateIconsFor( 67 | config: mockFLIConfig, 68 | flavor: null, 69 | prefixPath: prefixPath, 70 | logger: logger, 71 | platforms: (context) => [mockGenerator], 72 | ); 73 | verify(mockGenerator.validateRequirements()).called(equals(1)); 74 | verify(mockGenerator.createIcons()).called(equals(1)); 75 | expect(() => mockGenerator.createIcons(), throwsException); 76 | }); 77 | }); 78 | } 79 | -------------------------------------------------------------------------------- /test/abs/icon_generator_test.mocks.dart: -------------------------------------------------------------------------------- 1 | // Mocks generated by Mockito 5.4.4 from annotations 2 | // in flutter_launcher_icons/test/abs/icon_generator_test.dart. 3 | // Do not manually edit this file. 4 | 5 | // ignore_for_file: no_leading_underscores_for_library_prefixes 6 | import 'package:flutter_launcher_icons/abs/icon_generator.dart' as _i2; 7 | import 'package:flutter_launcher_icons/config/config.dart' as _i3; 8 | import 'package:mockito/mockito.dart' as _i1; 9 | import 'package:mockito/src/dummies.dart' as _i4; 10 | 11 | // ignore_for_file: type=lint 12 | // ignore_for_file: avoid_redundant_argument_values 13 | // ignore_for_file: avoid_setters_without_getters 14 | // ignore_for_file: comment_references 15 | // ignore_for_file: deprecated_member_use 16 | // ignore_for_file: deprecated_member_use_from_same_package 17 | // ignore_for_file: implementation_imports 18 | // ignore_for_file: invalid_use_of_visible_for_testing_member 19 | // ignore_for_file: prefer_const_constructors 20 | // ignore_for_file: unnecessary_parenthesis 21 | // ignore_for_file: camel_case_types 22 | // ignore_for_file: subtype_of_sealed_class 23 | 24 | class _FakeIconGeneratorContext_0 extends _i1.SmartFake 25 | implements _i2.IconGeneratorContext { 26 | _FakeIconGeneratorContext_0( 27 | Object parent, 28 | Invocation parentInvocation, 29 | ) : super( 30 | parent, 31 | parentInvocation, 32 | ); 33 | } 34 | 35 | /// A class which mocks [Config]. 36 | /// 37 | /// See the documentation for Mockito's code generation for more information. 38 | class MockConfig extends _i1.Mock implements _i3.Config { 39 | MockConfig() { 40 | _i1.throwOnMissingStub(this); 41 | } 42 | 43 | @override 44 | int get adaptiveIconForegroundInset => (super.noSuchMethod( 45 | Invocation.getter(#adaptiveIconForegroundInset), 46 | returnValue: 0, 47 | ) as int); 48 | 49 | @override 50 | int get minSdkAndroid => (super.noSuchMethod( 51 | Invocation.getter(#minSdkAndroid), 52 | returnValue: 0, 53 | ) as int); 54 | 55 | @override 56 | bool get removeAlphaIOS => (super.noSuchMethod( 57 | Invocation.getter(#removeAlphaIOS), 58 | returnValue: false, 59 | ) as bool); 60 | 61 | @override 62 | bool get desaturateTintedToGrayscaleIOS => (super.noSuchMethod( 63 | Invocation.getter(#desaturateTintedToGrayscaleIOS), 64 | returnValue: false, 65 | ) as bool); 66 | 67 | @override 68 | String get backgroundColorIOS => (super.noSuchMethod( 69 | Invocation.getter(#backgroundColorIOS), 70 | returnValue: _i4.dummyValue( 71 | this, 72 | Invocation.getter(#backgroundColorIOS), 73 | ), 74 | ) as String); 75 | 76 | @override 77 | bool get hasAndroidAdaptiveConfig => (super.noSuchMethod( 78 | Invocation.getter(#hasAndroidAdaptiveConfig), 79 | returnValue: false, 80 | ) as bool); 81 | 82 | @override 83 | bool get hasAndroidAdaptiveMonochromeConfig => (super.noSuchMethod( 84 | Invocation.getter(#hasAndroidAdaptiveMonochromeConfig), 85 | returnValue: false, 86 | ) as bool); 87 | 88 | @override 89 | bool get hasPlatformConfig => (super.noSuchMethod( 90 | Invocation.getter(#hasPlatformConfig), 91 | returnValue: false, 92 | ) as bool); 93 | 94 | @override 95 | bool get hasWebConfig => (super.noSuchMethod( 96 | Invocation.getter(#hasWebConfig), 97 | returnValue: false, 98 | ) as bool); 99 | 100 | @override 101 | bool get hasWindowsConfig => (super.noSuchMethod( 102 | Invocation.getter(#hasWindowsConfig), 103 | returnValue: false, 104 | ) as bool); 105 | 106 | @override 107 | bool get hasMacOSConfig => (super.noSuchMethod( 108 | Invocation.getter(#hasMacOSConfig), 109 | returnValue: false, 110 | ) as bool); 111 | 112 | @override 113 | bool get isCustomAndroidFile => (super.noSuchMethod( 114 | Invocation.getter(#isCustomAndroidFile), 115 | returnValue: false, 116 | ) as bool); 117 | 118 | @override 119 | bool get isNeedingNewAndroidIcon => (super.noSuchMethod( 120 | Invocation.getter(#isNeedingNewAndroidIcon), 121 | returnValue: false, 122 | ) as bool); 123 | 124 | @override 125 | bool get isNeedingNewIOSIcon => (super.noSuchMethod( 126 | Invocation.getter(#isNeedingNewIOSIcon), 127 | returnValue: false, 128 | ) as bool); 129 | 130 | @override 131 | Map toJson() => (super.noSuchMethod( 132 | Invocation.method( 133 | #toJson, 134 | [], 135 | ), 136 | returnValue: {}, 137 | ) as Map); 138 | } 139 | 140 | /// A class which mocks [IconGenerator]. 141 | /// 142 | /// See the documentation for Mockito's code generation for more information. 143 | class MockIconGenerator extends _i1.Mock implements _i2.IconGenerator { 144 | MockIconGenerator() { 145 | _i1.throwOnMissingStub(this); 146 | } 147 | 148 | @override 149 | _i2.IconGeneratorContext get context => (super.noSuchMethod( 150 | Invocation.getter(#context), 151 | returnValue: _FakeIconGeneratorContext_0( 152 | this, 153 | Invocation.getter(#context), 154 | ), 155 | ) as _i2.IconGeneratorContext); 156 | 157 | @override 158 | String get platformName => (super.noSuchMethod( 159 | Invocation.getter(#platformName), 160 | returnValue: _i4.dummyValue( 161 | this, 162 | Invocation.getter(#platformName), 163 | ), 164 | ) as String); 165 | 166 | @override 167 | void createIcons() => super.noSuchMethod( 168 | Invocation.method( 169 | #createIcons, 170 | [], 171 | ), 172 | returnValueForMissingStub: null, 173 | ); 174 | 175 | @override 176 | bool validateRequirements() => (super.noSuchMethod( 177 | Invocation.method( 178 | #validateRequirements, 179 | [], 180 | ), 181 | returnValue: false, 182 | ) as bool); 183 | } 184 | -------------------------------------------------------------------------------- /test/all_tests.dart: -------------------------------------------------------------------------------- 1 | import 'package:test/test.dart'; 2 | 3 | import 'abs/icon_generator_test.dart' as icon_generator_test; 4 | import 'android_test.dart' as android_test; 5 | import 'config_test.dart' as fli_config; 6 | import 'macos/macos_icon_generator_test.dart' as macos_icons_gen_test; 7 | import 'macos/macos_icon_template_test.dart' as macos_template_test; 8 | import 'main_test.dart' as main_test; 9 | import 'utils_test.dart' as utils_test; 10 | import 'web/web_icon_generator_test.dart' as web_icon_gen_test; 11 | import 'web/web_template_test.dart' as web_template_test; 12 | import 'windows/windows_icon_generator_test.dart' as windows_icon_gen_test; 13 | 14 | void main() { 15 | group('Flutter launcher icons', () { 16 | // others 17 | utils_test.main(); 18 | fli_config.main(); 19 | icon_generator_test.main(); 20 | 21 | main_test.main(); 22 | // android 23 | android_test.main(); 24 | // web 25 | web_template_test.main(); 26 | web_icon_gen_test.main(); 27 | // windows 28 | windows_icon_gen_test.main(); 29 | // macos 30 | macos_template_test.main(); 31 | macos_icons_gen_test.main(); 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /test/android_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter_launcher_icons/android.dart' as android; 4 | import 'package:flutter_launcher_icons/config/config.dart'; 5 | import 'package:flutter_launcher_icons/constants.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | // unit tests for android.dart 9 | void main() { 10 | test('Adaptive icon mipmap path is correct', () { 11 | const String path1 = 'android/app/src/main/res/'; 12 | const String path2 = 'mipmap-anydpi-v26/'; 13 | expect(android.isCorrectMipmapDirectoryForAdaptiveIcon(path1), false); 14 | expect(android.isCorrectMipmapDirectoryForAdaptiveIcon(path2), false); 15 | expect( 16 | android.isCorrectMipmapDirectoryForAdaptiveIcon( 17 | androidAdaptiveXmlFolder(null), 18 | ), 19 | true, 20 | ); 21 | }); 22 | 23 | test('Correct number of adaptive foreground icons', () { 24 | expect(android.adaptiveForegroundIcons.length, 5); 25 | }); 26 | 27 | test('Correct number of android launcher icons', () { 28 | expect(android.androidIcons.length, 5); 29 | }); 30 | 31 | test('Config contains string for generating new launcher icons', () { 32 | final Map flutterIconsConfig = { 33 | 'image_path': 'assets/images/icon-710x599.png', 34 | 'android': true, 35 | 'ios': true, 36 | }; 37 | expect( 38 | Config.fromJson(flutterIconsConfig).isCustomAndroidFile, 39 | isFalse, 40 | ); 41 | 42 | final Map flutterIconsNewIconConfig = { 43 | 'image_path': 'assets/images/icon-710x599.png', 44 | 'android': 'New Icon', 45 | 'ios': true, 46 | }; 47 | expect( 48 | Config.fromJson(flutterIconsNewIconConfig).isCustomAndroidFile, 49 | isTrue, 50 | ); 51 | }); 52 | 53 | test('Prioritise image_path_android over image_path', () { 54 | final Map flutterIconsNewIconConfig = { 55 | 'image_path': 'assets/images/icon-710x599.png', 56 | 'image_path_android': 'assets/images/icon-android.png', 57 | 'android': 'New Icon', 58 | 'ios': true, 59 | }; 60 | expect( 61 | Config.fromJson(flutterIconsNewIconConfig).getImagePathAndroid(), 62 | equals('assets/images/icon-android.png'), 63 | ); 64 | }); 65 | 66 | test('Transforming manifest without icon must add icon', () async { 67 | final String inputManifest = 68 | getAndroidManifestExample('android:icon="@mipmap/ic_launcher"'); 69 | final String expectedManifest = 70 | getAndroidManifestExample('android:icon="@mipmap/ic_other_icon_name"'); 71 | 72 | await withTempFile('AndroidManifest.xml', (File androidManifestFile) async { 73 | androidManifestFile.writeAsStringSync(inputManifest); 74 | await android.overwriteAndroidManifestWithNewLauncherIcon( 75 | 'ic_other_icon_name', 76 | androidManifestFile, 77 | ); 78 | expect(androidManifestFile.readAsStringSync(), equals(expectedManifest)); 79 | }); 80 | }); 81 | 82 | test( 83 | 'Transforming manifest with icon already in place should leave it unchanged', 84 | () async { 85 | final String inputManifest = 86 | getAndroidManifestExample('android:icon="@mipmap/ic_launcher"'); 87 | final String expectedManifest = 88 | getAndroidManifestExample('android:icon="@mipmap/ic_launcher"'); 89 | 90 | await withTempFile('AndroidManifest.xml', (File androidManifestFile) async { 91 | androidManifestFile.writeAsStringSync(inputManifest); 92 | await android.overwriteAndroidManifestWithNewLauncherIcon( 93 | 'ic_launcher', 94 | androidManifestFile, 95 | ); 96 | expect(androidManifestFile.readAsStringSync(), equals(expectedManifest)); 97 | }); 98 | }); 99 | 100 | test( 101 | 'Transforming manifest with trailing newline should keep newline untouched', 102 | () async { 103 | final String inputManifest = 104 | getAndroidManifestExample('android:icon="@mipmap/ic_launcher"') + '\n'; 105 | final String expectedManifest = inputManifest; 106 | 107 | await withTempFile('AndroidManifest.xml', (File androidManifestFile) async { 108 | androidManifestFile.writeAsStringSync(inputManifest); 109 | await android.overwriteAndroidManifestWithNewLauncherIcon( 110 | 'ic_launcher', 111 | androidManifestFile, 112 | ); 113 | expect(androidManifestFile.readAsStringSync(), equals(expectedManifest)); 114 | }); 115 | }); 116 | 117 | test( 118 | 'Transforming manifest with 3 trailing newlines should keep newlines untouched', 119 | () async { 120 | final String inputManifest = 121 | getAndroidManifestExample('android:icon="@mipmap/ic_launcher"') + 122 | '\n\n\n'; 123 | final String expectedManifest = inputManifest; 124 | 125 | await withTempFile('AndroidManifest.xml', (File androidManifestFile) async { 126 | androidManifestFile.writeAsStringSync(inputManifest); 127 | await android.overwriteAndroidManifestWithNewLauncherIcon( 128 | 'ic_launcher', 129 | androidManifestFile, 130 | ); 131 | expect(androidManifestFile.readAsStringSync(), equals(expectedManifest)); 132 | }); 133 | }); 134 | 135 | test( 136 | 'Transforming manifest with special newline characters should leave special newline characters untouched', 137 | () async { 138 | final String inputManifest = 139 | getAndroidManifestExample('android:icon="@mipmap/ic_launcher"') 140 | .replaceAll('\n', '\r\n'); 141 | final String expectedManifest = inputManifest; 142 | 143 | await withTempFile('AndroidManifest.xml', (File androidManifestFile) async { 144 | androidManifestFile.writeAsStringSync(inputManifest); 145 | await android.overwriteAndroidManifestWithNewLauncherIcon( 146 | 'ic_launcher', 147 | androidManifestFile, 148 | ); 149 | expect(androidManifestFile.readAsStringSync(), equals(expectedManifest)); 150 | }); 151 | }); 152 | } 153 | 154 | Future withTempFile(String fileName, Function block) async { 155 | final Directory tempDir = Directory.systemTemp.createTempSync(); 156 | final File file = File('${tempDir.path}/$fileName')..createSync(); 157 | if (!file.existsSync()) { 158 | fail('Could not create temp test file ${file.path}'); 159 | } 160 | try { 161 | await block(file); 162 | } finally { 163 | file.deleteSync(); 164 | } 165 | } 166 | 167 | String getAndroidManifestExample(String iconLine) { 168 | return ''' 169 | 170 | 172 | 173 | 180 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | ''' 194 | .trim(); 195 | } 196 | -------------------------------------------------------------------------------- /test/assets/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fluttercommunity/flutter_launcher_icons/f24d6bf72267db9ef7e443a283c4549c1413d914/test/assets/app_icon.png -------------------------------------------------------------------------------- /test/config/test_pubspec.yaml: -------------------------------------------------------------------------------- 1 | dev_dependencies: 2 | 3 | flutter_launcher_icons: 4 | path: ../../ 5 | 6 | flutter_launcher_icons: 7 | image_path: "assets/images/icon-710x599.png" 8 | android: true # can specify file name here e.g. "ic_launcher" 9 | ios: true # can specify file name here e.g. "My-Launcher-Icon" 10 | -------------------------------------------------------------------------------- /test/config_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_launcher_icons/config/config.dart'; 2 | import 'package:flutter_launcher_icons/custom_exceptions.dart'; 3 | import 'package:path/path.dart' as path; 4 | import 'package:test/test.dart'; 5 | import 'package:test_descriptor/test_descriptor.dart' as d; 6 | 7 | import './templates.dart' as templates; 8 | 9 | void main() { 10 | group('Config', () { 11 | late String prefixPath; 12 | setUpAll(() { 13 | prefixPath = path.join(d.sandbox, 'fli_test'); 14 | }); 15 | group('#loadConfigFromPath', () { 16 | setUpAll(() async { 17 | await d.dir('fli_test', [ 18 | d.file('flutter_launcher_icons.yaml', templates.fliConfigTemplate), 19 | d.file('invalid_fli_config.yaml', templates.invalidfliConfigTemplate), 20 | ]).create(); 21 | }); 22 | test('should return valid configs', () { 23 | final configs = Config.loadConfigFromPath( 24 | 'flutter_launcher_icons.yaml', 25 | prefixPath, 26 | ); 27 | expect(configs, isNotNull); 28 | // android configs 29 | expect(configs!.android, isTrue); 30 | expect(configs.imagePath, isNotNull); 31 | expect(configs.imagePathAndroid, isNotNull); 32 | expect(configs.adaptiveIconBackground, isNotNull); 33 | expect(configs.adaptiveIconForeground, isNotNull); 34 | expect(configs.minSdkAndroid, equals(21)); 35 | // ios configs 36 | expect(configs.ios, isTrue); 37 | expect(configs.imagePathIOS, isNotNull); 38 | expect(configs.removeAlphaIOS, isFalse); 39 | // web configs 40 | expect(configs.webConfig, isNotNull); 41 | expect(configs.webConfig!.generate, isTrue); 42 | expect(configs.webConfig!.backgroundColor, isNotNull); 43 | expect(configs.webConfig!.imagePath, isNotNull); 44 | expect(configs.webConfig!.themeColor, isNotNull); 45 | expect( 46 | configs.webConfig!.toJson(), 47 | equals({ 48 | 'generate': true, 49 | 'image_path': 'app_icon.png', 50 | 'background_color': '#0175C2', 51 | 'theme_color': '#0175C2', 52 | }), 53 | ); 54 | // windows 55 | expect(configs.windowsConfig, isNotNull); 56 | expect(configs.windowsConfig!.generate, isNotNull); 57 | expect(configs.windowsConfig!.iconSize, isNotNull); 58 | expect(configs.windowsConfig!.imagePath, isNotNull); 59 | expect( 60 | configs.windowsConfig!.toJson(), 61 | equals({ 62 | 'generate': true, 63 | 'image_path': 'app_icon.png', 64 | 'icon_size': 48, 65 | }), 66 | ); 67 | // macos 68 | expect(configs.macOSConfig, isNotNull); 69 | expect(configs.macOSConfig!.generate, isNotNull); 70 | expect(configs.macOSConfig!.imagePath, isNotNull); 71 | expect( 72 | configs.macOSConfig!.toJson(), 73 | equals({ 74 | 'generate': true, 75 | 'image_path': 'app_icon.png', 76 | }), 77 | ); 78 | }); 79 | 80 | test('should return null when invalid filePath is given', () { 81 | final configs = Config.loadConfigFromPath( 82 | 'file_that_dont_exist.yaml', 83 | prefixPath, 84 | ); 85 | expect(configs, isNull); 86 | }); 87 | 88 | test('should throw InvalidConfigException when config is invalid', () { 89 | expect( 90 | () => Config.loadConfigFromPath( 91 | 'invalid_fli_config.yaml', 92 | prefixPath, 93 | ), 94 | throwsA(isA()), 95 | ); 96 | }); 97 | }); 98 | group('#loadConfigFromTestPubSpec', () { 99 | test('should return valid configs', () { 100 | const String path = 'test/config/test_pubspec.yaml'; 101 | final configs = Config.loadConfigFromPath(path, '.'); 102 | expect(configs, isNotNull); 103 | const String imagePath = 'assets/images/icon-710x599.png'; 104 | expect(configs!.imagePath, equals(imagePath)); 105 | // android configs 106 | expect(configs.android, isTrue); 107 | expect(configs.imagePathAndroid, isNull); 108 | expect(configs.getImagePathAndroid(), equals(imagePath)); 109 | expect(configs.adaptiveIconBackground, isNull); 110 | expect(configs.adaptiveIconForeground, isNull); 111 | expect(configs.minSdkAndroid, equals(21)); 112 | // ios configs 113 | expect(configs.ios, isTrue); 114 | expect(configs.imagePathIOS, isNull); 115 | expect(configs.getImagePathIOS(), equals(imagePath)); 116 | expect(configs.removeAlphaIOS, isFalse); 117 | // web configs 118 | expect(configs.webConfig, isNull); 119 | // windows 120 | expect(configs.windowsConfig, isNull); 121 | // macos 122 | expect(configs.macOSConfig, isNull); 123 | }); 124 | }); 125 | group('#loadConfigFromPubSpec', () { 126 | setUpAll(() async { 127 | await d.dir('fli_test', [ 128 | d.file('pubspec.yaml', templates.pubspecTemplate), 129 | d.file('flutter_launcher_icons.yaml', templates.fliConfigTemplate), 130 | d.file('invalid_fli_config.yaml', templates.invalidfliConfigTemplate), 131 | ]).create(); 132 | }); 133 | test('should return valid configs', () { 134 | final configs = Config.loadConfigFromPubSpec(prefixPath); 135 | expect(configs, isNotNull); 136 | // android configs 137 | expect(configs!.android, isTrue); 138 | expect(configs.imagePath, isNotNull); 139 | expect(configs.imagePathAndroid, isNotNull); 140 | expect(configs.adaptiveIconBackground, isNotNull); 141 | expect(configs.adaptiveIconForeground, isNotNull); 142 | expect(configs.minSdkAndroid, equals(21)); 143 | // ios configs 144 | expect(configs.ios, isTrue); 145 | expect(configs.imagePathIOS, isNotNull); 146 | expect(configs.removeAlphaIOS, isFalse); 147 | // web configs 148 | expect(configs.webConfig, isNotNull); 149 | expect(configs.webConfig!.generate, isTrue); 150 | expect(configs.webConfig!.backgroundColor, isNotNull); 151 | expect(configs.webConfig!.imagePath, isNotNull); 152 | expect(configs.webConfig!.themeColor, isNotNull); 153 | expect( 154 | configs.webConfig!.toJson(), 155 | equals({ 156 | 'generate': true, 157 | 'image_path': 'app_icon.png', 158 | 'background_color': '#0175C2', 159 | 'theme_color': '#0175C2', 160 | }), 161 | ); 162 | // windows 163 | expect(configs.windowsConfig, isNotNull); 164 | expect(configs.windowsConfig!.generate, isNotNull); 165 | expect(configs.windowsConfig!.iconSize, isNotNull); 166 | expect(configs.windowsConfig!.imagePath, isNotNull); 167 | expect( 168 | configs.windowsConfig!.toJson(), 169 | equals({ 170 | 'generate': true, 171 | 'image_path': 'app_icon.png', 172 | 'icon_size': 48, 173 | }), 174 | ); 175 | // macos 176 | expect(configs.macOSConfig, isNotNull); 177 | expect(configs.macOSConfig!.generate, isNotNull); 178 | expect(configs.macOSConfig!.imagePath, isNotNull); 179 | expect( 180 | configs.macOSConfig!.toJson(), 181 | equals({ 182 | 'generate': true, 183 | 'image_path': 'app_icon.png', 184 | }), 185 | ); 186 | }); 187 | 188 | group('should throw', () { 189 | setUp(() async { 190 | await d.dir('fli_test', [ 191 | d.file('pubspec.yaml', templates.invalidPubspecTemplate), 192 | d.file('flutter_launcher_icons.yaml', templates.fliConfigTemplate), 193 | d.file( 194 | 'invalid_fli_config.yaml', 195 | templates.invalidfliConfigTemplate, 196 | ), 197 | ]).create(); 198 | }); 199 | test('InvalidConfigException when config is invalid', () { 200 | expect( 201 | () => Config.loadConfigFromPubSpec(prefixPath), 202 | throwsA(isA()), 203 | ); 204 | }); 205 | }); 206 | }); 207 | group('#loadConfigFromFlavor', () { 208 | setUpAll(() async { 209 | await d.dir('fli_test', [ 210 | d.file( 211 | 'flutter_launcher_icons-development.yaml', 212 | templates.flavorFLIConfigTemplate, 213 | ), 214 | ]).create(); 215 | }); 216 | test('should return valid config', () { 217 | final configs = Config.loadConfigFromFlavor( 218 | 'development', 219 | prefixPath, 220 | ); 221 | expect(configs, isNotNull); 222 | expect(configs!.android, isTrue); 223 | expect(configs.imagePath, isNotNull); 224 | expect(configs.imagePathAndroid, isNotNull); 225 | expect(configs.adaptiveIconBackground, isNotNull); 226 | expect(configs.adaptiveIconForeground, isNotNull); 227 | // ios configs 228 | expect(configs.ios, isTrue); 229 | expect(configs.imagePathIOS, isNotNull); 230 | // web configs 231 | expect(configs.webConfig, isNotNull); 232 | expect(configs.webConfig!.generate, isTrue); 233 | expect(configs.webConfig!.backgroundColor, isNotNull); 234 | expect(configs.webConfig!.imagePath, isNotNull); 235 | expect(configs.webConfig!.themeColor, isNotNull); 236 | expect( 237 | configs.webConfig!.toJson(), 238 | equals({ 239 | 'generate': true, 240 | 'image_path': 'app_icon.png', 241 | 'background_color': '#0175C2', 242 | 'theme_color': '#0175C2', 243 | }), 244 | ); 245 | // windows 246 | expect(configs.windowsConfig, isNotNull); 247 | expect(configs.windowsConfig!.generate, isNotNull); 248 | expect(configs.windowsConfig!.iconSize, isNotNull); 249 | expect(configs.windowsConfig!.imagePath, isNotNull); 250 | expect( 251 | configs.windowsConfig!.toJson(), 252 | equals({ 253 | 'generate': true, 254 | 'image_path': 'app_icon.png', 255 | 'icon_size': 48, 256 | }), 257 | ); 258 | // macos 259 | expect(configs.macOSConfig, isNotNull); 260 | expect(configs.macOSConfig!.generate, isNotNull); 261 | expect(configs.macOSConfig!.imagePath, isNotNull); 262 | expect( 263 | configs.macOSConfig!.toJson(), 264 | equals({ 265 | 'generate': true, 266 | 'image_path': 'app_icon.png', 267 | }), 268 | ); 269 | }); 270 | }); 271 | }); 272 | } 273 | -------------------------------------------------------------------------------- /test/macos/macos_icon_generator_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter_launcher_icons/abs/icon_generator.dart'; 4 | import 'package:flutter_launcher_icons/config/config.dart'; 5 | import 'package:flutter_launcher_icons/config/macos_config.dart'; 6 | import 'package:flutter_launcher_icons/logger.dart'; 7 | import 'package:flutter_launcher_icons/macos/macos_icon_generator.dart'; 8 | import 'package:mockito/annotations.dart'; 9 | import 'package:mockito/mockito.dart'; 10 | import 'package:path/path.dart' as path; 11 | import 'package:test/test.dart'; 12 | import 'package:test_descriptor/test_descriptor.dart' as d; 13 | 14 | import '../templates.dart' as templates; 15 | 16 | @GenerateNiceMocks([ 17 | MockSpec(), 18 | MockSpec(), 19 | MockSpec(), 20 | ]) 21 | import 'macos_icon_generator_test.mocks.dart'; 22 | 23 | void main() { 24 | group('MacOSIconGenerator', () { 25 | late IconGeneratorContext context; 26 | late IconGenerator generator; 27 | late Config mockConfig; 28 | late MacOSConfig mockMacOSConfig; 29 | late String prefixPath; 30 | late File testImageFile; 31 | late MockFLILogger mockLogger; 32 | final assetPath = path.join(Directory.current.path, 'test', 'assets'); 33 | 34 | group('#validateRequirments', () { 35 | setUpAll(() { 36 | testImageFile = File(path.join(assetPath, 'app_icon.png')); 37 | expect(testImageFile.existsSync(), isTrue); 38 | }); 39 | setUp(() { 40 | prefixPath = path.join(d.sandbox, 'fli_test'); 41 | mockConfig = MockConfig(); 42 | mockMacOSConfig = MockMacOSConfig(); 43 | mockLogger = MockFLILogger(); 44 | context = IconGeneratorContext( 45 | config: mockConfig, 46 | prefixPath: prefixPath, 47 | logger: mockLogger, 48 | ); 49 | generator = MacOSIconGenerator(context); 50 | 51 | // initilize mock defaults 52 | when(mockLogger.error(argThat(anything))).thenReturn(anything); 53 | when(mockLogger.verbose(argThat(anything))).thenReturn(anything); 54 | when(mockLogger.isVerbose).thenReturn(false); 55 | when(mockConfig.macOSConfig).thenReturn(mockMacOSConfig); 56 | when(mockMacOSConfig.generate).thenReturn(true); 57 | when(mockMacOSConfig.imagePath) 58 | .thenReturn(path.join(prefixPath, 'app_icon.png')); 59 | when(mockConfig.imagePath) 60 | .thenReturn(path.join(prefixPath, 'app_icon.png')); 61 | }); 62 | 63 | test('should return false when macos config is not provided', () { 64 | when(mockConfig.macOSConfig).thenReturn(null); 65 | expect(generator.validateRequirements(), isFalse); 66 | verify(mockConfig.macOSConfig).called(equals(1)); 67 | }); 68 | 69 | test( 70 | 'should return false when macosConfig is not null but macos.generate is false', 71 | () { 72 | when(mockConfig.macOSConfig).thenReturn(mockMacOSConfig); 73 | when(mockMacOSConfig.generate).thenReturn(false); 74 | expect(generator.validateRequirements(), isFalse); 75 | verify(mockConfig.macOSConfig).called(equals(1)); 76 | verify(mockMacOSConfig.generate).called(equals(1)); 77 | }); 78 | 79 | test('should return false when macos.image_path and imagePath is null', 80 | () { 81 | when(mockMacOSConfig.imagePath).thenReturn(null); 82 | when(mockConfig.imagePath).thenReturn(null); 83 | expect(generator.validateRequirements(), isFalse); 84 | 85 | verifyInOrder([ 86 | mockMacOSConfig.imagePath, 87 | mockConfig.imagePath, 88 | ]); 89 | }); 90 | 91 | test('should return false when macos dir does not exist', () async { 92 | await d.dir('fli_test', [ 93 | d.file('app_icon.png', testImageFile.readAsBytesSync()), 94 | ]).create(); 95 | await expectLater( 96 | d.dir('fli_test', [ 97 | d.file('app_icon.png', anything), 98 | ]).validate(), 99 | completes, 100 | ); 101 | expect(generator.validateRequirements(), isFalse); 102 | }); 103 | 104 | test('should return false when image file does not exist', () async { 105 | await d.dir('fli_test', [d.dir('macos')]).create(); 106 | await expectLater( 107 | d.dir('fli_test', [d.dir('macos')]).validate(), 108 | completes, 109 | ); 110 | expect(generator.validateRequirements(), isFalse); 111 | }); 112 | }); 113 | }); 114 | 115 | group('MacOSIconGenerator end-to-end', () { 116 | late IconGeneratorContext context; 117 | late IconGenerator generator; 118 | late Config config; 119 | late String prefixPath; 120 | final assetPath = path.join(Directory.current.path, 'test', 'assets'); 121 | 122 | setUp(() async { 123 | final imageFile = File(path.join(assetPath, 'app_icon.png')); 124 | expect(imageFile.existsSync(), isTrue); 125 | await d.dir('fli_test', [ 126 | d.dir('macos/Runner/Assets.xcassets/AppIcon.appiconset', [ 127 | d.file('Contents.json', templates.macOSContentsJsonFile), 128 | ]), 129 | d.file('flutter_launcher_icons.yaml', templates.fliConfigTemplate), 130 | d.file('app_icon.png', imageFile.readAsBytesSync()), 131 | ]).create(); 132 | prefixPath = path.join(d.sandbox, 'fli_test'); 133 | config = Config.loadConfigFromPath( 134 | 'flutter_launcher_icons.yaml', 135 | prefixPath, 136 | )!; 137 | context = IconGeneratorContext( 138 | config: config, 139 | prefixPath: prefixPath, 140 | logger: FLILogger(false), 141 | ); 142 | generator = MacOSIconGenerator(context); 143 | }); 144 | 145 | test('should generate valid icons & contents.json file', () async { 146 | expect(generator.validateRequirements(), isTrue); 147 | expect(() => generator.createIcons(), isNot(throwsException)); 148 | 149 | await expectLater( 150 | d.dir('fli_test', [ 151 | d.dir('macos/Runner/Assets.xcassets/AppIcon.appiconset', [ 152 | d.file('app_icon_1024.png', anything), 153 | d.file('app_icon_16.png', anything), 154 | d.file('app_icon_32.png', anything), 155 | d.file('app_icon_64.png', anything), 156 | d.file('app_icon_128.png', anything), 157 | d.file('app_icon_256.png', anything), 158 | d.file('app_icon_512.png', anything), 159 | ]), 160 | ]).validate(), 161 | completes, 162 | reason: 'All icon files are not generated', 163 | ); 164 | 165 | await expectLater( 166 | d.dir('fli_test', [ 167 | d.dir('macos/Runner/Assets.xcassets/AppIcon.appiconset', [ 168 | d.file('Contents.json', equals(templates.macOSContentsJsonFile)), 169 | ]), 170 | ]).validate(), 171 | completes, 172 | reason: 'Contents.json file contents are not equal', 173 | ); 174 | }); 175 | }); 176 | } 177 | -------------------------------------------------------------------------------- /test/macos/macos_icon_generator_test.mocks.dart: -------------------------------------------------------------------------------- 1 | // Mocks generated by Mockito 5.4.4 from annotations 2 | // in flutter_launcher_icons/test/macos/macos_icon_generator_test.dart. 3 | // Do not manually edit this file. 4 | 5 | // ignore_for_file: no_leading_underscores_for_library_prefixes 6 | import 'package:cli_util/cli_logging.dart' as _i2; 7 | import 'package:flutter_launcher_icons/config/config.dart' as _i3; 8 | import 'package:flutter_launcher_icons/config/macos_config.dart' as _i5; 9 | import 'package:flutter_launcher_icons/logger.dart' as _i6; 10 | import 'package:mockito/mockito.dart' as _i1; 11 | import 'package:mockito/src/dummies.dart' as _i4; 12 | 13 | // ignore_for_file: type=lint 14 | // ignore_for_file: avoid_redundant_argument_values 15 | // ignore_for_file: avoid_setters_without_getters 16 | // ignore_for_file: comment_references 17 | // ignore_for_file: deprecated_member_use 18 | // ignore_for_file: deprecated_member_use_from_same_package 19 | // ignore_for_file: implementation_imports 20 | // ignore_for_file: invalid_use_of_visible_for_testing_member 21 | // ignore_for_file: prefer_const_constructors 22 | // ignore_for_file: unnecessary_parenthesis 23 | // ignore_for_file: camel_case_types 24 | // ignore_for_file: subtype_of_sealed_class 25 | 26 | class _FakeLogger_0 extends _i1.SmartFake implements _i2.Logger { 27 | _FakeLogger_0( 28 | Object parent, 29 | Invocation parentInvocation, 30 | ) : super( 31 | parent, 32 | parentInvocation, 33 | ); 34 | } 35 | 36 | class _FakeProgress_1 extends _i1.SmartFake implements _i2.Progress { 37 | _FakeProgress_1( 38 | Object parent, 39 | Invocation parentInvocation, 40 | ) : super( 41 | parent, 42 | parentInvocation, 43 | ); 44 | } 45 | 46 | /// A class which mocks [Config]. 47 | /// 48 | /// See the documentation for Mockito's code generation for more information. 49 | class MockConfig extends _i1.Mock implements _i3.Config { 50 | @override 51 | int get adaptiveIconForegroundInset => (super.noSuchMethod( 52 | Invocation.getter(#adaptiveIconForegroundInset), 53 | returnValue: 0, 54 | returnValueForMissingStub: 0, 55 | ) as int); 56 | 57 | @override 58 | int get minSdkAndroid => (super.noSuchMethod( 59 | Invocation.getter(#minSdkAndroid), 60 | returnValue: 0, 61 | returnValueForMissingStub: 0, 62 | ) as int); 63 | 64 | @override 65 | bool get removeAlphaIOS => (super.noSuchMethod( 66 | Invocation.getter(#removeAlphaIOS), 67 | returnValue: false, 68 | returnValueForMissingStub: false, 69 | ) as bool); 70 | 71 | @override 72 | bool get desaturateTintedToGrayscaleIOS => (super.noSuchMethod( 73 | Invocation.getter(#desaturateTintedToGrayscaleIOS), 74 | returnValue: false, 75 | returnValueForMissingStub: false, 76 | ) as bool); 77 | 78 | @override 79 | String get backgroundColorIOS => (super.noSuchMethod( 80 | Invocation.getter(#backgroundColorIOS), 81 | returnValue: _i4.dummyValue( 82 | this, 83 | Invocation.getter(#backgroundColorIOS), 84 | ), 85 | returnValueForMissingStub: _i4.dummyValue( 86 | this, 87 | Invocation.getter(#backgroundColorIOS), 88 | ), 89 | ) as String); 90 | 91 | @override 92 | bool get hasAndroidAdaptiveConfig => (super.noSuchMethod( 93 | Invocation.getter(#hasAndroidAdaptiveConfig), 94 | returnValue: false, 95 | returnValueForMissingStub: false, 96 | ) as bool); 97 | 98 | @override 99 | bool get hasAndroidAdaptiveMonochromeConfig => (super.noSuchMethod( 100 | Invocation.getter(#hasAndroidAdaptiveMonochromeConfig), 101 | returnValue: false, 102 | returnValueForMissingStub: false, 103 | ) as bool); 104 | 105 | @override 106 | bool get hasPlatformConfig => (super.noSuchMethod( 107 | Invocation.getter(#hasPlatformConfig), 108 | returnValue: false, 109 | returnValueForMissingStub: false, 110 | ) as bool); 111 | 112 | @override 113 | bool get hasWebConfig => (super.noSuchMethod( 114 | Invocation.getter(#hasWebConfig), 115 | returnValue: false, 116 | returnValueForMissingStub: false, 117 | ) as bool); 118 | 119 | @override 120 | bool get hasWindowsConfig => (super.noSuchMethod( 121 | Invocation.getter(#hasWindowsConfig), 122 | returnValue: false, 123 | returnValueForMissingStub: false, 124 | ) as bool); 125 | 126 | @override 127 | bool get hasMacOSConfig => (super.noSuchMethod( 128 | Invocation.getter(#hasMacOSConfig), 129 | returnValue: false, 130 | returnValueForMissingStub: false, 131 | ) as bool); 132 | 133 | @override 134 | bool get isCustomAndroidFile => (super.noSuchMethod( 135 | Invocation.getter(#isCustomAndroidFile), 136 | returnValue: false, 137 | returnValueForMissingStub: false, 138 | ) as bool); 139 | 140 | @override 141 | bool get isNeedingNewAndroidIcon => (super.noSuchMethod( 142 | Invocation.getter(#isNeedingNewAndroidIcon), 143 | returnValue: false, 144 | returnValueForMissingStub: false, 145 | ) as bool); 146 | 147 | @override 148 | bool get isNeedingNewIOSIcon => (super.noSuchMethod( 149 | Invocation.getter(#isNeedingNewIOSIcon), 150 | returnValue: false, 151 | returnValueForMissingStub: false, 152 | ) as bool); 153 | 154 | @override 155 | Map toJson() => (super.noSuchMethod( 156 | Invocation.method( 157 | #toJson, 158 | [], 159 | ), 160 | returnValue: {}, 161 | returnValueForMissingStub: {}, 162 | ) as Map); 163 | } 164 | 165 | /// A class which mocks [MacOSConfig]. 166 | /// 167 | /// See the documentation for Mockito's code generation for more information. 168 | class MockMacOSConfig extends _i1.Mock implements _i5.MacOSConfig { 169 | @override 170 | bool get generate => (super.noSuchMethod( 171 | Invocation.getter(#generate), 172 | returnValue: false, 173 | returnValueForMissingStub: false, 174 | ) as bool); 175 | 176 | @override 177 | Map toJson() => (super.noSuchMethod( 178 | Invocation.method( 179 | #toJson, 180 | [], 181 | ), 182 | returnValue: {}, 183 | returnValueForMissingStub: {}, 184 | ) as Map); 185 | } 186 | 187 | /// A class which mocks [FLILogger]. 188 | /// 189 | /// See the documentation for Mockito's code generation for more information. 190 | class MockFLILogger extends _i1.Mock implements _i6.FLILogger { 191 | @override 192 | bool get isVerbose => (super.noSuchMethod( 193 | Invocation.getter(#isVerbose), 194 | returnValue: false, 195 | returnValueForMissingStub: false, 196 | ) as bool); 197 | 198 | @override 199 | _i2.Logger get rawLogger => (super.noSuchMethod( 200 | Invocation.getter(#rawLogger), 201 | returnValue: _FakeLogger_0( 202 | this, 203 | Invocation.getter(#rawLogger), 204 | ), 205 | returnValueForMissingStub: _FakeLogger_0( 206 | this, 207 | Invocation.getter(#rawLogger), 208 | ), 209 | ) as _i2.Logger); 210 | 211 | @override 212 | void error(Object? message) => super.noSuchMethod( 213 | Invocation.method( 214 | #error, 215 | [message], 216 | ), 217 | returnValueForMissingStub: null, 218 | ); 219 | 220 | @override 221 | void verbose(Object? message) => super.noSuchMethod( 222 | Invocation.method( 223 | #verbose, 224 | [message], 225 | ), 226 | returnValueForMissingStub: null, 227 | ); 228 | 229 | @override 230 | void info(Object? message) => super.noSuchMethod( 231 | Invocation.method( 232 | #info, 233 | [message], 234 | ), 235 | returnValueForMissingStub: null, 236 | ); 237 | 238 | @override 239 | _i2.Progress progress(String? message) => (super.noSuchMethod( 240 | Invocation.method( 241 | #progress, 242 | [message], 243 | ), 244 | returnValue: _FakeProgress_1( 245 | this, 246 | Invocation.method( 247 | #progress, 248 | [message], 249 | ), 250 | ), 251 | returnValueForMissingStub: _FakeProgress_1( 252 | this, 253 | Invocation.method( 254 | #progress, 255 | [message], 256 | ), 257 | ), 258 | ) as _i2.Progress); 259 | } 260 | -------------------------------------------------------------------------------- /test/macos/macos_icon_template_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_launcher_icons/macos/macos_icon_template.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | group('MacOSIconTemplate', () { 6 | late int size; 7 | late int scale; 8 | late MacOSIconTemplate template; 9 | 10 | setUp(() { 11 | size = 16; 12 | scale = 1; 13 | template = MacOSIconTemplate(size, scale); 14 | }); 15 | 16 | test('should pass', () { 17 | expect(template.size, equals(size)); 18 | expect(template.scale, equals(scale)); 19 | expect(template.scaledSize, equals(size * scale)); 20 | expect(template.iconFile, equals('app_icon_${template.scaledSize}.png')); 21 | expect( 22 | template.iconContent, 23 | equals( 24 | { 25 | 'size': '${size}x$size', 26 | 'idiom': 'mac', 27 | 'filename': 'app_icon_${template.scaledSize}.png', 28 | 'scale': '1x', 29 | }, 30 | ), 31 | ); 32 | }); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /test/main_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:args/args.dart'; 4 | import 'package:flutter_launcher_icons/android.dart' as android; 5 | import 'package:flutter_launcher_icons/config/config.dart'; 6 | import 'package:flutter_launcher_icons/ios.dart' as ios; 7 | import 'package:flutter_launcher_icons/main.dart' show defaultConfigFile; 8 | import 'package:flutter_launcher_icons/main.dart' as main_dart; 9 | import 'package:path/path.dart' show join; 10 | import 'package:test/test.dart'; 11 | 12 | // Unit tests for main.dart 13 | void main() { 14 | test('iOS icon list is correct size', () { 15 | expect(ios.iosIcons.length, 16); 16 | }); 17 | 18 | test('iOS legacy icon list is correct size', () { 19 | expect(ios.legacyIosIcons.length, 21); 20 | }); 21 | 22 | test('Android icon list is correct size', () { 23 | expect(android.androidIcons.length, 5); 24 | }); 25 | 26 | test( 27 | 'iOS image list used to generate legacy Contents.json for icon directory is correct size (no dark or tinted icons)', 28 | () { 29 | expect(ios.createLegacyImageList('blah').length, 25); 30 | }); 31 | 32 | test( 33 | 'iOS image list used to generate Contents.json for icon directory is correct size (with dark icon)', 34 | () { 35 | expect(ios.createImageList('blah', 'dark-blah', null).length, 16 * 2 + 1); // 16 normal, 16 dark icons + 1 marketing icon 36 | }); 37 | 38 | test( 39 | 'iOS image list used to generate Contents.json for icon directory is correct size (with tinted icon)', 40 | () { 41 | expect(ios.createImageList('blah', null, 'tinted-blah').length, 16 * 2 + 1); // 16 normal, 16 tinted icons + 1 marketing icon 42 | }); 43 | 44 | test( 45 | 'iOS image list used to generate Contents.json for icon directory is correct size (with dark and tinted icon)', 46 | () { 47 | expect(ios.createImageList('blah', 'dark-blah', 'tinted-blah').length, 16 * 3 + 1); // 16 normal, 16 dark, 16 tinted icons + 1 marketing icon 48 | }); 49 | 50 | group('config file from args', () { 51 | // Create mini parser with only the wanted option, mocking the real one 52 | final ArgParser parser = ArgParser() 53 | ..addOption( 54 | main_dart.fileOption, 55 | abbr: 'f', 56 | defaultsTo: defaultConfigFile, 57 | ) 58 | ..addOption( 59 | main_dart.prefixOption, 60 | abbr: 'p', 61 | defaultsTo: '.', 62 | ); 63 | final String testDir = 64 | join('.dart_tool', 'flutter_launcher_icons', 'test', 'config_file'); 65 | 66 | late String currentDirectory; 67 | Future setCurrentDirectory(String path) async { 68 | path = join(testDir, path); 69 | await Directory(path).create(recursive: true); 70 | Directory.current = path; 71 | } 72 | 73 | setUp(() { 74 | currentDirectory = Directory.current.path; 75 | }); 76 | tearDown(() { 77 | Directory.current = currentDirectory; 78 | }); 79 | 80 | test('default', () async { 81 | await setCurrentDirectory('default'); 82 | await File('flutter_launcher_icons.yaml').writeAsString(''' 83 | flutter_launcher_icons: 84 | android: true 85 | ios: false 86 | '''); 87 | final ArgResults argResults = parser.parse([]); 88 | final Config? config = main_dart.loadConfigFileFromArgResults(argResults); 89 | expect(config, isNotNull); 90 | expect(config!.android, isTrue); 91 | }); 92 | test('default_use_pubspec', () async { 93 | await setCurrentDirectory('pubspec_only'); 94 | await File('pubspec.yaml').writeAsString(''' 95 | flutter_launcher_icons: 96 | android: true 97 | ios: false 98 | '''); 99 | ArgResults argResults = parser.parse([]); 100 | final Config? config = main_dart.loadConfigFileFromArgResults(argResults); 101 | expect(config, isNotNull); 102 | expect(config!.ios, isFalse); 103 | 104 | // read pubspec if provided file is not found 105 | argResults = parser.parse(['-f', defaultConfigFile]); 106 | expect(main_dart.loadConfigFileFromArgResults(argResults), isNotNull); 107 | }); 108 | 109 | test('custom', () async { 110 | await setCurrentDirectory('custom'); 111 | await File('custom.yaml').writeAsString(''' 112 | flutter_launcher_icons: 113 | android: true 114 | ios: true 115 | '''); 116 | // if no argument set, should fail 117 | ArgResults argResults = parser.parse(['-f', 'custom.yaml']); 118 | final Config? config = main_dart.loadConfigFileFromArgResults(argResults); 119 | expect(config, isNotNull); 120 | expect(config!.ios, isTrue); 121 | 122 | // should fail if no argument 123 | argResults = parser.parse([]); 124 | expect(main_dart.loadConfigFileFromArgResults(argResults), isNull); 125 | 126 | // or missing file 127 | argResults = parser.parse(['-f', 'missing_custom.yaml']); 128 | expect(main_dart.loadConfigFileFromArgResults(argResults), isNull); 129 | }); 130 | }); 131 | 132 | test('image_path is in config', () { 133 | final Map flutterIconsConfig = { 134 | 'image_path': 'assets/images/icon-710x599.png', 135 | 'android': true, 136 | 'ios': true, 137 | }; 138 | final config = Config.fromJson(flutterIconsConfig); 139 | expect( 140 | config.getImagePathAndroid(), 141 | equals('assets/images/icon-710x599.png'), 142 | ); 143 | expect(config.getImagePathIOS(), equals('assets/images/icon-710x599.png')); 144 | final Map flutterIconsConfigAndroid = { 145 | 'image_path_android': 'assets/images/icon-710x599.png', 146 | 'android': true, 147 | 'ios': true, 148 | }; 149 | final configAndroid = Config.fromJson(flutterIconsConfigAndroid); 150 | expect( 151 | configAndroid.getImagePathAndroid(), 152 | equals('assets/images/icon-710x599.png'), 153 | ); 154 | expect(configAndroid.getImagePathIOS(), isNull); 155 | final Map flutterIconsConfigBoth = { 156 | 'image_path_android': 'assets/images/icon-android.png', 157 | 'image_path_ios': 'assets/images/icon-ios.png', 158 | 'android': true, 159 | 'ios': true, 160 | }; 161 | final configBoth = Config.fromJson(flutterIconsConfigBoth); 162 | expect( 163 | configBoth.getImagePathAndroid(), 164 | equals('assets/images/icon-android.png'), 165 | ); 166 | expect(configBoth.getImagePathIOS(), equals('assets/images/icon-ios.png')); 167 | }); 168 | 169 | test('At least one platform is in config file', () { 170 | final Map flutterIconsConfig = { 171 | 'image_path': 'assets/images/icon-710x599.png', 172 | 'android': true, 173 | 'ios': true, 174 | }; 175 | final config = Config.fromJson(flutterIconsConfig); 176 | expect(config.hasPlatformConfig, isTrue); 177 | }); 178 | 179 | test('No platform specified in config', () { 180 | final Map flutterIconsConfig = { 181 | 'image_path': 'assets/images/icon-710x599.png', 182 | }; 183 | final config = Config.fromJson(flutterIconsConfig); 184 | expect(config.hasPlatformConfig, isFalse); 185 | }); 186 | 187 | test('No new Android icon needed - android: false', () { 188 | final Map flutterIconsConfig = { 189 | 'image_path': 'assets/images/icon-710x599.png', 190 | 'android': false, 191 | 'ios': true, 192 | }; 193 | final config = Config.fromJson(flutterIconsConfig); 194 | expect(config.isNeedingNewAndroidIcon, isFalse); 195 | }); 196 | 197 | test('No new Android icon needed - no Android config', () { 198 | final Map flutterIconsConfig = { 199 | 'image_path': 'assets/images/icon-710x599.png', 200 | 'ios': true, 201 | }; 202 | final config = Config.fromJson(flutterIconsConfig); 203 | expect(config.isNeedingNewAndroidIcon, isFalse); 204 | }); 205 | 206 | test('No new iOS icon needed - ios: false', () { 207 | final Map flutterIconsConfig = { 208 | 'image_path': 'assets/images/icon-710x599.png', 209 | 'android': true, 210 | 'ios': false, 211 | }; 212 | final config = Config.fromJson(flutterIconsConfig); 213 | expect(config.isNeedingNewIOSIcon, isFalse); 214 | }); 215 | 216 | test('No new iOS icon needed - no iOS config', () { 217 | final Map flutterIconsConfig = { 218 | 'image_path': 'assets/images/icon-710x599.png', 219 | 'android': true, 220 | }; 221 | final config = Config.fromJson(flutterIconsConfig); 222 | expect(config.isNeedingNewIOSIcon, isFalse); 223 | }); 224 | } 225 | -------------------------------------------------------------------------------- /test/package_version_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_launcher_icons/pubspec_parser.dart'; 2 | import 'package:flutter_launcher_icons/src/version.dart'; 3 | import 'package:test/test.dart'; 4 | 5 | void main() { 6 | /// this helps avoid an issue where the pubspec version has been increased but 7 | /// build runner has not been run to up the version which is displayed 8 | /// when flutter_launcher_icons is run 9 | test('package version is correct', () { 10 | final yamlMap = PubspecParser.fromPathToMap('pubspec.yaml'); 11 | final yamlVersion = yamlMap['version'] as String; 12 | expect( 13 | yamlVersion, 14 | packageVersion, 15 | reason: 'Versions are not matching. Solution: Run build runner to ' 16 | 'updated generated version value.', 17 | ); 18 | }); 19 | } 20 | -------------------------------------------------------------------------------- /test/templates.dart: -------------------------------------------------------------------------------- 1 | const fliConfigTemplate = r''' 2 | flutter_launcher_icons: 3 | android: true 4 | ios: true 5 | image_path: "assets/images/icon-128x128.png" 6 | image_path_android: "assets/images/icon-710x599-android.png" 7 | image_path_ios: "assets/images/icon-1024x1024.png" 8 | adaptive_icon_background: "assets/images/christmas-background.png" 9 | adaptive_icon_foreground: "assets/images/icon-foreground-432x432.png" 10 | adaptive_icon_monochrome: "assets/images/icon-monochrome-432x432.png" 11 | min_sdk_android: 21 12 | remove_alpha_ios: false 13 | web: 14 | generate: true 15 | image_path: "app_icon.png" # filepath 16 | background_color: "#0175C2" # hex_color 17 | theme_color: "#0175C2" # hex_color 18 | apple_mobile_web_app_title: "demo" 19 | apple_mobile_web_app_status_bar_style: "hex_color" 20 | windows: 21 | generate: true 22 | image_path: "app_icon.png" 23 | icon_size: 48 24 | macos: 25 | generate: true 26 | image_path: "app_icon.png" 27 | '''; 28 | 29 | const flavorFLIConfigTemplate = fliConfigTemplate; 30 | 31 | const fliWebConfig = r''' 32 | flutter_launcher_icons: 33 | web: 34 | generate: true 35 | image_path: "app_icon.png" # filepath 36 | background_color: "#0175C2" # hex_color 37 | theme_color: "#0175C2" # hex_color 38 | apple_mobile_web_app_title: "demo" 39 | apple_mobile_web_app_status_bar_style: "hex_color" 40 | '''; 41 | 42 | const fliWindowsConfig = r''' 43 | flutter_launcher_icons: 44 | windows: 45 | generate: true 46 | image_path: "app_icon.png" 47 | icon_size: 48 48 | '''; 49 | 50 | const invalidfliConfigTemplate = r''' 51 | # flutter_launcher_icons 52 | android: true 53 | ios: true 54 | image_path: "assets/images/icon-128x128.png" 55 | ad 56 | image_path_android: "assets/images/icon-710x599-android.png" 57 | image_path_ios: "assets/images/icon-1024x1024.png" 58 | adaptive_icon_background: "assets/images/christmas-background.png" 59 | adaptive_icon_foreground: "assets/images/icon-foreground-432x432.png" 60 | adaptive_icon_monochrome: "assets/images/icon-monochrome-432x432.png" 61 | web: 62 | generate: true 63 | image_path: "app_icon.png" # filepath 64 | background_color: "#0175C2" # hex_color 65 | theme_color: "#0175C2" # hex_color 66 | apple_mobile_web_app_title: "demo" 67 | apple_mobile_web_app_status_bar_style: "hex_color" 68 | '''; 69 | 70 | const pubspecTemplate = r''' 71 | name: demo 72 | description: A new Flutter project. 73 | publish_to: 'none' 74 | version: 1.0.0+1 75 | 76 | environment: 77 | sdk: '>=2.18.0-44.1.beta <3.0.0' 78 | 79 | dependencies: 80 | flutter: 81 | sdk: flutter 82 | cupertino_icons: ^1.0.2 83 | 84 | dev_dependencies: 85 | flutter_test: 86 | sdk: flutter 87 | flutter_lints: ^2.0.0 88 | flutter_launcher_icons: 89 | path: C:/Users/asus/projects/flutter_launcher_icons 90 | 91 | flutter: 92 | uses-material-design: true 93 | assets: 94 | - images/a_dot_burr.jpeg 95 | - images/a_dot_ham.jpeg 96 | fonts: 97 | - family: Schyler 98 | fonts: 99 | - asset: fonts/Schyler-Regular.ttf 100 | - asset: fonts/Schyler-Italic.ttf 101 | style: italic 102 | - family: Trajan Pro 103 | fonts: 104 | - asset: fonts/TrajanPro.ttf 105 | - asset: fonts/TrajanPro_Bold.ttf 106 | weight: 700 107 | 108 | flutter_launcher_icons: 109 | android: true 110 | ios: true 111 | image_path: "assets/images/icon-128x128.png" 112 | image_path_android: "assets/images/icon-710x599-android.png" 113 | image_path_ios: "assets/images/icon-1024x1024.png" 114 | adaptive_icon_background: "assets/images/christmas-background.png" 115 | adaptive_icon_foreground: "assets/images/icon-foreground-432x432.png" 116 | adaptive_icon_monochrome: "assets/images/icon-monochrome-432x432.png" 117 | min_sdk_android: 21 118 | remove_alpha_ios: false 119 | web: 120 | generate: true 121 | image_path: "app_icon.png" # filepath 122 | background_color: "#0175C2" # hex_color 123 | theme_color: "#0175C2" # hex_color 124 | apple_mobile_web_app_title: "demo" 125 | apple_mobile_web_app_status_bar_style: "hex_color" 126 | windows: 127 | generate: true 128 | image_path: "app_icon.png" 129 | icon_size: 48 130 | macos: 131 | generate: true 132 | image_path: "app_icon.png" 133 | '''; 134 | 135 | const invalidPubspecTemplate = r''' 136 | name: demo 137 | description: A new Flutter project. 138 | publish_to: 'none' 139 | version: 1.0.0+1 140 | 141 | environment: 142 | sdk: '>=2.18.0-44.1.beta <3.0.0' 143 | 144 | dependencies: 145 | flutter: 146 | sdk: flutter 147 | cupertino_icons: ^1.0.2 148 | 149 | dev_dependencies: 150 | flutter_test: 151 | sdk: flutter 152 | flutter_lints: ^2.0.0 153 | flutter_launcher_icons: 154 | path: C:/Users/asus/projects/flutter_launcher_icons 155 | 156 | flutter: 157 | uses-material-design: true 158 | assets: 159 | - images/a_dot_burr.jpeg 160 | - images/a_dot_ham.jpeg 161 | fonts: 162 | - family: Schyler 163 | fonts: 164 | - asset: fonts/Schyler-Regular.ttf 165 | - asset: fonts/Schyler-Italic.ttf 166 | style: italic 167 | - family: Trajan Pro 168 | fonts: 169 | - asset: fonts/TrajanPro.ttf 170 | - asset: fonts/TrajanPro_Bold.ttf 171 | weight: 700 172 | 173 | flutter_launcher_icons: 174 | android: true 175 | invalid_indented_key_key 176 | ios: true 177 | image_path: "assets/images/icon-128x128.png" 178 | image_path_android: "assets/images/icon-710x599-android.png" 179 | image_path_ios: "assets/images/icon-1024x1024.png" 180 | adaptive_icon_background: "assets/images/christmas-background.png" 181 | adaptive_icon_foreground: "assets/images/icon-foreground-432x432.png" 182 | adaptive_icon_monochrome: "assets/images/icon-monochrome-432x432.png" 183 | web: 184 | generate: true 185 | image_path: "app_icon.png" # filepath 186 | background_color: "#0175C2" # hex_color 187 | theme_color: "#0175C2" # hex_color 188 | apple_mobile_web_app_title: "demo" 189 | apple_mobile_web_app_status_bar_style: "hex_color" 190 | windows: 191 | generate: true 192 | image_path: "app_icon.png" 193 | icon_size: 48 194 | '''; 195 | 196 | const webManifestTemplate = r''' 197 | { 198 | "name": "demo", 199 | "short_name": "demo", 200 | "start_url": ".", 201 | "display": "standalone", 202 | "background_color": "#0175C2", 203 | "theme_color": "#0175C2", 204 | "description": "A new Flutter project.", 205 | "orientation": "portrait-primary", 206 | "prefer_related_applications": false, 207 | "icons": [ 208 | { 209 | "src": "icons/Icon-192.png", 210 | "sizes": "192x192", 211 | "type": "image/png" 212 | }, 213 | { 214 | "src": "icons/Icon-512.png", 215 | "sizes": "512x512", 216 | "type": "image/png" 217 | }, 218 | { 219 | "src": "icons/Icon-maskable-192.png", 220 | "sizes": "192x192", 221 | "type": "image/png", 222 | "purpose": "maskable" 223 | }, 224 | { 225 | "src": "icons/Icon-maskable-512.png", 226 | "sizes": "512x512", 227 | "type": "image/png", 228 | "purpose": "maskable" 229 | } 230 | ] 231 | } 232 | '''; 233 | 234 | const webIndexTemplate = r''' 235 | 236 | 237 | 238 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | demo 267 | 268 | 269 | 273 | 274 | 275 | 276 | 277 | 291 | 292 | 293 | 294 | '''; 295 | 296 | // macos 297 | const macOSContentsJsonFile = r''' 298 | { 299 | "info": { 300 | "version": 1, 301 | "author": "xcode" 302 | }, 303 | "images": [ 304 | { 305 | "size": "16x16", 306 | "idiom": "mac", 307 | "filename": "app_icon_16.png", 308 | "scale": "1x" 309 | }, 310 | { 311 | "size": "16x16", 312 | "idiom": "mac", 313 | "filename": "app_icon_32.png", 314 | "scale": "2x" 315 | }, 316 | { 317 | "size": "32x32", 318 | "idiom": "mac", 319 | "filename": "app_icon_32.png", 320 | "scale": "1x" 321 | }, 322 | { 323 | "size": "32x32", 324 | "idiom": "mac", 325 | "filename": "app_icon_64.png", 326 | "scale": "2x" 327 | }, 328 | { 329 | "size": "128x128", 330 | "idiom": "mac", 331 | "filename": "app_icon_128.png", 332 | "scale": "1x" 333 | }, 334 | { 335 | "size": "128x128", 336 | "idiom": "mac", 337 | "filename": "app_icon_256.png", 338 | "scale": "2x" 339 | }, 340 | { 341 | "size": "256x256", 342 | "idiom": "mac", 343 | "filename": "app_icon_256.png", 344 | "scale": "1x" 345 | }, 346 | { 347 | "size": "256x256", 348 | "idiom": "mac", 349 | "filename": "app_icon_512.png", 350 | "scale": "2x" 351 | }, 352 | { 353 | "size": "512x512", 354 | "idiom": "mac", 355 | "filename": "app_icon_512.png", 356 | "scale": "1x" 357 | }, 358 | { 359 | "size": "512x512", 360 | "idiom": "mac", 361 | "filename": "app_icon_1024.png", 362 | "scale": "2x" 363 | } 364 | ] 365 | }'''; 366 | -------------------------------------------------------------------------------- /test/utils_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_launcher_icons/utils.dart' as utils; 2 | import 'package:path/path.dart' as path; 3 | import 'package:test/test.dart'; 4 | import 'package:test_descriptor/test_descriptor.dart' as d; 5 | 6 | void main() { 7 | group('#areFSEntitesExist', () { 8 | late String prefixPath; 9 | setUp(() async { 10 | prefixPath = path.join(d.sandbox, 'fli_test'); 11 | await d.dir('fli_test', [ 12 | d.file('file1.txt', 'contents1'), 13 | d.dir('dir1'), 14 | ]).create(); 15 | }); 16 | 17 | test('should return null when entites exists', () async { 18 | expect( 19 | utils.areFSEntiesExist([ 20 | path.join(prefixPath, 'file1.txt'), 21 | path.join(prefixPath, 'dir1'), 22 | ]), 23 | isNull, 24 | ); 25 | }); 26 | 27 | test('should return the file path that does not exist', () { 28 | final result = utils.areFSEntiesExist([ 29 | path.join(prefixPath, 'dir1'), 30 | path.join(prefixPath, 'file_that_does_not_exist.txt'), 31 | ]); 32 | expect(result, isNotNull); 33 | expect( 34 | result, 35 | equals(path.join(prefixPath, 'file_that_does_not_exist.txt')), 36 | ); 37 | }); 38 | 39 | test('should return the dir path that does not exist', () { 40 | final result = utils.areFSEntiesExist([ 41 | path.join(prefixPath, 'dir_that_does_not_exist'), 42 | path.join(prefixPath, 'file.txt'), 43 | ]); 44 | expect(result, isNotNull); 45 | expect(result, equals(path.join(prefixPath, 'dir_that_does_not_exist'))); 46 | }); 47 | 48 | test('should return the first entity path that does not exist', () { 49 | final result = utils.areFSEntiesExist([ 50 | path.join(prefixPath, 'dir_that_does_not_exist'), 51 | path.join(prefixPath, 'file_that_dodes_not_exist.txt'), 52 | ]); 53 | expect(result, isNotNull); 54 | expect(result, equals(path.join(prefixPath, 'dir_that_does_not_exist'))); 55 | }); 56 | }); 57 | 58 | group('#createDirIfNotExist', () { 59 | setUpAll(() async { 60 | await d.dir('fli_test', [ 61 | d.dir('dir_exists'), 62 | ]).create(); 63 | }); 64 | test('should create directory if it does not exist', () async { 65 | await expectLater( 66 | d.dir('fli_test', [d.dir('dir_that_does_not_exist')]).validate(), 67 | throwsException, 68 | ); 69 | final result = utils.createDirIfNotExist( 70 | path.join(d.sandbox, 'fli_test', 'dir_that_does_not_exist'), 71 | ); 72 | expect(result.existsSync(), isTrue); 73 | await expectLater( 74 | d.dir('fli_test', [d.dir('dir_that_does_not_exist')]).validate(), 75 | completes, 76 | ); 77 | }); 78 | test('should return dir if it exist', () async { 79 | await expectLater( 80 | d.dir('fli_test', [d.dir('dir_exists')]).validate(), 81 | completes, 82 | ); 83 | final result = utils 84 | .createDirIfNotExist(path.join(d.sandbox, 'fli_test', 'dir_exists')); 85 | expect(result.existsSync(), isTrue); 86 | await expectLater( 87 | d.dir('fli_test', [d.dir('dir_exists')]).validate(), 88 | completes, 89 | ); 90 | }); 91 | }); 92 | 93 | group('#createFileIfNotExist', () { 94 | setUpAll(() async { 95 | await d.dir('fli_test', [ 96 | d.file('file_exists.txt'), 97 | ]).create(); 98 | }); 99 | test('should create file if it does not exist', () async { 100 | await expectLater( 101 | d.dir('fli_test', [d.file('file_that_does_not_exist.txt')]).validate(), 102 | throwsException, 103 | ); 104 | final result = utils.createFileIfNotExist( 105 | path.join(d.sandbox, 'fli_test', 'file_that_does_not_exist.txt'), 106 | ); 107 | expect(result.existsSync(), isTrue); 108 | await expectLater( 109 | d.dir('fli_test', [d.file('file_that_does_not_exist.txt')]).validate(), 110 | completes, 111 | ); 112 | }); 113 | test('should return file if it exist', () async { 114 | await expectLater( 115 | d.dir('fli_test', [d.file('file_exists.txt')]).validate(), 116 | completes, 117 | ); 118 | final result = utils.createFileIfNotExist( 119 | path.join(d.sandbox, 'fli_test', 'file_exists.txt'), 120 | ); 121 | expect(result.existsSync(), isTrue); 122 | await expectLater( 123 | d.dir('fli_test', [d.file('file_exists.txt')]).validate(), 124 | completes, 125 | ); 126 | }); 127 | }); 128 | 129 | group('#prettifyJsonEncode', () { 130 | test('should return prettiffed json string 4 indents', () { 131 | const expectedValue = r''' 132 | { 133 | "key1": "value1", 134 | "key2": "value2" 135 | }'''; 136 | final result = utils.prettifyJsonEncode({ 137 | 'key1': 'value1', 138 | 'key2': 'value2', 139 | }); 140 | expect(result, equals(expectedValue)); 141 | }); 142 | }); 143 | } 144 | -------------------------------------------------------------------------------- /test/web/web_icon_generator_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter_launcher_icons/abs/icon_generator.dart'; 4 | import 'package:flutter_launcher_icons/config/config.dart'; 5 | import 'package:flutter_launcher_icons/logger.dart'; 6 | import 'package:flutter_launcher_icons/web/web_icon_generator.dart'; 7 | import 'package:path/path.dart' as path; 8 | import 'package:test/test.dart'; 9 | import 'package:test_descriptor/test_descriptor.dart' as d; 10 | 11 | import '../templates.dart' as templates; 12 | 13 | void main() { 14 | group('WebIconGenerator', () { 15 | late IconGeneratorContext context; 16 | late IconGenerator generator; 17 | late Config config; 18 | late String prefixPath; 19 | final assetPath = path.join(Directory.current.path, 'test', 'assets'); 20 | 21 | setUp(() async { 22 | final imageFile = File(path.join(assetPath, 'app_icon.png')); 23 | expect(imageFile.existsSync(), isTrue); 24 | await d.dir('fli_test', [ 25 | d.dir('web', [ 26 | d.dir('icons'), 27 | d.file('index.html', templates.webIndexTemplate), 28 | d.file('manifest.json', templates.webManifestTemplate), 29 | ]), 30 | d.file('flutter_launcher_icons.yaml', templates.fliWebConfig), 31 | d.file('pubspec.yaml', templates.pubspecTemplate), 32 | d.file('app_icon.png', imageFile.readAsBytesSync()), 33 | ]).create(); 34 | prefixPath = path.join(d.sandbox, 'fli_test'); 35 | config = Config.loadConfigFromPath( 36 | 'flutter_launcher_icons.yaml', 37 | prefixPath, 38 | )!; 39 | context = IconGeneratorContext( 40 | config: config, 41 | prefixPath: prefixPath, 42 | logger: FLILogger(false), 43 | ); 44 | generator = WebIconGenerator(context); 45 | }); 46 | 47 | // end to end test 48 | test('should generate valid icons', () async { 49 | expect(generator.validateRequirements(), isTrue); 50 | generator.createIcons(); 51 | await expectLater( 52 | d.dir('fli_test', [ 53 | d.dir('web', [ 54 | d.dir('icons', [ 55 | // this icons get created in fs 56 | d.file('Icon-192.png', anything), 57 | d.file('Icon-512.png', anything), 58 | d.file('Icon-maskable-192.png', anything), 59 | d.file('Icon-maskable-512.png', anything), 60 | ]), 61 | // this favicon get created in fs 62 | d.file('favicon.png', anything), 63 | d.file('index.html', anything), 64 | // this manifest.json get updated in fs 65 | d.file('manifest.json', anything), 66 | ]), 67 | d.file('flutter_launcher_icons.yaml', anything), 68 | d.file('pubspec.yaml', templates.pubspecTemplate), 69 | ]).validate(), 70 | completes, 71 | ); 72 | }); 73 | }); 74 | } 75 | -------------------------------------------------------------------------------- /test/web/web_template_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_launcher_icons/web/web_template.dart'; 2 | import 'package:test/test.dart'; 3 | 4 | void main() { 5 | group('WebTemplate', () { 6 | late WebIconTemplate icTemplate; 7 | late WebIconTemplate icMaskableTemplate; 8 | 9 | setUp(() { 10 | icTemplate = const WebIconTemplate(size: 512); 11 | icMaskableTemplate = const WebIconTemplate(size: 512, maskable: true); 12 | }); 13 | 14 | test('.iconFile should return valid file name', () async { 15 | expect(icTemplate.iconFile, equals('Icon-512.png')); 16 | expect(icMaskableTemplate.iconFile, equals('Icon-maskable-512.png')); 17 | }); 18 | 19 | test('.iconManifest should return valid manifest config', () { 20 | expect( 21 | icTemplate.iconManifest, 22 | equals({ 23 | 'src': 'icons/Icon-512.png', 24 | 'sizes': '512x512', 25 | 'type': 'image/png', 26 | }), 27 | ); 28 | expect( 29 | icMaskableTemplate.iconManifest, 30 | equals({ 31 | 'src': 'icons/Icon-maskable-512.png', 32 | 'sizes': '512x512', 33 | 'type': 'image/png', 34 | 'purpose': 'maskable', 35 | }), 36 | ); 37 | }); 38 | }); 39 | } 40 | -------------------------------------------------------------------------------- /test/windows/windows_icon_generator_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter_launcher_icons/abs/icon_generator.dart'; 4 | import 'package:flutter_launcher_icons/config/config.dart'; 5 | import 'package:flutter_launcher_icons/config/windows_config.dart'; 6 | import 'package:flutter_launcher_icons/logger.dart'; 7 | import 'package:flutter_launcher_icons/windows/windows_icon_generator.dart'; 8 | import 'package:mockito/annotations.dart'; 9 | import 'package:mockito/mockito.dart'; 10 | import 'package:path/path.dart' as path; 11 | import 'package:test/test.dart'; 12 | import 'package:test_descriptor/test_descriptor.dart' as d; 13 | 14 | import '../templates.dart' as templates; 15 | import 'windows_icon_generator_test.mocks.dart'; 16 | 17 | @GenerateMocks([Config, WindowsConfig, FLILogger]) 18 | void main() { 19 | group('WindowsIconGenerator', () { 20 | late IconGeneratorContext context; 21 | late IconGenerator generator; 22 | late Config mockConfig; 23 | late WindowsConfig mockWindowsConfig; 24 | late String prefixPath; 25 | late File testImageFile; 26 | late MockFLILogger mockLogger; 27 | final assetPath = path.join(Directory.current.path, 'test', 'assets'); 28 | 29 | group('#validateRequirments', () { 30 | setUpAll(() { 31 | // make sure test file exists before starting test 32 | testImageFile = File(path.join(assetPath, 'app_icon.png')); 33 | expect(testImageFile.existsSync(), isTrue); 34 | }); 35 | setUp(() async { 36 | prefixPath = path.join(d.sandbox, 'fli_test'); 37 | mockConfig = MockConfig(); 38 | mockWindowsConfig = MockWindowsConfig(); 39 | mockLogger = MockFLILogger(); 40 | context = IconGeneratorContext( 41 | config: mockConfig, 42 | prefixPath: prefixPath, 43 | logger: mockLogger, 44 | ); 45 | generator = WindowsIconGenerator(context); 46 | // initilize mock defaults 47 | when(mockLogger.error(argThat(anything))).thenReturn(anything); 48 | when(mockLogger.verbose(argThat(anything))).thenReturn(anything); 49 | when(mockLogger.isVerbose).thenReturn(false); 50 | when(mockConfig.windowsConfig).thenReturn(mockWindowsConfig); 51 | when(mockWindowsConfig.generate).thenReturn(true); 52 | when(mockWindowsConfig.imagePath) 53 | .thenReturn(path.join(prefixPath, 'app_icon.png')); 54 | when(mockConfig.imagePath) 55 | .thenReturn(path.join(prefixPath, 'app_icon.png')); 56 | when(mockWindowsConfig.iconSize).thenReturn(48); 57 | }); 58 | 59 | test('should return false when windows config is not provided', () { 60 | when(mockConfig.windowsConfig).thenReturn(null); 61 | expect(generator.validateRequirements(), isFalse); 62 | verify(mockConfig.windowsConfig).called(equals(1)); 63 | }); 64 | 65 | test( 66 | 'should return false when windowsConfig is not null but windows.generate is false', 67 | () { 68 | when(mockConfig.windowsConfig).thenReturn(mockWindowsConfig); 69 | when(mockWindowsConfig.generate).thenReturn(false); 70 | expect(generator.validateRequirements(), isFalse); 71 | verify(mockConfig.windowsConfig).called(equals(1)); 72 | verify(mockWindowsConfig.generate).called(equals(1)); 73 | }); 74 | 75 | test('should return false when windows.image_path and imagePath is null', 76 | () { 77 | when(mockWindowsConfig.imagePath).thenReturn(null); 78 | when(mockConfig.imagePath).thenReturn(null); 79 | expect(generator.validateRequirements(), isFalse); 80 | 81 | verifyInOrder([ 82 | mockWindowsConfig.imagePath, 83 | mockConfig.imagePath, 84 | ]); 85 | }); 86 | 87 | test( 88 | 'should return false when windows.icon_size is not between 48 and 256', 89 | () { 90 | when(mockWindowsConfig.iconSize).thenReturn(40); 91 | expect(generator.validateRequirements(), isFalse); 92 | verify(mockWindowsConfig.iconSize).called(equals(3)); 93 | 94 | when(mockWindowsConfig.iconSize).thenReturn(257); 95 | expect(generator.validateRequirements(), isFalse); 96 | verify(mockWindowsConfig.iconSize).called(equals(4)); 97 | }); 98 | 99 | test('should return false when windows dir does not exist', () async { 100 | await d.dir('fli_test', [ 101 | d.file('app_icon.png', testImageFile.readAsBytesSync()), 102 | ]).create(); 103 | await expectLater( 104 | d.dir('fli_test', [ 105 | d.file('app_icon.png', anything), 106 | ]).validate(), 107 | completes, 108 | ); 109 | expect(generator.validateRequirements(), isFalse); 110 | }); 111 | 112 | test('should return false when image file does not exist', () async { 113 | await d.dir('fli_test', [d.dir('windows')]).create(); 114 | await expectLater( 115 | d.dir('fli_test', [d.dir('windows')]).validate(), 116 | completes, 117 | ); 118 | expect(generator.validateRequirements(), isFalse); 119 | }); 120 | }); 121 | }); 122 | 123 | group('WindowsIconGenerator end-to-end', () { 124 | late IconGeneratorContext context; 125 | late IconGenerator generator; 126 | late Config config; 127 | late String prefixPath; 128 | final assetPath = path.join(Directory.current.path, 'test', 'assets'); 129 | 130 | setUp(() async { 131 | final imageFile = File(path.join(assetPath, 'app_icon.png')); 132 | expect(imageFile.existsSync(), isTrue); 133 | await d.dir('fli_test', [ 134 | d.dir('windows'), 135 | d.file('flutter_launcher_icons.yaml', templates.fliWindowsConfig), 136 | d.file('pubspec.yaml', templates.pubspecTemplate), 137 | d.file('app_icon.png', imageFile.readAsBytesSync()), 138 | ]).create(); 139 | prefixPath = path.join(d.sandbox, 'fli_test'); 140 | config = Config.loadConfigFromPath( 141 | 'flutter_launcher_icons.yaml', 142 | prefixPath, 143 | )!; 144 | context = IconGeneratorContext( 145 | config: config, 146 | prefixPath: prefixPath, 147 | logger: FLILogger(false), 148 | ); 149 | generator = WindowsIconGenerator(context); 150 | }); 151 | 152 | test('should generate valid icons', () async { 153 | expect(generator.validateRequirements(), isTrue); 154 | generator.createIcons(); 155 | await expectLater( 156 | d.dir('fli_test', [ 157 | d.dir('windows', [ 158 | d.dir('runner', [ 159 | d.dir('resources', [ 160 | d.file('app_icon.ico', anything), 161 | ]), 162 | ]), 163 | ]), 164 | ]).validate(), 165 | completes, 166 | ); 167 | }); 168 | }); 169 | } 170 | -------------------------------------------------------------------------------- /test/windows/windows_icon_generator_test.mocks.dart: -------------------------------------------------------------------------------- 1 | // Mocks generated by Mockito 5.4.4 from annotations 2 | // in flutter_launcher_icons/test/windows/windows_icon_generator_test.dart. 3 | // Do not manually edit this file. 4 | 5 | // ignore_for_file: no_leading_underscores_for_library_prefixes 6 | import 'package:cli_util/cli_logging.dart' as _i2; 7 | import 'package:flutter_launcher_icons/config/config.dart' as _i3; 8 | import 'package:flutter_launcher_icons/config/windows_config.dart' as _i5; 9 | import 'package:flutter_launcher_icons/logger.dart' as _i6; 10 | import 'package:mockito/mockito.dart' as _i1; 11 | import 'package:mockito/src/dummies.dart' as _i4; 12 | 13 | // ignore_for_file: type=lint 14 | // ignore_for_file: avoid_redundant_argument_values 15 | // ignore_for_file: avoid_setters_without_getters 16 | // ignore_for_file: comment_references 17 | // ignore_for_file: deprecated_member_use 18 | // ignore_for_file: deprecated_member_use_from_same_package 19 | // ignore_for_file: implementation_imports 20 | // ignore_for_file: invalid_use_of_visible_for_testing_member 21 | // ignore_for_file: prefer_const_constructors 22 | // ignore_for_file: unnecessary_parenthesis 23 | // ignore_for_file: camel_case_types 24 | // ignore_for_file: subtype_of_sealed_class 25 | 26 | class _FakeLogger_0 extends _i1.SmartFake implements _i2.Logger { 27 | _FakeLogger_0( 28 | Object parent, 29 | Invocation parentInvocation, 30 | ) : super( 31 | parent, 32 | parentInvocation, 33 | ); 34 | } 35 | 36 | class _FakeProgress_1 extends _i1.SmartFake implements _i2.Progress { 37 | _FakeProgress_1( 38 | Object parent, 39 | Invocation parentInvocation, 40 | ) : super( 41 | parent, 42 | parentInvocation, 43 | ); 44 | } 45 | 46 | /// A class which mocks [Config]. 47 | /// 48 | /// See the documentation for Mockito's code generation for more information. 49 | class MockConfig extends _i1.Mock implements _i3.Config { 50 | MockConfig() { 51 | _i1.throwOnMissingStub(this); 52 | } 53 | 54 | @override 55 | int get adaptiveIconForegroundInset => (super.noSuchMethod( 56 | Invocation.getter(#adaptiveIconForegroundInset), 57 | returnValue: 0, 58 | ) as int); 59 | 60 | @override 61 | int get minSdkAndroid => (super.noSuchMethod( 62 | Invocation.getter(#minSdkAndroid), 63 | returnValue: 0, 64 | ) as int); 65 | 66 | @override 67 | bool get removeAlphaIOS => (super.noSuchMethod( 68 | Invocation.getter(#removeAlphaIOS), 69 | returnValue: false, 70 | ) as bool); 71 | 72 | @override 73 | bool get desaturateTintedToGrayscaleIOS => (super.noSuchMethod( 74 | Invocation.getter(#desaturateTintedToGrayscaleIOS), 75 | returnValue: false, 76 | ) as bool); 77 | 78 | @override 79 | String get backgroundColorIOS => (super.noSuchMethod( 80 | Invocation.getter(#backgroundColorIOS), 81 | returnValue: _i4.dummyValue( 82 | this, 83 | Invocation.getter(#backgroundColorIOS), 84 | ), 85 | ) as String); 86 | 87 | @override 88 | bool get hasAndroidAdaptiveConfig => (super.noSuchMethod( 89 | Invocation.getter(#hasAndroidAdaptiveConfig), 90 | returnValue: false, 91 | ) as bool); 92 | 93 | @override 94 | bool get hasAndroidAdaptiveMonochromeConfig => (super.noSuchMethod( 95 | Invocation.getter(#hasAndroidAdaptiveMonochromeConfig), 96 | returnValue: false, 97 | ) as bool); 98 | 99 | @override 100 | bool get hasPlatformConfig => (super.noSuchMethod( 101 | Invocation.getter(#hasPlatformConfig), 102 | returnValue: false, 103 | ) as bool); 104 | 105 | @override 106 | bool get hasWebConfig => (super.noSuchMethod( 107 | Invocation.getter(#hasWebConfig), 108 | returnValue: false, 109 | ) as bool); 110 | 111 | @override 112 | bool get hasWindowsConfig => (super.noSuchMethod( 113 | Invocation.getter(#hasWindowsConfig), 114 | returnValue: false, 115 | ) as bool); 116 | 117 | @override 118 | bool get hasMacOSConfig => (super.noSuchMethod( 119 | Invocation.getter(#hasMacOSConfig), 120 | returnValue: false, 121 | ) as bool); 122 | 123 | @override 124 | bool get isCustomAndroidFile => (super.noSuchMethod( 125 | Invocation.getter(#isCustomAndroidFile), 126 | returnValue: false, 127 | ) as bool); 128 | 129 | @override 130 | bool get isNeedingNewAndroidIcon => (super.noSuchMethod( 131 | Invocation.getter(#isNeedingNewAndroidIcon), 132 | returnValue: false, 133 | ) as bool); 134 | 135 | @override 136 | bool get isNeedingNewIOSIcon => (super.noSuchMethod( 137 | Invocation.getter(#isNeedingNewIOSIcon), 138 | returnValue: false, 139 | ) as bool); 140 | 141 | @override 142 | Map toJson() => (super.noSuchMethod( 143 | Invocation.method( 144 | #toJson, 145 | [], 146 | ), 147 | returnValue: {}, 148 | ) as Map); 149 | } 150 | 151 | /// A class which mocks [WindowsConfig]. 152 | /// 153 | /// See the documentation for Mockito's code generation for more information. 154 | class MockWindowsConfig extends _i1.Mock implements _i5.WindowsConfig { 155 | MockWindowsConfig() { 156 | _i1.throwOnMissingStub(this); 157 | } 158 | 159 | @override 160 | bool get generate => (super.noSuchMethod( 161 | Invocation.getter(#generate), 162 | returnValue: false, 163 | ) as bool); 164 | 165 | @override 166 | Map toJson() => (super.noSuchMethod( 167 | Invocation.method( 168 | #toJson, 169 | [], 170 | ), 171 | returnValue: {}, 172 | ) as Map); 173 | } 174 | 175 | /// A class which mocks [FLILogger]. 176 | /// 177 | /// See the documentation for Mockito's code generation for more information. 178 | class MockFLILogger extends _i1.Mock implements _i6.FLILogger { 179 | MockFLILogger() { 180 | _i1.throwOnMissingStub(this); 181 | } 182 | 183 | @override 184 | bool get isVerbose => (super.noSuchMethod( 185 | Invocation.getter(#isVerbose), 186 | returnValue: false, 187 | ) as bool); 188 | 189 | @override 190 | _i2.Logger get rawLogger => (super.noSuchMethod( 191 | Invocation.getter(#rawLogger), 192 | returnValue: _FakeLogger_0( 193 | this, 194 | Invocation.getter(#rawLogger), 195 | ), 196 | ) as _i2.Logger); 197 | 198 | @override 199 | void error(Object? message) => super.noSuchMethod( 200 | Invocation.method( 201 | #error, 202 | [message], 203 | ), 204 | returnValueForMissingStub: null, 205 | ); 206 | 207 | @override 208 | void verbose(Object? message) => super.noSuchMethod( 209 | Invocation.method( 210 | #verbose, 211 | [message], 212 | ), 213 | returnValueForMissingStub: null, 214 | ); 215 | 216 | @override 217 | void info(Object? message) => super.noSuchMethod( 218 | Invocation.method( 219 | #info, 220 | [message], 221 | ), 222 | returnValueForMissingStub: null, 223 | ); 224 | 225 | @override 226 | _i2.Progress progress(String? message) => (super.noSuchMethod( 227 | Invocation.method( 228 | #progress, 229 | [message], 230 | ), 231 | returnValue: _FakeProgress_1( 232 | this, 233 | Invocation.method( 234 | #progress, 235 | [message], 236 | ), 237 | ), 238 | ) as _i2.Progress); 239 | } 240 | --------------------------------------------------------------------------------