diff --git a/README.md b/README.md
index 814e7c7..153c29b 100644
--- a/README.md
+++ b/README.md
@@ -86,22 +86,29 @@ These are our four core values:
# First steps
+## Manual steps
+
1. - [ ] Create a new repo from this template.
2. - [ ] Set up the GIT settings according to your team's needs.
3. - [ ] Clone the created repository and open it through File -> Open Workspace from File... -> `flutter.code-workspace`. Open.../Open folder... won't work ⚠️
-4. - [ ] Remove any unused platforms (Android, iOS, web, Windows, Linux (+snap), macOS)
+4. - [ ] Update `.github/CODEOWNERS` file.
+5. - [ ] Go through all the ToDo's inside the project, and react to them.
+6. - [ ] Add your own secrets. This process is described in the [Secrets handling](#secrets)
+
+## Automated steps
+
+The following steps can be completed or coordinated by `ai/skills/flutter-template-project-setup`.
+
+1. - [ ] Remove any unused platforms (Android, iOS, web, Windows, Linux (+snap), macOS)
- Remove them from the folder structure of the template.
- Remove them from the `AppPlatform` class.
- When removing the `Web` platform, remove also `firebase.json` and `.firebaserc`, `web_setup.dart`, `flutter_web_plugins` and `universal_html` from the `pubspec.yaml` file.
- When removing all Desktop platforms, `window_manager` should be safe to remove from `pubspec.yaml` file.
- Fix all the reported issues that the change caused.
-4. - [ ] Update `.github/CODEOWNERS` file.
-5. - [ ] Change the package and application name. This process is described in the [Application Rebranding](#application-rebranding) section.
-6. - [ ] Change the app icon. This process is described in the [Icons generation](./project_setup/README.md#app-icon-generation) section.
-7. - [ ] Change the app splash screen. This process is described in the [Splash screen generation](./project_setup/README.md#splash-screen-generation) section.
-8. - [ ] Set up Firebase or remove it completely. This process is described in the [Firebase setup](#firebase-setup) section.
-9. - [ ] Add your own secrets. This process is described in the [Secrets handling](#secrets)
-10. - [ ] Go through all the ToDo's inside the project, and react to them.
+2. - [ ] Change the package and application name. This process is described in the [Application Rebranding](./project_setup/README.md#application-rebranding) section.
+3. - [ ] Change the app icon. This process is described in the [Icons generation](./project_setup/README.md#app-icon-generation) section.
+4. - [ ] Change the app splash screen. This process is described in the [Splash screen generation](./project_setup/README.md#splash-screen-generation) section.
+5. - [ ] Set up Firebase or remove it completely. This process is described in the [Firebase setup](#firebase-setup) section.
@@ -497,6 +504,7 @@ Canonical project files:
- `.claude/CLAUDE.md` points Claude users at the shared project instructions.
Reusable workflow guides live under `ai/skills/`:
+- `flutter-template-project-setup` for app identity, icons, splash, platform cleanup, Firebase/secrets decisions, and initial validation.
- `flutter-template-feature-screen` for route and UI scaffolding.
- `flutter-template-feature-data-flow` for full backend-backed features with DTOs, entities, use cases, and state wiring.
- `flutter-template-upgrade` for Flutter and dependency upgrades.
diff --git a/ai/skills/flutter-template-project-setup/SKILL.md b/ai/skills/flutter-template-project-setup/SKILL.md
new file mode 100644
index 0000000..d2b4fd7
--- /dev/null
+++ b/ai/skills/flutter-template-project-setup/SKILL.md
@@ -0,0 +1,115 @@
+---
+name: flutter-template-project-setup
+description: Customize a new project created from this Flutter template, including app identity, package name, platform cleanup, icons, splash screen, Firebase/secrets decisions, setup tool execution, code generation, and validation.
+---
+
+# Flutter Template Project Setup
+
+Use this skill when preparing a new app from this template or reviewing whether setup is complete.
+
+## Read First
+- `AGENTS.md`
+- `README.md`
+- `project_setup/README.md`
+- `project_setup/lib/configuration.dart`
+- `makefile`
+- `docs/PROJECT_OVERVIEW.md`
+- `docs/PROJECT_GUIDELINES.md`
+
+## Safety Rules
+- Keep deterministic mutations in the Dart setup tool under `project_setup/`.
+- Do not touch decrypted `.env*`, signing files, or `extras/secrets/` unless the task explicitly includes secrets or signing setup.
+- Do not hand-edit generated files such as `*.g.dart`, `*.freezed.dart`, `*.gr.dart`, or `lib/assets/*`.
+- Treat Firebase, notifications, Remote Config, and web configuration as scaffolded until verified in `lib/app/setup/setup_app.dart`.
+
+## Setup Workflow
+1. Inspect the requested app name, package name, supported platforms, flavors, Firebase plan, and secret/signing needs.
+2. Update `project_setup/lib/configuration.dart` for:
+ - icon variants and colors
+ - splash background colors and platforms
+ - old/new app name
+ - old/new package name
+3. Verify setup image inputs:
+ - `project_setup/resources/icon.png` must be `900x900`
+ - `project_setup/resources/splash.png` must be `768x768`
+4. Run `make setup` from the repository root and select the needed setup operation.
+5. If platform support changes, run the Platform Cleanup Workflow below.
+6. If Firebase support changes, run the Firebase Workflow below.
+7. If secrets or signing material are needed, use the `flutter-template-secrets-bootstrap` workflow.
+8. Update the README First steps checklist to mark any completed setup items with `[x]`.
+9. Run `make gen` after setup changes that affect routes, localization, generated assets, or annotations.
+10. Validate with `fvm flutter analyze` and the relevant tests.
+
+## Platform Cleanup Workflow
+Use this workflow to automate step 4 from the README First steps checklist.
+
+1. Determine the final supported platforms from the user request. Supported platform names are Android, iOS, web, Windows, Linux, snap, and macOS. If the request does not clearly say which platforms to keep or remove, ask before deleting platform folders.
+2. Remove unsupported platform folders and files:
+ - Android: `android/`
+ - iOS: `ios/`
+ - Web: `web/`, `firebase.json`, `.firebaserc`
+ - Windows: `windows/`
+ - Linux: `linux/`
+ - Snap: `snap/`
+ - macOS: `macos/`
+3. Update `lib/app/setup/app_platform.dart`:
+ - Remove enum values for deleted platforms.
+ - Simplify derived getters such as `isMobile`, `isDesktop`, `isApple`, `isWeb`, and platform-specific getters so they only reference remaining enum values.
+4. Update `lib/app/configuration/configuration.dart` so runtime platform detection only assigns remaining `AppPlatform` values. Replace deleted-platform branches with either a remaining fallback or an `UnsupportedError`, depending on the app's intended platform policy.
+5. Update platform-specific setup code in `lib/app/setup/setup_app.dart`:
+ - If web is removed, remove `package:flutter_web_plugins/url_strategy.dart`, `lib/app/setup/web_setup.dart`, `WebSetup.setup(...)`, and `usePathUrlStrategy()`.
+ - If all desktop platforms are removed, remove `package:window_manager/window_manager.dart` and desktop window setup.
+ - If specific desktop platforms are removed but at least one desktop remains, keep desktop setup only when it still applies.
+6. Clean dependencies in `pubspec.yaml`:
+ - If web is removed, remove `flutter_web_plugins` and `universal_html`.
+ - If all desktop platforms are removed, remove `window_manager`.
+ - After editing dependencies, run `fvm flutter pub get`.
+7. Search for stale references with `rg`:
+ - Removed platform enum values and getters, such as `AppPlatform.isWeb`, `AppPlatform.isLinux`, or `AppPlatform.macos`.
+ - Removed imports, such as `web_setup.dart`, `flutter_web_plugins`, `universal_html`, and `window_manager`.
+ - Removed native folders or generated files mentioned from build scripts, README snippets, or setup config.
+8. Adjust or remove any code paths that only make sense for deleted platforms. Examples include native store open handling, force-update platform branches, Firebase messaging/web tokens, system bar setup, and launch/build documentation.
+9. Run `fvm flutter analyze`. Run relevant tests when platform cleanup touches shared runtime logic.
+
+## Firebase Workflow
+Use this workflow to automate step 9 from the README First steps checklist.
+
+1. Determine whether Firebase should be kept or removed. If the request is unclear, ask because Firebase affects authentication, analytics, crash reporting, push notifications, remote config, web hosting, release distribution, and secrets.
+2. If Firebase is kept:
+ - Confirm the final supported platforms first, then configure only those platforms.
+ - Ensure Firebase CLI and FlutterFire CLI are available through `make install` or `fvm dart pub global activate flutterfire_cli`.
+ - Prefer `fvm flutterfire configure` when the Firebase project exists and the user can authenticate locally.
+ - For manual setup, place Android `google-services.json` in `android/app/google-services.json` when Android is supported.
+ - For iOS flavors, place `GoogleService-Info.plist` files under `ios/config/{develop,staging,production}/` when iOS is supported.
+ - For web, replace placeholder web Firebase values in `lib/app/setup/setup_app.dart`, `web/firebase-messaging-sw.js`, and related `.env*` entries or document them as intentionally pending.
+ - Inspect native integration after setup: Android Gradle Google Services, Crashlytics, App Distribution config, iOS plist copy script, iOS Crashlytics symbol upload script, and web Firebase hosting files.
+ - Keep notification, Remote Config, and local notification startup disabled or enabled intentionally in `lib/app/setup/setup_app.dart`; do not assume scaffolded services are production-ready.
+3. If Firebase is removed completely:
+ - Remove Firebase packages from `pubspec.yaml`: `firebase_analytics`, `firebase_auth`, `firebase_core`, `firebase_crashlytics`, `firebase_messaging`, and `firebase_remote_config`.
+ - Remove Firebase setup and imports from `lib/app/setup/setup_app.dart`.
+ - Remove or replace Firebase-backed auth use cases under `lib/common/usecase/authentication/`.
+ - Remove or replace Firebase-specific providers such as `firebase_messaging_service.dart`, `firebase_remote_config_service.dart`, and notification code that depends on `FirebaseMessaging`.
+ - Remove or replace analytics and crash reporting wrappers that import Firebase, such as `lib/core/analytics/analytics_manager.dart` and `lib/core/analytics/crashlytics_manager.dart`.
+ - Update landing, force-update, routing observer, logging, and social-login code paths that rely on Firebase apps, Firebase Auth, Remote Config, Analytics, Crashlytics, or Messaging.
+ - Remove native Firebase integration from Android Gradle files, iOS/macOS generated plugin references if they are checked in, web Firebase service workers, Firebase hosting files when hosting is not used, and Firebase App Distribution workflows or docs if they no longer apply.
+ - Run `fvm flutter pub get` after dependency removal.
+4. Search for stale references with `rg`:
+ - `firebase`, `Firebase`, `GoogleService`, `google-services`, `flutterfire`, `Crashlytics`, `RemoteConfig`, `FirebaseMessaging`, `FirebaseAuth`, `.firebaserc`, and `firebase.json`.
+ - Treat encrypted secret files under `extras/secrets/` as secrets-owned unless the user explicitly asked to rotate or remove encrypted Firebase secrets.
+5. Validate with `make gen` when generated providers or routes changed, then run `fvm flutter analyze` and relevant tests.
+6. Mark README First steps item 9 as `[x]` only after Firebase has been configured for the intended platforms or removed completely.
+
+## Dependency Checks
+- Run `fvm dart pub outdated` inside `project_setup/` when touching the setup tool.
+- Run `fvm flutter pub outdated` at the repo root when reviewing app dependencies.
+- Keep setup-tool linting aligned with the root app when possible, especially `netglade_analysis`.
+
+## Completion Criteria
+- App name and package name are updated in native and Flutter files.
+- Icon and splash generation completed without ignored subprocess failures.
+- Platform decisions are reflected in native folders, `AppPlatform`, runtime setup, dependencies, and stale-reference searches.
+- Firebase decisions are reflected in setup code, dependencies, platform config, native integration, generated files, docs, and stale-reference searches.
+- Secrets decisions are reflected in files or documented as intentionally pending.
+- Completed README First steps items are checked off with `[x]`.
+- `make gen` completed when required.
+- Analyze and relevant tests completed, or any blockers are clearly reported.
diff --git a/android/app/src/developDebug/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/developDebug/res/mipmap-mdpi/ic_launcher_foreground.png
index a5f0d86..0a6b26f 100644
Binary files a/android/app/src/developDebug/res/mipmap-mdpi/ic_launcher_foreground.png and b/android/app/src/developDebug/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/developDebug/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/app/src/developDebug/res/mipmap-xhdpi/ic_launcher_foreground.png
index 1c1d3ea..3662e96 100644
Binary files a/android/app/src/developDebug/res/mipmap-xhdpi/ic_launcher_foreground.png and b/android/app/src/developDebug/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/developDebug/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/developDebug/res/mipmap-xxxhdpi/ic_launcher.png
index 431500c..5c58c2e 100644
Binary files a/android/app/src/developDebug/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/developDebug/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/developRelease/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/developRelease/res/mipmap-mdpi/ic_launcher_foreground.png
index c7ffa6c..ea95890 100644
Binary files a/android/app/src/developRelease/res/mipmap-mdpi/ic_launcher_foreground.png and b/android/app/src/developRelease/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/developRelease/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/app/src/developRelease/res/mipmap-xhdpi/ic_launcher_foreground.png
index aeb6374..ccb5b72 100644
Binary files a/android/app/src/developRelease/res/mipmap-xhdpi/ic_launcher_foreground.png and b/android/app/src/developRelease/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/developRelease/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/developRelease/res/mipmap-xxxhdpi/ic_launcher.png
index 4291e39..a8f485e 100644
Binary files a/android/app/src/developRelease/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/developRelease/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/productionDebug/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/productionDebug/res/mipmap-mdpi/ic_launcher_foreground.png
index 9b730c1..dc13627 100644
Binary files a/android/app/src/productionDebug/res/mipmap-mdpi/ic_launcher_foreground.png and b/android/app/src/productionDebug/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/productionDebug/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/app/src/productionDebug/res/mipmap-xhdpi/ic_launcher_foreground.png
index 8aa5b60..6461f07 100644
Binary files a/android/app/src/productionDebug/res/mipmap-xhdpi/ic_launcher_foreground.png and b/android/app/src/productionDebug/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/productionDebug/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/productionDebug/res/mipmap-xxxhdpi/ic_launcher.png
index 4c1c6b2..89d0326 100644
Binary files a/android/app/src/productionDebug/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/productionDebug/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/productionRelease/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/productionRelease/res/mipmap-mdpi/ic_launcher_foreground.png
index 86a3784..21ce0c1 100644
Binary files a/android/app/src/productionRelease/res/mipmap-mdpi/ic_launcher_foreground.png and b/android/app/src/productionRelease/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/productionRelease/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/app/src/productionRelease/res/mipmap-xhdpi/ic_launcher_foreground.png
index 84bcaa7..b5157c8 100644
Binary files a/android/app/src/productionRelease/res/mipmap-xhdpi/ic_launcher_foreground.png and b/android/app/src/productionRelease/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/productionRelease/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/productionRelease/res/mipmap-xxxhdpi/ic_launcher.png
index 42b7747..f4ddfcc 100644
Binary files a/android/app/src/productionRelease/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/productionRelease/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/stagingDebug/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/stagingDebug/res/mipmap-mdpi/ic_launcher_foreground.png
index 637a26a..9df62a4 100644
Binary files a/android/app/src/stagingDebug/res/mipmap-mdpi/ic_launcher_foreground.png and b/android/app/src/stagingDebug/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/stagingDebug/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/app/src/stagingDebug/res/mipmap-xhdpi/ic_launcher_foreground.png
index 228fdaa..2fc28cb 100644
Binary files a/android/app/src/stagingDebug/res/mipmap-xhdpi/ic_launcher_foreground.png and b/android/app/src/stagingDebug/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/stagingDebug/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/stagingDebug/res/mipmap-xxxhdpi/ic_launcher.png
index 9c1d0ba..12b3be2 100644
Binary files a/android/app/src/stagingDebug/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/stagingDebug/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/stagingRelease/res/mipmap-mdpi/ic_launcher_foreground.png b/android/app/src/stagingRelease/res/mipmap-mdpi/ic_launcher_foreground.png
index 826ff75..0e3871e 100644
Binary files a/android/app/src/stagingRelease/res/mipmap-mdpi/ic_launcher_foreground.png and b/android/app/src/stagingRelease/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/stagingRelease/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/app/src/stagingRelease/res/mipmap-xhdpi/ic_launcher_foreground.png
index fadb979..2e4e221 100644
Binary files a/android/app/src/stagingRelease/res/mipmap-xhdpi/ic_launcher_foreground.png and b/android/app/src/stagingRelease/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/stagingRelease/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/stagingRelease/res/mipmap-xxxhdpi/ic_launcher.png
index ec2f97a..e5c0c18 100644
Binary files a/android/app/src/stagingRelease/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/stagingRelease/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Contents.json
index d807305..246be5f 100644
--- a/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Contents.json
+++ b/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Contents.json
@@ -114,7 +114,7 @@
}
],
"info": {
- "author": "icons_launcher",
- "version": 1
+ "version": 1,
+ "author": "icons_launcher"
}
}
\ No newline at end of file
diff --git a/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-38x38@2x.png b/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-38x38@2x.png
index 31e39d6..df1da88 100644
Binary files a/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-38x38@2x.png and b/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-38x38@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-68x68@2x.png b/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-68x68@2x.png
index 00ad9db..6ebdb06 100644
Binary files a/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-68x68@2x.png and b/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-68x68@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-76x76@2x.png
index 89e3f1d..11fb876 100644
Binary files a/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-76x76@2x.png and b/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-83.5x83.5@2x.png
index 6054cff..370757b 100644
Binary files a/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Contents.json
index d807305..246be5f 100644
--- a/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Contents.json
+++ b/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Contents.json
@@ -114,7 +114,7 @@
}
],
"info": {
- "author": "icons_launcher",
- "version": 1
+ "version": 1,
+ "author": "icons_launcher"
}
}
\ No newline at end of file
diff --git a/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-38x38@2x.png b/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-38x38@2x.png
index 09af2ce..b0544ca 100644
Binary files a/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-38x38@2x.png and b/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-38x38@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-68x68@2x.png b/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-68x68@2x.png
index 6047c57..0bb3c06 100644
Binary files a/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-68x68@2x.png and b/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-68x68@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-76x76@2x.png
index 0e9b159..bf814e5 100644
Binary files a/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-76x76@2x.png and b/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-83.5x83.5@2x.png
index d891a7c..05a313c 100644
Binary files a/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Contents.json
index d807305..246be5f 100644
--- a/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Contents.json
+++ b/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Contents.json
@@ -114,7 +114,7 @@
}
],
"info": {
- "author": "icons_launcher",
- "version": 1
+ "version": 1,
+ "author": "icons_launcher"
}
}
\ No newline at end of file
diff --git a/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-38x38@2x.png b/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-38x38@2x.png
index dbd332b..10caf3e 100644
Binary files a/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-38x38@2x.png and b/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-38x38@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-68x68@2x.png b/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-68x68@2x.png
index 3d26170..a14de38 100644
Binary files a/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-68x68@2x.png and b/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-68x68@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-76x76@2x.png
index 7963ec4..e4421e2 100644
Binary files a/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-76x76@2x.png and b/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-83.5x83.5@2x.png
index 48a1508..ef77413 100644
Binary files a/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Contents.json
index d807305..246be5f 100644
--- a/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Contents.json
+++ b/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Contents.json
@@ -114,7 +114,7 @@
}
],
"info": {
- "author": "icons_launcher",
- "version": 1
+ "version": 1,
+ "author": "icons_launcher"
}
}
\ No newline at end of file
diff --git a/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-38x38@2x.png b/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-38x38@2x.png
index 047d9f8..844f8e1 100644
Binary files a/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-38x38@2x.png and b/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-38x38@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-68x68@2x.png b/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-68x68@2x.png
index bc48d80..098209a 100644
Binary files a/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-68x68@2x.png and b/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-68x68@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-76x76@2x.png
index 4160851..dce3219 100644
Binary files a/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-76x76@2x.png and b/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-83.5x83.5@2x.png
index 0697087..eb19069 100644
Binary files a/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Contents.json
index d807305..246be5f 100644
--- a/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Contents.json
+++ b/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Contents.json
@@ -114,7 +114,7 @@
}
],
"info": {
- "author": "icons_launcher",
- "version": 1
+ "version": 1,
+ "author": "icons_launcher"
}
}
\ No newline at end of file
diff --git a/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-38x38@2x.png b/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-38x38@2x.png
index 8252b35..d94d611 100644
Binary files a/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-38x38@2x.png and b/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-38x38@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-68x68@2x.png b/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-68x68@2x.png
index ad1d7f4..c4b43c7 100644
Binary files a/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-68x68@2x.png and b/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-68x68@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-76x76@2x.png
index 6459f22..44e72ac 100644
Binary files a/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-76x76@2x.png and b/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-83.5x83.5@2x.png
index 7eef908..21561e0 100644
Binary files a/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Contents.json b/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Contents.json
index d807305..246be5f 100644
--- a/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Contents.json
+++ b/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Contents.json
@@ -114,7 +114,7 @@
}
],
"info": {
- "author": "icons_launcher",
- "version": 1
+ "version": 1,
+ "author": "icons_launcher"
}
}
\ No newline at end of file
diff --git a/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-38x38@2x.png b/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-38x38@2x.png
index 3e76804..9f4a661 100644
Binary files a/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-38x38@2x.png and b/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-38x38@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-68x68@2x.png b/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-68x68@2x.png
index 88fc256..3008888 100644
Binary files a/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-68x68@2x.png and b/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-68x68@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-76x76@2x.png
index 9eeb5d3..9764769 100644
Binary files a/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-76x76@2x.png and b/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-83.5x83.5@2x.png
index 0c54ee2..4f84210 100644
Binary files a/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index 24ae9d7..05d5fd5 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -1,98 +1,97 @@
-
- CADisableMinimumFrameDurationOnPhone
-
- CFBundleDevelopmentRegion
- $(DEVELOPMENT_LANGUAGE)
- CFBundleDisplayName
- Flutter Template
- CFBundleExecutable
- $(EXECUTABLE_NAME)
- CFBundleIdentifier
- $(PRODUCT_BUNDLE_IDENTIFIER)
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- Flutter Template
- CFBundlePackageType
- APPL
- CFBundleShortVersionString
- $(FLUTTER_BUILD_NAME)
- CFBundleSignature
- ????
- CFBundleURLTypes
-
-
- CFBundleTypeRole
- Editor
- CFBundleURLName
- REVERSED_CLIENT_ID
- CFBundleURLSchemes
-
- $(REVERSED_GOOGLE_CLIENT_ID)
-
-
-
- CFBundleVersion
- $(FLUTTER_BUILD_NUMBER)
- GIDClientID
- $(GOOGLE_CLIENT_ID)
- ITSAppUsesNonExemptEncryption
-
- LSRequiresIPhoneOS
-
- NSLocationWhenInUseUsageDescription
- Your location is used for analytics purposes to help us improve the app and its
- services. It is not used for advertising or other tracking purposes.
- UIApplicationSceneManifest
- UIApplicationSupportsMultipleScenes
+ CADisableMinimumFrameDurationOnPhone
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ Flutter Template
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ Flutter Template
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleURLTypes
+
+
+ CFBundleTypeRole
+ Editor
+ CFBundleURLName
+ REVERSED_CLIENT_ID
+ CFBundleURLSchemes
+
+ $(REVERSED_GOOGLE_CLIENT_ID)
+
+
+
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ GIDClientID
+ $(GOOGLE_CLIENT_ID)
+ ITSAppUsesNonExemptEncryption
- UISceneConfigurations
+ LSRequiresIPhoneOS
+
+ NSLocationWhenInUseUsageDescription
+ Your location is used for analytics purposes to help us improve the app and its services. It is not used for advertising or other tracking purposes.
+ UIApplicationSceneManifest
- UIWindowSceneSessionRoleApplication
-
-
- UISceneClassName
- UIWindowScene
- UISceneConfigurationName
- flutter
- UISceneDelegateClassName
- FlutterSceneDelegate
- UISceneStoryboardFile
- Main
-
-
+ UIApplicationSupportsMultipleScenes
+
+ UISceneConfigurations
+
+ UIWindowSceneSessionRoleApplication
+
+
+ UISceneClassName
+ UIWindowScene
+ UISceneConfigurationName
+ flutter
+ UISceneDelegateClassName
+ FlutterSceneDelegate
+ UISceneStoryboardFile
+ Main
+
+
+
+ UIApplicationSupportsIndirectInputEvents
+
+ UIBackgroundModes
+
+ remote-notification
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UIStatusBarHidden
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
- UIApplicationSupportsIndirectInputEvents
-
- UIBackgroundModes
-
- remote-notification
-
- UILaunchStoryboardName
- LaunchScreen
- UIMainStoryboardFile
- Main
- UIStatusBarHidden
-
- UISupportedInterfaceOrientations
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
- UISupportedInterfaceOrientations~ipad
-
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationPortraitUpsideDown
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
-
- UIViewControllerBasedStatusBarAppearance
-
-
diff --git a/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..e146039
--- /dev/null
+++ b/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+ "images": [
+ {
+ "size": "16x16",
+ "idiom": "mac",
+ "filename": "app_icon_16.png",
+ "scale": "1x"
+ },
+ {
+ "size": "16x16",
+ "idiom": "mac",
+ "filename": "app_icon_32.png",
+ "scale": "2x"
+ },
+ {
+ "size": "32x32",
+ "idiom": "mac",
+ "filename": "app_icon_32.png",
+ "scale": "1x"
+ },
+ {
+ "size": "32x32",
+ "idiom": "mac",
+ "filename": "app_icon_64.png",
+ "scale": "2x"
+ },
+ {
+ "size": "128x128",
+ "idiom": "mac",
+ "filename": "app_icon_128.png",
+ "scale": "1x"
+ },
+ {
+ "size": "128x128",
+ "idiom": "mac",
+ "filename": "app_icon_256.png",
+ "scale": "2x"
+ },
+ {
+ "size": "256x256",
+ "idiom": "mac",
+ "filename": "app_icon_256.png",
+ "scale": "1x"
+ },
+ {
+ "size": "256x256",
+ "idiom": "mac",
+ "filename": "app_icon_512.png",
+ "scale": "2x"
+ },
+ {
+ "size": "512x512",
+ "idiom": "mac",
+ "filename": "app_icon_512.png",
+ "scale": "1x"
+ },
+ {
+ "size": "512x512",
+ "idiom": "mac",
+ "filename": "app_icon_1024.png",
+ "scale": "2x"
+ }
+ ],
+ "info": {
+ "version": 1,
+ "author": "icons_launcher"
+ }
+}
\ No newline at end of file
diff --git a/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_1024.png
new file mode 100644
index 0000000..13d7386
Binary files /dev/null and b/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_1024.png differ
diff --git a/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_128.png
new file mode 100644
index 0000000..7982458
Binary files /dev/null and b/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_128.png differ
diff --git a/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_16.png
new file mode 100644
index 0000000..683e31f
Binary files /dev/null and b/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_16.png differ
diff --git a/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_256.png
new file mode 100644
index 0000000..afc9fbf
Binary files /dev/null and b/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_256.png differ
diff --git a/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_32.png
new file mode 100644
index 0000000..60050a2
Binary files /dev/null and b/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_32.png differ
diff --git a/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_512.png
new file mode 100644
index 0000000..770e2f7
Binary files /dev/null and b/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_512.png differ
diff --git a/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_64.png
new file mode 100644
index 0000000..f5399c2
Binary files /dev/null and b/macos/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/app_icon_64.png differ
diff --git a/makefile b/makefile
index 2f1b94e..89220e5 100644
--- a/makefile
+++ b/makefile
@@ -2,6 +2,7 @@
.PHONY: setup watch clean gen locale install integration_test test generateAndroidProductionAppBundle generateIosDevelopIpa generateIosStagingIpa generateIosProductionIpa generateWebProduction deployWeb runner_gen secretsDecrypt secretsEncrypt secretsClean
setup: # Setup the project
+ @cd project_setup && fvm dart pub get
@fvm dart ./project_setup/lib/main.dart
watch: # Run build_runner
diff --git a/project_setup/README.md b/project_setup/README.md
index 1a11ff3..d48157b 100644
--- a/project_setup/README.md
+++ b/project_setup/README.md
@@ -8,7 +8,7 @@ This Dart program is designed to streamline the initial setup process for a new
## App Icon Generation
### Step 1
-Get your app icon from a designer and save it as `/project_setup/resources/icon.png`. This icon should be 750x750 with a transparent background.
+Get your app icon from a designer and save it as `/project_setup/resources/icon.png`. This icon must be 900x900 with a transparent background.
### Step 2
All you need to do now is set the correct configuration in `/project_setup/lib/configuration.dart`.
@@ -35,3 +35,9 @@ All you need to do is set the correct application name and package name configur
### Step 2
Run `make setup` in the project root, then select the rename option.
+
+## Validation Notes
+
+- `make setup` runs `dart pub get` for this package before opening the menu.
+- The icon and splash generators fail early when the input PNG has the wrong dimensions.
+- External generation commands are checked; setup stops if `icons_launcher` or `flutter_native_splash` fails.
diff --git a/project_setup/analysis_options.yaml b/project_setup/analysis_options.yaml
index e657abc..3279d6e 100644
--- a/project_setup/analysis_options.yaml
+++ b/project_setup/analysis_options.yaml
@@ -36,6 +36,8 @@ linter:
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
+ avoid_print: false
+ avoid_slow_async_io: false
sort_pub_dependencies: false
require_trailing_commas: false
avoid_positional_boolean_parameters: false
diff --git a/project_setup/lib/configuration.dart b/project_setup/lib/configuration.dart
index c049a9d..9cd7fb6 100644
--- a/project_setup/lib/configuration.dart
+++ b/project_setup/lib/configuration.dart
@@ -3,27 +3,27 @@ import 'package:project_setup/core/entity/setup_platform.dart';
class Configuration {
// Title: App Icon
- static final appIconBackgroundColor = '#F2F2F2';
- static final appIconVariants = [
+ static const appIconBackgroundColor = '#F2F2F2';
+ static const appIconVariants = [
AppIconVariantModel(name: 'developDebug', labelColorHex: '#37B73B', labelText: 'DEV', debugIndicator: true),
AppIconVariantModel(name: 'developRelease', labelColorHex: '#37B73B', labelText: 'DEV'),
AppIconVariantModel(name: 'stagingDebug', labelColorHex: '#3C45D9', labelText: 'STG', debugIndicator: true),
AppIconVariantModel(name: 'stagingRelease', labelColorHex: '#3C45D9', labelText: 'STG'),
AppIconVariantModel(name: 'productionDebug', labelColorHex: '#68217A', labelText: 'PROD', debugIndicator: true),
- AppIconVariantModel(name: 'productionRelease', platforms: SetupPlatform.all()),
+ AppIconVariantModel(name: 'productionRelease', platforms: SetupPlatform.values),
];
// Title: Splash Screen
- static final splashScreenBackgroundColor = '#F2F2F2';
- static final splashScreenDarkModeBackgroundColor = '#121618';
- static final splashScreenPlatforms = [SetupPlatform.android, SetupPlatform.ios, SetupPlatform.web];
+ static const splashScreenBackgroundColor = '#F2F2F2';
+ static const splashScreenDarkModeBackgroundColor = '#121618';
+ static const splashScreenPlatforms = [SetupPlatform.android, SetupPlatform.ios, SetupPlatform.web];
// Title: Rename
- static final renameOldAppName = 'Flutter Template';
- static final renameNewAppName = 'Flutter Template';
- static final renameOldPackageName = 'com.strv.flutter.template';
- static final renameNewPackageName = 'com.strv.flutter.template';
- static final renameAppNameIncludedFiles = [
+ static const renameOldAppName = 'Flutter Template';
+ static const renameNewAppName = 'Flutter Template';
+ static const renameOldPackageName = 'com.strv.flutter.template';
+ static const renameNewPackageName = 'com.strv.flutter.template';
+ static const renameAppNameIncludedFiles = [
'README.md',
'AndroidManifest.xml',
'Info.plist',
@@ -34,7 +34,7 @@ class Configuration {
'app_en.arb',
'app.dart',
];
- static final renamePackageNameIncludedFiles = [
+ static const renamePackageNameIncludedFiles = [
'pubspec.yaml',
'android_play_store_distribution.yml',
'build.gradle',
diff --git a/project_setup/lib/core/app_icon_generator.dart b/project_setup/lib/core/app_icon_generator.dart
index 54052e1..35044cb 100644
--- a/project_setup/lib/core/app_icon_generator.dart
+++ b/project_setup/lib/core/app_icon_generator.dart
@@ -7,6 +7,7 @@ import 'package:project_setup/configuration.dart';
import 'package:project_setup/core/entity/app_icon_variant_model.dart';
import 'package:project_setup/core/entity/setup_platform.dart';
import 'package:project_setup/core/util/print.dart';
+import 'package:project_setup/core/util/process.dart';
import 'package:project_setup/core/util/util.dart';
Future generateAppIcons() async {
@@ -17,43 +18,51 @@ Future generateAppIcons() async {
size: 0.25,
rightPrompt: (step) => ' $step / ${Configuration.appIconVariants.length * 2}',
).interact();
- progress.increase(1);
for (final appIconVariant in Configuration.appIconVariants) {
- // Title: Step 1 - Generate config file for variant
- final iconsLauncherConfigFile = await _generateConfigFile(appIconVariant: appIconVariant);
-
- // Title: Step 2 - Generate icon for variant
- final icons = await _generateIcons(
- labelColorHex: appIconVariant.labelColorHex,
- labelText: appIconVariant.labelText,
- displayDebugIndicator: appIconVariant.debugIndicator,
- iconShouldOverlayLabel: appIconVariant.iconShouldOverlayLabel,
- );
-
- progress.increase(1);
-
- // Title: Step 3 - Run icons_launcher
- await Process.run(
- 'fvm',
- [
- 'dart',
- 'pub',
- 'global',
- 'run',
- 'icons_launcher:create',
- '--flavor',
- '${appIconVariant.name}',
- '--path',
- '${getSetupDirectoryPath()}/tmp_icons_launcher_config.yaml',
- ],
- workingDirectory: getProjectRootDirectoryPath(),
- );
+ File? iconsLauncherConfigFile;
+ var icons = [];
+
+ try {
+ // Title: Step 1 - Generate config file for variant
+ iconsLauncherConfigFile = await _generateConfigFile(appIconVariant: appIconVariant);
+
+ // Title: Step 2 - Generate icon for variant
+ icons = await _generateIcons(
+ labelColorHex: appIconVariant.labelColorHex,
+ labelText: appIconVariant.labelText,
+ displayDebugIndicator: appIconVariant.debugIndicator,
+ iconShouldOverlayLabel: appIconVariant.iconShouldOverlayLabel,
+ );
- // Title: Step 4 - Cleanup the temporary files
- await iconsLauncherConfigFile.delete();
- for (final icon in icons) {
- await icon.delete();
+ progress.increase(1);
+
+ // Title: Step 3 - Run icons_launcher
+ await runRequiredProcess(
+ 'fvm',
+ [
+ 'dart',
+ 'pub',
+ 'global',
+ 'run',
+ 'icons_launcher:create',
+ '--flavor',
+ appIconVariant.name,
+ '--path',
+ '${getSetupDirectoryPath()}/tmp_icons_launcher_config.yaml',
+ ],
+ workingDirectory: getProjectRootDirectoryPath(),
+ );
+ } finally {
+ // Title: Step 4 - Cleanup the temporary files
+ if (iconsLauncherConfigFile != null && await iconsLauncherConfigFile.exists()) {
+ await iconsLauncherConfigFile.delete();
+ }
+ for (final icon in icons) {
+ if (await icon.exists()) {
+ await icon.delete();
+ }
+ }
}
progress.increase(1);
@@ -109,14 +118,21 @@ Future> _generateIcons({
required bool iconShouldOverlayLabel,
}) async {
// Load the input image file
- File inputImageFile = File('${getSetupDirectoryPath()}/resources/icon.png');
- File fontFile = File('${getSetupDirectoryPath()}/resources/exo_bold.zip');
- List inputImageBytes = await inputImageFile.readAsBytes();
- Image inputImage = decodeImage(Uint8List.fromList(inputImageBytes))!;
- Image outputImage = Image(width: 1500, height: 1500, numChannels: 4);
+ final inputImageFile = File('${getSetupDirectoryPath()}/resources/icon.png');
+ final fontFile = File('${getSetupDirectoryPath()}/resources/exo_bold.zip');
+ final inputImageBytes = await inputImageFile.readAsBytes();
+ final inputImage = decodeImage(Uint8List.fromList(inputImageBytes));
+ if (inputImage == null) {
+ throw StateError('Could not decode ${inputImageFile.path}.');
+ }
+ if (inputImage.width != 900 || inputImage.height != 900) {
+ throw StateError('Expected ${inputImageFile.path} to be 900x900, got ${inputImage.width}x${inputImage.height}.');
+ }
+
+ var outputImage = Image(width: 1500, height: 1500, numChannels: 4);
final font = BitmapFont.fromZip(await fontFile.readAsBytes());
- void _drawLabel() {
+ void drawLabel() {
// Draw label rectangle
drawLine(outputImage, x1: 0, y1: 1240, x2: 1500, y2: 1240, color: colorFromHex(labelColorHex!), thickness: 530);
@@ -142,15 +158,15 @@ Future> _generateIcons({
}
}
- void _drawOverlayImage() {
+ void drawOverlayImage() {
final widthOffset = 1500 ~/ 2 - inputImage.width ~/ 2;
final heightOffset = 1500 ~/ 2 - inputImage.height ~/ 2;
// Manually overlay the overlayImage onto the baseImage
- for (int y = 0; y < inputImage.height; y++) {
- for (int x = 0; x < inputImage.width; x++) {
+ for (var y = 0; y < inputImage.height; y++) {
+ for (var x = 0; x < inputImage.width; x++) {
// Get the pixel color and alpha value
- Pixel pixel = inputImage.getPixel(x, y);
- num alpha = pixel.a;
+ final pixel = inputImage.getPixel(x, y);
+ final alpha = pixel.a;
// If the pixel is fully transparent, fill it with the specified color
if (alpha != 0) {
@@ -160,32 +176,32 @@ Future> _generateIcons({
}
}
- Future _saveImage(String fileName) async {
- File outputImageFile = File('${getSetupDirectoryPath()}/$fileName');
+ Future saveImage(String fileName) async {
+ final outputImageFile = File('${getSetupDirectoryPath()}/$fileName');
await outputImageFile.writeAsBytes(encodePng(outputImage));
return outputImageFile;
}
// Title: Step 1 - Generate Android Foreground image (1500x1500 - transparent background)
- if (!iconShouldOverlayLabel) _drawOverlayImage();
- if (labelColorHex != null && labelText != null) _drawLabel();
- if (iconShouldOverlayLabel) _drawOverlayImage();
+ if (!iconShouldOverlayLabel) drawOverlayImage();
+ if (labelColorHex != null && labelText != null) drawLabel();
+ if (iconShouldOverlayLabel) drawOverlayImage();
- final androidForegroundFile = await _saveImage('tmp_app_icon_android_foreground.png');
+ final androidForegroundFile = await saveImage('tmp_app_icon_android_foreground.png');
// Title: Step 2 - Generate Android image (1500x1500 - filled background)
fill(outputImage, color: colorFromHex(Configuration.appIconBackgroundColor));
- if (!iconShouldOverlayLabel) _drawOverlayImage();
- if (labelColorHex != null && labelText != null) _drawLabel();
- if (iconShouldOverlayLabel) _drawOverlayImage();
+ if (!iconShouldOverlayLabel) drawOverlayImage();
+ if (labelColorHex != null && labelText != null) drawLabel();
+ if (iconShouldOverlayLabel) drawOverlayImage();
- final androidFile = await _saveImage('tmp_app_icon_android.png');
+ final androidFile = await saveImage('tmp_app_icon_android.png');
// Title: Step 3 - Generate Cropped image (1024x1024 - filled background)
- int space = (1500 - 1024) ~/ 2;
+ const space = (1500 - 1024) ~/ 2;
outputImage = copyCrop(outputImage, x: space, y: space, width: 1024, height: 1024);
- final croppedFile = await _saveImage('tmp_app_icon_cropped.png');
+ final croppedFile = await saveImage('tmp_app_icon_cropped.png');
return [androidForegroundFile, androidFile, croppedFile];
}
diff --git a/project_setup/lib/core/app_rename.dart b/project_setup/lib/core/app_rename.dart
index e82e1e7..88343f4 100644
--- a/project_setup/lib/core/app_rename.dart
+++ b/project_setup/lib/core/app_rename.dart
@@ -6,6 +6,7 @@ import 'package:project_setup/core/util/util.dart';
// Define lists of directories and files to exclude
Future appRename() async {
printBoldCyan('Rebranding the App...');
+ _validateConfiguration();
// Title: Step 1 - Recursively go trough all directories and replace application name string
if (Configuration.renameOldAppName != Configuration.renameNewAppName) {
@@ -32,7 +33,7 @@ Future appRename() async {
printBoldYellow('Renaming Android Directories');
await findAndReplaceDirectoryPath(
- directoryPath: getProjectRootDirectoryPath() + '/android',
+ directoryPath: '${getProjectRootDirectoryPath()}/android',
oldStructure: Configuration.renameOldPackageName.replaceAll('.', '/'),
newStructure: Configuration.renameNewPackageName.replaceAll('.', '/'),
excludedDirs: ['build', 'project_setup', '.git'],
@@ -42,3 +43,22 @@ Future appRename() async {
// Finish
printBoldGreen('✅ App rebranding successful!');
}
+
+void _validateConfiguration() {
+ if (Configuration.renameNewAppName.trim().isEmpty) {
+ throw ArgumentError.value(Configuration.renameNewAppName, 'renameNewAppName', 'App name must not be empty.');
+ }
+
+ if (Configuration.renameOldPackageName == Configuration.renameNewPackageName) {
+ return;
+ }
+
+ final packageNameRegExp = RegExp(r'^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+$');
+ if (!packageNameRegExp.hasMatch(Configuration.renameNewPackageName)) {
+ throw ArgumentError.value(
+ Configuration.renameNewPackageName,
+ 'renameNewPackageName',
+ 'Use a lowercase reverse-DNS package name, for example com.example.app.',
+ );
+ }
+}
diff --git a/project_setup/lib/core/entity/app_icon_variant_model.dart b/project_setup/lib/core/entity/app_icon_variant_model.dart
index 2804c60..5f1fabe 100644
--- a/project_setup/lib/core/entity/app_icon_variant_model.dart
+++ b/project_setup/lib/core/entity/app_icon_variant_model.dart
@@ -3,8 +3,8 @@ import 'package:project_setup/core/entity/setup_platform.dart';
class AppIconVariantModel {
const AppIconVariantModel({
required this.name,
- this.labelColorHex = null,
- this.labelText = null,
+ this.labelColorHex,
+ this.labelText,
this.debugIndicator = false,
this.iconShouldOverlayLabel = false,
this.platforms = const [SetupPlatform.android, SetupPlatform.ios],
diff --git a/project_setup/lib/core/entity/setup_platform.dart b/project_setup/lib/core/entity/setup_platform.dart
index c13513f..399fcd9 100644
--- a/project_setup/lib/core/entity/setup_platform.dart
+++ b/project_setup/lib/core/entity/setup_platform.dart
@@ -1,10 +1 @@
-enum SetupPlatform {
- android,
- ios,
- web,
- windows,
- macos,
- linux;
-
- static List all() => SetupPlatform.values.toList();
-}
+enum SetupPlatform { android, ios, web, windows, macos, linux }
diff --git a/project_setup/lib/core/splash_screen_generator.dart b/project_setup/lib/core/splash_screen_generator.dart
index 7f32dca..fa51c2b 100644
--- a/project_setup/lib/core/splash_screen_generator.dart
+++ b/project_setup/lib/core/splash_screen_generator.dart
@@ -6,6 +6,7 @@ import 'package:interact/interact.dart';
import 'package:project_setup/configuration.dart';
import 'package:project_setup/core/entity/setup_platform.dart';
import 'package:project_setup/core/util/print.dart';
+import 'package:project_setup/core/util/process.dart';
import 'package:project_setup/core/util/util.dart';
Future generateSplashScreen() async {
@@ -14,32 +15,40 @@ Future generateSplashScreen() async {
final progress = Progress(length: 5, size: 0.25, rightPrompt: (step) => ' $step / 5').interact();
progress.increase(1);
- // Title: Step 1 - Generate config file
- final configFile = await _generateConfigFile();
- progress.increase(1);
-
- // Title: Step 2 - Generate image
- final splashImage = await _generateSplashImage();
- progress.increase(1);
-
- // Title: Step 3 - Run flutter_native_splash
- await Process.run(
- 'fvm',
- [
- 'dart',
- 'pub',
- 'run',
- 'flutter_native_splash:create',
- '--path',
- '${getSetupDirectoryPath()}/tmp_splash_screen_config.yaml',
- ],
- workingDirectory: getProjectRootDirectoryPath(),
- );
- progress.increase(1);
-
- // Title: Step 4 - Cleanup the temporary files
- await configFile.delete();
- await splashImage.delete();
+ File? configFile;
+ File? splashImage;
+ try {
+ // Title: Step 1 - Generate config file
+ configFile = await _generateConfigFile();
+ progress.increase(1);
+
+ // Title: Step 2 - Generate image
+ splashImage = await _generateSplashImage();
+ progress.increase(1);
+
+ // Title: Step 3 - Run flutter_native_splash
+ await runRequiredProcess(
+ 'fvm',
+ [
+ 'dart',
+ 'pub',
+ 'run',
+ 'flutter_native_splash:create',
+ '--path',
+ '${getSetupDirectoryPath()}/tmp_splash_screen_config.yaml',
+ ],
+ workingDirectory: getProjectRootDirectoryPath(),
+ );
+ progress.increase(1);
+ } finally {
+ // Title: Step 4 - Cleanup the temporary files
+ if (configFile != null && await configFile.exists()) {
+ await configFile.delete();
+ }
+ if (splashImage != null && await splashImage.exists()) {
+ await splashImage.delete();
+ }
+ }
progress.increase(1);
@@ -74,19 +83,26 @@ flutter_native_splash:
/// Warning: The image must fit in a circle shape!!!
Future _generateSplashImage() async {
// Load the input image file
- File inputImageFile = File('${getSetupDirectoryPath()}/resources/splash.png');
- List inputImageBytes = await inputImageFile.readAsBytes();
- Image inputImage = decodeImage(Uint8List.fromList(inputImageBytes))!;
- Image outputImage = Image(width: 1152, height: 1152, numChannels: 4);
+ final inputImageFile = File('${getSetupDirectoryPath()}/resources/splash.png');
+ final inputImageBytes = await inputImageFile.readAsBytes();
+ final inputImage = decodeImage(Uint8List.fromList(inputImageBytes));
+ if (inputImage == null) {
+ throw StateError('Could not decode ${inputImageFile.path}.');
+ }
+ if (inputImage.width != 768 || inputImage.height != 768) {
+ throw StateError('Expected ${inputImageFile.path} to be 768x768, got ${inputImage.width}x${inputImage.height}.');
+ }
+
+ final outputImage = Image(width: 1152, height: 1152, numChannels: 4);
- void _drawOverlayImage() {
- final offset = 1152 ~/ 2 - 768 ~/ 2;
+ void drawOverlayImage() {
+ const offset = 1152 ~/ 2 - 768 ~/ 2;
// Manually overlay the overlayImage onto the baseImage
- for (int y = 0; y < inputImage.height; y++) {
- for (int x = 0; x < inputImage.width; x++) {
+ for (var y = 0; y < inputImage.height; y++) {
+ for (var x = 0; x < inputImage.width; x++) {
// Get the pixel color and alpha value
- Pixel pixel = inputImage.getPixel(x, y);
- num alpha = pixel.a;
+ final pixel = inputImage.getPixel(x, y);
+ final alpha = pixel.a;
// If the pixel is fully transparent, fill it with the specified color
if (alpha != 0) {
@@ -96,14 +112,14 @@ Future _generateSplashImage() async {
}
}
- Future _saveImage(String fileName) async {
- File outputImageFile = File('${getSetupDirectoryPath()}/$fileName');
+ Future saveImage(String fileName) async {
+ final outputImageFile = File('${getSetupDirectoryPath()}/$fileName');
await outputImageFile.writeAsBytes(encodePng(outputImage));
return outputImageFile;
}
// Title: Generate Larger image (1152x1152 with image in center)
- _drawOverlayImage();
+ drawOverlayImage();
- return await _saveImage('tmp_splash.png');
+ return saveImage('tmp_splash.png');
}
diff --git a/project_setup/lib/core/util/files.dart b/project_setup/lib/core/util/files.dart
index b87fb60..bee2a36 100644
--- a/project_setup/lib/core/util/files.dart
+++ b/project_setup/lib/core/util/files.dart
@@ -9,19 +9,41 @@ Future findAndReplaceTextInDirectory({
List? excludedDirs,
List? includedFiles,
}) async {
- final directory = Directory.fromUri(Uri.parse(directoryPath));
+ final directory = Directory(directoryPath);
- // List all entities (files and directories) in the current directory recursively
- await for (FileSystemEntity entity in directory.list(recursive: true, followLinks: false)) {
- // Check if the entity is a directory and if it's in the excludedDirs list
+ await _findAndReplaceTextInDirectory(
+ directory: directory,
+ searchString: searchString,
+ replaceString: replaceString,
+ excludedDirs: excludedDirs,
+ includedFiles: includedFiles,
+ );
+}
+
+Future _findAndReplaceTextInDirectory({
+ required Directory directory,
+ required String searchString,
+ required String replaceString,
+ List? excludedDirs,
+ List? includedFiles,
+}) async {
+ await for (final entity in directory.list(followLinks: false)) {
if (entity is Directory) {
- if (excludedDirs != null && excludedDirs.contains(entity.path.split(Platform.pathSeparator).last)) {
+ if (_isExcludedDirectory(entity, excludedDirs)) {
print('Skipping directory: ${entity.path}');
continue;
}
+
+ await _findAndReplaceTextInDirectory(
+ directory: entity,
+ searchString: searchString,
+ replaceString: replaceString,
+ excludedDirs: excludedDirs,
+ includedFiles: includedFiles,
+ );
+ continue;
}
- // Check if the entity is a file and if it's in the excludedFiles list
if (entity is File) {
if (includedFiles != null && includedFiles.contains(entity.path.split(Platform.pathSeparator).last)) {
await findAndReplaceTextInFile(
@@ -42,7 +64,7 @@ Future findAndReplaceTextInFile({
}) async {
try {
// Read the file content
- String content = await file.readAsString();
+ var content = await file.readAsString();
// Check if the file contains the search string
if (content.contains(searchString)) {
@@ -67,39 +89,75 @@ Future findAndReplaceDirectoryPath({
required String newStructure,
List? excludedDirs,
}) async {
- final directory = Directory.fromUri(Uri.parse(directoryPath));
+ final directory = Directory(directoryPath);
final dirStillExists = await directory.exists();
if (!dirStillExists) {
return;
}
- // List all entities (files and directories) in the current directory recursively
- await for (FileSystemEntity entity in directory.list(recursive: true, followLinks: false)) {
- // Check if the entity is a directory
- if (entity is Directory) {
- try {
- // Check if the entity is in the excludedDirs list
- if (excludedDirs != null && excludedDirs.contains(entity.path.split(Platform.pathSeparator).last)) {
- print('Skipping directory: ${entity.path}');
- continue;
- }
-
- await findAndReplaceDirectoryPath(directoryPath: entity.path, oldStructure: oldStructure, newStructure: newStructure);
-
- // Check if the directory path matches the old structure
- final dirStillExists = await entity.exists();
- if (entity.path.contains(oldStructure) && dirStillExists) {
- // Create the new directory path
- final newPath = entity.path.replaceFirst(oldStructure, newStructure);
- await Directory(newPath).create(recursive: true);
- await entity.rename(newPath);
- printBrightBlue('Renamed directory: ${entity.path} to $newPath');
- }
- // ignore: avoid_catches_without_on_clauses
- } catch (e) {
- // Print an error message if an exception occurs
- printBoldRed('Error processing directory ${entity.path}: $e');
+ final normalizedOldStructure = oldStructure.replaceAll('/', Platform.pathSeparator);
+ final normalizedNewStructure = newStructure.replaceAll('/', Platform.pathSeparator);
+ final matchingDirectories = [];
+
+ await _collectMatchingDirectories(
+ directory: directory,
+ normalizedOldStructure: normalizedOldStructure,
+ excludedDirs: excludedDirs,
+ matchingDirectories: matchingDirectories,
+ );
+
+ matchingDirectories.sort((a, b) => b.path.length.compareTo(a.path.length));
+
+ for (final entity in matchingDirectories) {
+ try {
+ if (!await entity.exists()) {
+ continue;
}
+
+ final newPath = entity.path.replaceFirst(normalizedOldStructure, normalizedNewStructure);
+ await Directory(newPath).parent.create(recursive: true);
+ await entity.rename(newPath);
+ printBrightBlue('Renamed directory: ${entity.path} to $newPath');
+ // ignore: avoid_catches_without_on_clauses
+ } catch (e) {
+ printBoldRed('Error processing directory ${entity.path}: $e');
+ }
+ }
+}
+
+Future _collectMatchingDirectories({
+ required Directory directory,
+ required String normalizedOldStructure,
+ required List matchingDirectories,
+ List? excludedDirs,
+}) async {
+ await for (final entity in directory.list(followLinks: false)) {
+ if (entity is! Directory) {
+ continue;
+ }
+
+ if (_isExcludedDirectory(entity, excludedDirs)) {
+ print('Skipping directory: ${entity.path}');
+ continue;
+ }
+
+ if (entity.path.contains(normalizedOldStructure)) {
+ matchingDirectories.add(entity);
}
+
+ await _collectMatchingDirectories(
+ directory: entity,
+ normalizedOldStructure: normalizedOldStructure,
+ excludedDirs: excludedDirs,
+ matchingDirectories: matchingDirectories,
+ );
}
}
+
+bool _isExcludedDirectory(Directory directory, List? excludedDirs) {
+ if (excludedDirs == null) {
+ return false;
+ }
+
+ return excludedDirs.contains(directory.path.split(Platform.pathSeparator).last);
+}
diff --git a/project_setup/lib/core/util/process.dart b/project_setup/lib/core/util/process.dart
new file mode 100644
index 0000000..49578a5
--- /dev/null
+++ b/project_setup/lib/core/util/process.dart
@@ -0,0 +1,40 @@
+import 'dart:io';
+
+import 'package:project_setup/core/util/print.dart';
+
+Future runRequiredProcess(
+ String executable,
+ List arguments, {
+ required String workingDirectory,
+}) async {
+ final result = await Process.run(
+ executable,
+ arguments,
+ workingDirectory: workingDirectory,
+ );
+
+ if (result.exitCode == 0) {
+ _printProcessOutput(result.stdout);
+ return;
+ }
+
+ printBoldRed('Command failed with exit code ${result.exitCode}: $executable ${arguments.join(' ')}');
+ _printProcessOutput(result.stdout);
+ _printProcessOutput(result.stderr);
+
+ throw ProcessException(
+ executable,
+ arguments,
+ result.stderr.toString(),
+ result.exitCode,
+ );
+}
+
+void _printProcessOutput(Object? output) {
+ final outputText = output?.toString().trim();
+ if (outputText == null || outputText.isEmpty) {
+ return;
+ }
+
+ print(outputText);
+}
diff --git a/project_setup/lib/core/util/util.dart b/project_setup/lib/core/util/util.dart
index 2b403ad..f956ce6 100644
--- a/project_setup/lib/core/util/util.dart
+++ b/project_setup/lib/core/util/util.dart
@@ -3,26 +3,41 @@ import 'dart:io';
import 'package:image/image.dart';
String getSetupDirectoryPath() {
- return Directory.current.path + '/project_setup';
+ return '${getProjectRootDirectoryPath()}${Platform.pathSeparator}project_setup';
}
String getProjectRootDirectoryPath() {
- return Directory.current.path;
+ final currentDirectory = Directory.current;
+ final currentPath = currentDirectory.path;
+
+ if (Directory('$currentPath${Platform.pathSeparator}project_setup').existsSync()) {
+ return currentPath;
+ }
+
+ final pathSegments = currentDirectory.uri.pathSegments.where((segment) => segment.isNotEmpty).toList();
+ if (pathSegments.isNotEmpty && pathSegments.last == 'project_setup') {
+ return currentDirectory.parent.path;
+ }
+
+ return currentPath;
}
Color colorFromHex(String hexString) {
- // Remove the hash sign if it exists
- hexString = hexString.replaceFirst('#', '');
+ final normalizedHexString = hexString.replaceFirst('#', '');
+ final isValidHexColor = RegExp(r'^[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$').hasMatch(normalizedHexString);
+ if (!isValidHexColor) {
+ throw FormatException('Expected a 6 or 8 character hex color value, got "$hexString".');
+ }
// Parse the hex string to get integer values for red, green, and blue
- int hexColor = int.parse(hexString, radix: 16);
- double red = ((hexColor >> 16) & 0xFF) / 255.0;
- double green = ((hexColor >> 8) & 0xFF) / 255.0;
- double blue = (hexColor & 0xFF) / 255.0;
+ final hexColor = int.parse(normalizedHexString, radix: 16);
+ final red = ((hexColor >> 16) & 0xFF) / 255.0;
+ final green = ((hexColor >> 8) & 0xFF) / 255.0;
+ final blue = (hexColor & 0xFF) / 255.0;
// If the hex string contains alpha (8 characters), parse it
- double alpha = 1.0;
- if (hexString.length == 8) {
+ var alpha = 1.0;
+ if (normalizedHexString.length == 8) {
alpha = ((hexColor >> 24) & 0xFF) / 255.0;
}
diff --git a/project_setup/lib/main.dart b/project_setup/lib/main.dart
index 20ca2be..055b29e 100644
--- a/project_setup/lib/main.dart
+++ b/project_setup/lib/main.dart
@@ -1,10 +1,3 @@
-// ignore_for_file: avoid_print
-
-/// You can build this as executable file using the following command:
-/// dart compile exe setup.dart -o run-linux
-
-import 'dart:io';
-
import 'package:interact/interact.dart';
import 'package:project_setup/core/app_icon_generator.dart';
import 'package:project_setup/core/app_rename.dart';
@@ -20,25 +13,24 @@ void main() async {
}
Future _displaySetupOptions() async {
- printBoldCyan('');
- printBoldCyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
- final setupOptions = ['Generate App Icons', 'Generate Splash Screen', 'Rename', 'Exit'];
- final selection = Select(
- prompt: 'What would you like to do?',
- options: setupOptions,
- ).interact();
+ while (true) {
+ printBoldCyan('');
+ printBoldCyan('━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
+ final setupOptions = ['Generate App Icons', 'Generate Splash Screen', 'Rename', 'Exit'];
+ final selection = Select(
+ prompt: 'What would you like to do?',
+ options: setupOptions,
+ ).interact();
- if (selection == 0) {
- await generateAppIcons();
- _displaySetupOptions();
- } else if (selection == 1) {
- await generateSplashScreen();
- _displaySetupOptions();
- } else if (selection == 2) {
- await appRename();
- _displaySetupOptions();
- } else {
- print('Exiting...');
- exit(0);
+ if (selection == 0) {
+ await generateAppIcons();
+ } else if (selection == 1) {
+ await generateSplashScreen();
+ } else if (selection == 2) {
+ await appRename();
+ } else {
+ print('Exiting...');
+ return;
+ }
}
}
diff --git a/project_setup/pubspec.lock b/project_setup/pubspec.lock
index a9f3b81..f2cdbfc 100644
--- a/project_setup/pubspec.lock
+++ b/project_setup/pubspec.lock
@@ -85,10 +85,10 @@ packages:
dependency: "direct dev"
description:
name: netglade_analysis
- sha256: f63413816d93c4efea92f59599e47c0fee5882bfd3342b2d21e387fabf59765a
+ sha256: cba2f98bf4b61c95aacdd2a1c90653c745bb6a79e81aeec04266bbafeafd2c5d
url: "https://pub.dev"
source: hosted
- version: "21.0.0"
+ version: "22.0.0"
path:
dependency: transitive
description:
diff --git a/project_setup/pubspec.yaml b/project_setup/pubspec.yaml
index 1f1a149..15bcca5 100644
--- a/project_setup/pubspec.yaml
+++ b/project_setup/pubspec.yaml
@@ -13,4 +13,4 @@ dependencies:
dev_dependencies:
- netglade_analysis: ^21.0.0
+ netglade_analysis: ^22.0.0
diff --git a/snap/gui/flutter-app.desktop b/snap/gui/flutter-app.desktop
new file mode 100644
index 0000000..844978e
--- /dev/null
+++ b/snap/gui/flutter-app.desktop
@@ -0,0 +1,8 @@
+[Desktop Entry]
+Name=flutter-app
+Comment=Generated by Icons Launcher
+Exec=flutter-app
+Icon=${SNAP}/meta/gui/flutter-app.png
+Terminal=false
+Type=Application
+Categories=Entertainment;
diff --git a/snap/gui/flutter-app.png b/snap/gui/flutter-app.png
new file mode 100644
index 0000000..afc9fbf
Binary files /dev/null and b/snap/gui/flutter-app.png differ
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
new file mode 100644
index 0000000..8bb2bc4
--- /dev/null
+++ b/snap/snapcraft.yaml
@@ -0,0 +1,28 @@
+name: flutter-app
+version: 1.0.0
+summary: Generated by Icons Launcher
+description: Generated by Icons Launcher package for Linux.
+
+confinement: strict
+base: core22
+grade: stable
+
+slots:
+ dbus-flutter-app: # adjust accordingly to your app name
+ interface: dbus
+ bus: session
+ name: org.bar.flutter-app # adjust accordingly to your app name
+
+apps:
+ flutter-app:
+ command: flutter_app
+ extensions: [gnome] # gnome includes the libraries required by flutter
+ plugs:
+ - network
+ slots:
+ - dbus-flutter-app
+parts:
+ flutter-app:
+ source: .
+ plugin: flutter
+ flutter-target: lib/main.dart # The main entry-point file of the application
diff --git a/web/favicon.png b/web/favicon.png
index 60050a2..7e8836a 100644
Binary files a/web/favicon.png and b/web/favicon.png differ
diff --git a/web/index.html b/web/index.html
index ff0d715..fed02f1 100644
--- a/web/index.html
+++ b/web/index.html
@@ -1,132 +1,107 @@
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
+
+
+
+
-
+
Flutter Template
-
+
-
+
+
+
-
-
+ background-color: #121618;
+ }
+ }
+
+
+
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
+