From d7246111d1c33e162530e38a2577d85ce2709678 Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Wed, 21 May 2025 15:57:42 +0200 Subject: [PATCH 1/9] chore: Readme update --- README.md | 26 ++++++++++++++++++++++---- docs/flutter_template.jpg | Bin 0 -> 69737 bytes 2 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 docs/flutter_template.jpg diff --git a/README.md b/README.md index 5587d9a..93e3e09 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,21 @@ +![LOGO](docs/flutter_template.jpg) + +
+ +[![Flutter](https://img.shields.io/badge/Flutter-3.32-blue)](#) +[![Dart](https://img.shields.io/badge/Dart-3.6.0-blue)](#) +[![License](https://img.shields.io/github/license/HE-LU/flutter-template)](LICENSE) + +
+ +
+ +[![CodeCheck](https://github.com/HE-LU/flutter-template/actions/workflows/flutter_project_codecheck_android.yml/badge.svg)](https://github.com/HE-LU/flutter-template/actions/workflows/flutter_project_codecheck_android.yml) +[![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/HE-LU/flutter-template?utm_source=oss&utm_medium=github&utm_campaign=HE-LU%2Fflutter-template&labelColor=171717&color=FF570A)](#) + +
+ + # Flutter Template - [Project notes](#project-notes) @@ -58,7 +76,7 @@ # Project notes -- Notes that could be benefitial to know during the development. +- Notes that could be beneficial to know during the development. - For example list of test accounts, required credentials, steps required to get testing account, etc. @@ -175,7 +193,7 @@ You can read about flavors setup in the following tutorials: - 🖥️ [Linux](https://docs.flutter.dev/deployment/linux) - Flavors are not supported yet. In case of using firebase with multiple flavors, you have to: -- for iOS add custom build run script phase and copy appropriate `GoogleService-Info.plist` file. It's already prepared, you just need to uncomment it and add appropriate plists to destinations according to the script. +- for iOS add custom build run script phase and copy appropriate `GoogleService-Info.plist` file. It's already prepared, you just need to uncomment it and add appropriate `plists` to destinations according to the script. For Google sign in: - for iOS, you have to copy value from `REVERSED_CLIENT_ID` from `GoogleService-Info.plist` into `GOOGLE_REVERSED_CLIENT_ID` in build settings. @@ -189,7 +207,7 @@ For Google sign in: - As we are using flavors, we have to specify which flavor to build using `--flavor` argument, and also select a correct main file using `-t` argument. - One of the arguments you should use is `--obfuscate`. This makes reverse engineering harder. This has to be used also with `--split-debug-info` which should make the app smaller, and also specify the directory where the mapping file to read obfuscated stack trace is stored. - Optional step is to include precompiled Shaders. To do that you have to first [Precompile Shaders](#precompiling-shaders) and add `--bundle-sksl-path` argument. -- In case we would like to build a Debugable version of the app, we have to add `--debug` argument. +- In case we would like to build a Debuggable version of the app, we have to add `--debug` argument. Here is an example of assembling an Android app bundle using all the commands: ``` @@ -339,7 +357,7 @@ This way we are ensuring that at least the Android version is buildable. We are ## Theming Theming is done inside `lib/core/themes/app_theme.dart`. Currently, we suggest using Material 3. The main setup is done in file `app_theme.dart`. -The main idea right now is to overwrite the whole `colorScheme` with an "undefined" pinkish color and to not use the default theme colorScheme anywhere in the app. Every widget like AppBar, EleveatedButton, TextField, etc. should have a custom implementation starting with the word `Custom` so it is easily recognizable. This Widget then should wrap the appropriate widget, and should utilize colors from `context.colorScheme`, which has an extension method, as is returning our own implementation of `CustomColorScheme`. All colors should be declared there. +The main idea right now is to overwrite the whole `colorScheme` with an "undefined" pinkish color and to not use the default theme colorScheme anywhere in the app. Every widget like AppBar, ElevatedButton, TextField, etc. should have a custom implementation starting with the word `Custom` so it is easily recognizable. This Widget then should wrap the appropriate widget, and should utilize colors from `context.colorScheme`, which has an extension method, as is returning our own implementation of `CustomColorScheme`. All colors should be declared there. The most tricky part of theming is to make sure that the app supports Edge-to-Edge, and that it has correct navigation and StatusBar colors set. To make this possible, we had to implement our own `CustomSystemBarsTheme` class. To use it properly, make sure you are calling `setupSystemBarsTheme` during the App startup. In case of the need of overriding the Brightness for a specific screen, there are two approaches. First, the simple one is to just use `CustomAppBar`, and set brightness to it. Second, wrap the whole Scaffold inside `CustomSystemBarsThemeWidget` and set Brightness to it. diff --git a/docs/flutter_template.jpg b/docs/flutter_template.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c37d2612742ca942020758a72bfd86424225ea81 GIT binary patch literal 69737 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 Date: Wed, 21 May 2025 16:45:49 +0200 Subject: [PATCH 2/9] chore: Flutter version and library upgrade --- .fvmrc | 2 +- .vscode/settings.json | 2 +- android/app/build.gradle | 4 - project_setup/pubspec.lock | 56 ++++++----- project_setup/pubspec.yaml | 6 +- pubspec.lock | 196 +++++++++++++++++++------------------ pubspec.yaml | 33 ++++--- 7 files changed, 156 insertions(+), 143 deletions(-) diff --git a/.fvmrc b/.fvmrc index b6a9abf..eb1057a 100644 --- a/.fvmrc +++ b/.fvmrc @@ -1,4 +1,4 @@ { - "flutter": "3.29.3", + "flutter": "3.32.0", "flavors": {} } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index e560150..17f2bdf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "dart.flutterSdkPath": ".fvm/versions/3.29.3" + "dart.flutterSdkPath": ".fvm/versions/3.32.0" } \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index a5d67c5..94b7aa2 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -73,7 +73,6 @@ android { } defaultConfig { - applicationId "com.strv.flutter.template" // You can update the following values to match your application needs. // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. minSdkVersion minAndroidSdkVersion @@ -117,9 +116,6 @@ android { shrinkResources true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - - // Comment this when testing dynamic links. - applicationIdSuffix = ".debug" } release { signingConfig signingConfigs.release diff --git a/project_setup/pubspec.lock b/project_setup/pubspec.lock index c343794..00d36ac 100644 --- a/project_setup/pubspec.lock +++ b/project_setup/pubspec.lock @@ -5,42 +5,42 @@ packages: dependency: transitive description: name: archive - sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" url: "https://pub.dev" source: hosted - version: "3.6.1" + version: "4.0.7" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.19.0" + version: "1.19.1" crypto: dependency: transitive description: name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.6" dart_console: dependency: transitive description: @@ -53,18 +53,18 @@ packages: dependency: transitive description: name: ffi - sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" image: dependency: "direct main" description: name: image - sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8" + sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "4.5.4" interact: dependency: "direct main" description: @@ -85,23 +85,31 @@ packages: dependency: transitive description: name: meta - sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.15.0" + version: "1.17.0" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" petitparser: dependency: transitive description: name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" + url: "https://pub.dev" + source: hosted + version: "6.1.0" + posix: + dependency: transitive + description: + name: posix + sha256: f0d7856b6ca1887cfa6d1d394056a296ae33489db914e365e2044fdada449e62 url: "https://pub.dev" source: hosted version: "6.0.2" @@ -117,18 +125,18 @@ packages: dependency: transitive description: name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.4.0" win32: dependency: transitive description: name: win32 - sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 + sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba" url: "https://pub.dev" source: hosted - version: "5.5.1" + version: "5.13.0" xml: dependency: transitive description: @@ -138,4 +146,4 @@ packages: source: hosted version: "6.5.0" sdks: - dart: ">=3.4.0 <4.0.0" + dart: ">=3.8.0 <4.0.0" diff --git a/project_setup/pubspec.yaml b/project_setup/pubspec.yaml index 6c81c69..f463410 100644 --- a/project_setup/pubspec.yaml +++ b/project_setup/pubspec.yaml @@ -4,9 +4,9 @@ version: 1.0.0 environment: - sdk: '>=3.2.0 <4.0.0' + sdk: '>=3.8.0 <4.0.0' dependencies: - image: 4.2.0 # https://pub.dev/packages/image - interact: 2.2.0 # https://pub.dev/packages/interact + image: 4.5.4 + interact: 2.2.0 diff --git a/pubspec.lock b/pubspec.lock index 0ba05c0..9c1997d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: de9ecbb3ddafd446095f7e833c853aff2fa1682b017921fe63a833f9d6f0e422 + sha256: "214e6f07e2a44f45972e0365c7b537eaeaddb4598db0778dd4ac64b4acd3f5b1" url: "https://pub.dev" source: hosted - version: "1.3.54" + version: "1.3.55" analyzer: dependency: transitive description: @@ -53,10 +53,10 @@ packages: dependency: transitive description: name: archive - sha256: "7dcbd0f87fe5f61cb28da39a1a8b70dbc106e2fe0516f7836eb7bb2948481a12" + sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" url: "https://pub.dev" source: hosted - version: "4.0.5" + version: "4.0.7" args: dependency: transitive description: @@ -69,10 +69,10 @@ packages: dependency: transitive description: name: async - sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" url: "https://pub.dev" source: hosted - version: "2.12.0" + version: "2.13.0" auto_route: dependency: "direct main" description: @@ -301,10 +301,10 @@ packages: dependency: transitive description: name: dart_style - sha256: "27eb0ae77836989a3bc541ce55595e8ceee0992807f14511552a898ddd0d88ac" + sha256: "5b236382b47ee411741447c1f1e111459c941ea1b3f2b540dde54c210a3662af" url: "https://pub.dev" source: hosted - version: "3.0.1" + version: "3.1.0" dartx: dependency: transitive description: @@ -325,10 +325,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: "306b78788d1bb569edb7c55d622953c2414ca12445b41c9117963e03afc5c513" + sha256: "0c6396126421b590089447154c5f98a5de423b70cfb15b1578fd018843ee6f53" url: "https://pub.dev" source: hosted - version: "11.3.3" + version: "11.4.0" device_info_plus_platform_interface: dependency: transitive description: @@ -373,10 +373,10 @@ packages: dependency: "direct main" description: name: extended_image - sha256: fcefcf3cba32696c639e9e305a790039709d05a7139320b91bb9d300993452e2 + sha256: f6cbb1d798f51262ed1a3d93b4f1f2aa0d76128df39af18ecb77fa740f88b2e0 url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.1" extended_image_library: dependency: transitive description: @@ -389,10 +389,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" url: "https://pub.dev" source: hosted - version: "1.3.2" + version: "1.3.3" ffi: dependency: transitive description: @@ -413,58 +413,58 @@ packages: dependency: "direct main" description: name: firebase_analytics - sha256: "2416b9d864412ab7b571dafded801bbcc7e29b5824623c055002d4d0819bea2b" + sha256: b5b31727ad7c7f41e139637d1d582e435fd14054abcbd162c64476f90737105c url: "https://pub.dev" source: hosted - version: "11.4.5" + version: "11.4.6" firebase_analytics_platform_interface: dependency: transitive description: name: firebase_analytics_platform_interface - sha256: "3ccf5c876a8bea186016de4bcf53fc1bc6fa01236d740fb501d7ef9be356c58e" + sha256: "86817ee7db6377e09830467932c31644713d8e8915cefff64610ad735612bc7d" url: "https://pub.dev" source: hosted - version: "4.3.5" + version: "4.3.6" firebase_analytics_web: dependency: transitive description: name: firebase_analytics_web - sha256: "5e4e3f001b67c2034b76cb2a42a0eed330fb3a8fb41ad13eceb04e8d9a74f662" + sha256: "4914e184a6aa37811d976db59f95fa8fda1d58db49ab7f73d4d8aac9dd9b19b7" url: "https://pub.dev" source: hosted - version: "0.5.10+11" + version: "0.5.10+12" firebase_auth: dependency: "direct main" description: name: firebase_auth - sha256: "54c62b2d187709114dd09ce658a8803ee91f9119b0e0d3fc2245130ad9bff9ad" + sha256: "10cd3f00a247f33b0a5c77574011a87379432bf3fec77a500b55f2bcc30ddd8b" url: "https://pub.dev" source: hosted - version: "5.5.2" + version: "5.5.4" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - sha256: "5402d13f4bb7f29f2fb819f3b6b5a5a56c9f714aef2276546d397e25ac1b6b8e" + sha256: "2d15872a8899b0459fab6b4c148fd142e135acfc8a303d383d80b455e4dba7bd" url: "https://pub.dev" source: hosted - version: "7.6.2" + version: "7.6.3" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - sha256: "2be496911f0807895d5fe8067b70b7d758142dd7fb26485cbe23e525e2547764" + sha256: efba45393050ca03d992eae1d305d5fc8c0c9f5980624053512e935c23767c4f url: "https://pub.dev" source: hosted - version: "5.14.2" + version: "5.14.3" firebase_core: dependency: "direct main" description: name: firebase_core - sha256: "017d17d9915670e6117497e640b2859e0b868026ea36bf3a57feb28c3b97debe" + sha256: "8cfe3c900512399ce8d50fcc817e5758ff8615eeb6fa5c846a4cc47bbf6353b6" url: "https://pub.dev" source: hosted - version: "3.13.0" + version: "3.13.1" firebase_core_platform_interface: dependency: transitive description: @@ -477,90 +477,90 @@ packages: dependency: transitive description: name: firebase_core_web - sha256: "129a34d1e0fb62e2b488d988a1fc26cc15636357e50944ffee2862efe8929b23" + sha256: ddd72baa6f727e5b23f32d9af23d7d453d67946f380bd9c21daf474ee0f7326e url: "https://pub.dev" source: hosted - version: "2.22.0" + version: "2.23.0" firebase_crashlytics: dependency: "direct main" description: name: firebase_crashlytics - sha256: f3fa4a17c2f061b16b2e3ac7aaed889ae954b8952d0fd3e0009a9870cde7bbd2 + sha256: "49d20b1dd87bb42746e27b5c72c1e3a3d84ae6415d062f8b31ecb71a35d36d0c" url: "https://pub.dev" source: hosted - version: "4.3.5" + version: "4.3.6" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: cedfbe39927711c0e56fc38bfecbd89e17816b21698a3d88d63298c530ed375c + sha256: "0a5b2c9a662db7563482547b31da06a3951277cbfb42af12c5b52a15456ea951" url: "https://pub.dev" source: hosted - version: "3.8.5" + version: "3.8.6" firebase_dynamic_links: dependency: "direct main" description: name: firebase_dynamic_links - sha256: ae8844d78a14a335e1d69d9a198dd5bcc4571ba4b028e45c0972e093b48530f8 + sha256: "417b07ed518cd33cd00d0b9546b5bb1e0a0b7edff694c322881cbafca01ebfc5" url: "https://pub.dev" source: hosted - version: "6.1.5" + version: "6.1.6" firebase_dynamic_links_platform_interface: dependency: transitive description: name: firebase_dynamic_links_platform_interface - sha256: "7cb3b86956268a18c49badd66eb9a9279b71bf7188a7a2a48204f41db2642e78" + sha256: "998fc351ded1fc5e366ace0e627b5e35b462d760f22058d86ee7351c2c96da97" url: "https://pub.dev" source: hosted - version: "0.2.7+5" + version: "0.2.7+6" firebase_messaging: dependency: "direct main" description: name: firebase_messaging - sha256: "5f8918848ee0c8eb172fc7698619b2bcd7dda9ade8b93522c6297dd8f9178356" + sha256: "38111089e511f03daa2c66b4c3614c16421b7d78c84ee04331a0a65b47df4542" url: "https://pub.dev" source: hosted - version: "15.2.5" + version: "15.2.6" firebase_messaging_platform_interface: dependency: transitive description: name: firebase_messaging_platform_interface - sha256: "0bbea00680249595fc896e7313a2bd90bd55be6e0abbe8b9a39d81b6b306acb6" + sha256: ba254769982e5f439e534eed68856181c74e2b3f417c8188afad2bb440807cc7 url: "https://pub.dev" source: hosted - version: "4.6.5" + version: "4.6.6" firebase_messaging_web: dependency: transitive description: name: firebase_messaging_web - sha256: ffb392ce2a7e8439cd0a9a80e3c702194e73c927e5c7b4f0adf6faa00b245b17 + sha256: dba89137272aac39e95f71408ba25c33fb8ed903cd5fa8d1e49b5cd0d96069e0 url: "https://pub.dev" source: hosted - version: "3.10.5" + version: "3.10.6" firebase_remote_config: dependency: "direct main" description: name: firebase_remote_config - sha256: "0178bc8f44a8a30e720ec837fc2a0bac3d99837091d53c2a84d2d5d11da55854" + sha256: e65062f7435c8be13826d38c75218b30251a096ca742e7da55342b33ff8b4749 url: "https://pub.dev" source: hosted - version: "5.4.3" + version: "5.4.4" firebase_remote_config_platform_interface: dependency: transitive description: name: firebase_remote_config_platform_interface - sha256: df77610b1d0b542729e66358c9a1fd3633b35eb58634269686d36058e57e3c84 + sha256: "6d2f94570edbea0b1fb47e623af576f1896d14f839b1e3ba9b393a5e7c93c84d" url: "https://pub.dev" source: hosted - version: "1.5.3" + version: "1.5.4" firebase_remote_config_web: dependency: transitive description: name: firebase_remote_config_web - sha256: "0cbfbe358f396f1663e53bdae626748efed4cf17bf316f29c680c975aa7601e8" + sha256: "5cf6eff2748193b64e9eb9965d5d727585f562d2946a53239385ad667e1c22e4" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.8.4" fixnum: dependency: transitive description: @@ -610,10 +610,10 @@ packages: dependency: "direct main" description: name: flutter_local_notifications - sha256: "33b3e0269ae9d51669957a923f2376bee96299b09915d856395af8c4238aebfa" + sha256: b94a50aabbe56ef254f95f3be75640f99120429f0a153b2dc30143cffc9bfdf3 url: "https://pub.dev" source: hosted - version: "19.1.0" + version: "19.2.1" flutter_local_notifications_linux: dependency: transitive description: @@ -655,10 +655,10 @@ packages: dependency: transitive description: name: flutter_plugin_android_lifecycle - sha256: "5a1e6fb2c0561958d7e4c33574674bda7b77caaca7a33b758876956f2902eea3" + sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e url: "https://pub.dev" source: hosted - version: "2.0.27" + version: "2.0.28" flutter_riverpod: dependency: "direct main" description: @@ -679,10 +679,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: c200fd79c918a40c5cd50ea0877fa13f81bdaf6f0a5d3dbcc2a13e3285d6aa1b + sha256: d44bf546b13025ec7353091516f6881f1d4c633993cb109c3916c3a0159dadf1 url: "https://pub.dev" source: hosted - version: "2.0.17" + version: "2.1.0" flutter_test: dependency: "direct dev" description: flutter @@ -737,10 +737,10 @@ packages: dependency: transitive description: name: google_identity_services_web - sha256: "55580f436822d64c8ff9a77e37d61f5fb1e6c7ec9d632a43ee324e2a05c3c6c9" + sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454" url: "https://pub.dev" source: hosted - version: "0.3.3" + version: "0.3.3+1" google_sign_in: dependency: "direct main" description: @@ -753,10 +753,10 @@ packages: dependency: transitive description: name: google_sign_in_android - sha256: "4e52c64366bdb3fe758f683b088ee514cc7a95e69c52b5ee9fc5919e1683d21b" + sha256: d5e23c56a4b84b6427552f1cf3f98f716db3b1d1a647f16b96dbb5b93afa2805 url: "https://pub.dev" source: hosted - version: "6.2.0" + version: "6.2.1" google_sign_in_ios: dependency: transitive description: @@ -817,18 +817,18 @@ packages: dependency: transitive description: name: html - sha256: "9475be233c437f0e3637af55e7702cbbe5c23a68bd56e8a5fa2d426297b7c6c8" + sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" url: "https://pub.dev" source: hosted - version: "0.15.5+1" + version: "0.15.6" http: dependency: transitive description: name: http - sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" http_client_helper: dependency: transitive description: @@ -873,10 +873,10 @@ packages: dependency: "direct main" description: name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" url: "https://pub.dev" source: hosted - version: "0.19.0" + version: "0.20.2" io: dependency: transitive description: @@ -905,18 +905,18 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: "81f04dee10969f89f604e1249382d46b97a1ccad53872875369622b5bfc9e58a" + sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c url: "https://pub.dev" source: hosted - version: "6.9.4" + version: "6.9.5" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec + sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0" url: "https://pub.dev" source: hosted - version: "10.0.8" + version: "10.0.9" leak_tracker_flutter_testing: dependency: transitive description: @@ -989,6 +989,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0" + netglade_analysis: + dependency: "direct dev" + description: + name: netglade_analysis + sha256: "67778583fbddc666daddb53a9ab948bf5a37cbfb949c5d7fdf51b87b0cd1746a" + url: "https://pub.dev" + source: hosted + version: "16.1.0" package_config: dependency: transitive description: @@ -1041,10 +1049,10 @@ packages: dependency: transitive description: name: path_provider_android - sha256: "0ca7359dad67fd7063cb2892ab0c0737b2daafd807cf1acecd62374c8fae6c12" + sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9 url: "https://pub.dev" source: hosted - version: "2.2.16" + version: "2.2.17" path_provider_foundation: dependency: transitive description: @@ -1137,10 +1145,10 @@ packages: dependency: transitive description: name: posix - sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a + sha256: f0d7856b6ca1887cfa6d1d394056a296ae33489db914e365e2044fdada449e62 url: "https://pub.dev" source: hosted - version: "6.0.1" + version: "6.0.2" pretty_dio_logger: dependency: "direct main" description: @@ -1281,10 +1289,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: c2c8c46297b5d6a80bed7741ec1f2759742c77d272f1a1698176ae828f8e1a18 + sha256: "20cbd561f743a342c76c151d6ddb93a9ce6005751e7aa458baad3858bfbfb6ac" url: "https://pub.dev" source: hosted - version: "2.4.9" + version: "2.4.10" shared_preferences_foundation: dependency: transitive description: @@ -1470,10 +1478,10 @@ packages: dependency: transitive description: name: talker_logger - sha256: "416b4d726358764c68c2de23c11bf9f146f2608ac244ec2ed9da741f89c49a6e" + sha256: d8d2afd912c4a185bc125d8ae84d5960c3505f7ec52856e732a4b8a0db510b5d url: "https://pub.dev" source: hosted - version: "4.7.2" + version: "4.7.9" term_glyph: dependency: transitive description: @@ -1502,10 +1510,10 @@ packages: dependency: transitive description: name: timezone - sha256: ffc9d5f4d1193534ef051f9254063fa53d588609418c84299956c3db9383587d + sha256: dd14a3b83cfd7cb19e7888f1cbc20f258b8d71b54c06f79ac585f14093a287d1 url: "https://pub.dev" source: hosted - version: "0.10.0" + version: "0.10.1" timing: dependency: transitive description: @@ -1550,10 +1558,10 @@ packages: dependency: transitive description: name: url_launcher_android - sha256: "1d0eae19bd7606ef60fe69ef3b312a437a16549476c42321d5dc1506c9ca3bf4" + sha256: "8582d7f6fe14d2652b4c45c9b6c14c0b678c2af2d083a11b604caeba51930d79" url: "https://pub.dev" source: hosted - version: "6.3.15" + version: "6.3.16" url_launcher_ios: dependency: transitive description: @@ -1590,10 +1598,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "3ba963161bd0fe395917ba881d320b9c4f6dd3c4a233da62ab18a5025c85f1e9" + sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.1" url_launcher_windows: dependency: transitive description: @@ -1646,10 +1654,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" + sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02 url: "https://pub.dev" source: hosted - version: "14.3.1" + version: "15.0.0" watcher: dependency: transitive description: @@ -1670,26 +1678,26 @@ packages: dependency: transitive description: name: web_socket - sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" url: "https://pub.dev" source: hosted - version: "0.1.6" + version: "1.0.1" web_socket_channel: dependency: transitive description: name: web_socket_channel - sha256: "0b8e2457400d8a859b7b2030786835a28a8e80836ef64402abef392ff4f1d0e5" + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 url: "https://pub.dev" source: hosted - version: "3.0.2" + version: "3.0.3" win32: dependency: transitive description: name: win32 - sha256: dc6ecaa00a7c708e5b4d10ee7bec8c270e9276dfcab1783f57e9962d7884305f + sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba" url: "https://pub.dev" source: hosted - version: "5.12.0" + version: "5.13.0" win32_registry: dependency: transitive description: @@ -1731,5 +1739,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.7.0 <4.0.0" - flutter: ">=3.29.0" + dart: ">=3.8.0 <4.0.0" + flutter: ">=3.32.0" diff --git a/pubspec.yaml b/pubspec.yaml index 6dcede4..6a87713 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,9 +5,8 @@ version: 1.0.0+10000000 #1 00 00 000 environment: - # TODO: Hold the version back to 3.6.0, until the formatting is fixed(https://github.com/dart-lang/dart_style/issues/1253) - sdk: ">=3.6.0 <4.0.0" - flutter: 3.29.0 + sdk: ">=3.8.0 <4.0.0" + flutter: 3.32.0 dependencies: @@ -36,20 +35,20 @@ dependencies: shared_preferences: 2.5.3 # Firebase - firebase_analytics: 11.4.5 - firebase_auth: 5.5.2 - firebase_core: 3.13.0 - firebase_crashlytics: 4.3.5 - firebase_dynamic_links: 6.1.5 - firebase_messaging: 15.2.5 - firebase_remote_config: 5.4.3 + firebase_analytics: 11.4.6 + firebase_auth: 5.5.4 + firebase_core: 3.13.1 + firebase_crashlytics: 4.3.6 + firebase_dynamic_links: 6.1.6 + firebase_messaging: 15.2.6 + firebase_remote_config: 5.4.4 # Authentication google_sign_in: 6.3.0 sign_in_with_apple: 7.0.1 # Device - device_info_plus: 11.3.3 + device_info_plus: 11.4.0 package_info_plus: 8.3.0 path_provider: 2.1.5 window_manager: 0.4.3 @@ -64,14 +63,14 @@ dependencies: talker_dio_logger: 4.4.7 # Resources - extended_image: 10.0.0 + extended_image: 10.0.1 flutter_native_splash: 2.4.6 - flutter_svg: 2.0.17 - intl: 0.19.0 + flutter_svg: 2.1.0 + intl: 0.20.2 # UI flutter_animate: 4.5.2 - flutter_local_notifications: 19.1.0 + flutter_local_notifications: 19.2.1 modal_bottom_sheet: 3.0.0 universal_html: 2.2.4 @@ -97,7 +96,7 @@ dev_dependencies: # Data Structures freezed: 3.0.6 - json_serializable: 6.9.4 + json_serializable: 6.9.5 # Resources flutter_gen_runner: 5.10.0 @@ -105,6 +104,7 @@ dev_dependencies: # Lint custom_lint: 0.7.5 flutter_lints: 5.0.0 + netglade_analysis: 16.1.0 riverpod_lint: 2.6.5 # UI Tests @@ -128,6 +128,7 @@ flutter_gen: flutter: uses-material-design: true + generate: true assets: - assets/png/ From 7da779531fd0b97a40c5b56e7c5578c51f306c47 Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Wed, 21 May 2025 17:48:30 +0200 Subject: [PATCH 3/9] feat: New analysis rules, code fix --- analysis_options.yaml | 44 ++-- android/app/build.gradle | 2 +- flutter.code-workspace | 7 +- lib/app/app.dart | 14 +- lib/app/configuration/configuration.dart | 54 ++--- .../configuration/develop/config_develop.dart | 2 +- .../production/config_production.dart | 2 +- .../configuration/staging/config_staging.dart | 2 +- lib/app/navigation/app_router.dart | 43 ++-- lib/app/setup/setup_app.dart | 46 ++-- lib/app/setup/web_setup.dart | 3 - lib/app/theme/app_theme.dart | 30 ++- lib/app/theme/custom_color_scheme.dart | 64 +++--- lib/app/theme/custom_system_bars_theme.dart | 17 +- lib/app/theme/custom_text_theme.dart | 208 +++++++++--------- .../custom_fade_slide_animation.dart | 10 +- lib/common/component/custom_app_bar.dart | 2 +- .../component/custom_bottom_app_bar.dart | 6 +- .../custom_button/custom_button_primary.dart | 4 +- .../custom_floating_action_button.dart | 2 +- lib/common/component/custom_icon_button.dart | 2 +- .../custom_ink_well_circular.dart | 10 +- .../custom_ink_well_rounded_rectangle.dart | 12 +- .../component/custom_network_image.dart | 9 +- .../component/custom_profile_avatar.dart | 5 +- .../component/custom_progress_indicator.dart | 80 +++---- .../component/custom_radio_button_group.dart | 6 +- .../custom_snackbar_error.dart | 2 +- .../custom_snackbar_message.dart | 2 +- .../custom_snackbar_success.dart | 2 +- lib/common/component/custom_switch.dart | 4 +- lib/common/component/custom_tab_bar.dart | 2 +- .../component/custom_text/custom_text.dart | 2 +- lib/common/component/custom_text_field.dart | 26 +-- .../component/custom_text_field_button.dart | 2 +- .../bottom_sheet_container_widget.dart | 2 +- .../dialog/custom_alert_dialog.dart | 2 +- .../dialog/custom_dialog_wrapper.dart | 7 +- .../expandable_single_child_scroll_view.dart | 2 +- .../placeholder/empty_placeholder_widget.dart | 4 +- .../placeholder/error_placeholder_widget.dart | 21 +- lib/common/composition/responsive_widget.dart | 2 +- .../model/exception/custom_exception.dart | 10 +- .../model/notification_payload_model.dart | 4 +- lib/common/data/model/user_model.dart | 3 +- lib/common/extension/async_value.dart | 28 +-- lib/common/extension/list.dart | 7 +- lib/common/extension/response.dart | 6 +- lib/common/extension/string.dart | 2 +- .../provider/firebase_messaging_service.dart | 28 +-- .../provider/notifications_service.dart | 9 +- .../get_authorization_token_use_case.dart | 2 +- .../sign_in_completion_use_case.dart | 2 +- .../sign_in_with_apple_use_case.dart | 2 +- ...sign_in_with_auth_credential_use_case.dart | 2 +- .../sign_in_with_google_use_case.dart | 2 +- .../usecase/create_device_token_use_case.dart | 4 +- .../usecase/native_store_open_use_case.dart | 34 +-- .../user/get_current_user_use_case.dart | 4 +- .../user/get_database_user_use_case.dart | 2 +- .../text_validator_controller_full_name.dart | 18 +- lib/core/analytics/analytics_event.dart | 6 +- lib/core/analytics/analytics_manager.dart | 5 +- .../analytics/analytics_route_observer.dart | 2 +- lib/core/analytics/crashlytics_manager.dart | 4 +- lib/core/flogger.dart | 154 +++++++------ .../dio_authorization_token_interceptor.dart | 2 +- lib/core/network/dio_provider.dart | 3 - lib/core/riverpod/event_notifier.dart | 4 +- lib/core/riverpod/provider_logger.dart | 6 +- lib/core/riverpod/state_handler.dart | 6 +- .../authentication/authentication_event.dart | 5 +- .../authentication_page_content.dart | 13 +- .../authentication/authentication_state.dart | 6 +- .../debug_tools/debug_tools_page_state.dart | 2 +- .../debug_tools_actions_page_content.dart | 6 +- .../page/popups/debug_tools_list_dialog.dart | 4 +- .../debug_tools_popups_page_content.dart | 2 +- .../debug_tools_widgets_page_content.dart | 7 +- .../debug_tools_widgets_page_event.dart | 3 +- .../debug_tools_widgets_page_state.dart | 3 +- lib/features/home/home_page_content.dart | 3 +- .../landing/force_update_page_content.dart | 13 +- lib/features/landing/landing_page.dart | 6 +- lib/features/profile/profile_event.dart | 5 +- .../profile/profile_page_content.dart | 1 - lib/features/profile/profile_state.dart | 2 +- project_setup/analysis_options.yaml | 45 ++++ project_setup/lib/core/util/files.dart | 4 +- project_setup/pubspec.lock | 12 +- project_setup/pubspec.yaml | 4 + 91 files changed, 676 insertions(+), 599 deletions(-) create mode 100644 project_setup/analysis_options.yaml diff --git a/analysis_options.yaml b/analysis_options.yaml index 607827c..c89c7e8 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -7,13 +7,21 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. -include: package:flutter_lints/flutter.yaml +include: package:netglade_analysis/lints.yaml analyzer: exclude: - 'project_setup/**' - '**.g.dart' - '**.gen.dart' + - '**.config.dart' + - '**.graphql.dart' + errors: + todo: info + +formatter: + page_width: 140 + trailing_commas: preserve linter: # The lint rules applied to this project can be customized in the @@ -28,36 +36,10 @@ linter: # `// ignore_for_file: name_of_lint` syntax on the line or in the file # producing the lint. rules: - # avoid_redundant_argument_values: true - always_declare_return_types: true - always_use_package_imports: true - avoid_catches_without_on_clauses: true - avoid_catching_errors: true - avoid_empty_else: true - avoid_field_initializers_in_const_classes: true - avoid_final_parameters: true - avoid_multiple_declarations_per_line: true - avoid_types_as_parameter_names: true - avoid_types_on_closure_parameters: true - avoid_unnecessary_containers: true - avoid_void_async: true - await_only_futures: true - camel_case_extensions: true - camel_case_types: true - curly_braces_in_flow_control_structures: true - empty_statements: true - eol_at_end_of_file: true - file_names: true - leading_newlines_in_multiline_strings: true - library_names: true - no_duplicate_case_values: true - no_logic_in_create_state: true - no_self_assignments: true - no_wildcard_variable_uses: true - prefer_single_quotes: true - prefer_void_to_null: true - slash_for_doc_comments: true - unnecessary_brace_in_string_interps: true + sort_pub_dependencies: false + require_trailing_commas: false + avoid_positional_boolean_parameters: false + no_default_cases: false # Additional information about this file can be found at # https://dart.dev/guides/language/analysis-options diff --git a/android/app/build.gradle b/android/app/build.gradle index 94b7aa2..e2107ef 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -125,7 +125,7 @@ android { proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - // TODO: Remove or change to true, once Firebase is set-up correctly + // TODO(HELU): Remove or change to true, once Firebase is set-up correctly firebaseCrashlytics { mappingFileUploadEnabled = false unstrippedNativeLibsDir file("build/app/intermediates/merged_native_libs/productionRelease/out/lib") diff --git a/flutter.code-workspace b/flutter.code-workspace index 2fd7f1e..3ec02ef 100644 --- a/flutter.code-workspace +++ b/flutter.code-workspace @@ -192,7 +192,7 @@ "todohighlight.isCaseSensitive": true, "todohighlight.keywords": [ { - "text": " TODO: ", + "text": " TODO(", "color": "#a8c023", "isWholeLine": true, "backgroundColor": "rgba(0,0,0,0)" @@ -227,6 +227,11 @@ "isWholeLine": true, "backgroundColor": "rgba(0,0,0,0)" } + ], + "cSpell.words": [ + "Buildless", + "Refreshable", + "usecase" ] } } diff --git a/lib/app/app.dart b/lib/app/app.dart index 01e10fd..0419d06 100644 --- a/lib/app/app.dart +++ b/lib/app/app.dart @@ -40,7 +40,7 @@ class App extends ConsumerWidget { message: Configuration.instance.flavor == Flavor.develop ? 'DEV' : 'STG', location: BannerLocation.topStart, color: Configuration.instance.flavor == Flavor.develop ? const Color(0xFF37B73B) : const Color(0xFF3C45D9), - ) + ), ], ), ); @@ -65,11 +65,13 @@ class App extends ConsumerWidget { ), builder: (context, child) { if (AppPlatform.isWeb) { - return Overlay(initialEntries: [ - OverlayEntry( - builder: (context) => SelectionArea(child: child ?? const SizedBox()), - ), - ]); + return Overlay( + initialEntries: [ + OverlayEntry( + builder: (context) => SelectionArea(child: child ?? const SizedBox()), + ), + ], + ); } else { return child ?? const SizedBox(); } diff --git a/lib/app/configuration/configuration.dart b/lib/app/configuration/configuration.dart index 2625bdd..be1b042 100644 --- a/lib/app/configuration/configuration.dart +++ b/lib/app/configuration/configuration.dart @@ -31,39 +31,39 @@ class Configuration { final IosDeviceInfo? iosDeviceInfo; String get apiHostUrl => switch (flavor) { - Flavor.develop => ConfigDevelop.apiHostUrl, - Flavor.staging => ConfigStaging.apiHostUrl, - Flavor.production => ConfigProduction.apiHostUrl, - }; + Flavor.develop => ConfigDevelop.apiHostUrl, + Flavor.staging => ConfigStaging.apiHostUrl, + Flavor.production => ConfigProduction.apiHostUrl, + }; // Used for Web notifications String get vapidKey => switch (flavor) { - Flavor.develop => ConfigDevelop.vapidKey, - Flavor.staging => ConfigStaging.vapidKey, - Flavor.production => ConfigProduction.vapidKey, - }; + Flavor.develop => ConfigDevelop.vapidKey, + Flavor.staging => ConfigStaging.vapidKey, + Flavor.production => ConfigProduction.vapidKey, + }; // Used for example for Google SignIn init String? get firebaseClientID => switch (flavor) { - Flavor.develop => switch (currentPlatform) { - AppPlatform.android => ConfigDevelop.androidClientId, - AppPlatform.iOS => ConfigDevelop.iosClientId, - AppPlatform.web => ConfigDevelop.webClientId, - _ => null, - }, - Flavor.staging => switch (currentPlatform) { - AppPlatform.android => ConfigStaging.androidClientId, - AppPlatform.iOS => ConfigStaging.iosClientId, - AppPlatform.web => ConfigStaging.webClientId, - _ => null, - }, - Flavor.production => switch (currentPlatform) { - AppPlatform.android => ConfigProduction.androidClientId, - AppPlatform.iOS => ConfigProduction.iosClientId, - AppPlatform.web => ConfigProduction.webClientId, - _ => null, - }, - }; + Flavor.develop => switch (currentPlatform) { + AppPlatform.android => ConfigDevelop.androidClientId, + AppPlatform.iOS => ConfigDevelop.iosClientId, + AppPlatform.web => ConfigDevelop.webClientId, + _ => null, + }, + Flavor.staging => switch (currentPlatform) { + AppPlatform.android => ConfigStaging.androidClientId, + AppPlatform.iOS => ConfigStaging.iosClientId, + AppPlatform.web => ConfigStaging.webClientId, + _ => null, + }, + Flavor.production => switch (currentPlatform) { + AppPlatform.android => ConfigProduction.androidClientId, + AppPlatform.iOS => ConfigProduction.iosClientId, + AppPlatform.web => ConfigProduction.webClientId, + _ => null, + }, + }; // Function which setups base configuration. static Future setup({required Flavor flavor}) async { diff --git a/lib/app/configuration/develop/config_develop.dart b/lib/app/configuration/develop/config_develop.dart index e983039..3a9e955 100644 --- a/lib/app/configuration/develop/config_develop.dart +++ b/lib/app/configuration/develop/config_develop.dart @@ -1,4 +1,4 @@ -// TODO: Fill correct values +// TODO(HELU): Fill correct values class ConfigDevelop { static const apiHostUrl = 'https://strv.com'; diff --git a/lib/app/configuration/production/config_production.dart b/lib/app/configuration/production/config_production.dart index 7dff0f2..9e5ae20 100644 --- a/lib/app/configuration/production/config_production.dart +++ b/lib/app/configuration/production/config_production.dart @@ -1,4 +1,4 @@ -// TODO: Fill correct values +// TODO(HELU): Fill correct values class ConfigProduction { static const apiHostUrl = 'https://strv.com'; diff --git a/lib/app/configuration/staging/config_staging.dart b/lib/app/configuration/staging/config_staging.dart index 5d06630..519c523 100644 --- a/lib/app/configuration/staging/config_staging.dart +++ b/lib/app/configuration/staging/config_staging.dart @@ -1,4 +1,4 @@ -// TODO: Fill correct values +// TODO(HELU): Fill correct values class ConfigStaging { static const apiHostUrl = 'https://strv.com'; diff --git a/lib/app/navigation/app_router.dart b/lib/app/navigation/app_router.dart index bd7bd8d..358ae79 100644 --- a/lib/app/navigation/app_router.dart +++ b/lib/app/navigation/app_router.dart @@ -23,27 +23,30 @@ class AppRouter extends RootStackRouter { @override List get routes => [ - // Subtitle: Landing Route - CustomRoute( - page: LandingRoute.page, - initial: true, - // We don't really need to animate landing page, because - // it doesn't have UI, it's covered by splash screen. - duration: Duration.zero, - ), + // Subtitle: Landing Route + CustomRoute( + page: LandingRoute.page, + initial: true, + // We don't really need to animate landing page, because + // it doesn't have UI, it's covered by splash screen. + duration: Duration.zero, + ), - // Subtitle: Authentication Route - AutoRoute(page: AuthenticationRoute.page), + // Subtitle: Authentication Route + AutoRoute(page: AuthenticationRoute.page), - // Subtitle: Main Routes - AutoRoute(page: RootRoute.page, children: [ - AutoRoute(page: HomeRoute.page), - AutoRoute(page: EventsRoute.page), - AutoRoute(page: ProfileRoute.page), - ]), - AutoRoute(page: EventDetailRoute.page), + // Subtitle: Main Routes + AutoRoute( + page: RootRoute.page, + children: [ + AutoRoute(page: HomeRoute.page), + AutoRoute(page: EventsRoute.page), + AutoRoute(page: ProfileRoute.page), + ], + ), + AutoRoute(page: EventDetailRoute.page), - // Subtitle: Debug Tools Routes - AutoRoute(page: DebugToolsRoute.page), - ]; + // Subtitle: Debug Tools Routes + AutoRoute(page: DebugToolsRoute.page), + ]; } diff --git a/lib/app/setup/setup_app.dart b/lib/app/setup/setup_app.dart index 9f16b5a..b5c01df 100644 --- a/lib/app/setup/setup_app.dart +++ b/lib/app/setup/setup_app.dart @@ -16,20 +16,21 @@ import 'package:flutter_app/common/provider/firebase_remote_config_service.dart' import 'package:flutter_app/common/provider/notifications_service.dart'; import 'package:flutter_app/common/provider/theme_mode_provider.dart'; import 'package:flutter_app/core/analytics/crashlytics_manager.dart'; +import 'package:flutter_app/core/flogger.dart'; import 'package:flutter_native_splash/flutter_native_splash.dart'; import 'package:flutter_web_plugins/url_strategy.dart'; import 'package:freerasp/freerasp.dart'; import 'package:window_manager/window_manager.dart'; Future setupApp({required Flavor flavor}) async { - WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); + final widgetsBinding = WidgetsFlutterBinding.ensureInitialized(); if (!kIsWeb) { FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding); } // Force app orientation to portrait and landscape. // This must be also forced in info.plist file for iOS, under [UISupportedInterfaceOrientations]. - SystemChrome.setPreferredOrientations([ + await SystemChrome.setPreferredOrientations([ DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, DeviceOrientation.landscapeLeft, @@ -66,14 +67,14 @@ Future setupApp({required Flavor flavor}) async { await CustomSystemBarsTheme.setupSystemBarsTheme(providerContainer: providerContainer); } -// TODO: Support it or remove it! +// TODO(HELU): Support it or remove it! Future _setupFirebase({required Flavor flavor}) async { if (AppPlatform.isMobile || AppPlatform.isMacOS) { await Firebase.initializeApp(); } else if (AppPlatform.isWeb) { await Firebase.initializeApp( // For WebApp we need to specify the FirebaseOptions here! - // TODO: Replace with actual values. Can be found inside Firebase -> Project Settings, under Web App + // TODO(HELU): Replace with actual values. Can be found inside Firebase -> Project Settings, under Web App options: const FirebaseOptions( apiKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', authDomain: 'strv-flutter-template.firebaseapp.com', @@ -87,7 +88,7 @@ Future _setupFirebase({required Flavor flavor}) async { } } -/// TODO: Support it or remove it! +// TODO(HELU): Support it or remove it! /// Setup Firebase crashlytics according [docs](https://firebase.google.com/docs/crashlytics/customize-crash-reports?platform=flutter). Future _setupFirebaseCrashlytics() async { if (!AppPlatform.isMobile) return; @@ -107,37 +108,46 @@ Future _setupFirebaseCrashlytics() async { }; // Handles errors that happen outside of the Flutter context - Isolate.current.addErrorListener(RawReceivePort((pair) async { - final List errorAndStacktrace = pair; - await CrashlyticsManager.logCritical( - errorAndStacktrace.first, - stack: errorAndStacktrace.last, - ); - }).sendPort); + Isolate.current.addErrorListener( + RawReceivePort((dynamic pair) async { + if (pair is List && pair.length == 2 && pair[1] is StackTrace) { + final dynamic error = pair[0]; + final stack = pair[1] as StackTrace; + + await CrashlyticsManager.logCritical( + error, + stack: stack, + ); + } else { + // Optionally log or handle unexpected message format + Flogger.e('Unexpected error message format from isolate: $pair'); + } + }).sendPort, + ); } -/// TODO: Support it or remove it! +// TODO(HELU): Support it or remove it! Future _setupFirebaseRemoteConfig() async { if (!AppPlatform.isMobile) return; await providerContainer.read(firebaseRemoteConfigServiceProvider.future); } -/// TODO: Support it or remove it! +// TODO(HELU): Support it or remove it! Future _setupFirebaseMessaging() async { if (!AppPlatform.isLinux && !AppPlatform.isWindows) { await providerContainer.read(firebaseMessagingServiceProvider.future); } } -/// TODO: Support it or remove it! +// TODO(HELU): Support it or remove it! Future _setupLocalNotificationsService() async { if (!AppPlatform.isLinux && !AppPlatform.isWindows) { await providerContainer.read(notificationsServiceProvider.future); } } -// TODO: [FreeRASP] Configure correct package names and other values +// TODO(HELU): [FreeRASP] Configure correct package names and other values Future _setupRASP({required Flavor flavor}) async { // Only Mobile platforms are supported! if (!AppPlatform.isMobile) return; @@ -198,7 +208,7 @@ Future _setupWebPlatform({required Flavor flavor}) async { Future _setupDesktopPlatform() async { if (AppPlatform.isDesktop) { await windowManager.ensureInitialized(); - windowManager.setMinimumSize(const Size(400, 400)); - windowManager.setSize(const Size(500, 950)); + await windowManager.setMinimumSize(const Size(400, 400)); + await windowManager.setSize(const Size(500, 950)); } } diff --git a/lib/app/setup/web_setup.dart b/lib/app/setup/web_setup.dart index 572df5b..667f454 100644 --- a/lib/app/setup/web_setup.dart +++ b/lib/app/setup/web_setup.dart @@ -10,13 +10,10 @@ class WebSetup { switch (flavor) { case Flavor.develop: script.id = 'develop'; - break; case Flavor.staging: script.id = 'staging'; - break; case Flavor.production: script.id = 'production'; - break; } document.head?.append(script); diff --git a/lib/app/theme/app_theme.dart b/lib/app/theme/app_theme.dart index 661ea42..034ba34 100644 --- a/lib/app/theme/app_theme.dart +++ b/lib/app/theme/app_theme.dart @@ -18,11 +18,11 @@ class AppTheme { return ThemeData( brightness: brightness, - colorScheme: (kDebugMode) + colorScheme: kDebugMode ? _getUndefinedColorScheme(brightness) : ColorScheme.fromSeed(seedColor: colorScheme.primary, brightness: brightness), visualDensity: VisualDensity.standard, // Needed for consistent web and app sizing - textTheme: (kDebugMode) ? _getUndefinedTextTheme() : null, + textTheme: kDebugMode ? _getUndefinedTextTheme() : null, scaffoldBackgroundColor: colorScheme.surface, // Warning: IconButtonTheme needs to be set till this is fixed: https://github.com/flutter/flutter/issues/130485 iconButtonTheme: const IconButtonThemeData(style: ButtonStyle()), @@ -101,20 +101,18 @@ class AppTheme { BottomSheetThemeData _getBottomSheetThemeData({ required CustomColorScheme colorScheme, required CustomTextTheme textTheme, -}) => - BottomSheetThemeData( - backgroundColor: colorScheme.surface, - surfaceTintColor: colorScheme.surface, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)), - ), - ); +}) => BottomSheetThemeData( + backgroundColor: colorScheme.surface, + surfaceTintColor: colorScheme.surface, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only(topLeft: Radius.circular(12), topRight: Radius.circular(12)), + ), +); TextSelectionThemeData _getTextSelectionThemeData({ required CustomColorScheme colorScheme, -}) => - TextSelectionThemeData( - cursorColor: colorScheme.secondary, - selectionColor: colorScheme.secondary.withValues(alpha: 0.3), - selectionHandleColor: colorScheme.secondary, - ); +}) => TextSelectionThemeData( + cursorColor: colorScheme.secondary, + selectionColor: colorScheme.secondary.withValues(alpha: 0.3), + selectionHandleColor: colorScheme.secondary, +); diff --git a/lib/app/theme/custom_color_scheme.dart b/lib/app/theme/custom_color_scheme.dart index 65e71c1..76a3900 100644 --- a/lib/app/theme/custom_color_scheme.dart +++ b/lib/app/theme/custom_color_scheme.dart @@ -6,40 +6,40 @@ class CustomColorScheme { (brightness.isLightMode) ? CustomColorScheme._light() : CustomColorScheme._dark(); CustomColorScheme._light() - : brightness = Brightness.light, - primary = const Color(0xFF0A59F8), - onPrimary = const Color(0xFFFFFFFF), - secondary = const Color(0xFF000000), - onSecondary = const Color(0xFFFFFFFF), - surface = const Color(0xFFFFFFFF), - surfaceVariant = const Color(0xFFF7F7F7), - onSurface = const Color(0xFF000000), - snackbarBackground = const Color(0xFF041028), - snackbarText = const Color(0xFFFFFFFF), - success = const Color(0xFF10B452), - error = const Color(0xFFE51934), - shadow = const Color(0x66000000), - divider = const Color(0xFFEBEBEB), - shimmerBackground = const Color(0xFFF0F0F0), - shimmerForeground = const Color(0xFFF7F7F7); + : brightness = Brightness.light, + primary = const Color(0xFF0A59F8), + onPrimary = const Color(0xFFFFFFFF), + secondary = const Color(0xFF000000), + onSecondary = const Color(0xFFFFFFFF), + surface = const Color(0xFFFFFFFF), + surfaceVariant = const Color(0xFFF7F7F7), + onSurface = const Color(0xFF000000), + snackbarBackground = const Color(0xFF041028), + snackbarText = const Color(0xFFFFFFFF), + success = const Color(0xFF10B452), + error = const Color(0xFFE51934), + shadow = const Color(0x66000000), + divider = const Color(0xFFEBEBEB), + shimmerBackground = const Color(0xFFF0F0F0), + shimmerForeground = const Color(0xFFF7F7F7); CustomColorScheme._dark() - : brightness = Brightness.dark, - primary = const Color(0xFF0A59F8), - onPrimary = const Color(0xFFFFFFFF), - secondary = const Color(0xFFFFFFFF), - onSecondary = const Color(0xFF000000), - surface = const Color(0xFF000000), - surfaceVariant = const Color(0x1AFFFFFF), - onSurface = const Color(0xFFFFFFFF), - snackbarBackground = const Color(0xFF041028), - snackbarText = const Color(0xFFFFFFFF), - success = const Color(0xFF10B452), - error = const Color(0xFFE51934), - shadow = const Color(0x66000000), - divider = const Color(0xFFEBEBEB), - shimmerBackground = const Color(0xFF1F1F1F), - shimmerForeground = const Color(0x1AFFFFFF); + : brightness = Brightness.dark, + primary = const Color(0xFF0A59F8), + onPrimary = const Color(0xFFFFFFFF), + secondary = const Color(0xFFFFFFFF), + onSecondary = const Color(0xFF000000), + surface = const Color(0xFF000000), + surfaceVariant = const Color(0x1AFFFFFF), + onSurface = const Color(0xFFFFFFFF), + snackbarBackground = const Color(0xFF041028), + snackbarText = const Color(0xFFFFFFFF), + success = const Color(0xFF10B452), + error = const Color(0xFFE51934), + shadow = const Color(0x66000000), + divider = const Color(0xFFEBEBEB), + shimmerBackground = const Color(0xFF1F1F1F), + shimmerForeground = const Color(0x1AFFFFFF); // Title: Theme specific colors final Brightness brightness; diff --git a/lib/app/theme/custom_system_bars_theme.dart b/lib/app/theme/custom_system_bars_theme.dart index 6c2ea46..f83561d 100644 --- a/lib/app/theme/custom_system_bars_theme.dart +++ b/lib/app/theme/custom_system_bars_theme.dart @@ -1,9 +1,9 @@ -import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_app/app/configuration/configuration.dart'; import 'package:flutter_app/app/setup/app_platform.dart'; +import 'package:flutter_app/common/component/custom_app_bar.dart' show CustomAppBar; import 'package:flutter_app/common/extension/brightness.dart'; import 'package:flutter_app/common/provider/theme_mode_provider.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -37,7 +37,7 @@ class CustomSystemBarsTheme { } else if (AppPlatform.isIOS) { return _getIosSystemBarsTheme(brightness: brightness); } else { - return (((brightness).isLightMode) ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark); + return ((brightness.isLightMode) ? SystemUiOverlayStyle.light : SystemUiOverlayStyle.dark); } } @@ -46,8 +46,8 @@ class CustomSystemBarsTheme { } static SystemUiOverlayStyle _getAndroidSystemBarsTheme({required Brightness brightness}) { - final AndroidDeviceInfo androidInfo = Configuration.instance.androidDeviceInfo!; - final bool canUseTransparentNavigation = androidInfo.version.sdkInt >= 29; // Enabled by default for iOS + final androidInfo = Configuration.instance.androidDeviceInfo!; + final canUseTransparentNavigation = androidInfo.version.sdkInt >= 29; // Enabled by default for iOS return SystemUiOverlayStyle( // StatusBar @@ -62,8 +62,8 @@ class CustomSystemBarsTheme { systemNavigationBarColor: canUseTransparentNavigation ? Colors.transparent : brightness.isLightMode - ? Colors.white - : Colors.black, + ? Colors.white + : Colors.black, ); } @@ -72,7 +72,6 @@ class CustomSystemBarsTheme { // StatusBar systemStatusBarContrastEnforced: false, statusBarBrightness: brightness, // For iOS - // NavigationBar systemNavigationBarContrastEnforced: false, ); @@ -90,7 +89,7 @@ class CustomSystemBarsTheme { // HotFix for API 28, that need SystemBarsTheme applied on each app resume! if (AppPlatform.isAndroid) { - final AndroidDeviceInfo androidInfo = Configuration.instance.androidDeviceInfo!; + final androidInfo = Configuration.instance.androidDeviceInfo!; if (androidInfo.version.sdkInt == 28) { SystemChannels.lifecycle.setMessageHandler((msg) async { if (msg == AppLifecycleState.resumed.toString()) { @@ -108,9 +107,9 @@ class CustomSystemBarsTheme { class CustomSystemBarsThemeWidget extends StatelessWidget { const CustomSystemBarsThemeWidget({ - super.key, required this.brightness, required this.child, + super.key, }); final Brightness brightness; diff --git a/lib/app/theme/custom_text_theme.dart b/lib/app/theme/custom_text_theme.dart index 0b8c82a..3bb7bcf 100644 --- a/lib/app/theme/custom_text_theme.dart +++ b/lib/app/theme/custom_text_theme.dart @@ -18,110 +18,110 @@ import 'package:flutter_app/assets/fonts.gen.dart'; /// class CustomTextTheme { CustomTextTheme({required CustomColorScheme colorScheme}) - : // Subtitle: Display - displayLarge = TextStyle( - fontSize: 57, - fontFamily: FontFamily.clashDisplay, - fontWeight: FontWeight.w400, - color: colorScheme.onSurface, - decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. - ), - displayMedium = TextStyle( - fontSize: 45, - fontFamily: FontFamily.clashDisplay, - fontWeight: FontWeight.w400, - color: colorScheme.onSurface, - decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. - ), - displaySmall = TextStyle( - fontSize: 36, - fontFamily: FontFamily.clashDisplay, - fontWeight: FontWeight.w400, - color: colorScheme.onSurface, - decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. - ), - // Subtitle: Headline - headlineLarge = TextStyle( - fontSize: 32, - fontFamily: FontFamily.clashDisplay, - fontWeight: FontWeight.w400, - color: colorScheme.onSurface, - decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. - ), - headlineMedium = TextStyle( - fontSize: 28, - fontFamily: FontFamily.clashDisplay, - fontWeight: FontWeight.w400, - color: colorScheme.onSurface, - decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. - ), - headlineSmall = TextStyle( - fontSize: 24, - fontFamily: FontFamily.clashDisplay, - fontWeight: FontWeight.w400, - color: colorScheme.onSurface, - decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. - ), - // Subtitle: Title - titleLarge = TextStyle( - fontSize: 22, - fontFamily: FontFamily.clashDisplay, - fontWeight: FontWeight.w400, - color: colorScheme.onSurface, - decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. - ), - titleMedium = TextStyle( - fontSize: 16, - fontFamily: FontFamily.clashDisplay, - fontWeight: FontWeight.w500, - color: colorScheme.onSurface, - decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. - ), - titleSmall = TextStyle( - fontSize: 14, - fontFamily: FontFamily.clashDisplay, - fontWeight: FontWeight.w500, - color: colorScheme.onSurface, - decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. - ), - // Subtitle: Label - labelLarge = TextStyle( - fontSize: 14, - fontWeight: FontWeight.w500, - color: colorScheme.onSurface, - decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. - ), - labelMedium = TextStyle( - fontSize: 12, - fontWeight: FontWeight.w500, - color: colorScheme.onSurface, - decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. - ), - labelSmall = TextStyle( - fontSize: 11, - fontWeight: FontWeight.w500, - color: colorScheme.onSurface, - decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. - ), - // Subtitle: Body - bodyLarge = TextStyle( - fontSize: 16, - fontWeight: FontWeight.w400, - color: colorScheme.onSurface, - decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. - ), - bodyMedium = TextStyle( - fontSize: 14, - fontWeight: FontWeight.w400, - color: colorScheme.onSurface, - decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. - ), - bodySmall = TextStyle( - fontSize: 12, - fontWeight: FontWeight.w400, - color: colorScheme.onSurface, - decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. - ); + : // Subtitle: Display + displayLarge = TextStyle( + fontSize: 57, + fontFamily: FontFamily.clashDisplay, + fontWeight: FontWeight.w400, + color: colorScheme.onSurface, + decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. + ), + displayMedium = TextStyle( + fontSize: 45, + fontFamily: FontFamily.clashDisplay, + fontWeight: FontWeight.w400, + color: colorScheme.onSurface, + decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. + ), + displaySmall = TextStyle( + fontSize: 36, + fontFamily: FontFamily.clashDisplay, + fontWeight: FontWeight.w400, + color: colorScheme.onSurface, + decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. + ), + // Subtitle: Headline + headlineLarge = TextStyle( + fontSize: 32, + fontFamily: FontFamily.clashDisplay, + fontWeight: FontWeight.w400, + color: colorScheme.onSurface, + decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. + ), + headlineMedium = TextStyle( + fontSize: 28, + fontFamily: FontFamily.clashDisplay, + fontWeight: FontWeight.w400, + color: colorScheme.onSurface, + decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. + ), + headlineSmall = TextStyle( + fontSize: 24, + fontFamily: FontFamily.clashDisplay, + fontWeight: FontWeight.w400, + color: colorScheme.onSurface, + decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. + ), + // Subtitle: Title + titleLarge = TextStyle( + fontSize: 22, + fontFamily: FontFamily.clashDisplay, + fontWeight: FontWeight.w400, + color: colorScheme.onSurface, + decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. + ), + titleMedium = TextStyle( + fontSize: 16, + fontFamily: FontFamily.clashDisplay, + fontWeight: FontWeight.w500, + color: colorScheme.onSurface, + decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. + ), + titleSmall = TextStyle( + fontSize: 14, + fontFamily: FontFamily.clashDisplay, + fontWeight: FontWeight.w500, + color: colorScheme.onSurface, + decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. + ), + // Subtitle: Label + labelLarge = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + color: colorScheme.onSurface, + decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. + ), + labelMedium = TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + color: colorScheme.onSurface, + decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. + ), + labelSmall = TextStyle( + fontSize: 11, + fontWeight: FontWeight.w500, + color: colorScheme.onSurface, + decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. + ), + // Subtitle: Body + bodyLarge = TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + color: colorScheme.onSurface, + decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. + ), + bodyMedium = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + color: colorScheme.onSurface, + decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. + ), + bodySmall = TextStyle( + fontSize: 12, + fontWeight: FontWeight.w400, + color: colorScheme.onSurface, + decorationColor: colorScheme.onSurface, // Used for autofill, suggestion underline, etc. + ); // Title: Theme specific TextStyles diff --git a/lib/common/animation/custom_fade_slide_animation.dart b/lib/common/animation/custom_fade_slide_animation.dart index 1459fb0..76b9141 100644 --- a/lib/common/animation/custom_fade_slide_animation.dart +++ b/lib/common/animation/custom_fade_slide_animation.dart @@ -13,15 +13,15 @@ import 'package:flutter/material.dart'; /// [textDirection] : direction of the text for slide animation (default: [TextDirection.ltr]) class CustomFadeSlideAnimation extends StatefulWidget { const CustomFadeSlideAnimation({ - super.key, required this.child, + super.key, this.beginOffset = const Offset(0, 0.5), - this.endOffset = const Offset(0, 0), + this.endOffset = Offset.zero, this.fadeDuration = const Duration(milliseconds: 1000), this.slideDuration = const Duration(milliseconds: 500), this.fadeCurve = Curves.decelerate, this.slideCurve = Curves.decelerate, - this.delay = const Duration(milliseconds: 0), + this.delay = Duration.zero, this.textDirection = TextDirection.ltr, }); @@ -46,7 +46,7 @@ class CustomFadeSlideAnimationState extends State with late AnimationController? _fadeController; late AnimationController? _slideController; - /// Controllers are initialised in [initState] with the values defined or received as parameters in the widget + /// Controllers are initialized in [initState] with the values defined or received as parameters in the widget @override void initState() { super.initState(); @@ -61,7 +61,7 @@ class CustomFadeSlideAnimationState extends State with ); // Start animation after the specified delay - Future.delayed(widget.delay).then((value) => {_fadeController?.forward(), _slideController?.forward()}); + Future.delayed(widget.delay).then((value) => {_fadeController?.forward(), _slideController?.forward()}); } /// Both the animation controllers are being disposed for safety diff --git a/lib/common/component/custom_app_bar.dart b/lib/common/component/custom_app_bar.dart index aac520f..8a470d8 100644 --- a/lib/common/component/custom_app_bar.dart +++ b/lib/common/component/custom_app_bar.dart @@ -44,7 +44,7 @@ class CustomAppBar extends ConsumerWidget implements PreferredSizeWidget { titleTextStyle: context.textTheme.titleLarge, shadowColor: context.colorScheme.shadow, scrolledUnderElevation: 4, - elevation: (forceElevated == true) ? 4 : 0, + elevation: (forceElevated ?? false) ? 4 : 0, actions: actions, bottom: bottom, ); diff --git a/lib/common/component/custom_bottom_app_bar.dart b/lib/common/component/custom_bottom_app_bar.dart index 4ccef6e..f991325 100644 --- a/lib/common/component/custom_bottom_app_bar.dart +++ b/lib/common/component/custom_bottom_app_bar.dart @@ -5,9 +5,9 @@ import 'package:flutter_app/common/extension/build_context.dart'; class CustomBottomAppBar extends StatelessWidget { const CustomBottomAppBar({ - super.key, required this.selectedIndex, required this.items, + super.key, }); final int selectedIndex; @@ -21,9 +21,8 @@ class CustomBottomAppBar extends StatelessWidget { elevation: 8, height: 64, shadowColor: Colors.black, - padding: const EdgeInsets.all(0), + padding: EdgeInsets.zero, child: Row( - mainAxisSize: MainAxisSize.max, children: items.mapIndexed((index, item) => _BottomAppBarButton(item: item, isSelected: index == selectedIndex)).toList(), ), ); @@ -32,7 +31,6 @@ class CustomBottomAppBar extends StatelessWidget { class CustomBottomAppBarItem { CustomBottomAppBarItem({ - Key? key, required this.label, required this.icon, required this.onSelectListener, diff --git a/lib/common/component/custom_button/custom_button_primary.dart b/lib/common/component/custom_button/custom_button_primary.dart index 7a96f4a..cb56aa7 100644 --- a/lib/common/component/custom_button/custom_button_primary.dart +++ b/lib/common/component/custom_button/custom_button_primary.dart @@ -6,9 +6,9 @@ import 'package:flutter_app/common/extension/build_context.dart'; class CustomButtonPrimary extends StatelessWidget { const CustomButtonPrimary({ - super.key, required this.text, required this.onPressed, + super.key, this.isLoading = false, this.isEnabled = true, }); @@ -29,7 +29,7 @@ class CustomButtonPrimary extends StatelessWidget { onPressed(); }, style: _getButtonStyle(context), - child: (isLoading) + child: isLoading ? SizedBox( width: 24, height: 24, diff --git a/lib/common/component/custom_floating_action_button.dart b/lib/common/component/custom_floating_action_button.dart index e556409..c5c6f4a 100644 --- a/lib/common/component/custom_floating_action_button.dart +++ b/lib/common/component/custom_floating_action_button.dart @@ -3,9 +3,9 @@ import 'package:flutter_app/common/extension/build_context.dart'; class CustomFloatingActionButton extends StatelessWidget { const CustomFloatingActionButton({ - super.key, required this.child, required this.onPressed, + super.key, }); final Icon child; diff --git a/lib/common/component/custom_icon_button.dart b/lib/common/component/custom_icon_button.dart index db249bf..1db6d65 100644 --- a/lib/common/component/custom_icon_button.dart +++ b/lib/common/component/custom_icon_button.dart @@ -3,9 +3,9 @@ import 'package:flutter_app/common/component/custom_progress_indicator.dart'; class CustomIconButton extends StatelessWidget { const CustomIconButton({ - super.key, required this.icon, required this.onPressed, + super.key, this.isLoading = false, this.buttonSize = 44, this.iconSize = 24, diff --git a/lib/common/component/custom_ink_well/custom_ink_well_circular.dart b/lib/common/component/custom_ink_well/custom_ink_well_circular.dart index 709972e..658e4ad 100644 --- a/lib/common/component/custom_ink_well/custom_ink_well_circular.dart +++ b/lib/common/component/custom_ink_well/custom_ink_well_circular.dart @@ -2,15 +2,15 @@ import 'package:flutter/material.dart'; class CustomInkWellCircular extends StatelessWidget { const CustomInkWellCircular({ - super.key, required this.onClick, + required this.child, + super.key, this.onLongClick, this.padding, - required this.child, }); - final Function()? onClick; - final Function()? onLongClick; + final VoidCallback? onClick; + final VoidCallback? onLongClick; final EdgeInsets? padding; final Widget child; @@ -20,7 +20,7 @@ class CustomInkWellCircular extends StatelessWidget { alignment: Alignment.center, children: [ Padding( - padding: padding ?? const EdgeInsets.all(0), + padding: padding ?? EdgeInsets.zero, child: child, ), Positioned.fill( diff --git a/lib/common/component/custom_ink_well/custom_ink_well_rounded_rectangle.dart b/lib/common/component/custom_ink_well/custom_ink_well_rounded_rectangle.dart index 815fce0..4d25c5f 100644 --- a/lib/common/component/custom_ink_well/custom_ink_well_rounded_rectangle.dart +++ b/lib/common/component/custom_ink_well/custom_ink_well_rounded_rectangle.dart @@ -2,16 +2,16 @@ import 'package:flutter/material.dart'; class CustomInkWellRoundedRectangle extends StatelessWidget { const CustomInkWellRoundedRectangle({ - super.key, required this.onClick, - this.onLongClick, required this.cornerRadius, - this.padding, required this.child, + super.key, + this.onLongClick, + this.padding, }); - final Function()? onClick; - final Function()? onLongClick; + final VoidCallback? onClick; + final VoidCallback? onLongClick; final double cornerRadius; final EdgeInsets? padding; final Widget child; @@ -21,7 +21,7 @@ class CustomInkWellRoundedRectangle extends StatelessWidget { return Stack( children: [ Padding( - padding: padding ?? const EdgeInsets.all(0), + padding: padding ?? EdgeInsets.zero, child: child, ), Positioned.fill( diff --git a/lib/common/component/custom_network_image.dart b/lib/common/component/custom_network_image.dart index d51e708..5c7265f 100644 --- a/lib/common/component/custom_network_image.dart +++ b/lib/common/component/custom_network_image.dart @@ -5,10 +5,10 @@ import 'package:flutter_app/common/component/custom_progress_indicator.dart'; class CustomNetworkImage extends StatelessWidget { const CustomNetworkImage({ - super.key, required this.url, required this.width, required this.height, + super.key, this.fit, this.shape, this.border, @@ -16,15 +16,15 @@ class CustomNetworkImage extends StatelessWidget { }); const CustomNetworkImage.square({ - super.key, required this.url, required double? size, + super.key, this.fit, this.shape, this.border, this.borderRadius, - }) : width = size, - height = size; + }) : width = size, + height = size; final String url; final double? width; @@ -41,7 +41,6 @@ class CustomNetworkImage extends StatelessWidget { width: width, height: height, fit: fit ?? BoxFit.cover, - cache: true, shape: shape, border: border, borderRadius: borderRadius, diff --git a/lib/common/component/custom_profile_avatar.dart b/lib/common/component/custom_profile_avatar.dart index b48da3c..1adafed 100644 --- a/lib/common/component/custom_profile_avatar.dart +++ b/lib/common/component/custom_profile_avatar.dart @@ -6,10 +6,10 @@ import 'package:flutter_app/common/component/custom_progress_indicator.dart'; class CustomClickableProfileAvatar extends StatelessWidget { const CustomClickableProfileAvatar({ - super.key, required this.imageUrl, required this.size, required this.onClick, + super.key, this.onLongClick, }); @@ -37,9 +37,9 @@ class CustomClickableProfileAvatar extends StatelessWidget { class CustomProfileAvatar extends StatelessWidget { const CustomProfileAvatar({ - super.key, required this.imageUrl, required this.size, + super.key, }); final String imageUrl; @@ -55,7 +55,6 @@ class CustomProfileAvatar extends StatelessWidget { width: size, height: size, fit: BoxFit.cover, - cache: true, loadStateChanged: (state) { switch (state.extendedImageLoadState) { case LoadState.loading: diff --git a/lib/common/component/custom_progress_indicator.dart b/lib/common/component/custom_progress_indicator.dart index 2456ca5..3b823be 100644 --- a/lib/common/component/custom_progress_indicator.dart +++ b/lib/common/component/custom_progress_indicator.dart @@ -24,50 +24,52 @@ class CustomProgressIndicator extends StatelessWidget { Widget build(BuildContext context) { final color = this.color ?? context.colorScheme.onSurface; - return LayoutBuilder(builder: (context, constraints) { - double realSize = size; - if (constraints.maxHeight < realSize && constraints.maxHeight > 0) realSize = constraints.maxHeight; - if (constraints.maxWidth < realSize && constraints.maxWidth > 0) realSize = constraints.maxWidth; + return LayoutBuilder( + builder: (context, constraints) { + var realSize = size; + if (constraints.maxHeight < realSize && constraints.maxHeight > 0) realSize = constraints.maxHeight; + if (constraints.maxWidth < realSize && constraints.maxWidth > 0) realSize = constraints.maxWidth; - if (AppPlatform.isApple) { - /// For the iOS, the issue is that the indicator does not scale at all. We can handle that using Transform. - return CupertinoActivityIndicator( - radius: realSize / 2 / iosIndicatorScalingFactor, - color: color, - ); - } else { - /// For Material indicator, the issue is that it is overlapping it parent by the half size of it stroke width. - /// Sadly to size it properly, we need to double wrap it in SizedBox. - /// Another issue is the scaling of the widget when we won't set the size. Because of that we need to wrap it inside LayoutBuilder. + if (AppPlatform.isApple) { + /// For the iOS, the issue is that the indicator does not scale at all. We can handle that using Transform. + return CupertinoActivityIndicator( + radius: realSize / 2 / iosIndicatorScalingFactor, + color: color, + ); + } else { + /// For Material indicator, the issue is that it is overlapping it parent by the half size of it stroke width. + /// Sadly to size it properly, we need to double wrap it in SizedBox. + /// Another issue is the scaling of the widget when we won't set the size. Because of that we need to wrap it inside LayoutBuilder. - double? strokeWidth = materialStrokeWidth; - if (strokeWidth == null) { - if (realSize < 16) { - strokeWidth = 1; - } else if (realSize < 24) { - strokeWidth = 2; - } else if (realSize < 36) { - strokeWidth = 3; - } else { - strokeWidth = 4; + var strokeWidth = materialStrokeWidth; + if (strokeWidth == null) { + if (realSize < 16) { + strokeWidth = 1; + } else if (realSize < 24) { + strokeWidth = 2; + } else if (realSize < 36) { + strokeWidth = 3; + } else { + strokeWidth = 4; + } } - } - return SizedBox( - width: realSize, - height: realSize, - child: Center( - child: SizedBox( - width: realSize - strokeWidth, - height: realSize - strokeWidth, - child: CircularProgressIndicator( - strokeWidth: strokeWidth, - valueColor: AlwaysStoppedAnimation(color), + return SizedBox( + width: realSize, + height: realSize, + child: Center( + child: SizedBox( + width: realSize - strokeWidth, + height: realSize - strokeWidth, + child: CircularProgressIndicator( + strokeWidth: strokeWidth, + valueColor: AlwaysStoppedAnimation(color), + ), ), ), - ), - ); - } - }); + ); + } + }, + ); } } diff --git a/lib/common/component/custom_radio_button_group.dart b/lib/common/component/custom_radio_button_group.dart index db3d3bb..a18a24a 100644 --- a/lib/common/component/custom_radio_button_group.dart +++ b/lib/common/component/custom_radio_button_group.dart @@ -5,15 +5,15 @@ import 'package:flutter_app/common/extension/build_context.dart'; class CustomRadioButtonGroup extends StatelessWidget { const CustomRadioButtonGroup({ - super.key, required this.options, required this.selectedOption, required this.onOptionSelected, + super.key, }); final Map options; final T? selectedOption; - final Function(T) onOptionSelected; + final void Function(T) onOptionSelected; @override Widget build(BuildContext context) { @@ -30,8 +30,6 @@ class CustomRadioButtonGroup extends StatelessWidget { child: Padding( padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, children: [ Radio( value: entry.key, diff --git a/lib/common/component/custom_snackbar/custom_snackbar_error.dart b/lib/common/component/custom_snackbar/custom_snackbar_error.dart index c048e80..b5b42a1 100644 --- a/lib/common/component/custom_snackbar/custom_snackbar_error.dart +++ b/lib/common/component/custom_snackbar/custom_snackbar_error.dart @@ -20,7 +20,7 @@ class CustomSnackbarError { Future show() async { if (_shownMessages.contains(message)) { - Flogger.i('[CustomSnackBar] Not going to show message $message, because it\'s already in `_shownMessages`.'); + Flogger.i("[CustomSnackBar] Not going to show message $message, because it's already in `_shownMessages`."); return; } diff --git a/lib/common/component/custom_snackbar/custom_snackbar_message.dart b/lib/common/component/custom_snackbar/custom_snackbar_message.dart index d106ba8..5335b99 100644 --- a/lib/common/component/custom_snackbar/custom_snackbar_message.dart +++ b/lib/common/component/custom_snackbar/custom_snackbar_message.dart @@ -19,7 +19,7 @@ class CustomSnackbarMessage { Future show() async { if (_shownMessages.contains(message)) { - Flogger.i('[CustomSnackBar] Not going to show message $message, because it\'s already in `_shownMessages`.'); + Flogger.i("[CustomSnackBar] Not going to show message $message, because it's already in `_shownMessages`."); return; } diff --git a/lib/common/component/custom_snackbar/custom_snackbar_success.dart b/lib/common/component/custom_snackbar/custom_snackbar_success.dart index fa0d770..296d59d 100644 --- a/lib/common/component/custom_snackbar/custom_snackbar_success.dart +++ b/lib/common/component/custom_snackbar/custom_snackbar_success.dart @@ -20,7 +20,7 @@ class CustomSnackbarSuccess { Future show() async { if (_shownMessages.contains(message)) { - Flogger.i('[CustomSnackBar] Not going to show message $message, because it\'s already in `_shownMessages`.'); + Flogger.i("[CustomSnackBar] Not going to show message $message, because it's already in `_shownMessages`."); return; } diff --git a/lib/common/component/custom_switch.dart b/lib/common/component/custom_switch.dart index 76c0537..cc70e51 100644 --- a/lib/common/component/custom_switch.dart +++ b/lib/common/component/custom_switch.dart @@ -4,17 +4,17 @@ import 'package:flutter_app/common/extension/build_context.dart'; class CustomSwitch extends StatelessWidget { const CustomSwitch({ - super.key, required this.title, required this.value, required this.onChanged, + super.key, this.subtitle, this.dense = false, }); final String title; final bool value; - final Function(bool value) onChanged; + final void Function(bool value) onChanged; final String? subtitle; final bool dense; diff --git a/lib/common/component/custom_tab_bar.dart b/lib/common/component/custom_tab_bar.dart index 4da50ac..2f5837d 100644 --- a/lib/common/component/custom_tab_bar.dart +++ b/lib/common/component/custom_tab_bar.dart @@ -3,9 +3,9 @@ import 'package:flutter_app/common/extension/build_context.dart'; class CustomTabBar extends StatelessWidget { const CustomTabBar({ + required this.tabs, super.key, this.tabController, - required this.tabs, this.onTap, }); diff --git a/lib/common/component/custom_text/custom_text.dart b/lib/common/component/custom_text/custom_text.dart index 68559ec..ac39e5c 100644 --- a/lib/common/component/custom_text/custom_text.dart +++ b/lib/common/component/custom_text/custom_text.dart @@ -4,9 +4,9 @@ import 'package:flutter_app/common/extension/build_context.dart'; class CustomText extends StatelessWidget { const CustomText({ - super.key, required this.text, required this.style, + super.key, this.color, this.padding = EdgeInsets.zero, this.textAlign = TextAlign.start, diff --git a/lib/common/component/custom_text_field.dart b/lib/common/component/custom_text_field.dart index c0fc508..6b081cc 100644 --- a/lib/common/component/custom_text_field.dart +++ b/lib/common/component/custom_text_field.dart @@ -25,8 +25,8 @@ class CustomTextField extends StatefulWidget { this.withCounterText = false, this.note, this.prefix, - }) : assert(!(controller != null && validatorController != null), 'You can only provide controller or validationController!'), - assert(!(withCounterText == true && maxLength == null), 'maxLength must be set when the withCounterText is used!'); + }) : assert(!(controller != null && validatorController != null), 'You can only provide controller or validationController!'), + assert(!(withCounterText && maxLength == null), 'maxLength must be set when the withCounterText is used!'); final String? label; final TextEditingController? controller; @@ -51,7 +51,7 @@ class CustomTextField extends StatefulWidget { } class _CustomTextFieldState extends State { - var textLength = 0; + int textLength = 0; final _focusNode = FocusNode(); bool hasFocus = false; String lastErrorMessage = ''; @@ -61,8 +61,8 @@ class _CustomTextFieldState extends State { @override void initState() { super.initState(); - widget.validatorController?.addListener(() => _onValidatorChange()); - _focusNode.addListener(() => _onFocusChange()); + widget.validatorController?.addListener(_onValidatorChange); + _focusNode.addListener(_onFocusChange); textLength = widget.validatorController?.text.length ?? widget.controller?.text.length ?? 0; } @@ -75,14 +75,14 @@ class _CustomTextFieldState extends State { @override Widget build(BuildContext context) { - final double contentTopPadding = (widget.label == null) ? 17 : 9; - final double contentBottomPadding = (widget.label == null) ? 20 : 12; + final contentTopPadding = (widget.label == null) ? 17.0 : 9.0; + final contentBottomPadding = (widget.label == null) ? 20.0 : 12.0; // Subtitle: Enabled State colors - Color inputBgColor = context.colorScheme.surfaceVariant; - Color inputTextColor = context.colorScheme.onSurface; - Color labelTextColor = context.colorScheme.onSurface; - Color floatingLabelTextColor = context.colorScheme.onSurface; + var inputBgColor = context.colorScheme.surfaceVariant; + var inputTextColor = context.colorScheme.onSurface; + var labelTextColor = context.colorScheme.onSurface; + var floatingLabelTextColor = context.colorScheme.onSurface; final errorTextColor = context.colorScheme.error; final counterTextColor = context.colorScheme.onSurface; final noteTextColor = context.colorScheme.onSurface; @@ -93,7 +93,7 @@ class _CustomTextFieldState extends State { inputTextColor = context.colorScheme.onSurface; labelTextColor = context.colorScheme.onSurface; floatingLabelTextColor = context.colorScheme.onSurface; - } else if (widget.enabled == false) { + } else if (!widget.enabled) { // Subtitle: Disabled State colors inputBgColor = context.colorScheme.surfaceVariant; inputTextColor = context.colorScheme.onSurface.withValues(alpha: 0.5); @@ -191,7 +191,7 @@ class _CustomTextFieldState extends State { errorTextColor: errorTextColor, ), ), - crossFadeState: widget.validatorController?.state.hasError == true ? CrossFadeState.showFirst : CrossFadeState.showSecond, + crossFadeState: (widget.validatorController?.state.hasError ?? false) ? CrossFadeState.showFirst : CrossFadeState.showSecond, ), // Title: Bottom note text diff --git a/lib/common/component/custom_text_field_button.dart b/lib/common/component/custom_text_field_button.dart index ea36101..7c73c87 100644 --- a/lib/common/component/custom_text_field_button.dart +++ b/lib/common/component/custom_text_field_button.dart @@ -5,8 +5,8 @@ import 'package:flutter_app/common/validator/text_validator_controller.dart'; class CustomTextFieldButton extends StatelessWidget { const CustomTextFieldButton({ - super.key, required this.onClick, + super.key, this.label, this.controller, this.validatorController, diff --git a/lib/common/composition/bottom_sheet/bottom_sheet_container_widget.dart b/lib/common/composition/bottom_sheet/bottom_sheet_container_widget.dart index f4a8e6c..f2fdf78 100644 --- a/lib/common/composition/bottom_sheet/bottom_sheet_container_widget.dart +++ b/lib/common/composition/bottom_sheet/bottom_sheet_container_widget.dart @@ -4,8 +4,8 @@ import 'package:modal_bottom_sheet/modal_bottom_sheet.dart'; class BottomSheetContainerWidget extends StatelessWidget { const BottomSheetContainerWidget({ - super.key, required this.child, + super.key, }); final Widget child; diff --git a/lib/common/composition/dialog/custom_alert_dialog.dart b/lib/common/composition/dialog/custom_alert_dialog.dart index 7acceef..1db9641 100644 --- a/lib/common/composition/dialog/custom_alert_dialog.dart +++ b/lib/common/composition/dialog/custom_alert_dialog.dart @@ -25,7 +25,7 @@ class CustomAlertDialog { final VoidCallback? negativeAction; Future show() async { - return await showDialog( + return showDialog( context: context, builder: (builderContext) => CustomDialogWrapper.alert( context: builderContext, diff --git a/lib/common/composition/dialog/custom_dialog_wrapper.dart b/lib/common/composition/dialog/custom_dialog_wrapper.dart index eab993a..4a4e796 100644 --- a/lib/common/composition/dialog/custom_dialog_wrapper.dart +++ b/lib/common/composition/dialog/custom_dialog_wrapper.dart @@ -6,9 +6,9 @@ import 'package:flutter_app/common/extension/build_context.dart'; class CustomDialogWrapper extends StatelessWidget { const CustomDialogWrapper.alert({ - super.key, required this.context, required this.title, + super.key, this.message, this.positiveActionTitle, this.positiveAction, @@ -21,9 +21,9 @@ class CustomDialogWrapper extends StatelessWidget { }) : content = null; const CustomDialogWrapper.custom({ - super.key, required this.context, required this.title, + super.key, this.content, this.positiveActionTitle, this.positiveAction, @@ -80,7 +80,8 @@ class CustomDialogWrapper extends StatelessWidget { } else { return AlertDialog( title: CustomText(text: title, style: context.textTheme.titleLarge), - content: content ?? + content: + content ?? (message != null ? SingleChildScrollView( child: CustomText(text: message!, style: context.textTheme.bodyMedium), diff --git a/lib/common/composition/expandable_single_child_scroll_view.dart b/lib/common/composition/expandable_single_child_scroll_view.dart index 76fc55f..660bed6 100644 --- a/lib/common/composition/expandable_single_child_scroll_view.dart +++ b/lib/common/composition/expandable_single_child_scroll_view.dart @@ -19,7 +19,7 @@ class ExpandableSingleChildScrollView extends StatelessWidget { return LayoutBuilder( builder: (context, constraint) { return SingleChildScrollView( - physics: (alwaysScrollablePhysics) ? const AlwaysScrollableScrollPhysics() : null, + physics: alwaysScrollablePhysics ? const AlwaysScrollableScrollPhysics() : null, child: ConstrainedBox( constraints: BoxConstraints(minHeight: constraint.maxHeight), child: Padding( diff --git a/lib/common/composition/placeholder/empty_placeholder_widget.dart b/lib/common/composition/placeholder/empty_placeholder_widget.dart index 2e40475..5deaa54 100644 --- a/lib/common/composition/placeholder/empty_placeholder_widget.dart +++ b/lib/common/composition/placeholder/empty_placeholder_widget.dart @@ -7,8 +7,8 @@ import 'package:flutter_app/common/extension/build_context.dart'; class EmptyPlaceholderWidget extends StatelessWidget { const EmptyPlaceholderWidget({ - super.key, required this.onRetry, + super.key, }); final VoidCallback onRetry; @@ -18,12 +18,10 @@ class EmptyPlaceholderWidget extends StatelessWidget { return ExpandableSingleChildScrollView( padding: const EdgeInsets.all(20), child: Align( - alignment: Alignment.center, child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: ResponsiveWidget.mediumSizeThreshold), child: Column( mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, children: [ CustomText(text: context.locale.generalEmptyStateTitle, style: context.textTheme.titleMedium), const SizedBox(height: 8), diff --git a/lib/common/composition/placeholder/error_placeholder_widget.dart b/lib/common/composition/placeholder/error_placeholder_widget.dart index b701f4a..98adf46 100644 --- a/lib/common/composition/placeholder/error_placeholder_widget.dart +++ b/lib/common/composition/placeholder/error_placeholder_widget.dart @@ -8,8 +8,8 @@ import 'package:flutter_app/common/extension/build_context.dart'; class ErrorPlaceholderWidget extends StatelessWidget { const ErrorPlaceholderWidget({ - super.key, required this.onRetry, + super.key, this.exception = const CustomException.general(), }); @@ -21,18 +21,27 @@ class ErrorPlaceholderWidget extends StatelessWidget { return ExpandableSingleChildScrollView( padding: const EdgeInsets.all(20), child: Align( - alignment: Alignment.center, child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: ResponsiveWidget.mediumSizeThreshold), child: Column( mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, children: [ - CustomText(text: exception.getMessage(context: context), style: context.textTheme.titleMedium), + CustomText( + text: exception.getMessage(context: context), + style: context.textTheme.titleMedium, + ), const SizedBox(height: 8), - CustomText(text: exception.getDetails(context: context), style: context.textTheme.bodyMedium), + + CustomText( + text: exception.getDetails(context: context), + style: context.textTheme.bodyMedium, + ), const SizedBox(height: 32), - CustomButtonPrimary(onPressed: onRetry, text: context.locale.generalErrorStateRetryButton), + + CustomButtonPrimary( + onPressed: onRetry, + text: context.locale.generalErrorStateRetryButton, + ), ], ), ), diff --git a/lib/common/composition/responsive_widget.dart b/lib/common/composition/responsive_widget.dart index 745d02a..5ecac2c 100644 --- a/lib/common/composition/responsive_widget.dart +++ b/lib/common/composition/responsive_widget.dart @@ -4,8 +4,8 @@ import 'package:flutter/material.dart'; /// Feel free to polish this Widget by app needs. Some projects may require just small and large sizes. class ResponsiveWidget extends StatelessWidget { const ResponsiveWidget({ - super.key, required this.small, + super.key, this.medium, this.large, }); diff --git a/lib/common/data/model/exception/custom_exception.dart b/lib/common/data/model/exception/custom_exception.dart index 6096ebe..ca3767a 100644 --- a/lib/common/data/model/exception/custom_exception.dart +++ b/lib/common/data/model/exception/custom_exception.dart @@ -37,13 +37,15 @@ sealed class CustomException with _$CustomException implements Exception { case DioExceptionType.connectionError: return const CustomException.notConnectedToTheInternet(); default: - if (error.response?.data is Map) { - Flogger.e("[CustomException] error.response: ${error.response?.data["errorCode"]}"); + final data = error.response?.data; + final statusCode = error.response?.statusCode; + if (data is Map) { + Flogger.e("[CustomException] error.response: ${data["errorCode"]}"); } // Note: This is the place to handle your own error codes, response, or states, and map them to you own CustomException. - if (error.response?.statusCode == 401) { + if (statusCode == 401) { return const CustomException.unauthenticated(); } @@ -52,7 +54,7 @@ sealed class CustomException with _$CustomException implements Exception { } else if (error is SignInWithAppleAuthorizationException) { switch (error.code) { case AuthorizationErrorCode.canceled: - return CustomException.signInCancelled(); + return const CustomException.signInCancelled(); default: return CustomException.withMessage(message: error.message); } diff --git a/lib/common/data/model/notification_payload_model.dart b/lib/common/data/model/notification_payload_model.dart index 9704b94..53e5452 100644 --- a/lib/common/data/model/notification_payload_model.dart +++ b/lib/common/data/model/notification_payload_model.dart @@ -37,11 +37,11 @@ sealed class NotificationPayloadModel with _$NotificationPayloadModel { }) = NotificationPayloadModelUnknown; factory NotificationPayloadModel.fromJson(Map json) { - switch (NotificationType.fromString(json['type'])) { + switch (NotificationType.fromString(json['type'] as String?)) { case NotificationType.sample: return NotificationPayloadModelSample.fromJson(json); - default: + case NotificationType.unknown: return const NotificationPayloadModelUnknown(); } } diff --git a/lib/common/data/model/user_model.dart b/lib/common/data/model/user_model.dart index 39b742e..4ccec67 100644 --- a/lib/common/data/model/user_model.dart +++ b/lib/common/data/model/user_model.dart @@ -7,8 +7,6 @@ part 'user_model.g.dart'; @freezed abstract class UserModel with _$UserModel { - const UserModel._(); - const factory UserModel({ required String id, required UserRole role, @@ -17,6 +15,7 @@ abstract class UserModel with _$UserModel { required String? imageUrl, required String? referredId, }) = _UserModel; + const UserModel._(); factory UserModel.fromJson(Map json) => _$UserModelFromJson(json); diff --git a/lib/common/extension/async_value.dart b/lib/common/extension/async_value.dart index f05e852..2efc898 100644 --- a/lib/common/extension/async_value.dart +++ b/lib/common/extension/async_value.dart @@ -8,13 +8,13 @@ import 'package:flutter_app/common/extension/dynamic.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; extension AsyncValueExtension on AsyncValue { - Widget mapState({ - required Refreshable provider, + Widget mapState({ + required Refreshable provider, + required Widget Function(T data) data, bool Function(T data)? isEmpty, Widget Function(T? error)? error, Widget Function(T? loading)? loading, Widget Function(T data)? empty, - required Widget Function(T data) data, }) { return map( // Title: Loading State @@ -43,24 +43,24 @@ extension AsyncValueExtension on AsyncValue { data: (dataParam) => (isEmpty?.let((it) => it(dataParam.value)) ?? false) // Title: Empty State ? (empty?.call(dataParam.value) ?? - Scaffold( - appBar: const CustomAppBar(), - body: SafeArea( - child: _EmptyPlaceholderWidget(provider), - ), - )) + Scaffold( + appBar: const CustomAppBar(), + body: SafeArea( + child: _EmptyPlaceholderWidget(provider), + ), + )) // Title: Data State : data(dataParam.value), ); } - Widget mapContentState({ - required Refreshable provider, + Widget mapContentState({ + required Refreshable provider, + required Widget Function(T data) data, bool Function(T data)? isEmpty, Widget Function(T? error)? error, Widget Function(T? loading)? loading, Widget Function(T data)? empty, - required Widget Function(T data) data, }) { return map( // Title: Loading State @@ -96,7 +96,7 @@ class _EmptyPlaceholderWidget extends StatelessWidget { this.provider, ); - final Refreshable provider; + final Refreshable provider; @override Widget build(BuildContext context) { @@ -114,7 +114,7 @@ class _ErrorPlaceholderWidget extends StatelessWidget { this.error, ); - final Refreshable provider; + final Refreshable provider; final Object error; @override diff --git a/lib/common/extension/list.dart b/lib/common/extension/list.dart index c75a3cd..8dc0bce 100644 --- a/lib/common/extension/list.dart +++ b/lib/common/extension/list.dart @@ -1,10 +1,11 @@ extension ListExtension on List { - List replace(oldEntity, newEntity) { + List replace(T oldEntity, T newEntity) { final newList = [...this]; final index = indexOf(oldEntity); if (index != -1) { - newList.remove(oldEntity); - newList.insert(index, newEntity); + newList + ..remove(oldEntity) + ..insert(index, newEntity); } return newList; } diff --git a/lib/common/extension/response.dart b/lib/common/extension/response.dart index d4382d2..20994b5 100644 --- a/lib/common/extension/response.dart +++ b/lib/common/extension/response.dart @@ -1,12 +1,12 @@ import 'package:dio/dio.dart'; import 'package:flutter_app/common/data/model/exception/custom_exception.dart'; -extension ResponseExtension on Response { - /// Tries to downcast `data` as `List`. If decoding failes, +extension ResponseExtension on Response> { + /// Tries to downcast `data` as `List`. If decoding fails, /// `CustomException.decodingFailed()` is thrown. List get listData { if (data is List) { - return data as List; + return data! as List; } else { throw const CustomException.decodingFailed(); } diff --git a/lib/common/extension/string.dart b/lib/common/extension/string.dart index 13d729a..b9f57f7 100644 --- a/lib/common/extension/string.dart +++ b/lib/common/extension/string.dart @@ -1,5 +1,5 @@ extension StringExtension on String? { bool get isNullOrEmpty { - return this == null || this?.isEmpty == true; + return this == null || this!.isEmpty; } } diff --git a/lib/common/provider/firebase_messaging_service.dart b/lib/common/provider/firebase_messaging_service.dart index a30a0bb..302254d 100644 --- a/lib/common/provider/firebase_messaging_service.dart +++ b/lib/common/provider/firebase_messaging_service.dart @@ -20,13 +20,13 @@ class FirebaseMessagingService extends _$FirebaseMessagingService { @pragma('vm:entry-point') static Future firebaseNotificationDisplayBgHandler(RemoteMessage message) async { Flogger.d('[Firebase Messaging] Display notification with payload: ${message.data}'); - NotificationsService.showNotification(NotificationPayloadModel.fromJson(message.data)); + await NotificationsService.showNotification(NotificationPayloadModel.fromJson(message.data)); } @pragma('vm:entry-point') static Future firebaseNotificationOpenBgHandler(RemoteMessage message) async { Flogger.d('[Firebase Messaging] Open notification with payload: ${message.data}'); - NotificationsService.tryOpeningNotificationFromRemoteMessage(message); + await NotificationsService.tryOpeningNotificationFromRemoteMessage(message); } @override @@ -38,7 +38,7 @@ class FirebaseMessagingService extends _$FirebaseMessagingService { /// Check whether user has at least any Notification permission allowed Future isPermissionsGranted() async { - NotificationSettings settings = await _firebaseMessaging.getNotificationSettings(); + final settings = await _firebaseMessaging.getNotificationSettings(); return settings.authorizationStatus == AuthorizationStatus.authorized || settings.authorizationStatus == AuthorizationStatus.provisional; } @@ -54,7 +54,7 @@ class FirebaseMessagingService extends _$FirebaseMessagingService { NotificationSettings? settings; if (dialogRequestCount == 0 || (dialogRequestCount == 1 && AppPlatform.isAndroid)) { await preferences.setInt(PreferencesKeys.notificationsPermissionRequestCount.value, dialogRequestCount + 1); - settings = await _firebaseMessaging.requestPermission(alert: true, badge: true, sound: true); + settings = await _firebaseMessaging.requestPermission(); } else { await AppSettings.openAppSettings(); settings = await _firebaseMessaging.getNotificationSettings(); @@ -95,16 +95,16 @@ class FirebaseMessagingService extends _$FirebaseMessagingService { /// We need to try to get APNS Token before. /// https://stackoverflow.com/questions/77089496/flutter-apns-token-has-not-been-set-yet-please-ensure-the-apns-token-is-avail if (AppPlatform.isIOS) { - int iosGetApnsTriesRemaining = 10; + var iosGetApnsTriesRemaining = 10; while ((await FirebaseMessaging.instance.getAPNSToken()) == null && iosGetApnsTriesRemaining != 0) { Flogger.d('[Firebase Messaging] Waiting for APNS token for 5ms!'); iosGetApnsTriesRemaining--; - await Future.delayed(const Duration(milliseconds: 5)); + await Future.delayed(const Duration(milliseconds: 5)); } if (iosGetApnsTriesRemaining == 0) { - CrashlyticsManager.logNonCritical('APNS Token could not be obtained even after 50ms of waiting!'); + await CrashlyticsManager.logNonCritical('APNS Token could not be obtained even after 50ms of waiting!'); return; } } @@ -113,12 +113,14 @@ class FirebaseMessagingService extends _$FirebaseMessagingService { await registerFCMToken(); // Set listener to register new FCM token - FirebaseMessaging.instance.onTokenRefresh.listen((fcmToken) async { - Flogger.d("[Firebase Messaging] User's FCM token refreshed: $fcmToken"); - await registerFCMToken(fcmToken: fcmToken); - }).onError((err) { - Flogger.e('[Firebase Messaging] Error while refreshed FCM token: $err'); - }); + FirebaseMessaging.instance.onTokenRefresh + .listen((fcmToken) async { + Flogger.d("[Firebase Messaging] User's FCM token refreshed: $fcmToken"); + await registerFCMToken(fcmToken: fcmToken); + }) + .onError((Object err) { + Flogger.e('[Firebase Messaging] Error while refreshed FCM token: $err'); + }); } /// On Android - foreground, background and killed notifications are handled from the `data` payload and are manually displayed. diff --git a/lib/common/provider/notifications_service.dart b/lib/common/provider/notifications_service.dart index 1f543b9..8fbe1c6 100644 --- a/lib/common/provider/notifications_service.dart +++ b/lib/common/provider/notifications_service.dart @@ -23,8 +23,7 @@ class NotificationsService extends _$NotificationsService { switch (notificationModel) { case NotificationPayloadModelSample(): Flogger.d('[Notifications] Handle open of Sample notification'); - // TODO: [Notifications] Handle Notification open action here - break; + // TODO(HELU): [Notifications] Handle Notification open action here case NotificationPayloadModelUnknown(): // Do nothing @@ -38,7 +37,7 @@ class NotificationsService extends _$NotificationsService { // Title: 2. Check FlutterLocalNotifications for initial Message final notificationAppLaunchDetails = await _flutterLocalNotifications.getNotificationAppLaunchDetails(); - _tryOpeningNotificationFromPayload(notificationAppLaunchDetails?.notificationResponse?.payload); + await _tryOpeningNotificationFromPayload(notificationAppLaunchDetails?.notificationResponse?.payload); } static Future showNotification(NotificationPayloadModel notification) async { @@ -61,7 +60,7 @@ class NotificationsService extends _$NotificationsService { } try { - handleNotificationOpen(NotificationPayloadModel.fromJson(jsonDecode(payload))); + handleNotificationOpen(NotificationPayloadModel.fromJson(jsonDecode(payload) as Map)); return Future.value(true); } on Exception catch (e) { Flogger.e('[Notifications] Could not parse payload from notification: $payload with error $e'); @@ -90,7 +89,7 @@ class NotificationsService extends _$NotificationsService { } Future _setupLocalNotifications() async { - InitializationSettings initializationSettings = const InitializationSettings( + const initializationSettings = InitializationSettings( // This icon must be inside Android native resources android: AndroidInitializationSettings('notification_icon'), // This makes sure that we can manually request permission later diff --git a/lib/common/usecase/authentication/get_authorization_token_use_case.dart b/lib/common/usecase/authentication/get_authorization_token_use_case.dart index 1a35133..fee3c7b 100644 --- a/lib/common/usecase/authentication/get_authorization_token_use_case.dart +++ b/lib/common/usecase/authentication/get_authorization_token_use_case.dart @@ -8,7 +8,7 @@ part 'get_authorization_token_use_case.g.dart'; @riverpod Future getAuthorizationTokenUseCase( Ref ref, { - forceRefresh = false, + bool forceRefresh = false, }) async { final firebaseUser = FirebaseAuth.instance.currentUser; final token = await firebaseUser?.getIdToken(forceRefresh); diff --git a/lib/common/usecase/authentication/sign_in_completion_use_case.dart b/lib/common/usecase/authentication/sign_in_completion_use_case.dart index 1bdbdc8..47de897 100644 --- a/lib/common/usecase/authentication/sign_in_completion_use_case.dart +++ b/lib/common/usecase/authentication/sign_in_completion_use_case.dart @@ -17,7 +17,7 @@ Future signInCompletionUseCase(Ref ref) async { final userResponse = UserResponseDTO.fromJson(response.data); final user = UserModel.fromAPI(user: userResponse); */ - // TODO: Remove this line and uncomment the above lines + // TODO(HELU): Remove this line and uncomment the above lines const user = UserModel( id: '1', email: 'john.doe@example.com', diff --git a/lib/common/usecase/authentication/sign_in_with_apple_use_case.dart b/lib/common/usecase/authentication/sign_in_with_apple_use_case.dart index 3b5559a..ae0041a 100644 --- a/lib/common/usecase/authentication/sign_in_with_apple_use_case.dart +++ b/lib/common/usecase/authentication/sign_in_with_apple_use_case.dart @@ -21,7 +21,7 @@ Future signInWithAppleUseCase(Ref ref) async { final sha256Nonce = sha256.convert(utf8.encode(rawNonce)).toString(); // webAuthenticationOptions is required on Android and on the Web. - // TODO: Configure for Android and web after the domain is registered. + // TODO(HELU): Configure for Android and web after the domain is registered. final appleCredential = await SignInWithApple.getAppleIDCredential( nonce: sha256Nonce, webAuthenticationOptions: WebAuthenticationOptions( diff --git a/lib/common/usecase/authentication/sign_in_with_auth_credential_use_case.dart b/lib/common/usecase/authentication/sign_in_with_auth_credential_use_case.dart index 5a86733..d5ac5ed 100644 --- a/lib/common/usecase/authentication/sign_in_with_auth_credential_use_case.dart +++ b/lib/common/usecase/authentication/sign_in_with_auth_credential_use_case.dart @@ -40,7 +40,7 @@ FutureOr signInWithAuthCredentialUseCase( } } else { // Title: Sign in with credentials - Flogger.d('[Authentication] Going to sign in user with received credential ${credential.asMap().toString()}'); + Flogger.d('[Authentication] Going to sign in user with received credential ${credential.asMap()}'); await FirebaseAuth.instance.signInWithCredential(credential); } diff --git a/lib/common/usecase/authentication/sign_in_with_google_use_case.dart b/lib/common/usecase/authentication/sign_in_with_google_use_case.dart index f0a5b3f..897e7c2 100644 --- a/lib/common/usecase/authentication/sign_in_with_google_use_case.dart +++ b/lib/common/usecase/authentication/sign_in_with_google_use_case.dart @@ -13,7 +13,7 @@ part 'sign_in_with_google_use_case.g.dart'; Future signInWithGoogleUseCase(Ref ref) async { Flogger.d('[Authentication] Sign in with Google started'); - final GoogleSignIn googleSignIn = GoogleSignIn(); + final googleSignIn = GoogleSignIn(); // Step 1: Logout from Current Google account if any if (await googleSignIn.isSignedIn()) { diff --git a/lib/common/usecase/create_device_token_use_case.dart b/lib/common/usecase/create_device_token_use_case.dart index 16ca7c0..c387fbb 100644 --- a/lib/common/usecase/create_device_token_use_case.dart +++ b/lib/common/usecase/create_device_token_use_case.dart @@ -12,8 +12,8 @@ Future createDeviceTokenUseCase( }) async { final dio = ref.read(dioProvider); - // TODO: Register FCM token on API - await dio.post( + // TODO(HELU): Register FCM token on API + await dio.post>( '/v1/device-tokens', data: CreateDeviceTokenRequestDTO(token: deviceToken).toJson(), ); diff --git a/lib/common/usecase/native_store_open_use_case.dart b/lib/common/usecase/native_store_open_use_case.dart index cabd05f..5718af8 100644 --- a/lib/common/usecase/native_store_open_use_case.dart +++ b/lib/common/usecase/native_store_open_use_case.dart @@ -15,7 +15,9 @@ final _platformNotSupportedException = Exception('Platform not supported'); /// This exception was thrown when page in store can't be launchd class CantLaunchPageException implements Exception { - CantLaunchPageException(String cause) : super(); + CantLaunchPageException(this.message) : super(); + + final String message; } @riverpod @@ -64,38 +66,36 @@ Future _open(String? appStoreId, String? appStoreIdMacOS, String? androidA throw _platformNotSupportedException; } -Future _openAndroid(String? androidAppBundleId) async { +Future _openAndroid(String? androidAppBundleId) async { if (androidAppBundleId != null) { await _openUrl('$_playMarketUrl$androidAppBundleId'); - return; + } else { + throw CantLaunchPageException('androidAppBundleId is not passed'); } - throw CantLaunchPageException('androidAppBundleId is not passed'); } -Future _openIOS(String? appStoreId) async { +Future _openIOS(String? appStoreId) async { if (appStoreId != null) { await _openUrl('$_appStoreUrlIOS$appStoreId'); - return; + } else { + throw CantLaunchPageException('appStoreId is not passed'); } - throw CantLaunchPageException('appStoreId is not passed'); } -Future _openMacOS(String? appStoreId, String? appStoreIdMacOS) async { +Future _openMacOS(String? appStoreId, String? appStoreIdMacOS) async { if (appStoreId != null || appStoreIdMacOS != null) { await _openUrl('$_appStoreUrlMacOS${appStoreIdMacOS ?? appStoreId}'); - return; + } else { + throw CantLaunchPageException('appStoreId and appStoreIdMacOS is not passed'); } - throw CantLaunchPageException( - 'appStoreId and appStoreIdMacOS is not passed', - ); } -Future _openWindows(String? windowsProductId) async { +Future _openWindows(String? windowsProductId) async { if (windowsProductId != null) { await _openUrl('$_microsoftStoreUrl$windowsProductId'); - return; + } else { + throw CantLaunchPageException('windowsProductId is not passed'); } - throw CantLaunchPageException('windowsProductId is not passed'); } Future _openUrl(String url) async { @@ -105,7 +105,7 @@ Future _openUrl(String url) async { uri, mode: LaunchMode.externalApplication, ); - return; + } else { + throw CantLaunchPageException('Could not launch $url'); } - throw CantLaunchPageException('Could not launch $url'); } diff --git a/lib/common/usecase/user/get_current_user_use_case.dart b/lib/common/usecase/user/get_current_user_use_case.dart index e3239fe..a89ba48 100644 --- a/lib/common/usecase/user/get_current_user_use_case.dart +++ b/lib/common/usecase/user/get_current_user_use_case.dart @@ -9,7 +9,7 @@ part 'get_current_user_use_case.g.dart'; @riverpod Future getCurrentUserUseCase(Ref ref) async { final dio = ref.read(dioProvider); - final response = await dio.get('/v1/users/me'); + final response = await dio.get>('/v1/users/me'); - return UserModel.fromAPI(user: UserResponseDTO.fromJson(response.data)); + return UserModel.fromAPI(user: UserResponseDTO.fromJson(response.data!)); } diff --git a/lib/common/usecase/user/get_database_user_use_case.dart b/lib/common/usecase/user/get_database_user_use_case.dart index d5f18ce..dc67515 100644 --- a/lib/common/usecase/user/get_database_user_use_case.dart +++ b/lib/common/usecase/user/get_database_user_use_case.dart @@ -15,7 +15,7 @@ Future getDatabaseUserUseCase(Ref ref) async { if (currentUserData != null) { try { - return UserModel.fromJson(jsonDecode(currentUserData)); + return UserModel.fromJson(jsonDecode(currentUserData) as Map); // ignore: avoid_catches_without_on_clauses } catch (e) { Flogger.e('[CurrentUserModelState] Error while parsing user data: $e'); diff --git a/lib/common/validator/controller/text_validator_controller_full_name.dart b/lib/common/validator/controller/text_validator_controller_full_name.dart index fba9589..a69aeaa 100644 --- a/lib/common/validator/controller/text_validator_controller_full_name.dart +++ b/lib/common/validator/controller/text_validator_controller_full_name.dart @@ -3,13 +3,13 @@ import 'package:flutter_app/common/validator/controller/text_validator_controlle class TextValidatorControllerFullName extends TextValidatorControllerGeneral { TextValidatorControllerFullName() - : super( - emptyText: (context) => context.locale.validatorExceptionFullNameIsEmpty, - minLength: 3, - tooShortText: (context) => context.locale.validatorExceptionFullNameTooShort, - maxLength: 48, - tooLongText: (context) => context.locale.validatorExceptionFullNameTooLong, - regex: RegExp(r"^[\w'\-,.][^0-9_!¡?÷?¿/\\+=@#$%ˆ&*(){}|~<>;:[\]]{2,}$", caseSensitive: false), - regexText: (context) => context.locale.validatorExceptionFullNameIsInvalid, - ); + : super( + emptyText: (context) => context.locale.validatorExceptionFullNameIsEmpty, + minLength: 3, + tooShortText: (context) => context.locale.validatorExceptionFullNameTooShort, + maxLength: 48, + tooLongText: (context) => context.locale.validatorExceptionFullNameTooLong, + regex: RegExp(r"^[\w'\-,.][^0-9_!¡?÷?¿/\\+=@#$%ˆ&*(){}|~<>;:[\]]{2,}$", caseSensitive: false), + regexText: (context) => context.locale.validatorExceptionFullNameIsInvalid, + ); } diff --git a/lib/core/analytics/analytics_event.dart b/lib/core/analytics/analytics_event.dart index c53a59f..d3737bb 100644 --- a/lib/core/analytics/analytics_event.dart +++ b/lib/core/analytics/analytics_event.dart @@ -11,7 +11,7 @@ abstract class AnalyticsEvent with _$AnalyticsEvent { factory AnalyticsEvent.onSampleEvent() => AnalyticsEvent._(firebaseEventId: 'on_sample_event'); factory AnalyticsEvent.onAnotherSampleEvent({required String sampleParamValue}) => AnalyticsEvent._( - firebaseEventId: 'on_another_sample_event', - firebaseEventParams: {'sample_parameter_id': sampleParamValue}, - ); + firebaseEventId: 'on_another_sample_event', + firebaseEventParams: {'sample_parameter_id': sampleParamValue}, + ); } diff --git a/lib/core/analytics/analytics_manager.dart b/lib/core/analytics/analytics_manager.dart index 9cda0f4..2e52165 100644 --- a/lib/core/analytics/analytics_manager.dart +++ b/lib/core/analytics/analytics_manager.dart @@ -9,9 +9,8 @@ part 'analytics_manager.g.dart'; @riverpod Future analyticsManagerUpdateUserInfo(Ref ref, {required UserModel? user}) async { - FirebaseAnalytics.instance - ..setUserId(id: user?.id) - ..setUserProperty(name: 'display_name', value: user?.displayName); + await FirebaseAnalytics.instance.setUserId(id: user?.id); + await FirebaseAnalytics.instance.setUserProperty(name: 'display_name', value: user?.displayName); } @riverpod diff --git a/lib/core/analytics/analytics_route_observer.dart b/lib/core/analytics/analytics_route_observer.dart index 887655f..e15cb90 100644 --- a/lib/core/analytics/analytics_route_observer.dart +++ b/lib/core/analytics/analytics_route_observer.dart @@ -16,7 +16,7 @@ class AnalyticsRouteObserver extends AutoRouterObserver { final Ref ref; @override - void didPush(Route route, Route? previousRoute) { + void didPush(Route route, Route? previousRoute) { if (route.settings.name == null) return; Flogger.d('[AnalyticsRouteObserver] New route pushed: ${route.settings.name}'); diff --git a/lib/core/analytics/crashlytics_manager.dart b/lib/core/analytics/crashlytics_manager.dart index e872a83..214b0d9 100644 --- a/lib/core/analytics/crashlytics_manager.dart +++ b/lib/core/analytics/crashlytics_manager.dart @@ -6,13 +6,13 @@ import 'package:flutter_app/app/setup/app_platform.dart'; class CrashlyticsManager { static Future logMessage(dynamic message) async { if (Firebase.apps.isNotEmpty && AppPlatform.isMobile && !kDebugMode) { - await FirebaseCrashlytics.instance.log(message); + await FirebaseCrashlytics.instance.log(message.toString()); } } static Future logNonCritical(dynamic exception, {StackTrace? stack, String? message}) async { if (Firebase.apps.isNotEmpty && AppPlatform.isMobile && !kDebugMode) { - await FirebaseCrashlytics.instance.recordError(exception, stack, reason: message, fatal: false); + await FirebaseCrashlytics.instance.recordError(exception, stack, reason: message); } } diff --git a/lib/core/flogger.dart b/lib/core/flogger.dart index dc2a736..6302748 100644 --- a/lib/core/flogger.dart +++ b/lib/core/flogger.dart @@ -6,7 +6,7 @@ import 'package:talker_flutter/talker_flutter.dart'; /// Wrapper over the Talker package. class Flogger { - static final bool _providerLogsEnabled = false; + static const bool _providerLogsEnabled = false; static final talker = TalkerFlutter.init( logger: TalkerLogger(formatter: _LoggerFormatter()), @@ -30,85 +30,103 @@ class Flogger { static void v(dynamic message, {Object? exception, StackTrace? stackTrace, DateTime? time}) { CrashlyticsManager.logMessage(message); - talker.logCustom(_CustomLog( - message.toString(), - exception: exception, - stackTrace: stackTrace, - pen: colors['verbose'], - key: TalkerLogType.verbose.key, - )); + talker.logCustom( + _CustomLog( + message.toString(), + exception: exception, + stackTrace: stackTrace, + pen: colors['verbose'], + key: TalkerLogType.verbose.key, + ), + ); } static void d(dynamic message, {Object? exception, StackTrace? stackTrace, DateTime? time}) { CrashlyticsManager.logMessage(message); - talker.logCustom(_CustomLog( - message.toString(), - exception: exception, - stackTrace: stackTrace, - pen: colors['debug'], - key: TalkerLogType.debug.key, - )); + talker.logCustom( + _CustomLog( + message.toString(), + exception: exception, + stackTrace: stackTrace, + pen: colors['debug'], + key: TalkerLogType.debug.key, + ), + ); } static void i(dynamic message, {Object? exception, StackTrace? stackTrace, DateTime? time}) { CrashlyticsManager.logMessage(message); - talker.logCustom(_CustomLog( - message.toString(), - exception: exception, - stackTrace: stackTrace, - pen: colors['info'], - key: TalkerLogType.info.key, - )); + talker.logCustom( + _CustomLog( + message.toString(), + exception: exception, + stackTrace: stackTrace, + pen: colors['info'], + key: TalkerLogType.info.key, + ), + ); } static void w(dynamic message, {Object? exception, StackTrace? stackTrace, DateTime? time}) { - CrashlyticsManager.logNonCritical(exception, stack: stackTrace, message: message); - talker.logCustom(_CustomLog( - message.toString(), - exception: exception, - stackTrace: stackTrace, - pen: colors['warning'], - key: TalkerLogType.warning.key, - )); + CrashlyticsManager.logNonCritical(exception, stack: stackTrace, message: message.toString()); + talker.logCustom( + _CustomLog( + message.toString(), + exception: exception, + stackTrace: stackTrace, + pen: colors['warning'], + key: TalkerLogType.warning.key, + ), + ); } static void e(dynamic message, {Object? exception, StackTrace? stackTrace, DateTime? time}) { - CrashlyticsManager.logNonCritical(exception, stack: stackTrace, message: message); - talker.logCustom(_CustomLog( - message.toString(), - exception: exception, - stackTrace: stackTrace, - pen: colors['error'], - key: TalkerLogType.error.key, - )); + CrashlyticsManager.logNonCritical(exception, stack: stackTrace, message: message.toString()); + talker.logCustom( + _CustomLog( + message.toString(), + exception: exception, + stackTrace: stackTrace, + pen: colors['error'], + key: TalkerLogType.error.key, + ), + ); } - static void navigation(dynamic message) => talker.logCustom(_CustomLog( - message.toString(), - pen: colors['navigation'], - key: TalkerLogType.route.key, - )); - - static void providerAdded(dynamic message) => (_providerLogsEnabled) - ? talker.logCustom(_CustomLog( - message.toString(), - pen: colors['providerAdded'], - key: TalkerLogType.riverpodAdd.key, - )) + static void navigation(dynamic message) => talker.logCustom( + _CustomLog( + message.toString(), + pen: colors['navigation'], + key: TalkerLogType.route.key, + ), + ); + + static void providerAdded(dynamic message) => _providerLogsEnabled + ? talker.logCustom( + _CustomLog( + message.toString(), + pen: colors['providerAdded'], + key: TalkerLogType.riverpodAdd.key, + ), + ) : null; - static void providerUpdated(dynamic message) => (_providerLogsEnabled) - ? talker.logCustom(_CustomLog( - message.toString(), - pen: colors['providerUpdated'], - key: TalkerLogType.riverpodUpdate.key, - )) + static void providerUpdated(dynamic message) => _providerLogsEnabled + ? talker.logCustom( + _CustomLog( + message.toString(), + pen: colors['providerUpdated'], + key: TalkerLogType.riverpodUpdate.key, + ), + ) : null; - static void providerRemoved(dynamic message) => (_providerLogsEnabled) - ? talker.logCustom(_CustomLog( - message.toString(), - pen: colors['providerRemoved'], - key: TalkerLogType.riverpodDispose.key, - )) + static void providerRemoved(dynamic message) => _providerLogsEnabled + ? talker.logCustom( + _CustomLog( + message.toString(), + pen: colors['providerRemoved'], + key: TalkerLogType.riverpodDispose.key, + ), + ) : null; } @@ -147,7 +165,7 @@ class _LoggerFormatter implements LoggerFormatter { final msg = details.message?.toString() ?? ''; final msgBorderedLines = _splitLongLines(msg, _maxOutputThreshold).split('\n').map((e) => '│ $e'); if (!settings.enableColors) { - return (showBorder) ? '$topline\n${msgBorderedLines.join('\n')}\n$underline' : msgBorderedLines.join('\n'); + return showBorder ? '$topline\n${msgBorderedLines.join('\n')}\n$underline' : msgBorderedLines.join('\n'); } var lines = [if (showBorder) topline, ...msgBorderedLines, if (showBorder) underline]; lines = lines.map((e) => details.pen.write(e)).toList(); @@ -156,14 +174,14 @@ class _LoggerFormatter implements LoggerFormatter { } static String _splitLongLines(String input, int maxLineLength) { - List lines = input.split('\n'); - List outputLines = []; + final lines = input.split('\n'); + final outputLines = []; - for (String line in lines) { + for (final line in lines) { if (line.length > maxLineLength) { // Split the line into chunks of maxLineLength - for (int i = 0; i < line.length; i += maxLineLength) { - int end = (i + maxLineLength < line.length) ? i + maxLineLength : line.length; + for (var i = 0; i < line.length; i += maxLineLength) { + final end = (i + maxLineLength < line.length) ? i + maxLineLength : line.length; outputLines.add(line.substring(i, end)); } } else { diff --git a/lib/core/network/dio_authorization_token_interceptor.dart b/lib/core/network/dio_authorization_token_interceptor.dart index 9609904..105a89d 100644 --- a/lib/core/network/dio_authorization_token_interceptor.dart +++ b/lib/core/network/dio_authorization_token_interceptor.dart @@ -21,7 +21,7 @@ class DioAuthorizationTokenInterceptor extends Interceptor { return handler.next(options); } - final token = await ref.read(getAuthorizationTokenUseCaseProvider(forceRefresh: false).future); + final token = await ref.read(getAuthorizationTokenUseCaseProvider().future); options.headers['Authorization'] = 'Bearer $token'; diff --git a/lib/core/network/dio_provider.dart b/lib/core/network/dio_provider.dart index 81a9958..9920de8 100644 --- a/lib/core/network/dio_provider.dart +++ b/lib/core/network/dio_provider.dart @@ -19,9 +19,6 @@ Dio dio(Ref ref) { TalkerDioLogger( talker: Flogger.talker, settings: TalkerDioLoggerSettings( - printRequestHeaders: false, - printResponseHeaders: false, - printResponseMessage: true, requestPen: Flogger.colors['httpRequest'], responsePen: Flogger.colors['httpResponse'], errorPen: Flogger.colors['httpError'], diff --git a/lib/core/riverpod/event_notifier.dart b/lib/core/riverpod/event_notifier.dart index 27042be..173a96f 100644 --- a/lib/core/riverpod/event_notifier.dart +++ b/lib/core/riverpod/event_notifier.dart @@ -2,13 +2,15 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; /// Notifier which is used only to listen. class EventNotifier extends StateNotifier { - EventNotifier(super.state); + // ignore: use_super_parameters + EventNotifier(T state) : super(state); @override bool updateShouldNotify(T old, T current) { return true; } + // ignore: use_setters_to_change_properties void send(T event) { state = event; } diff --git a/lib/core/riverpod/provider_logger.dart b/lib/core/riverpod/provider_logger.dart index 87139ae..840fbcc 100644 --- a/lib/core/riverpod/provider_logger.dart +++ b/lib/core/riverpod/provider_logger.dart @@ -4,7 +4,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; class ProvidersLogger extends ProviderObserver { @override void didAddProvider( - ProviderBase provider, + ProviderBase provider, Object? value, ProviderContainer container, ) { @@ -18,7 +18,7 @@ class ProvidersLogger extends ProviderObserver { @override void didUpdateProvider( - ProviderBase provider, + ProviderBase provider, Object? previousValue, Object? newValue, ProviderContainer container, @@ -34,7 +34,7 @@ class ProvidersLogger extends ProviderObserver { @override void didDisposeProvider( - ProviderBase provider, + ProviderBase provider, ProviderContainer container, ) { super.didDisposeProvider(provider, container); diff --git a/lib/core/riverpod/state_handler.dart b/lib/core/riverpod/state_handler.dart index edfc8ce..0692aaa 100644 --- a/lib/core/riverpod/state_handler.dart +++ b/lib/core/riverpod/state_handler.dart @@ -42,7 +42,7 @@ mixin NullableStateHandler on BuildlessAsyncNotifier { void setStateData(T? data) => _setStateDataNullable(data, (data) => state = AsyncData(data)); } -void _setStateLoading(Function() onStateChange) { +void _setStateLoading(void Function() onStateChange) { try { onStateChange(); // ignore: avoid_catching_errors @@ -51,7 +51,7 @@ void _setStateLoading(Function() onStateChange) { } } -void _setStateData(T? data, Function(T data) onStateChange) { +void _setStateData(T? data, void Function(T data) onStateChange) { try { if (data != null) { onStateChange(data); @@ -64,7 +64,7 @@ void _setStateData(T? data, Function(T data) onStateChange) { } } -void _setStateDataNullable(T? data, Function(T? data) onStateChange) { +void _setStateDataNullable(T? data, void Function(T? data) onStateChange) { try { onStateChange(data); // ignore: avoid_catching_errors diff --git a/lib/features/authentication/authentication_event.dart b/lib/features/authentication/authentication_event.dart index 9185638..20fa61f 100644 --- a/lib/features/authentication/authentication_event.dart +++ b/lib/features/authentication/authentication_event.dart @@ -9,5 +9,6 @@ sealed class AuthenticationEvent with _$AuthenticationEvent { const factory AuthenticationEvent.signedIn() = AuthenticationEventSignedIn; } -final authenticationEventNotifierProvider = - StateNotifierProvider.autoDispose, AuthenticationEvent?>((ref) => EventNotifier(null)); +final authenticationEventNotifierProvider = StateNotifierProvider.autoDispose, AuthenticationEvent?>( + (ref) => EventNotifier(null), +); diff --git a/lib/features/authentication/authentication_page_content.dart b/lib/features/authentication/authentication_page_content.dart index 1f3b7e3..8ef49d9 100644 --- a/lib/features/authentication/authentication_page_content.dart +++ b/lib/features/authentication/authentication_page_content.dart @@ -35,12 +35,13 @@ class _DataStateWidget extends ConsumerWidget { children: [ const Spacer(), CustomButtonPrimary( - text: 'Mock Sign In', - isLoading: data.isSigningIn, - onPressed: () async { - await ref.read(signInCompletionUseCaseProvider.future); - if (context.mounted) context.router.replaceAll([const LandingRoute()]); - }), + text: 'Mock Sign In', + isLoading: data.isSigningIn, + onPressed: () async { + await ref.read(signInCompletionUseCaseProvider.future); + if (context.mounted) await context.router.replaceAll([const LandingRoute()]); + }, + ), const SizedBox(height: 48), CustomButtonPrimary( text: 'Sign in Anonymously', diff --git a/lib/features/authentication/authentication_state.dart b/lib/features/authentication/authentication_state.dart index 8476594..14ca836 100644 --- a/lib/features/authentication/authentication_state.dart +++ b/lib/features/authentication/authentication_state.dart @@ -29,15 +29,15 @@ class AuthenticationStateNotifier extends _$AuthenticationStateNotifier with Aut } Future signInAnonymously() async { - _signInWithProvider(signInAnonymouslyUseCaseProvider); + await _signInWithProvider(signInAnonymouslyUseCaseProvider); } Future signInWithGoogle() async { - _signInWithProvider(signInWithGoogleUseCaseProvider); + await _signInWithProvider(signInWithGoogleUseCaseProvider); } Future signInWithApple() async { - _signInWithProvider(signInWithAppleUseCaseProvider); + await _signInWithProvider(signInWithAppleUseCaseProvider); } Future _signInWithProvider(AutoDisposeFutureProvider provider) async { diff --git a/lib/features/debug_tools/debug_tools_page_state.dart b/lib/features/debug_tools/debug_tools_page_state.dart index c3cbab0..3895ef7 100644 --- a/lib/features/debug_tools/debug_tools_page_state.dart +++ b/lib/features/debug_tools/debug_tools_page_state.dart @@ -9,7 +9,7 @@ enum DebugToolsSampleType { widgets, popups, colors, - textStyles; + textStyles, } @freezed diff --git a/lib/features/debug_tools/page/actions/debug_tools_actions_page_content.dart b/lib/features/debug_tools/page/actions/debug_tools_actions_page_content.dart index 017ce0e..90c004b 100644 --- a/lib/features/debug_tools/page/actions/debug_tools_actions_page_content.dart +++ b/lib/features/debug_tools/page/actions/debug_tools_actions_page_content.dart @@ -28,12 +28,12 @@ class _DebugToolsActionsPageContentState extends ConsumerState( builder: (context) => Theme( - data: ThemeData(colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.purple)), + data: ThemeData(colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.grey)), child: TalkerScreen( talker: Flogger.talker, - theme: TalkerScreenTheme( + theme: const TalkerScreenTheme( logColors: {TalkerLogType.debug: Colors.green}, ), ), diff --git a/lib/features/debug_tools/page/popups/debug_tools_list_dialog.dart b/lib/features/debug_tools/page/popups/debug_tools_list_dialog.dart index 65492d3..cdf093f 100644 --- a/lib/features/debug_tools/page/popups/debug_tools_list_dialog.dart +++ b/lib/features/debug_tools/page/popups/debug_tools_list_dialog.dart @@ -19,7 +19,7 @@ class DebugToolsListDialog { final void Function(String listItem) onItemSelect; Future show() async { - return await showDialog( + return showDialog( context: context, builder: (builderContext) => CustomDialogWrapper.custom( context: builderContext, @@ -30,7 +30,7 @@ class DebugToolsListDialog { shrinkWrap: true, itemCount: list.length, separatorBuilder: (context, index) => Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(8), child: Container(height: 1, color: context.colorScheme.divider), ), itemBuilder: (context, index) { diff --git a/lib/features/debug_tools/page/popups/debug_tools_popups_page_content.dart b/lib/features/debug_tools/page/popups/debug_tools_popups_page_content.dart index d2dbf76..69b3d2e 100644 --- a/lib/features/debug_tools/page/popups/debug_tools_popups_page_content.dart +++ b/lib/features/debug_tools/page/popups/debug_tools_popups_page_content.dart @@ -47,7 +47,7 @@ class DebugToolsPopupsPageContent extends ConsumerWidget { ), const SizedBox(height: 16), CustomButtonPrimary( - onPressed: () => showCustomModalBottomSheet( + onPressed: () => showCustomModalBottomSheet( context: context, containerWidget: (context, animation, child) => BottomSheetContainerWidget(child: child), builder: (context) => Padding( diff --git a/lib/features/debug_tools/page/widgets/debug_tools_widgets_page_content.dart b/lib/features/debug_tools/page/widgets/debug_tools_widgets_page_content.dart index efe7e06..c1ebee9 100644 --- a/lib/features/debug_tools/page/widgets/debug_tools_widgets_page_content.dart +++ b/lib/features/debug_tools/page/widgets/debug_tools_widgets_page_content.dart @@ -23,9 +23,9 @@ class DebugToolsWidgetsPageContent extends ConsumerWidget { debugToolsWidgetsPageEventNotifierProvider, (_, next) => switch (next) { DebugToolsWidgetsPageEventFieldValidated(message: final message) => CustomSnackbarMessage( - context: context, - message: message, - ).show(), + context: context, + message: message, + ).show(), _ => () {}, }, ); @@ -57,7 +57,6 @@ class DebugToolsWidgetsPageContent extends ConsumerWidget { CustomTextFieldButton( label: 'Example of custom text field button', validatorController: ref.read(debugToolsWidgetsPageStateNotifierProvider.notifier).exampleTextController, - enabled: true, onClick: () => Flogger.d('Clicked text field button'), ), const SizedBox(height: 64), diff --git a/lib/features/debug_tools/page/widgets/debug_tools_widgets_page_event.dart b/lib/features/debug_tools/page/widgets/debug_tools_widgets_page_event.dart index d8f5a64..5cceb02 100644 --- a/lib/features/debug_tools/page/widgets/debug_tools_widgets_page_event.dart +++ b/lib/features/debug_tools/page/widgets/debug_tools_widgets_page_event.dart @@ -11,4 +11,5 @@ sealed class DebugToolsWidgetsPageEvent with _$DebugToolsWidgetsPageEvent { final debugToolsWidgetsPageEventNotifierProvider = StateNotifierProvider.autoDispose, DebugToolsWidgetsPageEvent?>( - (ref) => EventNotifier(null)); + (ref) => EventNotifier(null), + ); diff --git a/lib/features/debug_tools/page/widgets/debug_tools_widgets_page_state.dart b/lib/features/debug_tools/page/widgets/debug_tools_widgets_page_state.dart index 3894f51..fa5117a 100644 --- a/lib/features/debug_tools/page/widgets/debug_tools_widgets_page_state.dart +++ b/lib/features/debug_tools/page/widgets/debug_tools_widgets_page_state.dart @@ -9,12 +9,11 @@ part 'debug_tools_widgets_page_state.g.dart'; @freezed abstract class DebugToolsWidgetsPageState with _$DebugToolsWidgetsPageState { - const DebugToolsWidgetsPageState._(); - const factory DebugToolsWidgetsPageState({ required String sampleFieldValidMessage, required String selectedOption, }) = _DebugToolsWidgetsPageState; + const DebugToolsWidgetsPageState._(); } @riverpod diff --git a/lib/features/home/home_page_content.dart b/lib/features/home/home_page_content.dart index 3069efe..e2ca31d 100644 --- a/lib/features/home/home_page_content.dart +++ b/lib/features/home/home_page_content.dart @@ -13,11 +13,10 @@ class HomePageContent extends StatelessWidget { Widget build(BuildContext context) { return Center( child: Padding( - padding: const EdgeInsets.all(16.0), + padding: const EdgeInsets.all(16), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, children: [ CustomFadeSlideAnimation( delay: const Duration(milliseconds: 300), diff --git a/lib/features/landing/force_update_page_content.dart b/lib/features/landing/force_update_page_content.dart index 72a34f6..0128044 100644 --- a/lib/features/landing/force_update_page_content.dart +++ b/lib/features/landing/force_update_page_content.dart @@ -18,7 +18,6 @@ class ForceUpdatePageContent extends ConsumerWidget { padding: const EdgeInsets.all(20), child: Column( mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, children: [ Assets.svg.imgRocket.svg(width: 200), const SizedBox(height: 48), @@ -38,11 +37,13 @@ class ForceUpdatePageContent extends ConsumerWidget { if (AppPlatform.isMobile) CustomButtonPrimary( onPressed: () { - // TODO: Fill correct Android and iOS app IDs - ref.read(nativeStoreOpenUseCaseProvider( - androidAppBundleId: 'com.template.app', - appStoreId: '123123123', - )); + // TODO(HELU): Fill correct Android and iOS app IDs + ref.read( + nativeStoreOpenUseCaseProvider( + androidAppBundleId: 'com.template.app', + appStoreId: '123123123', + ), + ); }, text: context.locale.forceUpdateButton, ), diff --git a/lib/features/landing/landing_page.dart b/lib/features/landing/landing_page.dart index 4437d90..1530228 100644 --- a/lib/features/landing/landing_page.dart +++ b/lib/features/landing/landing_page.dart @@ -41,7 +41,7 @@ class _LandingPageState extends ConsumerState { @override Widget build(BuildContext context) { return Scaffold( - body: (_forceUpdate == true) ? const ForceUpdatePageContent() : const SizedBox(), + body: _forceUpdate ? const ForceUpdatePageContent() : const SizedBox(), ); } @@ -56,10 +56,10 @@ class _LandingPageState extends ConsumerState { if (currentUser == null) { Flogger.d('[LandingPage] Redirecting to Authentication page'); - context.replaceRoute(const AuthenticationRoute()); + await context.replaceRoute(const AuthenticationRoute()); } else { Flogger.d('[LandingPage] Redirecting to Home page'); - context.replaceRoute(const RootRoute()); + await context.replaceRoute(const RootRoute()); } } } diff --git a/lib/features/profile/profile_event.dart b/lib/features/profile/profile_event.dart index bd3192d..8084aec 100644 --- a/lib/features/profile/profile_event.dart +++ b/lib/features/profile/profile_event.dart @@ -9,5 +9,6 @@ sealed class ProfileEvent with _$ProfileEvent { const factory ProfileEvent.signedOut() = ProfileEventSignedOut; } -final profileEventNotifierProvider = - StateNotifierProvider.autoDispose, ProfileEvent?>((ref) => EventNotifier(null)); +final profileEventNotifierProvider = StateNotifierProvider.autoDispose, ProfileEvent?>( + (ref) => EventNotifier(null), +); diff --git a/lib/features/profile/profile_page_content.dart b/lib/features/profile/profile_page_content.dart index e0cbca6..48e7771 100644 --- a/lib/features/profile/profile_page_content.dart +++ b/lib/features/profile/profile_page_content.dart @@ -46,7 +46,6 @@ class _LoadingStateWidget extends ConsumerWidget { const Spacer(), CustomButtonPrimary( text: 'Sign out', - isLoading: false, onPressed: () {}, ), ], diff --git a/lib/features/profile/profile_state.dart b/lib/features/profile/profile_state.dart index 25e43c1..b22c074 100644 --- a/lib/features/profile/profile_state.dart +++ b/lib/features/profile/profile_state.dart @@ -27,7 +27,7 @@ class ProfileStateNotifier extends _$ProfileStateNotifier with AutoDisposeStateH final currentUser = await ref.read(currentUserModelStateProvider.future); final imageUrl = ref.read(getRandomImageUrlUseCaseProvider(width: 120, height: 120)); - await Future.delayed(const Duration(seconds: 2)); + await Future.delayed(const Duration(seconds: 2)); return ProfileState( displayName: currentUser?.displayName ?? '', diff --git a/project_setup/analysis_options.yaml b/project_setup/analysis_options.yaml new file mode 100644 index 0000000..c89c7e8 --- /dev/null +++ b/project_setup/analysis_options.yaml @@ -0,0 +1,45 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:netglade_analysis/lints.yaml + +analyzer: + exclude: + - 'project_setup/**' + - '**.g.dart' + - '**.gen.dart' + - '**.config.dart' + - '**.graphql.dart' + errors: + todo: info + +formatter: + page_width: 140 + trailing_commas: preserve + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at + # https://dart-lang.github.io/linter/lints/index.html. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + sort_pub_dependencies: false + require_trailing_commas: false + avoid_positional_boolean_parameters: false + no_default_cases: false + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options diff --git a/project_setup/lib/core/util/files.dart b/project_setup/lib/core/util/files.dart index 873ab30..b87fb60 100644 --- a/project_setup/lib/core/util/files.dart +++ b/project_setup/lib/core/util/files.dart @@ -24,7 +24,7 @@ Future findAndReplaceTextInDirectory({ // 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 findAndRemplaceTextInFile( + await findAndReplaceTextInFile( file: entity, searchString: searchString, replaceString: replaceString, @@ -35,7 +35,7 @@ Future findAndReplaceTextInDirectory({ } } -Future findAndRemplaceTextInFile({ +Future findAndReplaceTextInFile({ required File file, required String searchString, required String replaceString, diff --git a/project_setup/pubspec.lock b/project_setup/pubspec.lock index 00d36ac..7ef026e 100644 --- a/project_setup/pubspec.lock +++ b/project_setup/pubspec.lock @@ -85,10 +85,18 @@ packages: dependency: transitive description: name: meta - sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.17.0" + version: "1.16.0" + netglade_analysis: + dependency: "direct dev" + description: + name: netglade_analysis + sha256: "67778583fbddc666daddb53a9ab948bf5a37cbfb949c5d7fdf51b87b0cd1746a" + url: "https://pub.dev" + source: hosted + version: "16.1.0" path: dependency: transitive description: diff --git a/project_setup/pubspec.yaml b/project_setup/pubspec.yaml index f463410..b2c162b 100644 --- a/project_setup/pubspec.yaml +++ b/project_setup/pubspec.yaml @@ -10,3 +10,7 @@ environment: dependencies: image: 4.5.4 interact: 2.2.0 + + +dev_dependencies: + netglade_analysis: 16.1.0 \ No newline at end of file From dfd6ecc2197a3f89eadf235846d440ee8ff686ce Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Thu, 22 May 2025 08:56:17 +0200 Subject: [PATCH 4/9] feat: Rebrand --- .../android_play_store_distribution.yml | 2 +- README.md | 2 +- android/app/build.gradle | 2 +- android/app/google-services.json | 40 +++++++++---------- .../flutter/template/MainActivityTest.java | 2 +- android/app/src/main/AndroidManifest.xml | 2 +- .../helu}/flutter/template/MainActivity.kt | 2 +- android/extras/keystore/README.md | 2 +- ios/Runner.xcodeproj/project.pbxproj | 18 ++++----- ios/config/develop/GoogleService-Info.plist | 6 +-- .../production/GoogleService-Info.plist | 6 +-- ios/config/staging/GoogleService-Info.plist | 6 +-- .../configuration/develop/config_develop.dart | 2 +- .../production/config_production.dart | 2 +- .../configuration/staging/config_staging.dart | 2 +- lib/app/setup/setup_app.dart | 12 +++--- linux/CMakeLists.txt | 2 +- macos/Runner/Configs/AppInfo.xcconfig | 4 +- project_setup/lib/configuration.dart | 4 +- pubspec.yaml | 4 +- web/firebase-messaging-sw.js | 6 +-- windows/runner/Runner.rc | 4 +- 22 files changed, 66 insertions(+), 66 deletions(-) rename android/app/src/androidTest/java/{com/strv => cz/helu}/flutter/template/MainActivityTest.java (96%) rename android/app/src/main/kotlin/{com/strv => cz/helu}/flutter/template/MainActivity.kt (76%) diff --git a/.github/workflows/android_play_store_distribution.yml b/.github/workflows/android_play_store_distribution.yml index 2c0bef3..4a8b081 100644 --- a/.github/workflows/android_play_store_distribution.yml +++ b/.github/workflows/android_play_store_distribution.yml @@ -52,7 +52,7 @@ jobs: uses: r0adkll/upload-google-play@v1.1.1 with: serviceAccountJsonPlainText: ${{ secrets.GOOGLE_PLAY_DISTRIBUTION_SERVICE_ACCOUNT_JSON }} - packageName: com.strv.flutter.template + packageName: cz.helu.flutter.template releaseFiles: build/app/outputs/bundle/productionRelease/app-production-release.aab mappingFile: build/app/outputs/mapping/productionRelease/mapping.txt whatsNewDirectory: whatsnew diff --git a/README.md b/README.md index 93e3e09..b6a0693 100644 --- a/README.md +++ b/README.md @@ -375,7 +375,7 @@ To be able to use the firebase provider for Apple login, we need to do some conf - Create a Service ID - this is the service that would provide sign in with apple. `Developer -> Certificates, Identifiers & Profiles -> Identifiers -> Create new -> Services IDs` (note: to get into services there is a filter on Identifiers screen on right top where you can switch from identifiers to services.) - Description will be visible to our users - e.g. Template - - Identifier - can be app/bundle ID `com.strv.flutter.template.develop` or simply `flutter-template-develop` + - Identifier - can be app/bundle ID `cz.helu.flutter.template.develop` or simply `flutter-template-develop` - Enable Sign in with Apple for the service - Domains is url of your page e.g. `template.com` - Return URL is crucial part - this will be taken from Firebase: Authentication -> Sign-in method -> Apple -> callback URL diff --git a/android/app/build.gradle b/android/app/build.gradle index e2107ef..287c744 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -41,7 +41,7 @@ android { compileSdkVersion compileAndroidSdkVersion ndkVersion flutter.ndkVersion flavorDimensions "env" - namespace "com.strv.flutter.template" + namespace "cz.helu.flutter.template" compileOptions { coreLibraryDesugaringEnabled true diff --git a/android/app/google-services.json b/android/app/google-services.json index 47872d5..d634e0e 100644 --- a/android/app/google-services.json +++ b/android/app/google-services.json @@ -1,15 +1,15 @@ { "project_info": { "project_number": "907121519909", - "project_id": "strv-flutter-template", - "storage_bucket": "strv-flutter-template.appspot.com" + "project_id": "helu-flutter-template", + "storage_bucket": "helu-flutter-template.appspot.com" }, "client": [ { "client_info": { "mobilesdk_app_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "android_client_info": { - "package_name": "com.strv.flutter.template" + "package_name": "cz.helu.flutter.template" } }, "oauth_client": [ @@ -17,7 +17,7 @@ "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "client_type": 1, "android_info": { - "package_name": "com.strv.flutter.template", + "package_name": "cz.helu.flutter.template", "certificate_hash": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" } }, @@ -42,7 +42,7 @@ "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "client_type": 2, "ios_info": { - "bundle_id": "com.strv.flutter.template.staging" + "bundle_id": "cz.helu.flutter.template.staging" } } ] @@ -53,7 +53,7 @@ "client_info": { "mobilesdk_app_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "android_client_info": { - "package_name": "com.strv.flutter.template.debug" + "package_name": "cz.helu.flutter.template.debug" } }, "oauth_client": [ @@ -61,7 +61,7 @@ "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "client_type": 1, "android_info": { - "package_name": "com.strv.flutter.template.debug", + "package_name": "cz.helu.flutter.template.debug", "certificate_hash": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" } }, @@ -86,7 +86,7 @@ "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "client_type": 2, "ios_info": { - "bundle_id": "com.strv.flutter.template.staging" + "bundle_id": "cz.helu.flutter.template.staging" } } ] @@ -97,7 +97,7 @@ "client_info": { "mobilesdk_app_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "android_client_info": { - "package_name": "com.strv.flutter.template.develop" + "package_name": "cz.helu.flutter.template.develop" } }, "oauth_client": [ @@ -105,7 +105,7 @@ "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "client_type": 1, "android_info": { - "package_name": "com.strv.flutter.template.develop", + "package_name": "cz.helu.flutter.template.develop", "certificate_hash": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" } }, @@ -130,7 +130,7 @@ "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "client_type": 2, "ios_info": { - "bundle_id": "com.strv.flutter.template.staging" + "bundle_id": "cz.helu.flutter.template.staging" } } ] @@ -141,7 +141,7 @@ "client_info": { "mobilesdk_app_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "android_client_info": { - "package_name": "com.strv.flutter.template.develop.debug" + "package_name": "cz.helu.flutter.template.develop.debug" } }, "oauth_client": [ @@ -149,7 +149,7 @@ "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "client_type": 1, "android_info": { - "package_name": "com.strv.flutter.template.develop.debug", + "package_name": "cz.helu.flutter.template.develop.debug", "certificate_hash": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" } }, @@ -174,7 +174,7 @@ "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "client_type": 2, "ios_info": { - "bundle_id": "com.strv.flutter.template.staging" + "bundle_id": "cz.helu.flutter.template.staging" } } ] @@ -185,7 +185,7 @@ "client_info": { "mobilesdk_app_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "android_client_info": { - "package_name": "com.strv.flutter.template.staging" + "package_name": "cz.helu.flutter.template.staging" } }, "oauth_client": [ @@ -193,7 +193,7 @@ "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "client_type": 1, "android_info": { - "package_name": "com.strv.flutter.template.staging", + "package_name": "cz.helu.flutter.template.staging", "certificate_hash": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" } }, @@ -218,7 +218,7 @@ "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "client_type": 2, "ios_info": { - "bundle_id": "com.strv.flutter.template.staging" + "bundle_id": "cz.helu.flutter.template.staging" } } ] @@ -229,7 +229,7 @@ "client_info": { "mobilesdk_app_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "android_client_info": { - "package_name": "com.strv.flutter.template.staging.debug" + "package_name": "cz.helu.flutter.template.staging.debug" } }, "oauth_client": [ @@ -237,7 +237,7 @@ "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "client_type": 1, "android_info": { - "package_name": "com.strv.flutter.template.staging.debug", + "package_name": "cz.helu.flutter.template.staging.debug", "certificate_hash": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" } }, @@ -262,7 +262,7 @@ "client_id": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "client_type": 2, "ios_info": { - "bundle_id": "com.strv.flutter.template.staging" + "bundle_id": "cz.helu.flutter.template.staging" } } ] diff --git a/android/app/src/androidTest/java/com/strv/flutter/template/MainActivityTest.java b/android/app/src/androidTest/java/cz/helu/flutter/template/MainActivityTest.java similarity index 96% rename from android/app/src/androidTest/java/com/strv/flutter/template/MainActivityTest.java rename to android/app/src/androidTest/java/cz/helu/flutter/template/MainActivityTest.java index db321bf..2eb6d7d 100644 --- a/android/app/src/androidTest/java/com/strv/flutter/template/MainActivityTest.java +++ b/android/app/src/androidTest/java/cz/helu/flutter/template/MainActivityTest.java @@ -1,4 +1,4 @@ -package com.strv.flutter.template; +package cz.helu.flutter.template; import androidx.test.platform.app.InstrumentationRegistry; import org.junit.Test; diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index f242817..99691e1 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ + package="cz.helu.flutter.template"> diff --git a/android/app/src/main/kotlin/com/strv/flutter/template/MainActivity.kt b/android/app/src/main/kotlin/cz/helu/flutter/template/MainActivity.kt similarity index 76% rename from android/app/src/main/kotlin/com/strv/flutter/template/MainActivity.kt rename to android/app/src/main/kotlin/cz/helu/flutter/template/MainActivity.kt index 1d10aa4..5c93400 100644 --- a/android/app/src/main/kotlin/com/strv/flutter/template/MainActivity.kt +++ b/android/app/src/main/kotlin/cz/helu/flutter/template/MainActivity.kt @@ -1,4 +1,4 @@ -package com.strv.flutter.template +package cz.helu.flutter.template import io.flutter.embedding.android.FlutterFragmentActivity diff --git a/android/extras/keystore/README.md b/android/extras/keystore/README.md index eacce0b..9b6f7a0 100644 --- a/android/extras/keystore/README.md +++ b/android/extras/keystore/README.md @@ -8,7 +8,7 @@ Note: The password for store and key must be the same for `PKCS12` storetype. Here is an example for organizational information that you need to fill during generating of a new key: ``` -CN=Lukas Hermann, OU=STRV, O=STRV, L=Prague, ST=Czechia, C=CZ +CN=Lukas Hermann, OU=HELU, O=HELU, L=Prague, ST=Czechia, C=CZ ``` To get the SHA1 Certificate of the keystore (for example for Firebase purpose) you can use following commands: diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index b1be846..fd1e423 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -442,7 +442,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.strv.flutter.template.develop; + PRODUCT_BUNDLE_IDENTIFIER = cz.helu.flutter.template.develop; PRODUCT_NAME = "[DEV] Template"; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -527,7 +527,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.strv.flutter.template.staging; + PRODUCT_BUNDLE_IDENTIFIER = cz.helu.flutter.template.staging; PRODUCT_NAME = "[STG] Template"; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -610,7 +610,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.strv.flutter.template.staging; + PRODUCT_BUNDLE_IDENTIFIER = cz.helu.flutter.template.staging; PRODUCT_NAME = "[STG] Template"; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -690,7 +690,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.strv.flutter.template.staging; + PRODUCT_BUNDLE_IDENTIFIER = cz.helu.flutter.template.staging; PRODUCT_NAME = "[STG] Template"; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -775,7 +775,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.strv.flutter.template; + PRODUCT_BUNDLE_IDENTIFIER = cz.helu.flutter.template; PRODUCT_NAME = Template; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -858,7 +858,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.strv.flutter.template; + PRODUCT_BUNDLE_IDENTIFIER = cz.helu.flutter.template; PRODUCT_NAME = Template; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -938,7 +938,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.strv.flutter.template; + PRODUCT_BUNDLE_IDENTIFIER = cz.helu.flutter.template; PRODUCT_NAME = Template; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -1075,7 +1075,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.strv.flutter.template.develop; + PRODUCT_BUNDLE_IDENTIFIER = cz.helu.flutter.template.develop; PRODUCT_NAME = "[DEV] Template"; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; @@ -1106,7 +1106,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.strv.flutter.template.develop; + PRODUCT_BUNDLE_IDENTIFIER = cz.helu.flutter.template.develop; PRODUCT_NAME = "[DEV] Template"; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; diff --git a/ios/config/develop/GoogleService-Info.plist b/ios/config/develop/GoogleService-Info.plist index 89e8039..2477627 100644 --- a/ios/config/develop/GoogleService-Info.plist +++ b/ios/config/develop/GoogleService-Info.plist @@ -13,11 +13,11 @@ PLIST_VERSION 1 BUNDLE_ID - com.strv.flutter.template.develop + cz.helu.flutter.template.develop PROJECT_ID - strv-flutter-template + helu-flutter-template STORAGE_BUCKET - strv-flutter-template.appspot.com + helu-flutter-template.appspot.com IS_ADS_ENABLED IS_ANALYTICS_ENABLED diff --git a/ios/config/production/GoogleService-Info.plist b/ios/config/production/GoogleService-Info.plist index 22d7ba0..2b0015f 100644 --- a/ios/config/production/GoogleService-Info.plist +++ b/ios/config/production/GoogleService-Info.plist @@ -13,11 +13,11 @@ PLIST_VERSION 1 BUNDLE_ID - com.strv.flutter.template + cz.helu.flutter.template PROJECT_ID - strv-flutter-template + helu-flutter-template STORAGE_BUCKET - strv-flutter-template.appspot.com + helu-flutter-template.appspot.com IS_ADS_ENABLED IS_ANALYTICS_ENABLED diff --git a/ios/config/staging/GoogleService-Info.plist b/ios/config/staging/GoogleService-Info.plist index 7b8e3e3..aa07344 100644 --- a/ios/config/staging/GoogleService-Info.plist +++ b/ios/config/staging/GoogleService-Info.plist @@ -13,11 +13,11 @@ PLIST_VERSION 1 BUNDLE_ID - com.strv.flutter.template.staging + cz.helu.flutter.template.staging PROJECT_ID - strv-flutter-template + helu-flutter-template STORAGE_BUCKET - strv-flutter-template.appspot.com + helu-flutter-template.appspot.com IS_ADS_ENABLED IS_ANALYTICS_ENABLED diff --git a/lib/app/configuration/develop/config_develop.dart b/lib/app/configuration/develop/config_develop.dart index 3a9e955..4578c01 100644 --- a/lib/app/configuration/develop/config_develop.dart +++ b/lib/app/configuration/develop/config_develop.dart @@ -1,6 +1,6 @@ // TODO(HELU): Fill correct values class ConfigDevelop { - static const apiHostUrl = 'https://strv.com'; + static const apiHostUrl = 'https://helu.cz'; static const vapidKey = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; static const androidClientId = ''; diff --git a/lib/app/configuration/production/config_production.dart b/lib/app/configuration/production/config_production.dart index 9e5ae20..704548e 100644 --- a/lib/app/configuration/production/config_production.dart +++ b/lib/app/configuration/production/config_production.dart @@ -1,6 +1,6 @@ // TODO(HELU): Fill correct values class ConfigProduction { - static const apiHostUrl = 'https://strv.com'; + static const apiHostUrl = 'https://helu.cz'; static const vapidKey = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; static const androidClientId = ''; diff --git a/lib/app/configuration/staging/config_staging.dart b/lib/app/configuration/staging/config_staging.dart index 519c523..14101a9 100644 --- a/lib/app/configuration/staging/config_staging.dart +++ b/lib/app/configuration/staging/config_staging.dart @@ -1,6 +1,6 @@ // TODO(HELU): Fill correct values class ConfigStaging { - static const apiHostUrl = 'https://strv.com'; + static const apiHostUrl = 'https://helu.cz'; static const vapidKey = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'; static const androidClientId = ''; diff --git a/lib/app/setup/setup_app.dart b/lib/app/setup/setup_app.dart index b5c01df..a85b089 100644 --- a/lib/app/setup/setup_app.dart +++ b/lib/app/setup/setup_app.dart @@ -77,9 +77,9 @@ Future _setupFirebase({required Flavor flavor}) async { // TODO(HELU): Replace with actual values. Can be found inside Firebase -> Project Settings, under Web App options: const FirebaseOptions( apiKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', - authDomain: 'strv-flutter-template.firebaseapp.com', - projectId: 'strv-flutter-template', - storageBucket: 'strv-flutter-template.appspot.com', + authDomain: 'helu-flutter-template.firebaseapp.com', + projectId: 'helu-flutter-template', + storageBucket: 'helu-flutter-template.appspot.com', messagingSenderId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', appId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', measurementId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', @@ -154,11 +154,11 @@ Future _setupRASP({required Flavor flavor}) async { final config = TalsecConfig( isProd: flavor == Flavor.production && !kDebugMode, - watcherMail: 'lukas.hermann@strv.com', + watcherMail: 'hermann@helu.cz', /// For Android androidConfig: AndroidConfig( - packageName: 'com.strv.flutter.template', + packageName: 'cz.helu.flutter.template', // signingCertHashes: list of hashes of the certificates of the keys which were used to sign the application. // At least one hash value must be provided. Hashes which are passed here must be encoded in Base64 form // https://github.com/talsec/Free-RASP-Community/wiki/Getting-your-signing-certificate-hash-of-app @@ -170,7 +170,7 @@ Future _setupRASP({required Flavor flavor}) async { /// For iOS iosConfig: IOSConfig( - bundleIds: ['com.strv.flutter.template'], + bundleIds: ['cz.helu.flutter.template'], teamId: 'M8AK35...', ), ); diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt index 53a0737..7bcf5d8 100644 --- a/linux/CMakeLists.txt +++ b/linux/CMakeLists.txt @@ -7,7 +7,7 @@ project(runner LANGUAGES CXX) set(BINARY_NAME "FlutterTemplate") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "com.strv.flutter.template") +set(APPLICATION_ID "cz.helu.flutter.template") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig index e2b7864..703833c 100644 --- a/macos/Runner/Configs/AppInfo.xcconfig +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -8,7 +8,7 @@ PRODUCT_NAME = Flutter Template; // The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.strv.flutter.template; +PRODUCT_BUNDLE_IDENTIFIER = cz.helu.flutter.template; // The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2024 com.strv All rights reserved. +PRODUCT_COPYRIGHT = Copyright © 2024 cz.helu All rights reserved. diff --git a/project_setup/lib/configuration.dart b/project_setup/lib/configuration.dart index d3a4f03..6f5e9f3 100644 --- a/project_setup/lib/configuration.dart +++ b/project_setup/lib/configuration.dart @@ -21,8 +21,8 @@ class Configuration { // 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 renameOldPackageName = 'cz.helu.flutter.template'; + static final renameNewPackageName = 'cz.helu.flutter.template'; static final renameAppNameIncludedFiles = [ 'README.md', 'AndroidManifest.xml', diff --git a/pubspec.yaml b/pubspec.yaml index 6a87713..280aac8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -114,9 +114,9 @@ dev_dependencies: patrol: app_name: flutter_app android: - package_name: com.strv.flutter.template + package_name: cz.helu.flutter.template ios: - bundle_id: com.strv.flutter.template + bundle_id: cz.helu.flutter.template flutter_gen: diff --git a/web/firebase-messaging-sw.js b/web/firebase-messaging-sw.js index 388d825..5e27b9d 100644 --- a/web/firebase-messaging-sw.js +++ b/web/firebase-messaging-sw.js @@ -3,9 +3,9 @@ importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js") firebase.initializeApp({ apiKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', - authDomain: 'strv-flutter-template.firebaseapp.com', - projectId: 'strv-flutter-template', - storageBucket: 'strv-flutter-template.appspot.com', + authDomain: 'helu-flutter-template.firebaseapp.com', + projectId: 'helu-flutter-template', + storageBucket: 'helu-flutter-template.appspot.com', messagingSenderId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', appId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', measurementId: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', diff --git a/windows/runner/Runner.rc b/windows/runner/Runner.rc index 8819026..43583a4 100644 --- a/windows/runner/Runner.rc +++ b/windows/runner/Runner.rc @@ -89,11 +89,11 @@ BEGIN BEGIN BLOCK "040904e4" BEGIN - VALUE "CompanyName", "com.strv" "\0" + VALUE "CompanyName", "cz.helu" "\0" VALUE "FileDescription", "Flutter Template" "\0" VALUE "FileVersion", VERSION_AS_STRING "\0" VALUE "InternalName", "Flutter Template" "\0" - VALUE "LegalCopyright", "Copyright (C) 2024 com.strv. All rights reserved." "\0" + VALUE "LegalCopyright", "Copyright (C) 2024 cz.helu. All rights reserved." "\0" VALUE "OriginalFilename", "template.exe" "\0" VALUE "ProductName", "Flutter Template" "\0" VALUE "ProductVersion", VERSION_AS_STRING "\0" From dfedf5c69b932b7a236cb6a08078c640d0c0afef Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Thu, 22 May 2025 09:10:45 +0200 Subject: [PATCH 5/9] chore: Feedback implementation --- .../android_firebase_app_distribution_production.yml | 2 +- .../android_firebase_app_distribution_staging.yml | 2 +- .../workflows/android_play_store_distribution.yml | 2 +- README.md | 12 ++++++------ analysis_options.yaml | 2 +- lib/app/setup/setup_app.dart | 5 +---- .../composition/dialog/custom_alert_dialog.dart | 2 +- lib/core/riverpod/event_notifier.dart | 2 ++ linux/flutter/CMakeLists.txt | 2 +- makefile | 2 +- project_setup/analysis_options.yaml | 2 +- project_setup/pubspec.yaml | 2 +- web/firebase-messaging-sw.js | 4 ++-- windows/flutter/CMakeLists.txt | 2 +- 14 files changed, 21 insertions(+), 22 deletions(-) diff --git a/.github/workflows/android_firebase_app_distribution_production.yml b/.github/workflows/android_firebase_app_distribution_production.yml index 17bef3d..7442b30 100644 --- a/.github/workflows/android_firebase_app_distribution_production.yml +++ b/.github/workflows/android_firebase_app_distribution_production.yml @@ -51,7 +51,7 @@ jobs: - name: "[Flutter - Upload to Firebase App Distribution]" run: cd android; ./gradlew appDistributionUploadProductionRelease; cd .. - # TODO: Add correct AppId from the Firebase Project + # TODO(HELU): Add correct AppId from the Firebase Project - name: "[Flutter - Upload Crashlytics Mapping Files]" run: firebase crashlytics:symbols:upload --app=XXX build/app/outputs/symbols diff --git a/.github/workflows/android_firebase_app_distribution_staging.yml b/.github/workflows/android_firebase_app_distribution_staging.yml index 72bd1de..bf9fd77 100644 --- a/.github/workflows/android_firebase_app_distribution_staging.yml +++ b/.github/workflows/android_firebase_app_distribution_staging.yml @@ -51,7 +51,7 @@ jobs: - name: "[Flutter - Upload to Firebase App Distribution]" run: cd android; ./gradlew appDistributionUplodaStagingRelease; cd .. - # TODO: Add correct AppId from the Firebase Project + # TODO(HELU): Add correct AppId from the Firebase Project - name: "[Flutter - Upload Crashlytics Mapping Files]" run: firebase crashlytics:symbols:upload --app=XXX build/app/outputs/symbols diff --git a/.github/workflows/android_play_store_distribution.yml b/.github/workflows/android_play_store_distribution.yml index 4a8b081..9545afc 100644 --- a/.github/workflows/android_play_store_distribution.yml +++ b/.github/workflows/android_play_store_distribution.yml @@ -61,7 +61,7 @@ jobs: #status: draft # Upload it as draft release if you want to manually publish the release from GooglePlay console #userFraction: 0.1 # Rollout of the release <0;1> - # TODO: Add correct AppId from the Firebase Project + # TODO(HELU): Add correct AppId from the Firebase Project - name: "[Flutter - Upload Crashlytics Mapping Files]" run: firebase crashlytics:symbols:upload --app=XXX build/app/outputs/symbols diff --git a/README.md b/README.md index b6a0693..2fdbb52 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -![LOGO](docs/flutter_template.jpg) +![Flutter Template Logo](docs/flutter_template.jpg)
-[![Flutter](https://img.shields.io/badge/Flutter-3.32-blue)](#) -[![Dart](https://img.shields.io/badge/Dart-3.6.0-blue)](#) +[![Flutter](https://img.shields.io/badge/Flutter-3.32-blue)](https://docs.flutter.dev/release/whats-new#flutter-332) +[![Dart](https://img.shields.io/badge/Dart-3.8.0-blue)](https://dart.dev/guides/whats-new#dart-380) [![License](https://img.shields.io/github/license/HE-LU/flutter-template)](LICENSE)
@@ -193,7 +193,7 @@ You can read about flavors setup in the following tutorials: - 🖥️ [Linux](https://docs.flutter.dev/deployment/linux) - Flavors are not supported yet. In case of using firebase with multiple flavors, you have to: -- for iOS add custom build run script phase and copy appropriate `GoogleService-Info.plist` file. It's already prepared, you just need to uncomment it and add appropriate `plists` to destinations according to the script. +- For iOS, add the custom Build Run Script phase and copy the appropriate `GoogleService-Info.plist` file. The script is already prepared; simply uncomment it and add the necessary `plist` files to their destinations. For Google sign in: - for iOS, you have to copy value from `REVERSED_CLIENT_ID` from `GoogleService-Info.plist` into `GOOGLE_REVERSED_CLIENT_ID` in build settings. @@ -207,7 +207,7 @@ For Google sign in: - As we are using flavors, we have to specify which flavor to build using `--flavor` argument, and also select a correct main file using `-t` argument. - One of the arguments you should use is `--obfuscate`. This makes reverse engineering harder. This has to be used also with `--split-debug-info` which should make the app smaller, and also specify the directory where the mapping file to read obfuscated stack trace is stored. - Optional step is to include precompiled Shaders. To do that you have to first [Precompile Shaders](#precompiling-shaders) and add `--bundle-sksl-path` argument. -- In case we would like to build a Debuggable version of the app, we have to add `--debug` argument. +- To build a debug version of the app, include the `--debug` flag. Here is an example of assembling an Android app bundle using all the commands: ``` @@ -357,7 +357,7 @@ This way we are ensuring that at least the Android version is buildable. We are ## Theming Theming is done inside `lib/core/themes/app_theme.dart`. Currently, we suggest using Material 3. The main setup is done in file `app_theme.dart`. -The main idea right now is to overwrite the whole `colorScheme` with an "undefined" pinkish color and to not use the default theme colorScheme anywhere in the app. Every widget like AppBar, ElevatedButton, TextField, etc. should have a custom implementation starting with the word `Custom` so it is easily recognizable. This Widget then should wrap the appropriate widget, and should utilize colors from `context.colorScheme`, which has an extension method, as is returning our own implementation of `CustomColorScheme`. All colors should be declared there. +The current approach is to completely override the default `colorScheme` with a custom "undefined" pinkish palette and avoid using the default theme anywhere in the app. Every widget—such as AppBar, ElevatedButton, and TextField—should have a custom implementation prefixed with `Custom` for easy identification. Each `Custom` widget should wrap the corresponding standard widget and use colors from `context.colorScheme` and text styles from `context.textTheme`. These context extensions return our custom implementations: `CustomColorScheme` and `CustomTextTheme`, where all colors and text styles are defined. The most tricky part of theming is to make sure that the app supports Edge-to-Edge, and that it has correct navigation and StatusBar colors set. To make this possible, we had to implement our own `CustomSystemBarsTheme` class. To use it properly, make sure you are calling `setupSystemBarsTheme` during the App startup. In case of the need of overriding the Brightness for a specific screen, there are two approaches. First, the simple one is to just use `CustomAppBar`, and set brightness to it. Second, wrap the whole Scaffold inside `CustomSystemBarsThemeWidget` and set Brightness to it. diff --git a/analysis_options.yaml b/analysis_options.yaml index c89c7e8..e657abc 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -10,7 +10,7 @@ include: package:netglade_analysis/lints.yaml analyzer: - exclude: + exclude: - 'project_setup/**' - '**.g.dart' - '**.gen.dart' diff --git a/lib/app/setup/setup_app.dart b/lib/app/setup/setup_app.dart index a85b089..3acc9fa 100644 --- a/lib/app/setup/setup_app.dart +++ b/lib/app/setup/setup_app.dart @@ -114,10 +114,7 @@ Future _setupFirebaseCrashlytics() async { final dynamic error = pair[0]; final stack = pair[1] as StackTrace; - await CrashlyticsManager.logCritical( - error, - stack: stack, - ); + await CrashlyticsManager.logCritical(error, stack: stack); } else { // Optionally log or handle unexpected message format Flogger.e('Unexpected error message format from isolate: $pair'); diff --git a/lib/common/composition/dialog/custom_alert_dialog.dart b/lib/common/composition/dialog/custom_alert_dialog.dart index 1db9641..e55f310 100644 --- a/lib/common/composition/dialog/custom_alert_dialog.dart +++ b/lib/common/composition/dialog/custom_alert_dialog.dart @@ -24,7 +24,7 @@ class CustomAlertDialog { final String? negativeActionTitle; final VoidCallback? negativeAction; - Future show() async { + Future show() { return showDialog( context: context, builder: (builderContext) => CustomDialogWrapper.alert( diff --git a/lib/core/riverpod/event_notifier.dart b/lib/core/riverpod/event_notifier.dart index 173a96f..f6bc474 100644 --- a/lib/core/riverpod/event_notifier.dart +++ b/lib/core/riverpod/event_notifier.dart @@ -2,6 +2,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; /// Notifier which is used only to listen. class EventNotifier extends StateNotifier { + /// Creates an [EventNotifier] with the given [state]. // ignore: use_super_parameters EventNotifier(T state) : super(state); @@ -10,6 +11,7 @@ class EventNotifier extends StateNotifier { return true; } + /// Updates the state with the provided event. // ignore: use_setters_to_change_properties void send(T event) { state = event; diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt index d5bd016..8cbca41 100644 --- a/linux/flutter/CMakeLists.txt +++ b/linux/flutter/CMakeLists.txt @@ -6,7 +6,7 @@ set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") # Configuration provided via flutter tool. include(${EPHEMERAL_DIR}/generated_config.cmake) -# TODO: Move the rest of this into files in ephemeral. See +# TODO(HELU): Move the rest of this into files in ephemeral. See # https://github.com/flutter/flutter/issues/57146. # Serves the same purpose as list(TRANSFORM ... PREPEND ...), diff --git a/makefile b/makefile index b9fe4b5..8312084 100644 --- a/makefile +++ b/makefile @@ -57,7 +57,7 @@ generateWebProduction: # Clean everything and build Web # # To get the project ID, run the following command: # firebase projects:list -# TODO: Update project name or remove in case there is no web or when web is not hosted on Firebase. +# TODO(HELU): Update project name or remove in case there is no web or when web is not hosted on Firebase. deployWeb: @firebase deploy --project XXX diff --git a/project_setup/analysis_options.yaml b/project_setup/analysis_options.yaml index c89c7e8..e657abc 100644 --- a/project_setup/analysis_options.yaml +++ b/project_setup/analysis_options.yaml @@ -10,7 +10,7 @@ include: package:netglade_analysis/lints.yaml analyzer: - exclude: + exclude: - 'project_setup/**' - '**.g.dart' - '**.gen.dart' diff --git a/project_setup/pubspec.yaml b/project_setup/pubspec.yaml index b2c162b..3ab661d 100644 --- a/project_setup/pubspec.yaml +++ b/project_setup/pubspec.yaml @@ -13,4 +13,4 @@ dependencies: dev_dependencies: - netglade_analysis: 16.1.0 \ No newline at end of file + netglade_analysis: 16.1.0 diff --git a/web/firebase-messaging-sw.js b/web/firebase-messaging-sw.js index 5e27b9d..37c3479 100644 --- a/web/firebase-messaging-sw.js +++ b/web/firebase-messaging-sw.js @@ -1,5 +1,5 @@ -importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-app.js"); -importScripts("https://www.gstatic.com/firebasejs/8.10.0/firebase-messaging.js"); +importScripts("https://www.gstatic.com/firebasejs/11.7.0/firebase-app.js"); +importScripts("https://www.gstatic.com/firebasejs/11.7.0/firebase-messaging.js"); firebase.initializeApp({ apiKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX', diff --git a/windows/flutter/CMakeLists.txt b/windows/flutter/CMakeLists.txt index 930d207..06dc589 100644 --- a/windows/flutter/CMakeLists.txt +++ b/windows/flutter/CMakeLists.txt @@ -6,7 +6,7 @@ set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") # Configuration provided via flutter tool. include(${EPHEMERAL_DIR}/generated_config.cmake) -# TODO: Move the rest of this into files in ephemeral. See +# TODO(HELU): Move the rest of this into files in ephemeral. See # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") From b8d7cd98528de69e41f3e9fd3876281fa6379b97 Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Thu, 22 May 2025 10:46:56 +0200 Subject: [PATCH 6/9] fix: iOS build --- flutter.code-workspace | 1 - ios/Podfile.lock | 229 +++++++++--------- .../xcshareddata/xcschemes/develop.xcscheme | 2 + .../xcschemes/production.xcscheme | 2 + .../xcshareddata/xcschemes/staging.xcscheme | 2 + macos/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 3 +- macos/Runner/AppDelegate.swift | 4 + 8 files changed, 131 insertions(+), 114 deletions(-) diff --git a/flutter.code-workspace b/flutter.code-workspace index 3ec02ef..1270637 100644 --- a/flutter.code-workspace +++ b/flutter.code-workspace @@ -18,7 +18,6 @@ "Dart-Code.dart-code", "Dart-Code.flutter", "SimonSiefke.svg-preview", - "Nash.awesome-flutter-snippets", "wayou.vscode-todo-highlight", "hzgood.dart-data-class-generator", "pkief.material-icon-theme", diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 88e33ad..548a3b1 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -7,99 +7,103 @@ PODS: - AppAuth/Core (1.7.6) - AppAuth/ExternalUserAgent (1.7.6): - AppAuth/Core + - AppCheckCore (11.2.0): + - GoogleUtilities/Environment (~> 8.0) + - GoogleUtilities/UserDefaults (~> 8.0) + - PromisesObjC (~> 2.4) - CocoaAsyncSocket (7.6.5) - device_info_plus (0.0.1): - Flutter - - Firebase/Analytics (11.7.0): + - Firebase/Analytics (11.10.0): - Firebase/Core - - Firebase/Auth (11.7.0): + - Firebase/Auth (11.10.0): - Firebase/CoreOnly - - FirebaseAuth (~> 11.7.0) - - Firebase/Core (11.7.0): + - FirebaseAuth (~> 11.10.0) + - Firebase/Core (11.10.0): - Firebase/CoreOnly - - FirebaseAnalytics (~> 11.7.0) - - Firebase/CoreOnly (11.7.0): - - FirebaseCore (~> 11.7.0) - - Firebase/Crashlytics (11.7.0): + - FirebaseAnalytics (~> 11.10.0) + - Firebase/CoreOnly (11.10.0): + - FirebaseCore (~> 11.10.0) + - Firebase/Crashlytics (11.10.0): - Firebase/CoreOnly - - FirebaseCrashlytics (~> 11.7.0) - - Firebase/DynamicLinks (11.7.0): + - FirebaseCrashlytics (~> 11.10.0) + - Firebase/DynamicLinks (11.10.0): - Firebase/CoreOnly - - FirebaseDynamicLinks (~> 11.7.0) - - Firebase/Messaging (11.7.0): + - FirebaseDynamicLinks (~> 11.10.0) + - Firebase/Messaging (11.10.0): - Firebase/CoreOnly - - FirebaseMessaging (~> 11.7.0) - - Firebase/RemoteConfig (11.7.0): + - FirebaseMessaging (~> 11.10.0) + - Firebase/RemoteConfig (11.10.0): - Firebase/CoreOnly - - FirebaseRemoteConfig (~> 11.7.0) - - firebase_analytics (11.4.2): - - Firebase/Analytics (= 11.7.0) + - FirebaseRemoteConfig (~> 11.10.0) + - firebase_analytics (11.4.6): + - Firebase/Analytics (= 11.10.0) - firebase_core - Flutter - - firebase_auth (5.4.2): - - Firebase/Auth (= 11.7.0) + - firebase_auth (5.5.4): + - Firebase/Auth (= 11.10.0) - firebase_core - Flutter - - firebase_core (3.11.0): - - Firebase/CoreOnly (= 11.7.0) + - firebase_core (3.13.1): + - Firebase/CoreOnly (= 11.10.0) - Flutter - - firebase_crashlytics (4.3.2): - - Firebase/Crashlytics (= 11.7.0) + - firebase_crashlytics (4.3.6): + - Firebase/Crashlytics (= 11.10.0) - firebase_core - Flutter - - firebase_dynamic_links (6.1.2): - - Firebase/DynamicLinks (= 11.7.0) + - firebase_dynamic_links (6.1.6): + - Firebase/DynamicLinks (= 11.10.0) - firebase_core - Flutter - - firebase_messaging (15.2.2): - - Firebase/Messaging (= 11.7.0) + - firebase_messaging (15.2.6): + - Firebase/Messaging (= 11.10.0) - firebase_core - Flutter - - firebase_remote_config (5.4.0): - - Firebase/RemoteConfig (= 11.7.0) + - firebase_remote_config (5.4.4): + - Firebase/RemoteConfig (= 11.10.0) - firebase_core - Flutter - - FirebaseABTesting (11.7.0): - - FirebaseCore (~> 11.7.0) - - FirebaseAnalytics (11.7.0): - - FirebaseAnalytics/AdIdSupport (= 11.7.0) - - FirebaseCore (~> 11.7.0) + - FirebaseABTesting (11.10.0): + - FirebaseCore (~> 11.10.0) + - FirebaseAnalytics (11.10.0): + - FirebaseAnalytics/AdIdSupport (= 11.10.0) + - FirebaseCore (~> 11.10.0) - FirebaseInstallations (~> 11.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.0) - GoogleUtilities/MethodSwizzler (~> 8.0) - GoogleUtilities/Network (~> 8.0) - "GoogleUtilities/NSData+zlib (~> 8.0)" - nanopb (~> 3.30910.0) - - FirebaseAnalytics/AdIdSupport (11.7.0): - - FirebaseCore (~> 11.7.0) + - FirebaseAnalytics/AdIdSupport (11.10.0): + - FirebaseCore (~> 11.10.0) - FirebaseInstallations (~> 11.0) - - GoogleAppMeasurement (= 11.7.0) + - GoogleAppMeasurement (= 11.10.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.0) - GoogleUtilities/MethodSwizzler (~> 8.0) - GoogleUtilities/Network (~> 8.0) - "GoogleUtilities/NSData+zlib (~> 8.0)" - nanopb (~> 3.30910.0) - - FirebaseAppCheckInterop (11.9.0) - - FirebaseAuth (11.7.0): + - FirebaseAppCheckInterop (11.13.0) + - FirebaseAuth (11.10.0): - FirebaseAppCheckInterop (~> 11.0) - FirebaseAuthInterop (~> 11.0) - - FirebaseCore (~> 11.7.0) - - FirebaseCoreExtension (~> 11.7.0) + - FirebaseCore (~> 11.10.0) + - FirebaseCoreExtension (~> 11.10.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.0) - GoogleUtilities/Environment (~> 8.0) - GTMSessionFetcher/Core (< 5.0, >= 3.4) - - RecaptchaInterop (~> 100.0) - - FirebaseAuthInterop (11.9.0) - - FirebaseCore (11.7.0): - - FirebaseCoreInternal (~> 11.7.0) + - RecaptchaInterop (~> 101.0) + - FirebaseAuthInterop (11.13.0) + - FirebaseCore (11.10.0): + - FirebaseCoreInternal (~> 11.10.0) - GoogleUtilities/Environment (~> 8.0) - GoogleUtilities/Logger (~> 8.0) - - FirebaseCoreExtension (11.7.0): - - FirebaseCore (~> 11.7.0) - - FirebaseCoreInternal (11.7.0): + - FirebaseCoreExtension (11.10.0): + - FirebaseCore (~> 11.10.0) + - FirebaseCoreInternal (11.10.0): - "GoogleUtilities/NSData+zlib (~> 8.0)" - - FirebaseCrashlytics (11.7.0): - - FirebaseCore (~> 11.7.0) + - FirebaseCrashlytics (11.10.0): + - FirebaseCore (~> 11.10.0) - FirebaseInstallations (~> 11.0) - FirebaseRemoteConfigInterop (~> 11.0) - FirebaseSessions (~> 11.0) @@ -107,15 +111,15 @@ PODS: - GoogleUtilities/Environment (~> 8.0) - nanopb (~> 3.30910.0) - PromisesObjC (~> 2.4) - - FirebaseDynamicLinks (11.7.0): - - FirebaseCore (~> 11.7.0) - - FirebaseInstallations (11.7.0): - - FirebaseCore (~> 11.7.0) + - FirebaseDynamicLinks (11.10.0): + - FirebaseCore (~> 11.10.0) + - FirebaseInstallations (11.10.0): + - FirebaseCore (~> 11.10.0) - GoogleUtilities/Environment (~> 8.0) - GoogleUtilities/UserDefaults (~> 8.0) - PromisesObjC (~> 2.4) - - FirebaseMessaging (11.7.0): - - FirebaseCore (~> 11.7.0) + - FirebaseMessaging (11.10.0): + - FirebaseCore (~> 11.10.0) - FirebaseInstallations (~> 11.0) - GoogleDataTransport (~> 10.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.0) @@ -123,25 +127,25 @@ PODS: - GoogleUtilities/Reachability (~> 8.0) - GoogleUtilities/UserDefaults (~> 8.0) - nanopb (~> 3.30910.0) - - FirebaseRemoteConfig (11.7.0): + - FirebaseRemoteConfig (11.10.0): - FirebaseABTesting (~> 11.0) - - FirebaseCore (~> 11.7.0) + - FirebaseCore (~> 11.10.0) - FirebaseInstallations (~> 11.0) - FirebaseRemoteConfigInterop (~> 11.0) - FirebaseSharedSwift (~> 11.0) - GoogleUtilities/Environment (~> 8.0) - "GoogleUtilities/NSData+zlib (~> 8.0)" - - FirebaseRemoteConfigInterop (11.9.0) - - FirebaseSessions (11.7.0): - - FirebaseCore (~> 11.7.0) - - FirebaseCoreExtension (~> 11.7.0) + - FirebaseRemoteConfigInterop (11.13.0) + - FirebaseSessions (11.10.0): + - FirebaseCore (~> 11.10.0) + - FirebaseCoreExtension (~> 11.10.0) - FirebaseInstallations (~> 11.0) - GoogleDataTransport (~> 10.0) - GoogleUtilities/Environment (~> 8.0) - GoogleUtilities/UserDefaults (~> 8.0) - nanopb (~> 3.30910.0) - PromisesSwift (~> 2.1) - - FirebaseSharedSwift (11.9.0) + - FirebaseSharedSwift (11.13.0) - Flutter (1.0.0) - flutter_local_notifications (0.0.1): - Flutter @@ -153,23 +157,23 @@ PODS: - AppAuth (>= 1.7.4) - Flutter - FlutterMacOS - - GoogleSignIn (~> 7.1) + - GoogleSignIn (~> 8.0) - GTMSessionFetcher (>= 3.4.0) - - GoogleAppMeasurement (11.7.0): - - GoogleAppMeasurement/AdIdSupport (= 11.7.0) + - GoogleAppMeasurement (11.10.0): + - GoogleAppMeasurement/AdIdSupport (= 11.10.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.0) - GoogleUtilities/MethodSwizzler (~> 8.0) - GoogleUtilities/Network (~> 8.0) - "GoogleUtilities/NSData+zlib (~> 8.0)" - nanopb (~> 3.30910.0) - - GoogleAppMeasurement/AdIdSupport (11.7.0): - - GoogleAppMeasurement/WithoutAdIdSupport (= 11.7.0) + - GoogleAppMeasurement/AdIdSupport (11.10.0): + - GoogleAppMeasurement/WithoutAdIdSupport (= 11.10.0) - GoogleUtilities/AppDelegateSwizzler (~> 8.0) - GoogleUtilities/MethodSwizzler (~> 8.0) - GoogleUtilities/Network (~> 8.0) - "GoogleUtilities/NSData+zlib (~> 8.0)" - nanopb (~> 3.30910.0) - - GoogleAppMeasurement/WithoutAdIdSupport (11.7.0): + - GoogleAppMeasurement/WithoutAdIdSupport (11.10.0): - GoogleUtilities/AppDelegateSwizzler (~> 8.0) - GoogleUtilities/MethodSwizzler (~> 8.0) - GoogleUtilities/Network (~> 8.0) @@ -178,35 +182,36 @@ PODS: - GoogleDataTransport (10.1.0): - nanopb (~> 3.30910.0) - PromisesObjC (~> 2.4) - - GoogleSignIn (7.1.0): + - GoogleSignIn (8.0.0): - AppAuth (< 2.0, >= 1.7.3) + - AppCheckCore (~> 11.0) - GTMAppAuth (< 5.0, >= 4.1.1) - GTMSessionFetcher/Core (~> 3.3) - - GoogleUtilities/AppDelegateSwizzler (8.0.2): + - GoogleUtilities/AppDelegateSwizzler (8.1.0): - GoogleUtilities/Environment - GoogleUtilities/Logger - GoogleUtilities/Network - GoogleUtilities/Privacy - - GoogleUtilities/Environment (8.0.2): + - GoogleUtilities/Environment (8.1.0): - GoogleUtilities/Privacy - - GoogleUtilities/Logger (8.0.2): + - GoogleUtilities/Logger (8.1.0): - GoogleUtilities/Environment - GoogleUtilities/Privacy - - GoogleUtilities/MethodSwizzler (8.0.2): + - GoogleUtilities/MethodSwizzler (8.1.0): - GoogleUtilities/Logger - GoogleUtilities/Privacy - - GoogleUtilities/Network (8.0.2): + - GoogleUtilities/Network (8.1.0): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" - GoogleUtilities/Privacy - GoogleUtilities/Reachability - - "GoogleUtilities/NSData+zlib (8.0.2)": + - "GoogleUtilities/NSData+zlib (8.1.0)": - GoogleUtilities/Privacy - - GoogleUtilities/Privacy (8.0.2) - - GoogleUtilities/Reachability (8.0.2): + - GoogleUtilities/Privacy (8.1.0) + - GoogleUtilities/Reachability (8.1.0): - GoogleUtilities/Logger - GoogleUtilities/Privacy - - GoogleUtilities/UserDefaults (8.0.2): + - GoogleUtilities/UserDefaults (8.1.0): - GoogleUtilities/Logger - GoogleUtilities/Privacy - GTMAppAuth (4.1.1): @@ -234,7 +239,7 @@ PODS: - PromisesObjC (2.4.0) - PromisesSwift (2.4.0): - PromisesObjC (= 2.4.0) - - RecaptchaInterop (100.0.0) + - RecaptchaInterop (101.0.0) - share_plus (0.0.1): - Flutter - shared_preferences_foundation (0.0.1): @@ -271,6 +276,7 @@ DEPENDENCIES: SPEC REPOS: trunk: - AppAuth + - AppCheckCore - CocoaAsyncSocket - Firebase - FirebaseABTesting @@ -347,50 +353,51 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: app_settings: 58017cd26b604ae98c3e65acbdd8ba173703cc82 AppAuth: d4f13a8fe0baf391b2108511793e4b479691fb73 + AppCheckCore: cc8fd0a3a230ddd401f326489c99990b013f0c4f CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 device_info_plus: bf2e3232933866d73fe290f2942f2156cdd10342 - Firebase: a64bf6a8546e6eab54f1c715cd6151f39d2329f4 - firebase_analytics: 7236e6115c1b4e62c2270faa29c052a317e31107 - firebase_auth: bcc25c2992fb3e655b59faca6f0f5bc9215d463d - firebase_core: aa979ae726f00b3ef4ccf59dfb96170af84efbd4 - firebase_crashlytics: f2a7d93add38e165a0101a7be761703ed7292aad - firebase_dynamic_links: de96307a996b1c92b3b715f683ed5e320ad3c033 - firebase_messaging: 3af84b6a90aeac4d7a67fbf4c43a91e7083bea1f - firebase_remote_config: 91ee99e3404c50ce0e10afb2acdfb4142f701f70 - FirebaseABTesting: 08b3e19b28504632a9cd03e7a796b355c5d39b27 - FirebaseAnalytics: bc9e565af9044ba8d6c6e4157e4edca8e5fdf7ec - FirebaseAppCheckInterop: 9226f7217b43e99dfa0bc9f674ad8108cef89feb - FirebaseAuth: 77e25aa24f3e1c626c5babd3338551fc1669ee0e - FirebaseAuthInterop: 2a26ee1bea6d47df8048683cfa071e7da657798f - FirebaseCore: 3227e35f4197a924206fbcdc0349325baf4f5de4 - FirebaseCoreExtension: 206c1b399f0d103055207c16f299b28e3dbd1949 - FirebaseCoreInternal: d6c17dafc8dc33614733a8b52df78fcb4394c881 - FirebaseCrashlytics: 785a73b624715bbc09a40bb56cdc3829a801cc98 - FirebaseDynamicLinks: e81e03f6076bd02081ae6e06631797e134380a76 - FirebaseInstallations: 9347e719c3d52d8d7b9074b2c32407dd027305e9 - FirebaseMessaging: 00ece041b71ddb52a2862ffdee73fb6e9824bd0c - FirebaseRemoteConfig: aa1d4cb05ef4caad203448dfc87842de12f1ea8d - FirebaseRemoteConfigInterop: 710954a00e956c5fe5144a8e46164f0361389203 - FirebaseSessions: 32ed7a9387ae71efe3a35a7f20f3a7292950957b - FirebaseSharedSwift: 574e6a5602afe4397a55c8d4f767382d620285de + Firebase: 1fe1c0a7d9aaea32efe01fbea5f0ebd8d70e53a2 + firebase_analytics: 943aa4699bf6afe056a96991ba8ee34776323d4c + firebase_auth: 519455ee22dea9cb8b4e05d7821f93f77db88882 + firebase_core: 3c2f323cae65c97a636a05a23b17730ef93df2cf + firebase_crashlytics: 6f5f71ba7ec7b6a0412c4ed076272643c434f69f + firebase_dynamic_links: 9ac84226585cfd0e723a13d5547122155fed99c4 + firebase_messaging: 456e01ff29a451c90097d0b45925551d5be0c143 + firebase_remote_config: 6dc3e79fe219c24f1393faf91463559bf3784fd4 + FirebaseABTesting: dfc10eb6cc08fe3b391ac9e5aa40396d43ea6675 + FirebaseAnalytics: 4e42333f02cf78ed93703a5c36f36dd518aebdef + FirebaseAppCheckInterop: 72066489c209823649a997132bcd9269bc33a4bb + FirebaseAuth: c4146bdfdc87329f9962babd24dae89373f49a32 + FirebaseAuthInterop: 4fa327ec3c551a80a6929561f83af80b1dd44937 + FirebaseCore: 8344daef5e2661eb004b177488d6f9f0f24251b7 + FirebaseCoreExtension: 6f357679327f3614e995dc7cf3f2d600bdc774ac + FirebaseCoreInternal: ef4505d2afb1d0ebbc33162cb3795382904b5679 + FirebaseCrashlytics: 84b073c997235740e6a951b7ee49608932877e5c + FirebaseDynamicLinks: a76f75ddb0612301c34b3f598100449b41bc2669 + FirebaseInstallations: 9980995bdd06ec8081dfb6ab364162bdd64245c3 + FirebaseMessaging: 2b9f56aa4ed286e1f0ce2ee1d413aabb8f9f5cb9 + FirebaseRemoteConfig: 10695bc0ce3b103e3706a5578c43f2a9f69d5aaa + FirebaseRemoteConfigInterop: 7915cec47731a806cda541f90898ad0fab8f9f86 + FirebaseSessions: 9b3b30947b97a15370e0902ee7a90f50ef60ead6 + FirebaseSharedSwift: aca73668bc95e8efccb618e0167eab05d19d3a75 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9 + flutter_local_notifications: ff50f8405aaa0ccdc7dcfb9022ca192e8ad9688f flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29 freerasp: bb827d80b926abcfb8f4ca4ff4557c2fe4a5ae21 - google_sign_in_ios: 4111e87aa5e24a4404f00ea13479f35e571969cc - GoogleAppMeasurement: 0471a5b5bff51f3a91b1e76df22c952d04c63967 + google_sign_in_ios: 7411fab6948df90490dc4620ecbcabdc3ca04017 + GoogleAppMeasurement: 36684bfb3ee034e2b42b4321eb19da3a1b81e65d GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 - GoogleSignIn: d4281ab6cf21542b1cfaff85c191f230b399d2db - GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d + GoogleSignIn: ce8c89bb9b37fb624b92e7514cc67335d1e277e4 + GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 GTMAppAuth: f69bd07d68cd3b766125f7e072c45d7340dea0de GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 package_info_plus: c0502532a26c7662a62a356cebe2692ec5fe4ec4 path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 - patrol: 0564cee315ff6c86fb802b3647db05cc2d3d0624 + patrol: cf2cd48c7f3e5171610111994f7b466cd76d1f57 PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 - RecaptchaInterop: 7d1a4a01a6b2cb1610a47ef3f85f0c411434cb21 + RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 sign_in_with_apple: f3bf75217ea4c2c8b91823f225d70230119b8440 diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/develop.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/develop.xcscheme index 42d5fa8..f1dbece 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/develop.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/develop.xcscheme @@ -26,6 +26,7 @@ buildConfiguration = "Debug-develop" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" shouldUseLaunchSchemeArgsEnv = "YES"> @@ -34,6 +35,7 @@ buildConfiguration = "Debug-develop" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/production.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/production.xcscheme index 510487c..8b61820 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/production.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/production.xcscheme @@ -26,6 +26,7 @@ buildConfiguration = "Debug-production" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" shouldUseLaunchSchemeArgsEnv = "YES"> @@ -34,6 +35,7 @@ buildConfiguration = "Debug-production" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/staging.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/staging.xcscheme index 1b9a10c..bee9437 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/staging.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/staging.xcscheme @@ -26,6 +26,7 @@ buildConfiguration = "Debug-staging" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" shouldUseLaunchSchemeArgsEnv = "YES"> @@ -34,6 +35,7 @@ buildConfiguration = "Debug-staging" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index b8539f5..8874b9e 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -182,7 +182,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 58a36ce..b4bb6f4 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift index 8e02df2..b3c1761 100644 --- a/macos/Runner/AppDelegate.swift +++ b/macos/Runner/AppDelegate.swift @@ -6,4 +6,8 @@ class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } From 63f2967a0ef4fb8025d592b5e682c5e15e378a48 Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Thu, 22 May 2025 13:40:03 +0200 Subject: [PATCH 7/9] feat: ValueChange instead of Function --- lib/common/component/custom_radio_button_group.dart | 2 +- lib/common/component/custom_switch.dart | 2 +- lib/common/component/custom_tab_bar.dart | 2 +- .../debug_tools/page/popups/debug_tools_list_dialog.dart | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/common/component/custom_radio_button_group.dart b/lib/common/component/custom_radio_button_group.dart index a18a24a..e1ede2f 100644 --- a/lib/common/component/custom_radio_button_group.dart +++ b/lib/common/component/custom_radio_button_group.dart @@ -13,7 +13,7 @@ class CustomRadioButtonGroup extends StatelessWidget { final Map options; final T? selectedOption; - final void Function(T) onOptionSelected; + final ValueChanged onOptionSelected; @override Widget build(BuildContext context) { diff --git a/lib/common/component/custom_switch.dart b/lib/common/component/custom_switch.dart index cc70e51..4937c40 100644 --- a/lib/common/component/custom_switch.dart +++ b/lib/common/component/custom_switch.dart @@ -14,7 +14,7 @@ class CustomSwitch extends StatelessWidget { final String title; final bool value; - final void Function(bool value) onChanged; + final ValueChanged onChanged; final String? subtitle; final bool dense; diff --git a/lib/common/component/custom_tab_bar.dart b/lib/common/component/custom_tab_bar.dart index 2f5837d..ebcae0b 100644 --- a/lib/common/component/custom_tab_bar.dart +++ b/lib/common/component/custom_tab_bar.dart @@ -11,7 +11,7 @@ class CustomTabBar extends StatelessWidget { final TabController? tabController; final List tabs; - final void Function(int)? onTap; + final ValueChanged? onTap; @override Widget build(BuildContext context) { diff --git a/lib/features/debug_tools/page/popups/debug_tools_list_dialog.dart b/lib/features/debug_tools/page/popups/debug_tools_list_dialog.dart index cdf093f..8108bea 100644 --- a/lib/features/debug_tools/page/popups/debug_tools_list_dialog.dart +++ b/lib/features/debug_tools/page/popups/debug_tools_list_dialog.dart @@ -16,7 +16,7 @@ class DebugToolsListDialog { final BuildContext context; final String title; final List list; - final void Function(String listItem) onItemSelect; + final ValueChanged onItemSelect; Future show() async { return showDialog( From d01bac19c93ed8ca145d11577d4fca8f3a2f22b3 Mon Sep 17 00:00:00 2001 From: Lukas Hermann Date: Thu, 22 May 2025 14:00:47 +0200 Subject: [PATCH 8/9] feat: Assets update --- .../developDebug/ic_launcher-playstore.png | Bin 12490 -> 28711 bytes .../res/mipmap-hdpi/ic_launcher.png | Bin 2100 -> 2312 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 5085 -> 6414 bytes .../res/mipmap-mdpi/ic_launcher.png | Bin 1256 -> 1396 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 3492 -> 3858 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 2955 -> 3395 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 6449 -> 8869 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 4462 -> 5551 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 8723 -> 15017 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 5580 -> 7650 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 11362 -> 22090 bytes .../src/developDebug/res/values/colors.xml | 2 +- .../developRelease/ic_launcher-playstore.png | Bin 11800 -> 28039 bytes .../res/mipmap-hdpi/ic_launcher.png | Bin 1954 -> 2169 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 4762 -> 6084 bytes .../res/mipmap-mdpi/ic_launcher.png | Bin 1157 -> 1296 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 3283 -> 3647 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 2769 -> 3206 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 6008 -> 8447 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 4191 -> 5285 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 8136 -> 14423 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 5193 -> 7248 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 10683 -> 21588 bytes .../src/developRelease/res/values/colors.xml | 2 +- .../res/drawable-hdpi/android12splash.png | Bin 10447 -> 20688 bytes .../app/src/main/res/drawable-hdpi/splash.png | Bin 10447 -> 20688 bytes .../res/drawable-mdpi/android12splash.png | Bin 7264 -> 10916 bytes .../app/src/main/res/drawable-mdpi/splash.png | Bin 7264 -> 10916 bytes .../drawable-night-hdpi/android12splash.png | Bin 10447 -> 20688 bytes .../drawable-night-mdpi/android12splash.png | Bin 7264 -> 10916 bytes .../res/drawable-night-v21/background.png | Bin 69 -> 69 bytes .../drawable-night-xhdpi/android12splash.png | Bin 14328 -> 32551 bytes .../drawable-night-xxhdpi/android12splash.png | Bin 22892 -> 66309 bytes .../android12splash.png | Bin 31731 -> 109243 bytes .../main/res/drawable-night/background.png | Bin 69 -> 69 bytes .../src/main/res/drawable-v21/background.png | Bin 69 -> 69 bytes .../res/drawable-xhdpi/android12splash.png | Bin 14328 -> 32551 bytes .../src/main/res/drawable-xhdpi/splash.png | Bin 14328 -> 32551 bytes .../res/drawable-xxhdpi/android12splash.png | Bin 22892 -> 66309 bytes .../src/main/res/drawable-xxhdpi/splash.png | Bin 22892 -> 66309 bytes .../res/drawable-xxxhdpi/android12splash.png | Bin 31731 -> 109243 bytes .../src/main/res/drawable-xxxhdpi/splash.png | Bin 31731 -> 109243 bytes .../app/src/main/res/drawable/background.png | Bin 69 -> 69 bytes .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 544 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 442 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 721 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1031 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1443 -> 0 bytes .../app/src/main/res/values-v31/styles.xml | 2 +- .../productionDebug/ic_launcher-playstore.png | Bin 12815 -> 29048 bytes .../res/mipmap-hdpi/ic_launcher.png | Bin 2225 -> 2438 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 5328 -> 6671 bytes .../res/mipmap-mdpi/ic_launcher.png | Bin 1357 -> 1486 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 3698 -> 4049 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 3137 -> 3585 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 6777 -> 9194 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 4720 -> 5797 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 9114 -> 15415 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 5889 -> 7939 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 11824 -> 22534 bytes .../src/productionDebug/res/values/colors.xml | 2 +- .../ic_launcher-playstore.png | Bin 10255 -> 26582 bytes .../res/mipmap-hdpi/ic_launcher.png | Bin 1534 -> 1780 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 3883 -> 5204 bytes .../res/mipmap-mdpi/ic_launcher.png | Bin 861 -> 1018 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 2675 -> 3026 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 2171 -> 2621 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 4990 -> 7458 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 3327 -> 4442 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 6724 -> 13025 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 4232 -> 6310 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 9220 -> 20075 bytes .../productionRelease/res/values/colors.xml | 2 +- .../stagingDebug/ic_launcher-playstore.png | Bin 12238 -> 28453 bytes .../res/mipmap-hdpi/ic_launcher.png | Bin 2071 -> 2287 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 5011 -> 6344 bytes .../res/mipmap-mdpi/ic_launcher.png | Bin 1241 -> 1371 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 3440 -> 3787 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 2916 -> 3351 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 6355 -> 8770 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 4371 -> 5459 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 8479 -> 14759 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 5520 -> 7581 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 11202 -> 21907 bytes .../src/stagingDebug/res/values/colors.xml | 2 +- .../stagingRelease/ic_launcher-playstore.png | Bin 11572 -> 27798 bytes .../res/mipmap-hdpi/ic_launcher.png | Bin 1925 -> 2145 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 4680 -> 6008 bytes .../res/mipmap-mdpi/ic_launcher.png | Bin 1131 -> 1269 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 3231 -> 3582 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 2716 -> 3147 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 5921 -> 8356 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 4093 -> 5188 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 7882 -> 14164 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 5129 -> 7187 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 10505 -> 21392 bytes .../src/stagingRelease/res/values/colors.xml | 2 +- .../LaunchBackground.imageset/background.png | Bin 69 -> 69 bytes .../darkbackground.png | Bin 69 -> 69 bytes .../LaunchImage.imageset/LaunchImage.png | Bin 7264 -> 10916 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 14328 -> 32551 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 22892 -> 66309 bytes .../Contents.json | 120 +++++----- .../Icon-App-1024x1024@1x.png | Bin 18252 -> 122671 bytes .../Icon-App-20x20@1x.png | Bin 546 -> 0 bytes .../Icon-App-20x20@2x.png | Bin 1269 -> 1436 bytes .../Icon-App-20x20@3x.png | Bin 2039 -> 2364 bytes .../Icon-App-29x29@1x.png | Bin 822 -> 0 bytes .../Icon-App-29x29@2x.png | Bin 1988 -> 2262 bytes .../Icon-App-29x29@3x.png | Bin 2968 -> 3833 bytes .../Icon-App-38x38@2x.png | Bin 0 -> 3128 bytes .../Icon-App-38x38@3x.png | Bin 0 -> 4911 bytes .../Icon-App-40x40@1x.png | Bin 1269 -> 0 bytes .../Icon-App-40x40@2x.png | Bin 2680 -> 3283 bytes .../Icon-App-40x40@3x.png | Bin 4124 -> 5432 bytes .../Icon-App-60x60@2x.png | Bin 4124 -> 5432 bytes .../Icon-App-60x60@3x.png | Bin 5434 -> 8747 bytes .../Icon-App-64x64@2x.png | Bin 0 -> 5748 bytes .../Icon-App-64x64@3x.png | Bin 0 -> 9484 bytes .../Icon-App-68x68@2x.png | Bin 0 -> 6301 bytes .../Icon-App-76x76@1x.png | Bin 2614 -> 0 bytes .../Icon-App-76x76@2x.png | Bin 4574 -> 7061 bytes .../Icon-App-83.5x83.5@2x.png | Bin 4850 -> 8112 bytes .../Contents.json | 120 +++++----- .../Icon-App-1024x1024@1x.png | Bin 17750 -> 122163 bytes .../Icon-App-20x20@1x.png | Bin 497 -> 0 bytes .../Icon-App-20x20@2x.png | Bin 1161 -> 1326 bytes .../Icon-App-20x20@3x.png | Bin 1864 -> 2208 bytes .../Icon-App-29x29@1x.png | Bin 747 -> 0 bytes .../Icon-App-29x29@2x.png | Bin 1833 -> 2113 bytes .../Icon-App-29x29@3x.png | Bin 2752 -> 3609 bytes .../Icon-App-38x38@2x.png | Bin 0 -> 2923 bytes .../Icon-App-38x38@3x.png | Bin 0 -> 4641 bytes .../Icon-App-40x40@1x.png | Bin 1161 -> 0 bytes .../Icon-App-40x40@2x.png | Bin 2460 -> 3063 bytes .../Icon-App-40x40@3x.png | Bin 3802 -> 5117 bytes .../Icon-App-60x60@2x.png | Bin 3802 -> 5117 bytes .../Icon-App-60x60@3x.png | Bin 4996 -> 8296 bytes .../Icon-App-64x64@2x.png | Bin 0 -> 5421 bytes .../Icon-App-64x64@3x.png | Bin 0 -> 9007 bytes .../Icon-App-68x68@2x.png | Bin 0 -> 5963 bytes .../Icon-App-76x76@1x.png | Bin 2406 -> 0 bytes .../Icon-App-76x76@2x.png | Bin 4172 -> 6659 bytes .../Icon-App-83.5x83.5@2x.png | Bin 4448 -> 7706 bytes .../Contents.json | 120 +++++----- .../Icon-App-1024x1024@1x.png | Bin 18617 -> 123061 bytes .../Icon-App-20x20@1x.png | Bin 560 -> 0 bytes .../Icon-App-20x20@2x.png | Bin 1348 -> 1515 bytes .../Icon-App-20x20@3x.png | Bin 2147 -> 2473 bytes .../Icon-App-29x29@1x.png | Bin 871 -> 0 bytes .../Icon-App-29x29@2x.png | Bin 2120 -> 2396 bytes .../Icon-App-29x29@3x.png | Bin 3192 -> 4053 bytes .../Icon-App-38x38@2x.png | Bin 0 -> 3329 bytes .../Icon-App-38x38@3x.png | Bin 0 -> 5199 bytes .../Icon-App-40x40@1x.png | Bin 1348 -> 0 bytes .../Icon-App-40x40@2x.png | Bin 2886 -> 3489 bytes .../Icon-App-40x40@3x.png | Bin 4345 -> 5668 bytes .../Icon-App-60x60@2x.png | Bin 4345 -> 5668 bytes .../Icon-App-60x60@3x.png | Bin 5801 -> 9121 bytes .../Icon-App-64x64@2x.png | Bin 0 -> 6033 bytes .../Icon-App-64x64@3x.png | Bin 0 -> 9852 bytes .../Icon-App-68x68@2x.png | Bin 0 -> 6600 bytes .../Icon-App-76x76@1x.png | Bin 2802 -> 0 bytes .../Icon-App-76x76@2x.png | Bin 4905 -> 7414 bytes .../Icon-App-83.5x83.5@2x.png | Bin 5105 -> 8392 bytes .../Contents.json | 120 +++++----- .../Icon-App-1024x1024@1x.png | Bin 16742 -> 121104 bytes .../Icon-App-20x20@1x.png | Bin 359 -> 0 bytes .../Icon-App-20x20@2x.png | Bin 849 -> 1020 bytes .../Icon-App-20x20@3x.png | Bin 1377 -> 1707 bytes .../Icon-App-29x29@1x.png | Bin 548 -> 0 bytes .../Icon-App-29x29@2x.png | Bin 1373 -> 1636 bytes .../Icon-App-29x29@3x.png | Bin 2093 -> 2915 bytes .../Icon-App-38x38@2x.png | Bin 0 -> 2292 bytes .../Icon-App-38x38@3x.png | Bin 0 -> 3825 bytes .../Icon-App-40x40@1x.png | Bin 849 -> 0 bytes .../Icon-App-40x40@2x.png | Bin 1812 -> 2405 bytes .../Icon-App-40x40@3x.png | Bin 2865 -> 4179 bytes .../Icon-App-60x60@2x.png | Bin 2865 -> 4179 bytes .../Icon-App-60x60@3x.png | Bin 3797 -> 7081 bytes .../Icon-App-64x64@2x.png | Bin 0 -> 4492 bytes .../Icon-App-64x64@3x.png | Bin 0 -> 7748 bytes .../Icon-App-68x68@2x.png | Bin 0 -> 4983 bytes .../Icon-App-76x76@1x.png | Bin 1767 -> 0 bytes .../Icon-App-76x76@2x.png | Bin 3106 -> 5588 bytes .../Icon-App-83.5x83.5@2x.png | Bin 3359 -> 6602 bytes .../Contents.json | 120 +++++----- .../Icon-App-1024x1024@1x.png | Bin 18143 -> 122546 bytes .../Icon-App-20x20@1x.png | Bin 535 -> 0 bytes .../Icon-App-20x20@2x.png | Bin 1244 -> 1412 bytes .../Icon-App-20x20@3x.png | Bin 1976 -> 2311 bytes .../Icon-App-29x29@1x.png | Bin 799 -> 0 bytes .../Icon-App-29x29@2x.png | Bin 1940 -> 2206 bytes .../Icon-App-29x29@3x.png | Bin 2919 -> 3758 bytes .../Icon-App-38x38@2x.png | Bin 0 -> 3051 bytes .../Icon-App-38x38@3x.png | Bin 0 -> 4846 bytes .../Icon-App-40x40@1x.png | Bin 1244 -> 0 bytes .../Icon-App-40x40@2x.png | Bin 2612 -> 3203 bytes .../Icon-App-40x40@3x.png | Bin 3928 -> 5240 bytes .../Icon-App-60x60@2x.png | Bin 3928 -> 5240 bytes .../Icon-App-60x60@3x.png | Bin 5208 -> 8518 bytes .../Icon-App-64x64@2x.png | Bin 0 -> 5651 bytes .../Icon-App-64x64@3x.png | Bin 0 -> 9252 bytes .../Icon-App-68x68@2x.png | Bin 0 -> 6178 bytes .../Icon-App-76x76@1x.png | Bin 2528 -> 0 bytes .../Icon-App-76x76@2x.png | Bin 4443 -> 6930 bytes .../Icon-App-83.5x83.5@2x.png | Bin 4683 -> 7927 bytes .../Contents.json | 120 +++++----- .../Icon-App-1024x1024@1x.png | Bin 17638 -> 122036 bytes .../Icon-App-20x20@1x.png | Bin 480 -> 0 bytes .../Icon-App-20x20@2x.png | Bin 1140 -> 1304 bytes .../Icon-App-20x20@3x.png | Bin 1809 -> 2148 bytes .../Icon-App-29x29@1x.png | Bin 725 -> 0 bytes .../Icon-App-29x29@2x.png | Bin 1790 -> 2062 bytes .../Icon-App-29x29@3x.png | Bin 2699 -> 3528 bytes .../Icon-App-38x38@2x.png | Bin 0 -> 2847 bytes .../Icon-App-38x38@3x.png | Bin 0 -> 4573 bytes .../Icon-App-40x40@1x.png | Bin 1140 -> 0 bytes .../Icon-App-40x40@2x.png | Bin 2394 -> 2979 bytes .../Icon-App-40x40@3x.png | Bin 3618 -> 4928 bytes .../Icon-App-60x60@2x.png | Bin 3618 -> 4928 bytes .../Icon-App-60x60@3x.png | Bin 4766 -> 8054 bytes .../Icon-App-64x64@2x.png | Bin 0 -> 5312 bytes .../Icon-App-64x64@3x.png | Bin 0 -> 8790 bytes .../Icon-App-68x68@2x.png | Bin 0 -> 5824 bytes .../Icon-App-76x76@1x.png | Bin 2318 -> 0 bytes .../Icon-App-76x76@2x.png | Bin 4042 -> 6534 bytes .../Icon-App-83.5x83.5@2x.png | Bin 4279 -> 7515 bytes ios/Runner/Info.plist | 132 +++++------ .../AppIcon.appiconset/app_icon_1024.png | Bin 25019 -> 158793 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 3675 -> 5764 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 334 -> 399 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 6158 -> 15197 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 749 -> 962 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 11996 -> 46468 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 1848 -> 2423 bytes project_setup/lib/configuration.dart | 4 +- .../lib/core/app_icon_generator.dart | 11 +- project_setup/resources/icon.png | Bin 27384 -> 156156 bytes project_setup/resources/splash.png | Bin 22977 -> 104645 bytes snap/gui/app_icon.png | Bin 6158 -> 15197 bytes web/favicon.png | Bin 334 -> 962 bytes web/icons/Icon-192.png | Bin 5233 -> 9939 bytes web/icons/Icon-512.png | Bin 11996 -> 46468 bytes web/icons/Icon-maskable-192.png | Bin 5233 -> 9939 bytes web/icons/Icon-maskable-512.png | Bin 11996 -> 46468 bytes web/index.html | 222 ++++++++++-------- web/splash/img/dark-1x.png | Bin 7264 -> 10916 bytes web/splash/img/dark-2x.png | Bin 14328 -> 32551 bytes web/splash/img/dark-3x.png | Bin 22892 -> 66309 bytes web/splash/img/dark-4x.png | Bin 31731 -> 109243 bytes web/splash/img/light-1x.png | Bin 7264 -> 10916 bytes web/splash/img/light-2x.png | Bin 14328 -> 32551 bytes web/splash/img/light-3x.png | Bin 22892 -> 66309 bytes web/splash/img/light-4x.png | Bin 31731 -> 109243 bytes windows/runner/resources/app_icon.ico | Bin 17532 -> 31234 bytes 256 files changed, 555 insertions(+), 548 deletions(-) delete mode 100644 android/app/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 android/app/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 android/app/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-20x20@1x.png delete mode 100644 ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-38x38@2x.png create mode 100644 ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-38x38@3x.png delete mode 100644 ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-64x64@2x.png create mode 100644 ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-64x64@3x.png create mode 100644 ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-68x68@2x.png delete mode 100644 ios/Runner/Assets.xcassets/developDebugAppIcon.appiconset/Icon-App-76x76@1x.png delete mode 100644 ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-20x20@1x.png delete mode 100644 ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-38x38@2x.png create mode 100644 ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-38x38@3x.png delete mode 100644 ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-64x64@2x.png create mode 100644 ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-64x64@3x.png create mode 100644 ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-68x68@2x.png delete mode 100644 ios/Runner/Assets.xcassets/developReleaseAppIcon.appiconset/Icon-App-76x76@1x.png delete mode 100644 ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-20x20@1x.png delete mode 100644 ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-38x38@2x.png create mode 100644 ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-38x38@3x.png delete mode 100644 ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-64x64@2x.png create mode 100644 ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-64x64@3x.png create mode 100644 ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-68x68@2x.png delete mode 100644 ios/Runner/Assets.xcassets/productionDebugAppIcon.appiconset/Icon-App-76x76@1x.png delete mode 100644 ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-20x20@1x.png delete mode 100644 ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-38x38@2x.png create mode 100644 ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-38x38@3x.png delete mode 100644 ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-64x64@2x.png create mode 100644 ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-64x64@3x.png create mode 100644 ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-68x68@2x.png delete mode 100644 ios/Runner/Assets.xcassets/productionReleaseAppIcon.appiconset/Icon-App-76x76@1x.png delete mode 100644 ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-20x20@1x.png delete mode 100644 ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-38x38@2x.png create mode 100644 ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-38x38@3x.png delete mode 100644 ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-64x64@2x.png create mode 100644 ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-64x64@3x.png create mode 100644 ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-68x68@2x.png delete mode 100644 ios/Runner/Assets.xcassets/stagingDebugAppIcon.appiconset/Icon-App-76x76@1x.png delete mode 100644 ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-20x20@1x.png delete mode 100644 ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-29x29@1x.png create mode 100644 ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-38x38@2x.png create mode 100644 ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-38x38@3x.png delete mode 100644 ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-40x40@1x.png create mode 100644 ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-64x64@2x.png create mode 100644 ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-64x64@3x.png create mode 100644 ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-68x68@2x.png delete mode 100644 ios/Runner/Assets.xcassets/stagingReleaseAppIcon.appiconset/Icon-App-76x76@1x.png diff --git a/android/app/src/developDebug/ic_launcher-playstore.png b/android/app/src/developDebug/ic_launcher-playstore.png index 3f1a310c29fab69dbffdba04458eb81bddebbf16..b75ab474a97faf5804f3e1a576dffc028cdd3ea2 100644 GIT binary patch literal 28711 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 literal 12490 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 literal 2100 zcmbuBi9Ztz1INuU%w4wJbCfGZibRggvE0Wf_mP*#6>}dWN4b`eYgitVay1sUw%UY9 z5gC~)`^9|mKvl7 zl+kFPa^QyXk&{6VwX)~_c_8@&sVz`MyDaN{%|Tcv;2ZFPxz+u2itzR+&I=-YfyBmE z>rXBkfsa}!WToz$8cr)C{++lsT3vUrYWV8l_kIV`+srNNqs>UorjR{K#$5LQr{buD z3v5RU5nX~0O4`_IG9|jtLL}aH_Sk4UZbt`m?aCdD1wG}>eIMVLusVNKkgSK$(!`Bv zdK(n37qD+HrK`nEUcU4fKK7f0GrvSX3Tmzi62!Z&>rd?avo?YY=QGR-*`2AZr}R2N zH0Z~CpNa_}N9>lGZJ|ntyaOU{XYEd7vzH|T@VNlK^6JAN*t@l5-;LsNKsb4Wc4rIwL-_FTwBY=3BH5-LhuC@GA)&>5G>5Rq17N9fo|PB}d1_Eeyhmd>r~ z?k5kAl!h|N?hzVxA}_q%kqgBcKU71126M%Epr>}AQ!$nLYU@YKFifwXrnA!^y|pai z`lsC#arl`$MQDlw-I_#ASg(i|4FP<*A&zMeO@;>F;V6^}LTxl9?qC=LBlnQzUwQa+ zFJD{rO?Eb&bqrg9>@kB@j6Et;+6Ej`ZH)^2UlH`zVNvt0A4n(B)MJuVp{)d!?~8&` zu_81w&wy7FikD=W5ENi!3rEN1cI>xI*3JR$j17P@3QqOfytxP4J|!weQIR?Sq|M&w zp+ajDdf(YUV|Z%?2p=pV{2q1mGzkvjduG&ra*W?a>veKKd9jl6JwNG<73qTW7ti9r zA3NY2*le`hVuL_As|;q2+g$u~t6$=coe|ghIRC07PCXisYhxg=#9#;AB{*e(D4&PK zSb;tHm|4=U91VybU~& zzfkDW(WE{#)cOeRv8z?pf?Z)dEnokkpb??Sap)bU#u_!eR_B(?5@xL}$fgEvqC~h9UmL z5pOwa#~M_1ILfJKN6S=n&lS7{{gxdiRu>=K<6b-V)aG-J9CY8zO-vdr+^X z0Uu8UK;gBPqFppp`{2@j4d@Y*$c69PnKgC1>xZRZ)9sV8bF<`Z#86yM-;I}DYT)z3Q$nqhN8P#!z z9M0{F0u1GCWgqt$1NgO`E;H}s&O$+OL~l$~xISw&YMoAu0SBhxX~Ss(y>2&DmuE|= zFOYZ0ql3t@BCCV1nVOjJmqG0<4D`N>Bt4P(kxbE&2UmS$AtOv)&a}5l`1UJ-80Sw4 zV&7&ILKxQjT;cS2cqXGM+xoZD?bDqK>5y=p_tL4Fr4Q{J`!F~A`eo1wBX)lNbtMl) zU_~xybz~TRGM%lWyu=*U@EJCeZ#Pn6K z>w4H@2MUpy-Cdn|KBnGnXfnjb}vCS0<==}TrMZ2HY>5vxdLTtui3kZ7}SUeogK!Zf1TLZ%*& zZzK_X#OPfxSP_srl)yN*_=|Jqr#JUMNk75j7E-;dQ>?-O)N~DgEY=l(oJB0T!u8ZY zon{}(CW!3hV5>Cb78GXz;#^z^Or+U)l>&&tzF%6P*vw$fs%!M&qonS|m^s<4lB zeOz+5sqPt<&^&s?Tcx~6AUEfD!wb=U48by*+`Ad4V~k6jNd0nH@T1YmsRL9W<}FRg zXR3>gKu?@5G6_#iapDW>=d|l8kV1j=;bjl-K(D9WUoGhk;h7hHVcXh)(N+z^6Gjah h_q_z}TK|u5fu;PWkd4S1#Ge}AL0CD$J1zxe{|kmK<=6lK diff --git a/android/app/src/developDebug/res/mipmap-hdpi/ic_launcher_foreground.png b/android/app/src/developDebug/res/mipmap-hdpi/ic_launcher_foreground.png index 8fc642f00f30023412d693e572ae601e6bac1678..87394b06cf2e121734ab5427da89c2d70fb60d3e 100644 GIT binary patch literal 6414 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