From 34d758795aa39a0f379aa50aef8ec648ce34f8c1 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Wed, 29 Oct 2025 16:31:03 +0700 Subject: [PATCH 01/32] feat(core): add base Clean Architecture structure --- CHANGELOG.md | 2 ++ assets/fonts/.gitkeep | 0 assets/icons/.gitkeep | 0 assets/images/.gitkeep | 0 assets/splash/.gitkeep | 0 docs/.gitkeep | 0 lib/core/di/.gitkeep | 0 lib/core/di/di.dart | 1 + lib/core/errors/.gitkeep | 0 lib/core/localization/.gitkeep | 0 lib/core/result/.gitkeep | 0 lib/core/router/.gitkeep | 0 lib/core/services/.gitkeep | 0 lib/core/utils/.gitkeep | 0 lib/core/utils/analytics/.gitkeep | 0 lib/core/utils/logger/.gitkeep | 0 lib/features/app/.gitkeep | 0 lib/features/auth/data/mappers/.gitkeep | 0 lib/features/auth/data/models/.gitkeep | 0 lib/features/auth/data/repositories/.gitkeep | 0 lib/features/auth/data/sources/.gitkeep | 0 lib/features/auth/domain/entities/.gitkeep | 0 lib/features/auth/domain/repositories/.gitkeep | 0 lib/features/auth/domain/sources/.gitkeep | 0 lib/features/auth/domain/usecases/.gitkeep | 0 lib/features/auth/presentation/pages/.gitkeep | 0 lib/features/auth/presentation/widgets/.gitkeep | 0 lib/features/common/.gitkeep | 0 lib/uikit/buttons/.gitkeep | 0 lib/uikit/images/.gitkeep | 0 lib/uikit/themes/colors/.gitkeep | 0 lib/uikit/themes/text/.gitkeep | 0 pubspec.yaml | 11 +++++++++++ test/.gitkeep | 0 34 files changed, 14 insertions(+) create mode 100644 assets/fonts/.gitkeep create mode 100644 assets/icons/.gitkeep create mode 100644 assets/images/.gitkeep create mode 100644 assets/splash/.gitkeep create mode 100644 docs/.gitkeep create mode 100644 lib/core/di/.gitkeep create mode 100644 lib/core/di/di.dart create mode 100644 lib/core/errors/.gitkeep create mode 100644 lib/core/localization/.gitkeep create mode 100644 lib/core/result/.gitkeep create mode 100644 lib/core/router/.gitkeep create mode 100644 lib/core/services/.gitkeep create mode 100644 lib/core/utils/.gitkeep create mode 100644 lib/core/utils/analytics/.gitkeep create mode 100644 lib/core/utils/logger/.gitkeep create mode 100644 lib/features/app/.gitkeep create mode 100644 lib/features/auth/data/mappers/.gitkeep create mode 100644 lib/features/auth/data/models/.gitkeep create mode 100644 lib/features/auth/data/repositories/.gitkeep create mode 100644 lib/features/auth/data/sources/.gitkeep create mode 100644 lib/features/auth/domain/entities/.gitkeep create mode 100644 lib/features/auth/domain/repositories/.gitkeep create mode 100644 lib/features/auth/domain/sources/.gitkeep create mode 100644 lib/features/auth/domain/usecases/.gitkeep create mode 100644 lib/features/auth/presentation/pages/.gitkeep create mode 100644 lib/features/auth/presentation/widgets/.gitkeep create mode 100644 lib/features/common/.gitkeep create mode 100644 lib/uikit/buttons/.gitkeep create mode 100644 lib/uikit/images/.gitkeep create mode 100644 lib/uikit/themes/colors/.gitkeep create mode 100644 lib/uikit/themes/text/.gitkeep create mode 100644 test/.gitkeep diff --git a/CHANGELOG.md b/CHANGELOG.md index 59554c0..8bc7a48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- ## [Unreleased] +### Added +- Implemented base Clean Architecture structure (`core`, `features`, `uikit`) with `data`, `domain`, and `presentation` layers. --- diff --git a/assets/fonts/.gitkeep b/assets/fonts/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/assets/icons/.gitkeep b/assets/icons/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/assets/images/.gitkeep b/assets/images/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/assets/splash/.gitkeep b/assets/splash/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/.gitkeep b/docs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/core/di/.gitkeep b/lib/core/di/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/core/di/di.dart b/lib/core/di/di.dart new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/core/di/di.dart @@ -0,0 +1 @@ + diff --git a/lib/core/errors/.gitkeep b/lib/core/errors/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/core/localization/.gitkeep b/lib/core/localization/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/core/result/.gitkeep b/lib/core/result/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/core/router/.gitkeep b/lib/core/router/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/core/services/.gitkeep b/lib/core/services/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/core/utils/.gitkeep b/lib/core/utils/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/core/utils/analytics/.gitkeep b/lib/core/utils/analytics/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/core/utils/logger/.gitkeep b/lib/core/utils/logger/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/features/app/.gitkeep b/lib/features/app/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/features/auth/data/mappers/.gitkeep b/lib/features/auth/data/mappers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/features/auth/data/models/.gitkeep b/lib/features/auth/data/models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/features/auth/data/repositories/.gitkeep b/lib/features/auth/data/repositories/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/features/auth/data/sources/.gitkeep b/lib/features/auth/data/sources/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/features/auth/domain/entities/.gitkeep b/lib/features/auth/domain/entities/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/features/auth/domain/repositories/.gitkeep b/lib/features/auth/domain/repositories/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/features/auth/domain/sources/.gitkeep b/lib/features/auth/domain/sources/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/features/auth/domain/usecases/.gitkeep b/lib/features/auth/domain/usecases/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/features/auth/presentation/pages/.gitkeep b/lib/features/auth/presentation/pages/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/features/auth/presentation/widgets/.gitkeep b/lib/features/auth/presentation/widgets/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/features/common/.gitkeep b/lib/features/common/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/uikit/buttons/.gitkeep b/lib/uikit/buttons/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/uikit/images/.gitkeep b/lib/uikit/images/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/uikit/themes/colors/.gitkeep b/lib/uikit/themes/colors/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/lib/uikit/themes/text/.gitkeep b/lib/uikit/themes/text/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/pubspec.yaml b/pubspec.yaml index ae3fa28..c3bb77e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,3 +16,14 @@ dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^6.0.0 + +flutter: + uses-material-design: true + generate: true + + assets: + - assets/icons/ + - assets/images/ + - assets/splash/ + + # fonts: \ No newline at end of file diff --git a/test/.gitkeep b/test/.gitkeep new file mode 100644 index 0000000..e69de29 From 8c558e82b4456c86bfe140c0852fddd60a227573 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Wed, 29 Oct 2025 16:51:37 +0700 Subject: [PATCH 02/32] chore(deps): add project dependencies --- CHANGELOG.md | 1 + pubspec.lock | 1234 +++++++++++++++++++++++++++++++++++++++++++++++--- pubspec.yaml | 31 +- 3 files changed, 1197 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bc7a48..51590c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added - Implemented base Clean Architecture structure (`core`, `features`, `uikit`) with `data`, `domain`, and `presentation` layers. +- Added all project dependencies from `pubspec.yaml`. --- diff --git a/pubspec.lock b/pubspec.lock index 0638fc1..19072a8 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,141 +1,1039 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f + url: "https://pub.dev" + source: hosted + version: "85.0.0" + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: f871a7d1b686bea1f13722aa51ab31554d05c81f47054d6de48cc8c45153508b + url: "https://pub.dev" + source: hosted + version: "1.3.63" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "974859dc0ff5f37bc4313244b3218c791810d03ab3470a579580279ba971a48d" + url: "https://pub.dev" + source: hosted + version: "7.7.1" + ansicolor: + dependency: transitive + description: + name: ansicolor + sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f" + url: "https://pub.dev" + source: hosted + version: "2.0.3" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" async: dependency: transitive description: - name: async - sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + name: async + sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" + url: "https://pub.dev" + source: hosted + version: "2.13.0" + bloc: + dependency: transitive + description: + name: bloc + sha256: a2cebb899f91d36eeeaa55c7b20b5915db5a9df1b8fd4a3c9c825e22e474537d + url: "https://pub.dev" + source: hosted + version: "9.1.0" + bloc_test: + dependency: "direct main" + description: + name: bloc_test + sha256: "1dd549e58be35148bc22a9135962106aa29334bc1e3f285994946a1057b29d7b" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + build: + dependency: transitive + description: + name: build + sha256: ce76b1d48875e3233fde17717c23d1f60a91cc631597e49a400c89b475395b1d + url: "https://pub.dev" + source: hosted + version: "3.1.0" + build_config: + dependency: transitive + description: + name: build_config + sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "409002f1adeea601018715d613115cfaf0e31f512cb80ae4534c79867ae2363d" + url: "https://pub.dev" + source: hosted + version: "4.1.0" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: d1d57f7807debd7349b4726a19fd32ec8bc177c71ad0febf91a20f84cd2d4b46 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: b24597fceb695969d47025c958f3837f9f0122e237c6a22cb082a5ac66c3ca30 + url: "https://pub.dev" + source: hosted + version: "2.7.1" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "066dda7f73d8eb48ba630a55acb50c4a84a2e6b453b1cb4567f581729e794f7b" + url: "https://pub.dev" + source: hosted + version: "9.3.1" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d + url: "https://pub.dev" + source: hosted + version: "8.12.0" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916" + url: "https://pub.dev" + source: hosted + version: "3.4.1" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829" + url: "https://pub.dev" + source: hosted + version: "4.1.1" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + characters: + dependency: transitive + description: + name: characters + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" + url: "https://pub.dev" + source: hosted + version: "2.0.4" + cli_config: + dependency: transitive + description: + name: cli_config + sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec + url: "https://pub.dev" + source: hosted + version: "0.2.0" + clock: + dependency: transitive + description: + name: clock + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + url: "https://pub.dev" + source: hosted + version: "1.1.2" + cloud_firestore: + dependency: "direct main" + description: + name: cloud_firestore + sha256: ea3fe98eaf0c3b675ab2a242d98430518da60f89d5cc9774df7b2d9110aaf160 + url: "https://pub.dev" + source: hosted + version: "6.0.3" + cloud_firestore_platform_interface: + dependency: transitive + description: + name: cloud_firestore_platform_interface + sha256: b5592862c451ebfd229b430907b6feb2082333d6e5a61d0eafd547c2afc6f68e + url: "https://pub.dev" + source: hosted + version: "7.0.3" + cloud_firestore_web: + dependency: transitive + description: + name: cloud_firestore_web + sha256: eca0337985e0eedcef8a3e5380950af01c0853951b6b8427e7909115d81d7b9c + url: "https://pub.dev" + source: hosted + version: "5.0.3" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243" + url: "https://pub.dev" + source: hosted + version: "4.11.0" + collection: + dependency: transitive + description: + name: collection + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + url: "https://pub.dev" + source: hosted + version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + coverage: + dependency: transitive + description: + name: coverage + sha256: "5da775aa218eaf2151c721b16c01c7676fbfdd99cebba2bf64e8b807a28ff94d" + url: "https://pub.dev" + source: hosted + version: "1.15.0" + crypto: + dependency: transitive + description: + name: crypto + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "8a0e5fba27e8ee025d2ffb4ee820b4e6e2cf5e4246a6b1a477eb66866947e0bb" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + diff_match_patch: + dependency: transitive + description: + name: diff_match_patch + sha256: "2efc9e6e8f449d0abe15be240e2c2a3bcd977c8d126cfd70598aee60af35c0a4" + url: "https://pub.dev" + source: hosted + version: "0.4.1" + dio: + dependency: "direct main" + description: + name: dio + sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9 + url: "https://pub.dev" + source: hosted + version: "5.9.0" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + equatable: + dependency: "direct main" + description: + name: equatable + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" + url: "https://pub.dev" + source: hosted + version: "2.0.7" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + url: "https://pub.dev" + source: hosted + version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" + firebase_auth: + dependency: "direct main" + description: + name: firebase_auth + sha256: "3150a56fc0f20b4b926e1343bfdca3acb591a0aa1c95bae5426353f384085352" + url: "https://pub.dev" + source: hosted + version: "6.1.1" + firebase_auth_platform_interface: + dependency: transitive + description: + name: firebase_auth_platform_interface + sha256: "7bc50c0d74dd8f4c72d7840ae64ea7b638f203d089e5c4a90a157b2f2ead1963" + url: "https://pub.dev" + source: hosted + version: "8.1.3" + firebase_auth_web: + dependency: transitive + description: + name: firebase_auth_web + sha256: "351dcb82bc542e21a426cd97ffcc40d7232981dafc3fd89a6c932876a09240e1" + url: "https://pub.dev" + source: hosted + version: "6.0.4" + firebase_core: + dependency: "direct main" + description: + name: firebase_core + sha256: "132e1c311bc41e7d387b575df0aacdf24efbf4930365eb61042be5bde3978f03" + url: "https://pub.dev" + source: hosted + version: "4.2.0" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: cccb4f572325dc14904c02fcc7db6323ad62ba02536833dddb5c02cac7341c64 + url: "https://pub.dev" + source: hosted + version: "6.0.2" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: ecde2def458292404a4fcd3731ee4992fd631a0ec359d2d67c33baa8da5ec8ae + url: "https://pub.dev" + source: hosted + version: "3.2.0" + firebase_storage: + dependency: "direct main" + description: + name: firebase_storage + sha256: d740b9ea0105f27d7286d7ad5957d778bf7fa967796c6e3fc26491cf5245b486 + url: "https://pub.dev" + source: hosted + version: "13.0.3" + firebase_storage_platform_interface: + dependency: transitive + description: + name: firebase_storage_platform_interface + sha256: "52a1dfb2c93f49a8e800d4b9aed107d1c0f2f6dd3ebf10a5f7e2222442960e50" + url: "https://pub.dev" + source: hosted + version: "5.2.14" + firebase_storage_web: + dependency: transitive + description: + name: firebase_storage_web + sha256: d5ce115e3d5a58fddd7631d5246aa8389d8000fa7790ecb04f65bf751bbf6a92 + url: "https://pub.dev" + source: hosted + version: "3.10.21" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be + url: "https://pub.dev" + source: hosted + version: "1.1.1" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: cf51747952201a455a1c840f8171d273be009b932c75093020f9af64f2123e38 + url: "https://pub.dev" + source: hosted + version: "9.1.1" + flutter_cache_manager: + dependency: transitive + description: + name: flutter_cache_manager + sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386" + url: "https://pub.dev" + source: hosted + version: "3.4.1" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" + url: "https://pub.dev" + source: hosted + version: "6.0.0" + flutter_localizations: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_svg: + dependency: "direct main" + description: + name: flutter_svg + sha256: b9c2ad5872518a27507ab432d1fb97e8813b05f0fc693f9d40fad06d073e0678 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + freezed: + dependency: "direct dev" + description: + name: freezed + sha256: "13065f10e135263a4f5a4391b79a8efc5fb8106f8dd555a9e49b750b45393d77" + url: "https://pub.dev" + source: hosted + version: "3.2.3" + freezed_annotation: + dependency: "direct main" + description: + name: freezed_annotation + sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + get_it: + dependency: "direct main" + description: + name: get_it + sha256: a4292e7cf67193f8e7c1258203104eb2a51ec8b3a04baa14695f4064c144297b + url: "https://pub.dev" + source: hosted + version: "8.2.0" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: d8f590a69729f719177ea68eb1e598295e8dbc41bbc247fed78b2c8a25660d7c + url: "https://pub.dev" + source: hosted + version: "16.3.0" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + hotreloader: + dependency: transitive + description: + name: hotreloader + sha256: bc167a1163807b03bada490bfe2df25b0d744df359227880220a5cbd04e5734b + url: "https://pub.dev" + source: hosted + version: "4.3.0" + http: + dependency: transitive + description: + name: http + sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007 + url: "https://pub.dev" + source: hosted + version: "1.5.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.dev" + source: hosted + version: "3.2.2" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" + io: + dependency: transitive + description: + name: io + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.dev" + source: hosted + version: "1.0.5" + js: + dependency: transitive + description: + name: js + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + json_annotation: + dependency: "direct main" + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + sha256: "33a040668b31b320aafa4822b7b1e177e163fc3c1e835c6750319d4ab23aa6fe" + url: "https://pub.dev" + source: hosted + version: "6.11.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + url: "https://pub.dev" + source: hosted + version: "11.0.2" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + url: "https://pub.dev" + source: hosted + version: "3.0.10" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + lean_builder: + dependency: transitive + description: + name: lean_builder + sha256: ef5cd5f907157eb7aa87d1704504b5a6386d2cbff88a3c2b3344477bab323ee9 + url: "https://pub.dev" + source: hosted + version: "0.1.2" + lints: + dependency: transitive + description: + name: lints + sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 + url: "https://pub.dev" + source: hosted + version: "6.0.0" + logger: + dependency: "direct main" + description: + name: logger + sha256: a7967e31b703831a893bbc3c3dd11db08126fe5f369b5c648a36f821979f5be3 + url: "https://pub.dev" + source: hosted + version: "2.6.2" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + url: "https://pub.dev" + source: hosted + version: "0.12.17" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + url: "https://pub.dev" + source: hosted + version: "0.11.1" + meta: + dependency: transitive + description: + name: meta + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + url: "https://pub.dev" + source: hosted + version: "1.16.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + mockito: + dependency: "direct main" + description: + name: mockito + sha256: "2314cbe9165bcd16106513df9cf3c3224713087f09723b128928dc11a4379f99" + url: "https://pub.dev" + source: hosted + version: "5.5.0" + mocktail: + dependency: transitive + description: + name: mocktail + sha256: "890df3f9688106f25755f26b1c60589a92b3ab91a22b8b224947ad041bf172d8" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + native_splash_screen: + dependency: "direct main" + description: + name: native_splash_screen + sha256: "346f713538ccfb5dd0723e7f699985d64468aabe776de8b544804de253467446" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + native_splash_screen_linux: + dependency: transitive + description: + name: native_splash_screen_linux + sha256: e8b6ecc6a2cd9e5c29a57eace024f02e00207378f824a645bf4d5248e1655117 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + native_splash_screen_macos: + dependency: transitive + description: + name: native_splash_screen_macos + sha256: cae3c86844d9ebb362c2437a234cffd4839d9ecbe58cf74352b6574c29c3078a + url: "https://pub.dev" + source: hosted + version: "3.0.0" + native_splash_screen_platform_interface: + dependency: transitive + description: + name: native_splash_screen_platform_interface + sha256: d1ba329116ed47dd6422442beaad1cc3ea4f7c2d926c4450b946a959de80d446 + url: "https://pub.dev" + source: hosted + version: "3.0.0" + native_splash_screen_windows: + dependency: transitive + description: + name: native_splash_screen_windows + sha256: "9c9f5130b3fc9b0300d564270517ce79530fb04d69b7f7c030ab8678e04e2834" + url: "https://pub.dev" + source: hosted + version: "3.0.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + node_preamble: + dependency: transitive + description: + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" + url: "https://pub.dev" + source: hosted + version: "2.0.2" + octo_image: + dependency: transitive + description: + name: octo_image + sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc url: "https://pub.dev" source: hosted - version: "2.13.0" - boolean_selector: + version: "2.2.0" + path: + dependency: "direct main" + description: + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + url: "https://pub.dev" + source: hosted + version: "1.9.1" + path_parsing: dependency: transitive description: - name: boolean_selector - sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" + name: path_parsing + sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + path_provider: + dependency: "direct main" + description: + name: path_provider + sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" + url: "https://pub.dev" + source: hosted + version: "2.1.5" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + sha256: e122c5ea805bb6773bb12ce667611265980940145be920cd09a4b0ec0285cb16 + url: "https://pub.dev" + source: hosted + version: "2.2.20" + path_provider_foundation: + dependency: transitive + description: + name: path_provider_foundation + sha256: efaec349ddfc181528345c56f8eda9d6cccd71c177511b132c6a0ddaefaa2738 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.dev" source: hosted version: "2.1.2" - characters: + path_provider_windows: dependency: transitive description: - name: characters - sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 url: "https://pub.dev" source: hosted - version: "1.4.0" - clock: + version: "2.3.0" + petitparser: dependency: transitive description: - name: clock - sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b + name: petitparser + sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" url: "https://pub.dev" source: hosted - version: "1.1.2" - collection: + version: "7.0.1" + platform: dependency: transitive description: - name: collection - sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" + name: platform + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" url: "https://pub.dev" source: hosted - version: "1.19.1" - fake_async: + version: "3.1.6" + plugin_platform_interface: dependency: transitive description: - name: fake_async - sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted - version: "1.3.3" - flutter: + version: "2.1.8" + pool: + dependency: transitive + description: + name: pool + sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" + url: "https://pub.dev" + source: hosted + version: "1.5.2" + protobuf: + dependency: transitive + description: + name: protobuf + sha256: "826d6a306be26f29e5cd9faeb0c97aad5897270341dab6dbd7b8acd675937006" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + provider: dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_lints: - dependency: "direct dev" description: - name: flutter_lints - sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1" + name: provider + sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272" url: "https://pub.dev" source: hosted - version: "6.0.0" - flutter_test: + version: "6.1.5+1" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + retrofit: + dependency: "direct main" + description: + name: retrofit + sha256: "661ec56ecb891dfb8bbfad40827ad012daca7c69d6693b1703d72eb1ae1a5ec5" + url: "https://pub.dev" + source: hosted + version: "4.8.0" + retrofit_generator: dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - leak_tracker: + description: + name: retrofit_generator + sha256: "37d74a07075b57d02772e5082610fe9f97d799de92a3aeb1417ec5e43c35c78d" + url: "https://pub.dev" + source: hosted + version: "10.1.1" + rxdart: dependency: transitive description: - name: leak_tracker - sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" + name: rxdart + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" url: "https://pub.dev" source: hosted - version: "11.0.2" - leak_tracker_flutter_testing: + version: "0.28.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.dev" + source: hosted + version: "2.5.3" + shared_preferences_android: dependency: transitive description: - name: leak_tracker_flutter_testing - sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" + name: shared_preferences_android + sha256: "34266009473bf71d748912da4bf62d439185226c03e01e2d9687bc65bbfcb713" url: "https://pub.dev" source: hosted - version: "3.0.10" - leak_tracker_testing: + version: "2.4.15" + shared_preferences_foundation: dependency: transitive description: - name: leak_tracker_testing - sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" + name: shared_preferences_foundation + sha256: "1c33a907142607c40a7542768ec9badfd16293bac51da3a4482623d15845f88b" url: "https://pub.dev" source: hosted - version: "3.0.2" - lints: + version: "2.5.5" + shared_preferences_linux: dependency: transitive description: - name: lints - sha256: a5e2b223cb7c9c8efdc663ef484fdd95bb243bff242ef5b13e26883547fce9a0 + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" url: "https://pub.dev" source: hosted - version: "6.0.0" - matcher: + version: "2.4.1" + shared_preferences_platform_interface: dependency: transitive description: - name: matcher - sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" url: "https://pub.dev" source: hosted - version: "0.12.17" - material_color_utilities: + version: "2.4.1" + shared_preferences_web: dependency: transitive description: - name: material_color_utilities - sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 url: "https://pub.dev" source: hosted - version: "0.11.1" - meta: + version: "2.4.3" + shared_preferences_windows: dependency: transitive description: - name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" url: "https://pub.dev" source: hosted - version: "1.16.0" - path: + version: "2.4.1" + shelf: dependency: transitive description: - name: path - sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.4.2" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" + url: "https://pub.dev" + source: hosted + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + url: "https://pub.dev" + source: hosted + version: "3.0.0" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.0" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "7b19d6ba131c6eb98bfcbf8d56c1a7002eba438af2e7ae6f8398b2b0f4f381e3" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723" + url: "https://pub.dev" + source: hosted + version: "1.3.8" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812" + url: "https://pub.dev" + source: hosted + version: "0.10.13" source_span: dependency: transitive description: @@ -144,6 +1042,54 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.1" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + sqflite: + dependency: transitive + description: + name: sqflite + sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03 + url: "https://pub.dev" + source: hosted + version: "2.4.2" + sqflite_android: + dependency: transitive + description: + name: sqflite_android + sha256: ecd684501ebc2ae9a83536e8b15731642b9570dc8623e0073d227d0ee2bfea88 + url: "https://pub.dev" + source: hosted + version: "2.4.2+2" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "6ef422a4525ecc601db6c0a2233ff448c731307906e92cabc9ba292afaae16a6" + url: "https://pub.dev" + source: hosted + version: "2.5.6" + sqflite_darwin: + dependency: transitive + description: + name: sqflite_darwin + sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + sqflite_platform_interface: + dependency: transitive + description: + name: sqflite_platform_interface + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" + url: "https://pub.dev" + source: hosted + version: "2.4.0" stack_trace: dependency: transitive description: @@ -160,6 +1106,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.dev" + source: hosted + version: "2.1.1" string_scanner: dependency: transitive description: @@ -168,6 +1122,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.1" + synchronized: + dependency: transitive + description: + name: synchronized + sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0 + url: "https://pub.dev" + source: hosted + version: "3.4.0" term_glyph: dependency: transitive description: @@ -176,6 +1138,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.2" + test: + dependency: transitive + description: + name: test + sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb" + url: "https://pub.dev" + source: hosted + version: "1.26.2" test_api: dependency: transitive description: @@ -184,6 +1154,62 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.6" + test_core: + dependency: transitive + description: + name: test_core + sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a" + url: "https://pub.dev" + source: hosted + version: "0.6.11" + timing: + dependency: transitive + description: + name: timing + sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + url: "https://pub.dev" + source: hosted + version: "1.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" + uuid: + dependency: transitive + description: + name: uuid + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + url: "https://pub.dev" + source: hosted + version: "4.5.1" + vector_graphics: + dependency: transitive + description: + name: vector_graphics + sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6 + url: "https://pub.dev" + source: hosted + version: "1.1.19" + vector_graphics_codec: + dependency: transitive + description: + name: vector_graphics_codec + sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146" + url: "https://pub.dev" + source: hosted + version: "1.1.13" + vector_graphics_compiler: + dependency: transitive + description: + name: vector_graphics_compiler + sha256: d354a7ec6931e6047785f4db12a1f61ec3d43b207fc0790f863818543f8ff0dc + url: "https://pub.dev" + source: hosted + version: "1.1.19" vector_math: dependency: transitive description: @@ -200,6 +1226,78 @@ packages: url: "https://pub.dev" source: hosted version: "15.0.2" + watcher: + dependency: transitive + description: + name: watcher + sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a" + url: "https://pub.dev" + source: hosted + version: "1.1.4" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.dev" + source: hosted + version: "3.0.3" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" + url: "https://pub.dev" + source: hosted + version: "6.6.1" + xxh3: + dependency: transitive + description: + name: xxh3 + sha256: "399a0438f5d426785723c99da6b16e136f4953fb1e9db0bf270bd41dd4619916" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" sdks: dart: ">=3.9.2 <4.0.0" - flutter: ">=3.18.0-18.0.pre.54" + flutter: ">=3.35.0" diff --git a/pubspec.yaml b/pubspec.yaml index c3bb77e..a96b4d1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,13 +9,42 @@ environment: license: "MIT" dependencies: + bloc_test: ^10.0.0 + cached_network_image: ^3.4.1 + cloud_firestore: ^6.0.3 + dio: ^5.9.0 + equatable: ^2.0.7 + firebase_auth: ^6.1.1 + firebase_core: ^4.2.0 + firebase_storage: ^13.0.3 flutter: sdk: flutter + flutter_bloc: ^9.1.1 + flutter_localizations: + sdk: flutter + flutter_svg: ^2.2.1 + freezed_annotation: ^3.1.0 + get_it: ^8.2.0 + go_router: ^16.3.0 + intl: ^0.20.2 + json_annotation: ^4.9.0 + logger: ^2.6.2 + mockito: ^5.5.0 + native_splash_screen: ^3.0.0 + path: ^1.9.1 + path_provider: ^2.1.5 + provider: ^6.1.5+1 + retrofit: ^4.7.3 + shared_preferences: ^2.5.3 dev_dependencies: + build_runner: ^2.7.1 + freezed: ^3.2.3 + flutter_lints: ^6.0.0 flutter_test: sdk: flutter - flutter_lints: ^6.0.0 + json_serializable: ^6.11.1 + retrofit_generator: ^10.0.6 flutter: uses-material-design: true From aabeae4dc74cc96bf9f049df4a910f22a3a85f72 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Wed, 29 Oct 2025 17:59:20 +0700 Subject: [PATCH 03/32] feat(linter): add public_member_api_docs rule to analysis options --- CHANGELOG.md | 3 +++ analysis_options.yaml | 1 + 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51590c0..a1f0160 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Implemented base Clean Architecture structure (`core`, `features`, `uikit`) with `data`, `domain`, and `presentation` layers. - Added all project dependencies from `pubspec.yaml`. +### Changed +- Added `public_member_api_docs` rule to `analysis_options.yaml`. + --- ## [0.1.0] - 2025-10-29 diff --git a/analysis_options.yaml b/analysis_options.yaml index 13ffaac..2f5712c 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -42,6 +42,7 @@ linter: - prefer_const_declarations - prefer_relative_imports - prefer_single_quotes + - public_member_api_docs - use_super_parameters - unnecessary_parenthesis From 1543364e21161c03c31814be0dc2d44a49a9eebf Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Wed, 29 Oct 2025 18:02:15 +0700 Subject: [PATCH 04/32] refactor(app): extract FlutterStarterTemplate widget and add runner.dart --- CHANGELOG.md | 1 + lib/features/app/.gitkeep | 0 lib/features/app/app.dart | 18 ++++++++++++++++++ lib/main.dart | 17 ++--------------- lib/runner.dart | 19 +++++++++++++++++++ 5 files changed, 40 insertions(+), 15 deletions(-) delete mode 100644 lib/features/app/.gitkeep create mode 100644 lib/features/app/app.dart create mode 100644 lib/runner.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index a1f0160..fe50f03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Added `public_member_api_docs` rule to `analysis_options.yaml`. +- Extracted main application widget to `app.dart`, added `runner.dart` for app initialization with error handling, and simplified `main.dart`. --- diff --git a/lib/features/app/.gitkeep b/lib/features/app/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/lib/features/app/app.dart b/lib/features/app/app.dart new file mode 100644 index 0000000..453596a --- /dev/null +++ b/lib/features/app/app.dart @@ -0,0 +1,18 @@ +import 'package:flutter/material.dart'; + +/// The main application widget. +class FlutterStarterTemplate extends StatelessWidget { + /// Creates a [FlutterStarterTemplate] widget. + const FlutterStarterTemplate({super.key}); + + @override + Widget build(BuildContext context) { + return const MaterialApp( + home: Scaffold( + body: Center( + child: Text('Flutter Starter Template'), + ), + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index 640824f..e6d5fc4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,16 +1,3 @@ -import 'package:flutter/material.dart'; +import 'runner.dart'; -void main() { - runApp(const MainApp()); -} - -class MainApp extends StatelessWidget { - const MainApp({super.key}); - - @override - Widget build(BuildContext context) { - return const MaterialApp( - home: Scaffold(body: Center(child: Text('Hello World!'))), - ); - } -} +void main() => run(); diff --git a/lib/runner.dart b/lib/runner.dart new file mode 100644 index 0000000..bd99385 --- /dev/null +++ b/lib/runner.dart @@ -0,0 +1,19 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +import 'features/app/app.dart'; + +/// The main entry point of the application. +void run() { + WidgetsFlutterBinding.ensureInitialized(); + runZonedGuarded( + () { + runApp(const FlutterStarterTemplate()); + }, + (error, stackTrace) { + debugPrint('Uncaught error: $error'); + debugPrint('Stack trace: $stackTrace'); + }, + ); +} From 382702c4a5402f4682f8dab6bed1acb04a6ad8f2 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Wed, 29 Oct 2025 18:12:05 +0700 Subject: [PATCH 05/32] feat(di): add GetIt service locator and setup function --- CHANGELOG.md | 1 + lib/core/di/.gitkeep | 0 lib/core/di/di.dart | 9 +++++++++ 3 files changed, 10 insertions(+) delete mode 100644 lib/core/di/.gitkeep diff --git a/CHANGELOG.md b/CHANGELOG.md index fe50f03..a17d455 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Implemented base Clean Architecture structure (`core`, `features`, `uikit`) with `data`, `domain`, and `presentation` layers. - Added all project dependencies from `pubspec.yaml`. +- Integrated `GetIt` for dependency injection. ### Changed - Added `public_member_api_docs` rule to `analysis_options.yaml`. diff --git a/lib/core/di/.gitkeep b/lib/core/di/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/lib/core/di/di.dart b/lib/core/di/di.dart index 8b13789..6a7fd2d 100644 --- a/lib/core/di/di.dart +++ b/lib/core/di/di.dart @@ -1 +1,10 @@ +import 'package:get_it/get_it.dart'; +/// Global instance of the GetIt service locator for dependency management. +final di = GetIt.instance; + +/// Initializes the application's dependencies using GetIt. +void setupDI() { + // Register your dependencies here, for example: + // di.registerLazySingleton(() => SomeServiceImpl()); +} From 90077f390571ce856e9a34aa50a2ea24a9810956 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Wed, 29 Oct 2025 18:56:27 +0700 Subject: [PATCH 06/32] chore(ios): update generated Xcode and CocoaPods files --- ios/Podfile.lock | 1485 +++++++++++++++++ ios/Runner.xcodeproj/project.pbxproj | 115 ++ .../contents.xcworkspacedata | 3 + 3 files changed, 1603 insertions(+) create mode 100644 ios/Podfile.lock diff --git a/ios/Podfile.lock b/ios/Podfile.lock new file mode 100644 index 0000000..046c54b --- /dev/null +++ b/ios/Podfile.lock @@ -0,0 +1,1485 @@ +PODS: + - abseil/algorithm (1.20240722.0): + - abseil/algorithm/algorithm (= 1.20240722.0) + - abseil/algorithm/container (= 1.20240722.0) + - abseil/algorithm/algorithm (1.20240722.0): + - abseil/base/config + - abseil/xcprivacy + - abseil/algorithm/container (1.20240722.0): + - abseil/algorithm/algorithm + - abseil/base/config + - abseil/base/core_headers + - abseil/base/nullability + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/base (1.20240722.0): + - abseil/base/atomic_hook (= 1.20240722.0) + - abseil/base/base (= 1.20240722.0) + - abseil/base/base_internal (= 1.20240722.0) + - abseil/base/config (= 1.20240722.0) + - abseil/base/core_headers (= 1.20240722.0) + - abseil/base/cycleclock_internal (= 1.20240722.0) + - abseil/base/dynamic_annotations (= 1.20240722.0) + - abseil/base/endian (= 1.20240722.0) + - abseil/base/errno_saver (= 1.20240722.0) + - abseil/base/fast_type_id (= 1.20240722.0) + - abseil/base/log_severity (= 1.20240722.0) + - abseil/base/malloc_internal (= 1.20240722.0) + - abseil/base/no_destructor (= 1.20240722.0) + - abseil/base/nullability (= 1.20240722.0) + - abseil/base/poison (= 1.20240722.0) + - abseil/base/prefetch (= 1.20240722.0) + - abseil/base/pretty_function (= 1.20240722.0) + - abseil/base/raw_logging_internal (= 1.20240722.0) + - abseil/base/spinlock_wait (= 1.20240722.0) + - abseil/base/strerror (= 1.20240722.0) + - abseil/base/throw_delegate (= 1.20240722.0) + - abseil/base/atomic_hook (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/base/base (1.20240722.0): + - abseil/base/atomic_hook + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/cycleclock_internal + - abseil/base/dynamic_annotations + - abseil/base/log_severity + - abseil/base/nullability + - abseil/base/raw_logging_internal + - abseil/base/spinlock_wait + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/base/base_internal (1.20240722.0): + - abseil/base/config + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/base/config (1.20240722.0): + - abseil/xcprivacy + - abseil/base/core_headers (1.20240722.0): + - abseil/base/config + - abseil/xcprivacy + - abseil/base/cycleclock_internal (1.20240722.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/xcprivacy + - abseil/base/dynamic_annotations (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/base/endian (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/nullability + - abseil/xcprivacy + - abseil/base/errno_saver (1.20240722.0): + - abseil/base/config + - abseil/xcprivacy + - abseil/base/fast_type_id (1.20240722.0): + - abseil/base/config + - abseil/xcprivacy + - abseil/base/log_severity (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/base/malloc_internal (1.20240722.0): + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/raw_logging_internal + - abseil/xcprivacy + - abseil/base/no_destructor (1.20240722.0): + - abseil/base/config + - abseil/base/nullability + - abseil/xcprivacy + - abseil/base/nullability (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/base/poison (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/malloc_internal + - abseil/xcprivacy + - abseil/base/prefetch (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/base/pretty_function (1.20240722.0): + - abseil/xcprivacy + - abseil/base/raw_logging_internal (1.20240722.0): + - abseil/base/atomic_hook + - abseil/base/config + - abseil/base/core_headers + - abseil/base/errno_saver + - abseil/base/log_severity + - abseil/xcprivacy + - abseil/base/spinlock_wait (1.20240722.0): + - abseil/base/base_internal + - abseil/base/core_headers + - abseil/base/errno_saver + - abseil/xcprivacy + - abseil/base/strerror (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/errno_saver + - abseil/xcprivacy + - abseil/base/throw_delegate (1.20240722.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/xcprivacy + - abseil/cleanup/cleanup (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/cleanup/cleanup_internal + - abseil/xcprivacy + - abseil/cleanup/cleanup_internal (1.20240722.0): + - abseil/base/base_internal + - abseil/base/core_headers + - abseil/utility/utility + - abseil/xcprivacy + - abseil/container/common (1.20240722.0): + - abseil/meta/type_traits + - abseil/types/optional + - abseil/xcprivacy + - abseil/container/common_policy_traits (1.20240722.0): + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/container/compressed_tuple (1.20240722.0): + - abseil/utility/utility + - abseil/xcprivacy + - abseil/container/container_memory (1.20240722.0): + - abseil/base/config + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/utility/utility + - abseil/xcprivacy + - abseil/container/fixed_array (1.20240722.0): + - abseil/algorithm/algorithm + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/throw_delegate + - abseil/container/compressed_tuple + - abseil/memory/memory + - abseil/xcprivacy + - abseil/container/flat_hash_map (1.20240722.0): + - abseil/algorithm/container + - abseil/base/core_headers + - abseil/container/container_memory + - abseil/container/hash_container_defaults + - abseil/container/raw_hash_map + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/container/flat_hash_set (1.20240722.0): + - abseil/algorithm/container + - abseil/base/core_headers + - abseil/container/container_memory + - abseil/container/hash_container_defaults + - abseil/container/raw_hash_set + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/container/hash_container_defaults (1.20240722.0): + - abseil/base/config + - abseil/container/hash_function_defaults + - abseil/xcprivacy + - abseil/container/hash_function_defaults (1.20240722.0): + - abseil/base/config + - abseil/container/common + - abseil/hash/hash + - abseil/meta/type_traits + - abseil/strings/cord + - abseil/strings/strings + - abseil/xcprivacy + - abseil/container/hash_policy_traits (1.20240722.0): + - abseil/container/common_policy_traits + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/container/hashtable_debug_hooks (1.20240722.0): + - abseil/base/config + - abseil/xcprivacy + - abseil/container/hashtablez_sampler (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/no_destructor + - abseil/base/raw_logging_internal + - abseil/debugging/stacktrace + - abseil/memory/memory + - abseil/profiling/exponential_biased + - abseil/profiling/sample_recorder + - abseil/synchronization/synchronization + - abseil/time/time + - abseil/utility/utility + - abseil/xcprivacy + - abseil/container/inlined_vector (1.20240722.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/base/throw_delegate + - abseil/container/inlined_vector_internal + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/container/inlined_vector_internal (1.20240722.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/container/compressed_tuple + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/types/span + - abseil/xcprivacy + - abseil/container/layout (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/debugging/demangle_internal + - abseil/meta/type_traits + - abseil/strings/strings + - abseil/types/span + - abseil/utility/utility + - abseil/xcprivacy + - abseil/container/raw_hash_map (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/throw_delegate + - abseil/container/container_memory + - abseil/container/raw_hash_set + - abseil/xcprivacy + - abseil/container/raw_hash_set (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/endian + - abseil/base/prefetch + - abseil/base/raw_logging_internal + - abseil/container/common + - abseil/container/compressed_tuple + - abseil/container/container_memory + - abseil/container/hash_policy_traits + - abseil/container/hashtable_debug_hooks + - abseil/container/hashtablez_sampler + - abseil/hash/hash + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/utility/utility + - abseil/xcprivacy + - abseil/crc/cpu_detect (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/xcprivacy + - abseil/crc/crc32c (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/prefetch + - abseil/crc/cpu_detect + - abseil/crc/crc_internal + - abseil/crc/non_temporal_memcpy + - abseil/strings/str_format + - abseil/strings/strings + - abseil/xcprivacy + - abseil/crc/crc_cord_state (1.20240722.0): + - abseil/base/config + - abseil/base/no_destructor + - abseil/crc/crc32c + - abseil/numeric/bits + - abseil/xcprivacy + - abseil/crc/crc_internal (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/prefetch + - abseil/base/raw_logging_internal + - abseil/crc/cpu_detect + - abseil/memory/memory + - abseil/numeric/bits + - abseil/xcprivacy + - abseil/crc/non_temporal_arm_intrinsics (1.20240722.0): + - abseil/base/config + - abseil/xcprivacy + - abseil/crc/non_temporal_memcpy (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/crc/non_temporal_arm_intrinsics + - abseil/xcprivacy + - abseil/debugging/bounded_utf8_length_sequence (1.20240722.0): + - abseil/base/config + - abseil/numeric/bits + - abseil/xcprivacy + - abseil/debugging/debugging_internal (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/errno_saver + - abseil/base/raw_logging_internal + - abseil/xcprivacy + - abseil/debugging/decode_rust_punycode (1.20240722.0): + - abseil/base/config + - abseil/base/nullability + - abseil/debugging/bounded_utf8_length_sequence + - abseil/debugging/utf8_for_code_point + - abseil/xcprivacy + - abseil/debugging/demangle_internal (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/nullability + - abseil/debugging/demangle_rust + - abseil/numeric/bits + - abseil/xcprivacy + - abseil/debugging/demangle_rust (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/debugging/decode_rust_punycode + - abseil/xcprivacy + - abseil/debugging/examine_stack (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/debugging/stacktrace + - abseil/debugging/symbolize + - abseil/xcprivacy + - abseil/debugging/stacktrace (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/raw_logging_internal + - abseil/debugging/debugging_internal + - abseil/xcprivacy + - abseil/debugging/symbolize (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/malloc_internal + - abseil/base/raw_logging_internal + - abseil/debugging/debugging_internal + - abseil/debugging/demangle_internal + - abseil/strings/strings + - abseil/xcprivacy + - abseil/debugging/utf8_for_code_point (1.20240722.0): + - abseil/base/config + - abseil/xcprivacy + - abseil/flags/commandlineflag (1.20240722.0): + - abseil/base/config + - abseil/base/fast_type_id + - abseil/flags/commandlineflag_internal + - abseil/strings/strings + - abseil/types/optional + - abseil/xcprivacy + - abseil/flags/commandlineflag_internal (1.20240722.0): + - abseil/base/config + - abseil/base/fast_type_id + - abseil/xcprivacy + - abseil/flags/config (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/flags/path_util + - abseil/flags/program_name + - abseil/strings/strings + - abseil/synchronization/synchronization + - abseil/xcprivacy + - abseil/flags/flag (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/flags/commandlineflag + - abseil/flags/config + - abseil/flags/flag_internal + - abseil/flags/reflection + - abseil/strings/strings + - abseil/xcprivacy + - abseil/flags/flag_internal (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/flags/commandlineflag + - abseil/flags/commandlineflag_internal + - abseil/flags/config + - abseil/flags/marshalling + - abseil/flags/reflection + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/strings/strings + - abseil/synchronization/synchronization + - abseil/utility/utility + - abseil/xcprivacy + - abseil/flags/marshalling (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/log_severity + - abseil/numeric/int128 + - abseil/strings/str_format + - abseil/strings/strings + - abseil/types/optional + - abseil/xcprivacy + - abseil/flags/path_util (1.20240722.0): + - abseil/base/config + - abseil/strings/strings + - abseil/xcprivacy + - abseil/flags/private_handle_accessor (1.20240722.0): + - abseil/base/config + - abseil/flags/commandlineflag + - abseil/flags/commandlineflag_internal + - abseil/strings/strings + - abseil/xcprivacy + - abseil/flags/program_name (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/flags/path_util + - abseil/strings/strings + - abseil/synchronization/synchronization + - abseil/xcprivacy + - abseil/flags/reflection (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/no_destructor + - abseil/container/flat_hash_map + - abseil/flags/commandlineflag + - abseil/flags/commandlineflag_internal + - abseil/flags/config + - abseil/flags/private_handle_accessor + - abseil/strings/strings + - abseil/synchronization/synchronization + - abseil/xcprivacy + - abseil/functional/any_invocable (1.20240722.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/utility/utility + - abseil/xcprivacy + - abseil/functional/bind_front (1.20240722.0): + - abseil/base/base_internal + - abseil/container/compressed_tuple + - abseil/meta/type_traits + - abseil/utility/utility + - abseil/xcprivacy + - abseil/functional/function_ref (1.20240722.0): + - abseil/base/base_internal + - abseil/base/core_headers + - abseil/functional/any_invocable + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/hash/city (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/xcprivacy + - abseil/hash/hash (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/container/fixed_array + - abseil/functional/function_ref + - abseil/hash/city + - abseil/hash/low_level_hash + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/strings/strings + - abseil/types/optional + - abseil/types/variant + - abseil/utility/utility + - abseil/xcprivacy + - abseil/hash/low_level_hash (1.20240722.0): + - abseil/base/config + - abseil/base/endian + - abseil/base/prefetch + - abseil/numeric/int128 + - abseil/xcprivacy + - abseil/log/absl_check (1.20240722.0): + - abseil/log/internal/check_impl + - abseil/xcprivacy + - abseil/log/absl_log (1.20240722.0): + - abseil/log/internal/log_impl + - abseil/xcprivacy + - abseil/log/absl_vlog_is_on (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/log/internal/vlog_config + - abseil/strings/strings + - abseil/xcprivacy + - abseil/log/check (1.20240722.0): + - abseil/log/internal/check_impl + - abseil/log/internal/check_op + - abseil/log/internal/conditions + - abseil/log/internal/log_message + - abseil/log/internal/strip + - abseil/xcprivacy + - abseil/log/globals (1.20240722.0): + - abseil/base/atomic_hook + - abseil/base/config + - abseil/base/core_headers + - abseil/base/log_severity + - abseil/base/raw_logging_internal + - abseil/hash/hash + - abseil/log/internal/vlog_config + - abseil/strings/strings + - abseil/xcprivacy + - abseil/log/internal/append_truncated (1.20240722.0): + - abseil/base/config + - abseil/strings/strings + - abseil/types/span + - abseil/xcprivacy + - abseil/log/internal/check_impl (1.20240722.0): + - abseil/base/core_headers + - abseil/log/internal/check_op + - abseil/log/internal/conditions + - abseil/log/internal/log_message + - abseil/log/internal/strip + - abseil/xcprivacy + - abseil/log/internal/check_op (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/log/internal/nullguard + - abseil/log/internal/nullstream + - abseil/log/internal/strip + - abseil/strings/strings + - abseil/xcprivacy + - abseil/log/internal/conditions (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/log/internal/voidify + - abseil/xcprivacy + - abseil/log/internal/config (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/log/internal/fnmatch (1.20240722.0): + - abseil/base/config + - abseil/strings/strings + - abseil/xcprivacy + - abseil/log/internal/format (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/log_severity + - abseil/log/internal/append_truncated + - abseil/log/internal/config + - abseil/log/internal/globals + - abseil/strings/str_format + - abseil/strings/strings + - abseil/time/time + - abseil/types/span + - abseil/xcprivacy + - abseil/log/internal/globals (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/log_severity + - abseil/base/raw_logging_internal + - abseil/strings/strings + - abseil/time/time + - abseil/xcprivacy + - abseil/log/internal/log_impl (1.20240722.0): + - abseil/log/absl_vlog_is_on + - abseil/log/internal/conditions + - abseil/log/internal/log_message + - abseil/log/internal/strip + - abseil/xcprivacy + - abseil/log/internal/log_message (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/errno_saver + - abseil/base/log_severity + - abseil/base/raw_logging_internal + - abseil/base/strerror + - abseil/container/inlined_vector + - abseil/debugging/examine_stack + - abseil/log/globals + - abseil/log/internal/append_truncated + - abseil/log/internal/format + - abseil/log/internal/globals + - abseil/log/internal/log_sink_set + - abseil/log/internal/nullguard + - abseil/log/internal/proto + - abseil/log/log_entry + - abseil/log/log_sink + - abseil/log/log_sink_registry + - abseil/memory/memory + - abseil/strings/strings + - abseil/time/time + - abseil/types/span + - abseil/xcprivacy + - abseil/log/internal/log_sink_set (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/log_severity + - abseil/base/no_destructor + - abseil/base/raw_logging_internal + - abseil/cleanup/cleanup + - abseil/log/globals + - abseil/log/internal/config + - abseil/log/internal/globals + - abseil/log/log_entry + - abseil/log/log_sink + - abseil/strings/strings + - abseil/synchronization/synchronization + - abseil/types/span + - abseil/xcprivacy + - abseil/log/internal/nullguard (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/log/internal/nullstream (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/log_severity + - abseil/strings/strings + - abseil/xcprivacy + - abseil/log/internal/proto (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/strings/strings + - abseil/types/span + - abseil/xcprivacy + - abseil/log/internal/strip (1.20240722.0): + - abseil/base/core_headers + - abseil/base/log_severity + - abseil/log/internal/log_message + - abseil/log/internal/nullstream + - abseil/xcprivacy + - abseil/log/internal/vlog_config (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/no_destructor + - abseil/log/internal/fnmatch + - abseil/memory/memory + - abseil/strings/strings + - abseil/synchronization/synchronization + - abseil/types/optional + - abseil/xcprivacy + - abseil/log/internal/voidify (1.20240722.0): + - abseil/base/config + - abseil/xcprivacy + - abseil/log/log (1.20240722.0): + - abseil/log/internal/log_impl + - abseil/log/vlog_is_on + - abseil/xcprivacy + - abseil/log/log_entry (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/log_severity + - abseil/log/internal/config + - abseil/strings/strings + - abseil/time/time + - abseil/types/span + - abseil/xcprivacy + - abseil/log/log_sink (1.20240722.0): + - abseil/base/config + - abseil/log/log_entry + - abseil/xcprivacy + - abseil/log/log_sink_registry (1.20240722.0): + - abseil/base/config + - abseil/log/internal/log_sink_set + - abseil/log/log_sink + - abseil/xcprivacy + - abseil/log/vlog_is_on (1.20240722.0): + - abseil/log/absl_vlog_is_on + - abseil/xcprivacy + - abseil/memory (1.20240722.0): + - abseil/memory/memory (= 1.20240722.0) + - abseil/memory/memory (1.20240722.0): + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/meta (1.20240722.0): + - abseil/meta/type_traits (= 1.20240722.0) + - abseil/meta/type_traits (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/numeric/bits (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/numeric/int128 (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/numeric/bits + - abseil/types/compare + - abseil/xcprivacy + - abseil/numeric/representation (1.20240722.0): + - abseil/base/config + - abseil/xcprivacy + - abseil/profiling/exponential_biased (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/xcprivacy + - abseil/profiling/sample_recorder (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/synchronization/synchronization + - abseil/time/time + - abseil/xcprivacy + - abseil/random/bit_gen_ref (1.20240722.0): + - abseil/base/core_headers + - abseil/base/fast_type_id + - abseil/meta/type_traits + - abseil/random/internal/distribution_caller + - abseil/random/internal/fast_uniform_bits + - abseil/random/random + - abseil/xcprivacy + - abseil/random/distributions (1.20240722.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/random/internal/distribution_caller + - abseil/random/internal/fast_uniform_bits + - abseil/random/internal/fastmath + - abseil/random/internal/generate_real + - abseil/random/internal/iostream_state_saver + - abseil/random/internal/traits + - abseil/random/internal/uniform_helper + - abseil/random/internal/wide_multiply + - abseil/strings/strings + - abseil/xcprivacy + - abseil/random/internal/distribution_caller (1.20240722.0): + - abseil/base/config + - abseil/base/fast_type_id + - abseil/utility/utility + - abseil/xcprivacy + - abseil/random/internal/fast_uniform_bits (1.20240722.0): + - abseil/base/config + - abseil/meta/type_traits + - abseil/random/internal/traits + - abseil/xcprivacy + - abseil/random/internal/fastmath (1.20240722.0): + - abseil/numeric/bits + - abseil/xcprivacy + - abseil/random/internal/generate_real (1.20240722.0): + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/random/internal/fastmath + - abseil/random/internal/traits + - abseil/xcprivacy + - abseil/random/internal/iostream_state_saver (1.20240722.0): + - abseil/meta/type_traits + - abseil/numeric/int128 + - abseil/xcprivacy + - abseil/random/internal/nonsecure_base (1.20240722.0): + - abseil/base/core_headers + - abseil/container/inlined_vector + - abseil/meta/type_traits + - abseil/random/internal/pool_urbg + - abseil/random/internal/salted_seed_seq + - abseil/random/internal/seed_material + - abseil/types/span + - abseil/xcprivacy + - abseil/random/internal/pcg_engine (1.20240722.0): + - abseil/base/config + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/random/internal/fastmath + - abseil/random/internal/iostream_state_saver + - abseil/xcprivacy + - abseil/random/internal/platform (1.20240722.0): + - abseil/base/config + - abseil/xcprivacy + - abseil/random/internal/pool_urbg (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/random/internal/randen + - abseil/random/internal/seed_material + - abseil/random/internal/traits + - abseil/random/seed_gen_exception + - abseil/types/span + - abseil/xcprivacy + - abseil/random/internal/randen (1.20240722.0): + - abseil/base/raw_logging_internal + - abseil/random/internal/platform + - abseil/random/internal/randen_hwaes + - abseil/random/internal/randen_slow + - abseil/xcprivacy + - abseil/random/internal/randen_engine (1.20240722.0): + - abseil/base/endian + - abseil/meta/type_traits + - abseil/random/internal/iostream_state_saver + - abseil/random/internal/randen + - abseil/xcprivacy + - abseil/random/internal/randen_hwaes (1.20240722.0): + - abseil/base/config + - abseil/random/internal/platform + - abseil/random/internal/randen_hwaes_impl + - abseil/xcprivacy + - abseil/random/internal/randen_hwaes_impl (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/numeric/int128 + - abseil/random/internal/platform + - abseil/xcprivacy + - abseil/random/internal/randen_slow (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/numeric/int128 + - abseil/random/internal/platform + - abseil/xcprivacy + - abseil/random/internal/salted_seed_seq (1.20240722.0): + - abseil/container/inlined_vector + - abseil/meta/type_traits + - abseil/random/internal/seed_material + - abseil/types/optional + - abseil/types/span + - abseil/xcprivacy + - abseil/random/internal/seed_material (1.20240722.0): + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/raw_logging_internal + - abseil/random/internal/fast_uniform_bits + - abseil/strings/strings + - abseil/types/optional + - abseil/types/span + - abseil/xcprivacy + - abseil/random/internal/traits (1.20240722.0): + - abseil/base/config + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/xcprivacy + - abseil/random/internal/uniform_helper (1.20240722.0): + - abseil/base/config + - abseil/meta/type_traits + - abseil/random/internal/traits + - abseil/xcprivacy + - abseil/random/internal/wide_multiply (1.20240722.0): + - abseil/base/config + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/random/internal/traits + - abseil/xcprivacy + - abseil/random/random (1.20240722.0): + - abseil/random/distributions + - abseil/random/internal/nonsecure_base + - abseil/random/internal/pcg_engine + - abseil/random/internal/pool_urbg + - abseil/random/internal/randen_engine + - abseil/random/seed_sequences + - abseil/xcprivacy + - abseil/random/seed_gen_exception (1.20240722.0): + - abseil/base/config + - abseil/xcprivacy + - abseil/random/seed_sequences (1.20240722.0): + - abseil/base/config + - abseil/base/nullability + - abseil/random/internal/pool_urbg + - abseil/random/internal/salted_seed_seq + - abseil/random/internal/seed_material + - abseil/random/seed_gen_exception + - abseil/strings/string_view + - abseil/types/span + - abseil/xcprivacy + - abseil/status/status (1.20240722.0): + - abseil/base/atomic_hook + - abseil/base/config + - abseil/base/core_headers + - abseil/base/no_destructor + - abseil/base/nullability + - abseil/base/raw_logging_internal + - abseil/base/strerror + - abseil/container/inlined_vector + - abseil/debugging/stacktrace + - abseil/debugging/symbolize + - abseil/functional/function_ref + - abseil/memory/memory + - abseil/strings/cord + - abseil/strings/str_format + - abseil/strings/strings + - abseil/types/optional + - abseil/types/span + - abseil/xcprivacy + - abseil/status/statusor (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/nullability + - abseil/base/raw_logging_internal + - abseil/meta/type_traits + - abseil/status/status + - abseil/strings/has_ostream_operator + - abseil/strings/str_format + - abseil/strings/strings + - abseil/types/variant + - abseil/utility/utility + - abseil/xcprivacy + - abseil/strings/charset (1.20240722.0): + - abseil/base/core_headers + - abseil/strings/string_view + - abseil/xcprivacy + - abseil/strings/cord (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/nullability + - abseil/base/raw_logging_internal + - abseil/container/inlined_vector + - abseil/crc/crc32c + - abseil/crc/crc_cord_state + - abseil/functional/function_ref + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/strings/cord_internal + - abseil/strings/cordz_functions + - abseil/strings/cordz_info + - abseil/strings/cordz_statistics + - abseil/strings/cordz_update_scope + - abseil/strings/cordz_update_tracker + - abseil/strings/internal + - abseil/strings/strings + - abseil/types/compare + - abseil/types/optional + - abseil/types/span + - abseil/xcprivacy + - abseil/strings/cord_internal (1.20240722.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/base/throw_delegate + - abseil/container/compressed_tuple + - abseil/container/container_memory + - abseil/container/inlined_vector + - abseil/container/layout + - abseil/crc/crc_cord_state + - abseil/functional/function_ref + - abseil/meta/type_traits + - abseil/strings/strings + - abseil/types/span + - abseil/xcprivacy + - abseil/strings/cordz_functions (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/profiling/exponential_biased + - abseil/xcprivacy + - abseil/strings/cordz_handle (1.20240722.0): + - abseil/base/config + - abseil/base/no_destructor + - abseil/base/raw_logging_internal + - abseil/synchronization/synchronization + - abseil/xcprivacy + - abseil/strings/cordz_info (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/container/inlined_vector + - abseil/debugging/stacktrace + - abseil/strings/cord_internal + - abseil/strings/cordz_functions + - abseil/strings/cordz_handle + - abseil/strings/cordz_statistics + - abseil/strings/cordz_update_tracker + - abseil/synchronization/synchronization + - abseil/time/time + - abseil/types/span + - abseil/xcprivacy + - abseil/strings/cordz_statistics (1.20240722.0): + - abseil/base/config + - abseil/strings/cordz_update_tracker + - abseil/xcprivacy + - abseil/strings/cordz_update_scope (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/strings/cord_internal + - abseil/strings/cordz_info + - abseil/strings/cordz_update_tracker + - abseil/xcprivacy + - abseil/strings/cordz_update_tracker (1.20240722.0): + - abseil/base/config + - abseil/xcprivacy + - abseil/strings/has_ostream_operator (1.20240722.0): + - abseil/base/config + - abseil/xcprivacy + - abseil/strings/internal (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/raw_logging_internal + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/strings/str_format (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/nullability + - abseil/strings/str_format_internal + - abseil/strings/string_view + - abseil/types/span + - abseil/xcprivacy + - abseil/strings/str_format_internal (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/container/fixed_array + - abseil/container/inlined_vector + - abseil/functional/function_ref + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/numeric/representation + - abseil/strings/strings + - abseil/types/optional + - abseil/types/span + - abseil/utility/utility + - abseil/xcprivacy + - abseil/strings/string_view (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/nullability + - abseil/base/throw_delegate + - abseil/xcprivacy + - abseil/strings/strings (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/endian + - abseil/base/nullability + - abseil/base/raw_logging_internal + - abseil/base/throw_delegate + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/numeric/bits + - abseil/numeric/int128 + - abseil/strings/charset + - abseil/strings/internal + - abseil/strings/string_view + - abseil/xcprivacy + - abseil/synchronization/graphcycles_internal (1.20240722.0): + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/malloc_internal + - abseil/base/raw_logging_internal + - abseil/xcprivacy + - abseil/synchronization/kernel_timeout_internal (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/time/time + - abseil/xcprivacy + - abseil/synchronization/synchronization (1.20240722.0): + - abseil/base/atomic_hook + - abseil/base/base + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/dynamic_annotations + - abseil/base/malloc_internal + - abseil/base/raw_logging_internal + - abseil/debugging/stacktrace + - abseil/debugging/symbolize + - abseil/synchronization/graphcycles_internal + - abseil/synchronization/kernel_timeout_internal + - abseil/time/time + - abseil/xcprivacy + - abseil/time (1.20240722.0): + - abseil/time/internal (= 1.20240722.0) + - abseil/time/time (= 1.20240722.0) + - abseil/time/internal (1.20240722.0): + - abseil/time/internal/cctz (= 1.20240722.0) + - abseil/time/internal/cctz (1.20240722.0): + - abseil/time/internal/cctz/civil_time (= 1.20240722.0) + - abseil/time/internal/cctz/time_zone (= 1.20240722.0) + - abseil/time/internal/cctz/civil_time (1.20240722.0): + - abseil/base/config + - abseil/xcprivacy + - abseil/time/internal/cctz/time_zone (1.20240722.0): + - abseil/base/config + - abseil/time/internal/cctz/civil_time + - abseil/xcprivacy + - abseil/time/time (1.20240722.0): + - abseil/base/base + - abseil/base/config + - abseil/base/core_headers + - abseil/base/raw_logging_internal + - abseil/numeric/int128 + - abseil/strings/strings + - abseil/time/internal/cctz/civil_time + - abseil/time/internal/cctz/time_zone + - abseil/types/optional + - abseil/xcprivacy + - abseil/types (1.20240722.0): + - abseil/types/any (= 1.20240722.0) + - abseil/types/bad_any_cast (= 1.20240722.0) + - abseil/types/bad_any_cast_impl (= 1.20240722.0) + - abseil/types/bad_optional_access (= 1.20240722.0) + - abseil/types/bad_variant_access (= 1.20240722.0) + - abseil/types/compare (= 1.20240722.0) + - abseil/types/optional (= 1.20240722.0) + - abseil/types/span (= 1.20240722.0) + - abseil/types/variant (= 1.20240722.0) + - abseil/types/any (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/base/fast_type_id + - abseil/meta/type_traits + - abseil/types/bad_any_cast + - abseil/utility/utility + - abseil/xcprivacy + - abseil/types/bad_any_cast (1.20240722.0): + - abseil/base/config + - abseil/types/bad_any_cast_impl + - abseil/xcprivacy + - abseil/types/bad_any_cast_impl (1.20240722.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/xcprivacy + - abseil/types/bad_optional_access (1.20240722.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/xcprivacy + - abseil/types/bad_variant_access (1.20240722.0): + - abseil/base/config + - abseil/base/raw_logging_internal + - abseil/xcprivacy + - abseil/types/compare (1.20240722.0): + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/types/optional (1.20240722.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/base/nullability + - abseil/memory/memory + - abseil/meta/type_traits + - abseil/types/bad_optional_access + - abseil/utility/utility + - abseil/xcprivacy + - abseil/types/span (1.20240722.0): + - abseil/algorithm/algorithm + - abseil/base/core_headers + - abseil/base/nullability + - abseil/base/throw_delegate + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/types/variant (1.20240722.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/base/core_headers + - abseil/meta/type_traits + - abseil/types/bad_variant_access + - abseil/utility/utility + - abseil/xcprivacy + - abseil/utility/utility (1.20240722.0): + - abseil/base/base_internal + - abseil/base/config + - abseil/meta/type_traits + - abseil/xcprivacy + - abseil/xcprivacy (1.20240722.0) + - BoringSSL-GRPC (0.0.37): + - BoringSSL-GRPC/Implementation (= 0.0.37) + - BoringSSL-GRPC/Interface (= 0.0.37) + - BoringSSL-GRPC/Implementation (0.0.37): + - BoringSSL-GRPC/Interface (= 0.0.37) + - BoringSSL-GRPC/Interface (0.0.37) + - cloud_firestore (6.0.3): + - Firebase/Firestore (= 12.4.0) + - firebase_core + - Flutter + - Firebase/Auth (12.4.0): + - Firebase/CoreOnly + - FirebaseAuth (~> 12.4.0) + - Firebase/CoreOnly (12.4.0): + - FirebaseCore (~> 12.4.0) + - Firebase/Firestore (12.4.0): + - Firebase/CoreOnly + - FirebaseFirestore (~> 12.4.0) + - Firebase/Storage (12.4.0): + - Firebase/CoreOnly + - FirebaseStorage (~> 12.4.0) + - firebase_auth (6.1.1): + - Firebase/Auth (= 12.4.0) + - firebase_core + - Flutter + - firebase_core (4.2.0): + - Firebase/CoreOnly (= 12.4.0) + - Flutter + - firebase_storage (13.0.3): + - Firebase/Storage (= 12.4.0) + - firebase_core + - Flutter + - FirebaseAppCheckInterop (12.4.0) + - FirebaseAuth (12.4.0): + - FirebaseAppCheckInterop (~> 12.4.0) + - FirebaseAuthInterop (~> 12.4.0) + - FirebaseCore (~> 12.4.0) + - FirebaseCoreExtension (~> 12.4.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/Environment (~> 8.1) + - GTMSessionFetcher/Core (< 6.0, >= 3.4) + - RecaptchaInterop (~> 101.0) + - FirebaseAuthInterop (12.4.0) + - FirebaseCore (12.4.0): + - FirebaseCoreInternal (~> 12.4.0) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/Logger (~> 8.1) + - FirebaseCoreExtension (12.4.0): + - FirebaseCore (~> 12.4.0) + - FirebaseCoreInternal (12.4.0): + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - FirebaseFirestore (12.4.0): + - FirebaseCore (~> 12.4.0) + - FirebaseCoreExtension (~> 12.4.0) + - FirebaseFirestoreInternal (~> 12.4.0) + - FirebaseSharedSwift (~> 12.4.0) + - FirebaseFirestoreInternal (12.4.0): + - abseil/algorithm (~> 1.20240722.0) + - abseil/base (~> 1.20240722.0) + - abseil/container/flat_hash_map (~> 1.20240722.0) + - abseil/memory (~> 1.20240722.0) + - abseil/meta (~> 1.20240722.0) + - abseil/strings/strings (~> 1.20240722.0) + - abseil/time (~> 1.20240722.0) + - abseil/types (~> 1.20240722.0) + - FirebaseAppCheckInterop (~> 12.4.0) + - FirebaseCore (~> 12.4.0) + - "gRPC-C++ (~> 1.69.0)" + - gRPC-Core (~> 1.69.0) + - leveldb-library (~> 1.22) + - nanopb (~> 3.30910.0) + - FirebaseSharedSwift (12.4.0) + - FirebaseStorage (12.4.0): + - FirebaseAppCheckInterop (~> 12.4.0) + - FirebaseAuthInterop (~> 12.4.0) + - FirebaseCore (~> 12.4.0) + - FirebaseCoreExtension (~> 12.4.0) + - GoogleUtilities/Environment (~> 8.1) + - GTMSessionFetcher/Core (< 6.0, >= 3.4) + - Flutter (1.0.0) + - GoogleUtilities/AppDelegateSwizzler (8.1.0): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Privacy + - GoogleUtilities/Environment (8.1.0): + - GoogleUtilities/Privacy + - GoogleUtilities/Logger (8.1.0): + - GoogleUtilities/Environment + - GoogleUtilities/Privacy + - GoogleUtilities/Network (8.1.0): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Privacy + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (8.1.0)": + - GoogleUtilities/Privacy + - GoogleUtilities/Privacy (8.1.0) + - GoogleUtilities/Reachability (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy + - "gRPC-C++ (1.69.0)": + - "gRPC-C++/Implementation (= 1.69.0)" + - "gRPC-C++/Interface (= 1.69.0)" + - "gRPC-C++/Implementation (1.69.0)": + - abseil/algorithm/container (~> 1.20240722.0) + - abseil/base/base (~> 1.20240722.0) + - abseil/base/config (~> 1.20240722.0) + - abseil/base/core_headers (~> 1.20240722.0) + - abseil/base/log_severity (~> 1.20240722.0) + - abseil/base/no_destructor (~> 1.20240722.0) + - abseil/cleanup/cleanup (~> 1.20240722.0) + - abseil/container/flat_hash_map (~> 1.20240722.0) + - abseil/container/flat_hash_set (~> 1.20240722.0) + - abseil/container/inlined_vector (~> 1.20240722.0) + - abseil/flags/flag (~> 1.20240722.0) + - abseil/flags/marshalling (~> 1.20240722.0) + - abseil/functional/any_invocable (~> 1.20240722.0) + - abseil/functional/bind_front (~> 1.20240722.0) + - abseil/functional/function_ref (~> 1.20240722.0) + - abseil/hash/hash (~> 1.20240722.0) + - abseil/log/absl_check (~> 1.20240722.0) + - abseil/log/absl_log (~> 1.20240722.0) + - abseil/log/check (~> 1.20240722.0) + - abseil/log/globals (~> 1.20240722.0) + - abseil/log/log (~> 1.20240722.0) + - abseil/memory/memory (~> 1.20240722.0) + - abseil/meta/type_traits (~> 1.20240722.0) + - abseil/numeric/bits (~> 1.20240722.0) + - abseil/random/bit_gen_ref (~> 1.20240722.0) + - abseil/random/distributions (~> 1.20240722.0) + - abseil/random/random (~> 1.20240722.0) + - abseil/status/status (~> 1.20240722.0) + - abseil/status/statusor (~> 1.20240722.0) + - abseil/strings/cord (~> 1.20240722.0) + - abseil/strings/str_format (~> 1.20240722.0) + - abseil/strings/strings (~> 1.20240722.0) + - abseil/synchronization/synchronization (~> 1.20240722.0) + - abseil/time/time (~> 1.20240722.0) + - abseil/types/optional (~> 1.20240722.0) + - abseil/types/span (~> 1.20240722.0) + - abseil/types/variant (~> 1.20240722.0) + - abseil/utility/utility (~> 1.20240722.0) + - "gRPC-C++/Interface (= 1.69.0)" + - "gRPC-C++/Privacy (= 1.69.0)" + - gRPC-Core (= 1.69.0) + - "gRPC-C++/Interface (1.69.0)" + - "gRPC-C++/Privacy (1.69.0)" + - gRPC-Core (1.69.0): + - gRPC-Core/Implementation (= 1.69.0) + - gRPC-Core/Interface (= 1.69.0) + - gRPC-Core/Implementation (1.69.0): + - abseil/algorithm/container (~> 1.20240722.0) + - abseil/base/base (~> 1.20240722.0) + - abseil/base/config (~> 1.20240722.0) + - abseil/base/core_headers (~> 1.20240722.0) + - abseil/base/log_severity (~> 1.20240722.0) + - abseil/base/no_destructor (~> 1.20240722.0) + - abseil/cleanup/cleanup (~> 1.20240722.0) + - abseil/container/flat_hash_map (~> 1.20240722.0) + - abseil/container/flat_hash_set (~> 1.20240722.0) + - abseil/container/inlined_vector (~> 1.20240722.0) + - abseil/flags/flag (~> 1.20240722.0) + - abseil/flags/marshalling (~> 1.20240722.0) + - abseil/functional/any_invocable (~> 1.20240722.0) + - abseil/functional/bind_front (~> 1.20240722.0) + - abseil/functional/function_ref (~> 1.20240722.0) + - abseil/hash/hash (~> 1.20240722.0) + - abseil/log/check (~> 1.20240722.0) + - abseil/log/globals (~> 1.20240722.0) + - abseil/log/log (~> 1.20240722.0) + - abseil/memory/memory (~> 1.20240722.0) + - abseil/meta/type_traits (~> 1.20240722.0) + - abseil/numeric/bits (~> 1.20240722.0) + - abseil/random/bit_gen_ref (~> 1.20240722.0) + - abseil/random/distributions (~> 1.20240722.0) + - abseil/random/random (~> 1.20240722.0) + - abseil/status/status (~> 1.20240722.0) + - abseil/status/statusor (~> 1.20240722.0) + - abseil/strings/cord (~> 1.20240722.0) + - abseil/strings/str_format (~> 1.20240722.0) + - abseil/strings/strings (~> 1.20240722.0) + - abseil/synchronization/synchronization (~> 1.20240722.0) + - abseil/time/time (~> 1.20240722.0) + - abseil/types/optional (~> 1.20240722.0) + - abseil/types/span (~> 1.20240722.0) + - abseil/types/variant (~> 1.20240722.0) + - abseil/utility/utility (~> 1.20240722.0) + - BoringSSL-GRPC (= 0.0.37) + - gRPC-Core/Interface (= 1.69.0) + - gRPC-Core/Privacy (= 1.69.0) + - gRPC-Core/Interface (1.69.0) + - gRPC-Core/Privacy (1.69.0) + - GTMSessionFetcher/Core (5.0.0) + - leveldb-library (1.22.6) + - nanopb (3.30910.0): + - nanopb/decode (= 3.30910.0) + - nanopb/encode (= 3.30910.0) + - nanopb/decode (3.30910.0) + - nanopb/encode (3.30910.0) + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS + - RecaptchaInterop (101.0.0) + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - sqflite_darwin (0.0.4): + - Flutter + - FlutterMacOS + +DEPENDENCIES: + - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`) + - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) + - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - firebase_storage (from `.symlinks/plugins/firebase_storage/ios`) + - Flutter (from `Flutter`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) + - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) + +SPEC REPOS: + trunk: + - abseil + - BoringSSL-GRPC + - Firebase + - FirebaseAppCheckInterop + - FirebaseAuth + - FirebaseAuthInterop + - FirebaseCore + - FirebaseCoreExtension + - FirebaseCoreInternal + - FirebaseFirestore + - FirebaseFirestoreInternal + - FirebaseSharedSwift + - FirebaseStorage + - GoogleUtilities + - "gRPC-C++" + - gRPC-Core + - GTMSessionFetcher + - leveldb-library + - nanopb + - RecaptchaInterop + +EXTERNAL SOURCES: + cloud_firestore: + :path: ".symlinks/plugins/cloud_firestore/ios" + firebase_auth: + :path: ".symlinks/plugins/firebase_auth/ios" + firebase_core: + :path: ".symlinks/plugins/firebase_core/ios" + firebase_storage: + :path: ".symlinks/plugins/firebase_storage/ios" + Flutter: + :path: Flutter + path_provider_foundation: + :path: ".symlinks/plugins/path_provider_foundation/darwin" + shared_preferences_foundation: + :path: ".symlinks/plugins/shared_preferences_foundation/darwin" + sqflite_darwin: + :path: ".symlinks/plugins/sqflite_darwin/darwin" + +SPEC CHECKSUMS: + abseil: a05cc83bf02079535e17169a73c5be5ba47f714b + BoringSSL-GRPC: dded2a44897e45f28f08ae87a55ee4bcd19bc508 + cloud_firestore: 79014bb3b303d451717ed5fe69fded8a2b2e8dc2 + Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e + firebase_auth: c2b8be95d602d4e8a9148fae72333ef78e69cc20 + firebase_core: 744984dbbed8b3036abf34f0b98d80f130a7e464 + firebase_storage: 0ba617a05b24aec050395e4d5d3773c0d7518a15 + FirebaseAppCheckInterop: f734c802f21fe1da0837708f0f9a27218c8a4ed0 + FirebaseAuth: 4a2aed737c84114a9d9b33d11ae1b147d6b94889 + FirebaseAuthInterop: 858e6b754966e70740a4370dd1503dfffe6dbb49 + FirebaseCore: bb595f3114953664e3c1dc032f008a244147cfd3 + FirebaseCoreExtension: 7e1f7118ee970e001a8013719fb90950ee5e0018 + FirebaseCoreInternal: d7f5a043c2cd01a08103ab586587c1468047bca6 + FirebaseFirestore: 2a6183381cf7679b1bb000eb76a8e3178e25dee2 + FirebaseFirestoreInternal: 6577a27cd5dc3722b900042527f86d4ea1626134 + FirebaseSharedSwift: 93426a1de92f19e1199fac5295a4f8df16458daa + FirebaseStorage: 20d6b56fb8a40ebaa03d6a2889fe33dac64adb73 + Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 + GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 + "gRPC-C++": cc207623316fb041a7a3e774c252cf68a058b9e8 + gRPC-Core: 860978b7db482de8b4f5e10677216309b5ff6330 + GTMSessionFetcher: 02d6e866e90bc236f48a703a041dfe43e6221a29 + leveldb-library: cc8b8f8e013647a295ad3f8cd2ddf49a6f19be19 + nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 + path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880 + RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba + shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb + sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 + +PODFILE CHECKSUM: 3c63482e143d1b91d2d2560aee9fb04ecc74ac7e + +COCOAPODS: 1.16.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 4a5d436..4c9e96c 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; + 341139FAC055BA9A06DCDCAF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5ED32C8606A316B7B31483E7 /* Pods_Runner.framework */; }; + 36FFEE815E5C239ECA957FD8 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CFAD14AFF1F90B8D3A191D09 /* Pods_RunnerTests.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; @@ -42,9 +44,12 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; + 219192A994BE2E810D21CF3E /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 5ED32C8606A316B7B31483E7 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 6AF687E372B820F28E6339F0 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -55,13 +60,27 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9F6D4F116EE51B9C9970A524 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + A3F457A9E261AAC5A4F510DE /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + CFAD14AFF1F90B8D3A191D09 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D07CA0E6FCECE164629DBA3A /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + E1FE5A7588919AB0262267D9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 95CA8DCA7A2925DEEFFC154C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 36FFEE815E5C239ECA957FD8 /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 97C146EB1CF9000F007C117D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 341139FAC055BA9A06DCDCAF /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -76,6 +95,15 @@ path = RunnerTests; sourceTree = ""; }; + 41888A3DB1CEF07094BFA7D0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 5ED32C8606A316B7B31483E7 /* Pods_Runner.framework */, + CFAD14AFF1F90B8D3A191D09 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -94,6 +122,8 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, + B4A1811494B588835818801A /* Pods */, + 41888A3DB1CEF07094BFA7D0 /* Frameworks */, ); sourceTree = ""; }; @@ -121,6 +151,20 @@ path = Runner; sourceTree = ""; }; + B4A1811494B588835818801A /* Pods */ = { + isa = PBXGroup; + children = ( + 219192A994BE2E810D21CF3E /* Pods-Runner.debug.xcconfig */, + E1FE5A7588919AB0262267D9 /* Pods-Runner.release.xcconfig */, + A3F457A9E261AAC5A4F510DE /* Pods-Runner.profile.xcconfig */, + 9F6D4F116EE51B9C9970A524 /* Pods-RunnerTests.debug.xcconfig */, + D07CA0E6FCECE164629DBA3A /* Pods-RunnerTests.release.xcconfig */, + 6AF687E372B820F28E6339F0 /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -128,8 +172,10 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( + 1D789ECFE31345547CE34B50 /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, + 95CA8DCA7A2925DEEFFC154C /* Frameworks */, ); buildRules = ( ); @@ -145,12 +191,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 1D144EDA24AD260BDCD8A5DC /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 3D0E01DF18316EDF773EAB0D /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -222,6 +270,50 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 1D144EDA24AD260BDCD8A5DC /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 1D789ECFE31345547CE34B50 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -238,6 +330,23 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 3D0E01DF18316EDF773EAB0D /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -365,6 +474,7 @@ DEVELOPMENT_TEAM = CBCA453YCM; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -379,6 +489,7 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 9F6D4F116EE51B9C9970A524 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -396,6 +507,7 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = D07CA0E6FCECE164629DBA3A /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -411,6 +523,7 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 6AF687E372B820F28E6339F0 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; @@ -545,6 +658,7 @@ DEVELOPMENT_TEAM = CBCA453YCM; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -568,6 +682,7 @@ DEVELOPMENT_TEAM = CBCA453YCM; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.6; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + From 24b82041c0eaa9d09fedba42cfd148199699e5fc Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Wed, 29 Oct 2025 18:57:35 +0700 Subject: [PATCH 07/32] fix(runner): move WidgetsFlutterBinding.ensureInitialized() inside runZonedGuarded for proper app initialization --- CHANGELOG.md | 3 +++ lib/runner.dart | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a17d455..507ecc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `public_member_api_docs` rule to `analysis_options.yaml`. - Extracted main application widget to `app.dart`, added `runner.dart` for app initialization with error handling, and simplified `main.dart`. +### Fixed +- Moved `WidgetsFlutterBinding.ensureInitialized()` inside `runZonedGuarded` in `runner.dart` to ensure proper app initialization and error handling. + --- ## [0.1.0] - 2025-10-29 diff --git a/lib/runner.dart b/lib/runner.dart index bd99385..8264454 100644 --- a/lib/runner.dart +++ b/lib/runner.dart @@ -6,9 +6,9 @@ import 'features/app/app.dart'; /// The main entry point of the application. void run() { - WidgetsFlutterBinding.ensureInitialized(); runZonedGuarded( () { + WidgetsFlutterBinding.ensureInitialized(); runApp(const FlutterStarterTemplate()); }, (error, stackTrace) { From cf980482d412727cc0adb4c7e9e83041909f3971 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Wed, 29 Oct 2025 19:10:22 +0700 Subject: [PATCH 08/32] feat(debug): add DebugScreen widget for debugging purposes --- CHANGELOG.md | 1 + lib/features/app/app.dart | 10 +++------- .../debug/presentation/debug_screen.dart | 16 ++++++++++++++++ 3 files changed, 20 insertions(+), 7 deletions(-) create mode 100644 lib/features/debug/presentation/debug_screen.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 507ecc8..11120c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Implemented base Clean Architecture structure (`core`, `features`, `uikit`) with `data`, `domain`, and `presentation` layers. - Added all project dependencies from `pubspec.yaml`. - Integrated `GetIt` for dependency injection. +- Added `DebugScreen` widget for testing and debugging purposes. ### Changed - Added `public_member_api_docs` rule to `analysis_options.yaml`. diff --git a/lib/features/app/app.dart b/lib/features/app/app.dart index 453596a..f40ab5d 100644 --- a/lib/features/app/app.dart +++ b/lib/features/app/app.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; +import '../debug/presentation/debug_screen.dart'; + /// The main application widget. class FlutterStarterTemplate extends StatelessWidget { /// Creates a [FlutterStarterTemplate] widget. @@ -7,12 +9,6 @@ class FlutterStarterTemplate extends StatelessWidget { @override Widget build(BuildContext context) { - return const MaterialApp( - home: Scaffold( - body: Center( - child: Text('Flutter Starter Template'), - ), - ), - ); + return const MaterialApp(home: DebugScreen()); } } diff --git a/lib/features/debug/presentation/debug_screen.dart b/lib/features/debug/presentation/debug_screen.dart new file mode 100644 index 0000000..f3276e4 --- /dev/null +++ b/lib/features/debug/presentation/debug_screen.dart @@ -0,0 +1,16 @@ +import 'package:flutter/material.dart'; + +/// A screen for debugging purposes. +class DebugScreen extends StatelessWidget { + /// Creates a [DebugScreen]. + const DebugScreen({super.key}); + + @override + Widget build(BuildContext context) { + return const Scaffold( + body: Center( + child: Text('Flutter Starter Template'), + ), + ); + } +} From 9034180fe1dd1381ef26f235a2078ad42a930f68 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Wed, 29 Oct 2025 19:13:37 +0700 Subject: [PATCH 09/32] feat(router): add router and route paths for app navigation --- CHANGELOG.md | 1 + lib/core/router/.gitkeep | 0 lib/core/router/router.dart | 16 ++++++++++++++++ lib/core/router/router_paths.dart | 11 +++++++++++ lib/features/app/app.dart | 4 ++-- 5 files changed, 30 insertions(+), 2 deletions(-) delete mode 100644 lib/core/router/.gitkeep create mode 100644 lib/core/router/router.dart create mode 100644 lib/core/router/router_paths.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 11120c5..25215fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added all project dependencies from `pubspec.yaml`. - Integrated `GetIt` for dependency injection. - Added `DebugScreen` widget for testing and debugging purposes. +- Added application router with `GoRouter` and defined route paths for navigation. ### Changed - Added `public_member_api_docs` rule to `analysis_options.yaml`. diff --git a/lib/core/router/.gitkeep b/lib/core/router/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/lib/core/router/router.dart b/lib/core/router/router.dart new file mode 100644 index 0000000..47fd912 --- /dev/null +++ b/lib/core/router/router.dart @@ -0,0 +1,16 @@ +import 'package:go_router/go_router.dart'; + +import '../../features/debug/presentation/debug_screen.dart'; +import 'router_paths.dart'; + +/// The application's router using GoRouter. +final router = GoRouter( + initialLocation: AppRoutePaths.debugPath, + routes: [ + // Define your app's routes here + GoRoute( + path: AppRoutePaths.debugPath, + builder: (context, state) => const DebugScreen(), + ), + ], +); diff --git a/lib/core/router/router_paths.dart b/lib/core/router/router_paths.dart new file mode 100644 index 0000000..8cec8e7 --- /dev/null +++ b/lib/core/router/router_paths.dart @@ -0,0 +1,11 @@ +/// Contains all the route paths used for app navigation. +/// +/// Each path should be defined as a static constant string, for example: +/// ```dart +/// /// Description of the route. +/// static const String flowPath = 'flowPath'; +/// ``` +abstract class AppRoutePaths { + /// Route path for the debug screen. + static const debugPath = '/debug'; +} diff --git a/lib/features/app/app.dart b/lib/features/app/app.dart index f40ab5d..d721fa9 100644 --- a/lib/features/app/app.dart +++ b/lib/features/app/app.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../debug/presentation/debug_screen.dart'; +import '../../core/router/router.dart'; /// The main application widget. class FlutterStarterTemplate extends StatelessWidget { @@ -9,6 +9,6 @@ class FlutterStarterTemplate extends StatelessWidget { @override Widget build(BuildContext context) { - return const MaterialApp(home: DebugScreen()); + return MaterialApp.router(routerConfig: router); } } From ff299efc40f73b5071ff7c94d778e785ceb00be2 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Wed, 12 Nov 2025 11:50:22 +0700 Subject: [PATCH 10/32] feat(logger): integrate logger with AppLogger abstraction, DI setup and error handling --- CHANGELOG.md | 1 + lib/core/di/di.dart | 11 ++++++ lib/core/utils/.gitkeep | 0 lib/core/utils/logger/.gitkeep | 0 lib/core/utils/logger/app_logger.dart | 20 +++++++++++ lib/core/utils/logger/app_logger_impl.dart | 41 ++++++++++++++++++++++ lib/core/utils/logger/logger_setup.dart | 25 +++++++++++++ lib/features/common/.gitkeep | 0 lib/main.dart | 2 +- lib/runner.dart | 32 +++++++++++++---- 10 files changed, 124 insertions(+), 8 deletions(-) delete mode 100644 lib/core/utils/.gitkeep delete mode 100644 lib/core/utils/logger/.gitkeep create mode 100644 lib/core/utils/logger/app_logger.dart create mode 100644 lib/core/utils/logger/app_logger_impl.dart create mode 100644 lib/core/utils/logger/logger_setup.dart delete mode 100644 lib/features/common/.gitkeep diff --git a/CHANGELOG.md b/CHANGELOG.md index 25215fd..ab1cd86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Integrated `GetIt` for dependency injection. - Added `DebugScreen` widget for testing and debugging purposes. - Added application router with `GoRouter` and defined route paths for navigation. +- Integrated `logger` package with `AppLogger` abstraction, DI setup, and global error handling (Flutter & platform-level). ### Changed - Added `public_member_api_docs` rule to `analysis_options.yaml`. diff --git a/lib/core/di/di.dart b/lib/core/di/di.dart index 6a7fd2d..95ded3d 100644 --- a/lib/core/di/di.dart +++ b/lib/core/di/di.dart @@ -1,4 +1,9 @@ import 'package:get_it/get_it.dart'; +import 'package:logger/logger.dart'; + +import '../utils/logger/app_logger.dart'; +import '../utils/logger/app_logger_impl.dart'; +import '../utils/logger/logger_setup.dart'; /// Global instance of the GetIt service locator for dependency management. final di = GetIt.instance; @@ -7,4 +12,10 @@ final di = GetIt.instance; void setupDI() { // Register your dependencies here, for example: // di.registerLazySingleton(() => SomeServiceImpl()); + + // Logger + final logger = createLogger(); + + di.registerLazySingleton(() => logger); + di.registerLazySingleton(() => AppLoggerImpl(di())); } diff --git a/lib/core/utils/.gitkeep b/lib/core/utils/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/lib/core/utils/logger/.gitkeep b/lib/core/utils/logger/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/lib/core/utils/logger/app_logger.dart b/lib/core/utils/logger/app_logger.dart new file mode 100644 index 0000000..26c3c31 --- /dev/null +++ b/lib/core/utils/logger/app_logger.dart @@ -0,0 +1,20 @@ +/// Abstract class for application logging. +abstract class AppLogger { + /// Log a message at trace level. + void t(String message, [Object? error, StackTrace? stackTrace]); + + /// Log a message at debug level. + void d(String message, [Object? error, StackTrace? stackTrace]); + + /// Log a message at info level. + void i(String message, [Object? error, StackTrace? stackTrace]); + + /// Log a message at warning level. + void w(String message, [Object? error, StackTrace? stackTrace]); + + /// Log a message at error level. + void e(String message, [Object? error, StackTrace? stackTrace]); + + /// Log a message at fatal level. + void f(String message, [Object? error, StackTrace? stackTrace]); +} diff --git a/lib/core/utils/logger/app_logger_impl.dart b/lib/core/utils/logger/app_logger_impl.dart new file mode 100644 index 0000000..4b05457 --- /dev/null +++ b/lib/core/utils/logger/app_logger_impl.dart @@ -0,0 +1,41 @@ +import 'package:logger/logger.dart'; + +import 'app_logger.dart'; + +/// Implementation of [AppLogger]. +class AppLoggerImpl implements AppLogger { + final Logger _logger; + + /// Creates an instance of [AppLoggerImpl] with the provided [Logger]. + AppLoggerImpl(this._logger); + + @override + void t(String message, [Object? error, StackTrace? stackTrace]) { + _logger.t(message, error: error, stackTrace: stackTrace); + } + + @override + void d(String message, [Object? error, StackTrace? stackTrace]) { + _logger.d(message, error: error, stackTrace: stackTrace); + } + + @override + void i(String message, [Object? error, StackTrace? stackTrace]) { + _logger.i(message, error: error, stackTrace: stackTrace); + } + + @override + void w(String message, [Object? error, StackTrace? stackTrace]) { + _logger.w(message, error: error, stackTrace: stackTrace); + } + + @override + void e(String message, [Object? error, StackTrace? stackTrace]) { + _logger.e(message, error: error, stackTrace: stackTrace); + } + + @override + void f(String message, [Object? error, StackTrace? stackTrace]) { + _logger.f(message, error: error, stackTrace: stackTrace); + } +} diff --git a/lib/core/utils/logger/logger_setup.dart b/lib/core/utils/logger/logger_setup.dart new file mode 100644 index 0000000..9cdd252 --- /dev/null +++ b/lib/core/utils/logger/logger_setup.dart @@ -0,0 +1,25 @@ +import 'package:flutter/foundation.dart'; +import 'package:logger/logger.dart'; + +/// Creates and configures the [Logger] instance based on the environment. +Logger createLogger() { + const isRelease = kReleaseMode; + final printer = isRelease + ? PrettyPrinter( + methodCount: 0, + errorMethodCount: 5, + lineLength: 100, + colors: false, + printEmojis: false, + ) + : PrettyPrinter( + errorMethodCount: 5, + lineLength: 100, + colors: false, + ); + return Logger( + level: isRelease ? Level.warning : Level.debug, + printer: printer, + // TODO: later add custom LogOutput that duplicates in Crashlytics + ); +} diff --git a/lib/features/common/.gitkeep b/lib/features/common/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/lib/main.dart b/lib/main.dart index e6d5fc4..acb02c6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,3 @@ import 'runner.dart'; -void main() => run(); +Future main() async => await run(); diff --git a/lib/runner.dart b/lib/runner.dart index 8264454..c1774af 100644 --- a/lib/runner.dart +++ b/lib/runner.dart @@ -1,19 +1,37 @@ import 'dart:async'; +import 'dart:ui'; import 'package:flutter/material.dart'; +import 'core/di/di.dart'; +import 'core/utils/logger/app_logger.dart'; import 'features/app/app.dart'; /// The main entry point of the application. -void run() { - runZonedGuarded( - () { +Future run() async { + await runZonedGuarded>( + () async { WidgetsFlutterBinding.ensureInitialized(); + + // Setup dependency injection + setupDI(); + final logger = di(); + + // Catch errors from Flutter framework + FlutterError.onError = (details) { + logger.e('FlutterError', details.exception, details.stack); + // TODO: forward to Crashlytics + }; + + // Catch errors from outside Flutter framework + PlatformDispatcher.instance.onError = (error, stack) { + logger.f('PlatformDispatcherError', error, stack); + // TODO: forward to Crashlytics + return true; + }; + runApp(const FlutterStarterTemplate()); }, - (error, stackTrace) { - debugPrint('Uncaught error: $error'); - debugPrint('Stack trace: $stackTrace'); - }, + (error, stackTrace) => di().f('UncaughtZoneError', error, stackTrace), ); } From 11eab76f091b34577c3befc76b9d13a2cfd299f3 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Thu, 13 Nov 2025 15:20:07 +0700 Subject: [PATCH 11/32] feat(firebase): add firebase core setup --- .gitignore | 3 +++ CHANGELOG.md | 1 + android/app/build.gradle.kts | 4 ++++ android/settings.gradle.kts | 4 ++++ firebase.json | 30 ++++++++++++++++++++++++++++ ios/Podfile.lock | 4 ++-- ios/Runner.xcodeproj/project.pbxproj | 23 +++++++++++++++++++++ lib/runner.dart | 6 ++++++ pubspec.lock | 12 +++++------ 9 files changed, 79 insertions(+), 8 deletions(-) create mode 100644 firebase.json diff --git a/.gitignore b/.gitignore index c6ca0e1..d540810 100644 --- a/.gitignore +++ b/.gitignore @@ -59,6 +59,9 @@ linked_*.ds unlinked.ds unlinked_spec.ds +# Firebase related +firebase_options.dart + # Symbolication related app.*.symbols diff --git a/CHANGELOG.md b/CHANGELOG.md index ab1cd86..df97e02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `DebugScreen` widget for testing and debugging purposes. - Added application router with `GoRouter` and defined route paths for navigation. - Integrated `logger` package with `AppLogger` abstraction, DI setup, and global error handling (Flutter & platform-level). +- Integrated `Firebase Core` for authentication and analytics features. ### Changed - Added `public_member_api_docs` rule to `analysis_options.yaml`. diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index cfc331e..09adac3 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -1,5 +1,9 @@ plugins { id("com.android.application") + // START: FlutterFire Configuration + id("com.google.gms.google-services") + id("com.google.firebase.crashlytics") + // END: FlutterFire Configuration id("kotlin-android") // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. id("dev.flutter.flutter-gradle-plugin") diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts index fb605bc..9d2e323 100644 --- a/android/settings.gradle.kts +++ b/android/settings.gradle.kts @@ -20,6 +20,10 @@ pluginManagement { plugins { id("dev.flutter.flutter-plugin-loader") version "1.0.0" id("com.android.application") version "8.9.1" apply false + // START: FlutterFire Configuration + id("com.google.gms.google-services") version("4.3.15") apply false + id("com.google.firebase.crashlytics") version("2.8.1") apply false + // END: FlutterFire Configuration id("org.jetbrains.kotlin.android") version "2.1.0" apply false } diff --git a/firebase.json b/firebase.json new file mode 100644 index 0000000..ccedb7e --- /dev/null +++ b/firebase.json @@ -0,0 +1,30 @@ +{ + "flutter": { + "platforms": { + "android": { + "default": { + "projectId": "flutter-starter-template-demo", + "appId": "1:342615478088:android:90b7d2d8512fa74206ad7d", + "fileOutput": "android/app/google-services.json" + } + }, + "ios": { + "default": { + "projectId": "flutter-starter-template-demo", + "appId": "1:342615478088:ios:efb343cce927d23906ad7d", + "uploadDebugSymbols": true, + "fileOutput": "ios/Runner/GoogleService-Info.plist" + } + }, + "dart": { + "lib/firebase_options.dart": { + "projectId": "flutter-starter-template-demo", + "configurations": { + "android": "1:342615478088:android:90b7d2d8512fa74206ad7d", + "ios": "1:342615478088:ios:efb343cce927d23906ad7d" + } + } + } + } + } +} \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 046c54b..f3b8bc3 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1213,7 +1213,7 @@ PODS: - Firebase/Auth (= 12.4.0) - firebase_core - Flutter - - firebase_core (4.2.0): + - firebase_core (4.2.1): - Firebase/CoreOnly (= 12.4.0) - Flutter - firebase_storage (13.0.3): @@ -1456,7 +1456,7 @@ SPEC CHECKSUMS: cloud_firestore: 79014bb3b303d451717ed5fe69fded8a2b2e8dc2 Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e firebase_auth: c2b8be95d602d4e8a9148fae72333ef78e69cc20 - firebase_core: 744984dbbed8b3036abf34f0b98d80f130a7e464 + firebase_core: f1aafb21c14f497e5498f7ffc4dc63cbb52b2594 firebase_storage: 0ba617a05b24aec050395e4d5d3773c0d7518a15 FirebaseAppCheckInterop: f734c802f21fe1da0837708f0f9a27218c8a4ed0 FirebaseAuth: 4a2aed737c84114a9d9b33d11ae1b147d6b94889 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 4c9e96c..c15b095 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 341139FAC055BA9A06DCDCAF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5ED32C8606A316B7B31483E7 /* Pods_Runner.framework */; }; 36FFEE815E5C239ECA957FD8 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CFAD14AFF1F90B8D3A191D09 /* Pods_RunnerTests.framework */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 54FFB4BB9263E21E8BB6898C /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = E55C273BDD75DB4F8B5AC5E9 /* GoogleService-Info.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -65,6 +66,7 @@ CFAD14AFF1F90B8D3A191D09 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D07CA0E6FCECE164629DBA3A /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; E1FE5A7588919AB0262267D9 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + E55C273BDD75DB4F8B5AC5E9 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -124,6 +126,7 @@ 331C8082294A63A400263BE5 /* RunnerTests */, B4A1811494B588835818801A /* Pods */, 41888A3DB1CEF07094BFA7D0 /* Frameworks */, + E55C273BDD75DB4F8B5AC5E9 /* GoogleService-Info.plist */, ); sourceTree = ""; }; @@ -199,6 +202,7 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3D0E01DF18316EDF773EAB0D /* [CP] Embed Pods Frameworks */, + 05D396F55CC39F0934932997 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */, ); buildRules = ( ); @@ -264,12 +268,31 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + 54FFB4BB9263E21E8BB6898C /* GoogleService-Info.plist in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 05D396F55CC39F0934932997 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "FlutterFire: \"flutterfire upload-crashlytics-symbols\""; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\n#!/bin/bash\nPATH=\"${PATH}:$FLUTTER_ROOT/bin:${PUB_CACHE}/bin:$HOME/.pub-cache/bin\"\n\nif [ -z \"$PODS_ROOT\" ] || [ ! -d \"$PODS_ROOT/FirebaseCrashlytics\" ]; then\n # Cannot use \"BUILD_DIR%/Build/*\" as per Firebase documentation, it points to \"flutter-project/build/ios/*\" path which doesn't have run script\n DERIVED_DATA_PATH=$(echo \"$BUILD_ROOT\" | sed -E 's|(.*DerivedData/[^/]+).*|\\1|')\n PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT=\"${DERIVED_DATA_PATH}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run\"\nelse\n PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT=\"$PODS_ROOT/FirebaseCrashlytics/run\"\nfi\n\n# Command to upload symbols script used to upload symbols to Firebase server\nflutterfire upload-crashlytics-symbols --upload-symbols-script-path=\"$PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT\" --platform=ios --apple-project-path=\"${SRCROOT}\" --env-platform-name=\"${PLATFORM_NAME}\" --env-configuration=\"${CONFIGURATION}\" --env-project-dir=\"${PROJECT_DIR}\" --env-built-products-dir=\"${BUILT_PRODUCTS_DIR}\" --env-dwarf-dsym-folder-path=\"${DWARF_DSYM_FOLDER_PATH}\" --env-dwarf-dsym-file-name=\"${DWARF_DSYM_FILE_NAME}\" --env-infoplist-path=\"${INFOPLIST_PATH}\" --default-config=default\n"; + }; 1D144EDA24AD260BDCD8A5DC /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; diff --git a/lib/runner.dart b/lib/runner.dart index c1774af..e5753a7 100644 --- a/lib/runner.dart +++ b/lib/runner.dart @@ -1,11 +1,13 @@ import 'dart:async'; import 'dart:ui'; +import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; import 'core/di/di.dart'; import 'core/utils/logger/app_logger.dart'; import 'features/app/app.dart'; +import 'firebase_options.dart'; /// The main entry point of the application. Future run() async { @@ -13,6 +15,10 @@ Future run() async { () async { WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp( + options: DefaultFirebaseOptions.currentPlatform, + ); + // Setup dependency injection setupDI(); final logger = di(); diff --git a/pubspec.lock b/pubspec.lock index 19072a8..459e49a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: f871a7d1b686bea1f13722aa51ab31554d05c81f47054d6de48cc8c45153508b + sha256: "8a1f5f3020ef2a74fb93f7ab3ef127a8feea33a7a2276279113660784ee7516a" url: "https://pub.dev" source: hosted - version: "1.3.63" + version: "1.3.64" analyzer: dependency: transitive description: @@ -349,10 +349,10 @@ packages: dependency: "direct main" description: name: firebase_core - sha256: "132e1c311bc41e7d387b575df0aacdf24efbf4930365eb61042be5bde3978f03" + sha256: "1f2dfd9f535d81f8b06d7a50ecda6eac1e6922191ed42e09ca2c84bd2288927c" url: "https://pub.dev" source: hosted - version: "4.2.0" + version: "4.2.1" firebase_core_platform_interface: dependency: transitive description: @@ -365,10 +365,10 @@ packages: dependency: transitive description: name: firebase_core_web - sha256: ecde2def458292404a4fcd3731ee4992fd631a0ec359d2d67c33baa8da5ec8ae + sha256: ff18fabb0ad0ed3595d2f2c85007ecc794aadecdff5b3bb1460b7ee47cded398 url: "https://pub.dev" source: hosted - version: "3.2.0" + version: "3.3.0" firebase_storage: dependency: "direct main" description: From 46cfb621dfd4261a52934e6e1d5f206b60bcefc0 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Sat, 15 Nov 2025 09:29:49 +0700 Subject: [PATCH 12/32] feat(firebase): add Crashlytics integration for global error handling --- CHANGELOG.md | 1 + ios/Podfile.lock | 58 +++++++++++++++++++++++++ lib/core/utils/logger/logger_setup.dart | 1 - lib/runner.dart | 13 ++++-- pubspec.lock | 16 +++++++ pubspec.yaml | 1 + 6 files changed, 85 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df97e02..006f077 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added application router with `GoRouter` and defined route paths for navigation. - Integrated `logger` package with `AppLogger` abstraction, DI setup, and global error handling (Flutter & platform-level). - Integrated `Firebase Core` for authentication and analytics features. +- Integrated `Firebase Crashlytics` for global error handling. ### Changed - Added `public_member_api_docs` rule to `analysis_options.yaml`. diff --git a/ios/Podfile.lock b/ios/Podfile.lock index f3b8bc3..e8923fb 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1203,6 +1203,9 @@ PODS: - FirebaseAuth (~> 12.4.0) - Firebase/CoreOnly (12.4.0): - FirebaseCore (~> 12.4.0) + - Firebase/Crashlytics (12.4.0): + - Firebase/CoreOnly + - FirebaseCrashlytics (~> 12.4.0) - Firebase/Firestore (12.4.0): - Firebase/CoreOnly - FirebaseFirestore (~> 12.4.0) @@ -1216,6 +1219,10 @@ PODS: - firebase_core (4.2.1): - Firebase/CoreOnly (= 12.4.0) - Flutter + - firebase_crashlytics (5.0.4): + - Firebase/Crashlytics (= 12.4.0) + - firebase_core + - Flutter - firebase_storage (13.0.3): - Firebase/Storage (= 12.4.0) - firebase_core @@ -1239,6 +1246,15 @@ PODS: - FirebaseCore (~> 12.4.0) - FirebaseCoreInternal (12.4.0): - "GoogleUtilities/NSData+zlib (~> 8.1)" + - FirebaseCrashlytics (12.4.0): + - FirebaseCore (~> 12.4.0) + - FirebaseInstallations (~> 12.4.0) + - FirebaseRemoteConfigInterop (~> 12.4.0) + - FirebaseSessions (~> 12.4.0) + - GoogleDataTransport (~> 10.1) + - GoogleUtilities/Environment (~> 8.1) + - nanopb (~> 3.30910.0) + - PromisesObjC (~> 2.4) - FirebaseFirestore (12.4.0): - FirebaseCore (~> 12.4.0) - FirebaseCoreExtension (~> 12.4.0) @@ -1259,6 +1275,21 @@ PODS: - gRPC-Core (~> 1.69.0) - leveldb-library (~> 1.22) - nanopb (~> 3.30910.0) + - FirebaseInstallations (12.4.0): + - FirebaseCore (~> 12.4.0) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/UserDefaults (~> 8.1) + - PromisesObjC (~> 2.4) + - FirebaseRemoteConfigInterop (12.4.0) + - FirebaseSessions (12.4.0): + - FirebaseCore (~> 12.4.0) + - FirebaseCoreExtension (~> 12.4.0) + - FirebaseInstallations (~> 12.4.0) + - GoogleDataTransport (~> 10.1) + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/UserDefaults (~> 8.1) + - nanopb (~> 3.30910.0) + - PromisesSwift (~> 2.1) - FirebaseSharedSwift (12.4.0) - FirebaseStorage (12.4.0): - FirebaseAppCheckInterop (~> 12.4.0) @@ -1268,6 +1299,9 @@ PODS: - GoogleUtilities/Environment (~> 8.1) - GTMSessionFetcher/Core (< 6.0, >= 3.4) - Flutter (1.0.0) + - GoogleDataTransport (10.1.0): + - nanopb (~> 3.30910.0) + - PromisesObjC (~> 2.4) - GoogleUtilities/AppDelegateSwizzler (8.1.0): - GoogleUtilities/Environment - GoogleUtilities/Logger @@ -1289,6 +1323,9 @@ PODS: - GoogleUtilities/Reachability (8.1.0): - GoogleUtilities/Logger - GoogleUtilities/Privacy + - GoogleUtilities/UserDefaults (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy - "gRPC-C++ (1.69.0)": - "gRPC-C++/Implementation (= 1.69.0)" - "gRPC-C++/Interface (= 1.69.0)" @@ -1391,6 +1428,9 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS + - PromisesObjC (2.4.0) + - PromisesSwift (2.4.0): + - PromisesObjC (= 2.4.0) - RecaptchaInterop (101.0.0) - shared_preferences_foundation (0.0.1): - Flutter @@ -1403,6 +1443,7 @@ DEPENDENCIES: - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`) - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) + - firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`) - firebase_storage (from `.symlinks/plugins/firebase_storage/ios`) - Flutter (from `Flutter`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) @@ -1420,16 +1461,23 @@ SPEC REPOS: - FirebaseCore - FirebaseCoreExtension - FirebaseCoreInternal + - FirebaseCrashlytics - FirebaseFirestore - FirebaseFirestoreInternal + - FirebaseInstallations + - FirebaseRemoteConfigInterop + - FirebaseSessions - FirebaseSharedSwift - FirebaseStorage + - GoogleDataTransport - GoogleUtilities - "gRPC-C++" - gRPC-Core - GTMSessionFetcher - leveldb-library - nanopb + - PromisesObjC + - PromisesSwift - RecaptchaInterop EXTERNAL SOURCES: @@ -1439,6 +1487,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/firebase_auth/ios" firebase_core: :path: ".symlinks/plugins/firebase_core/ios" + firebase_crashlytics: + :path: ".symlinks/plugins/firebase_crashlytics/ios" firebase_storage: :path: ".symlinks/plugins/firebase_storage/ios" Flutter: @@ -1457,6 +1507,7 @@ SPEC CHECKSUMS: Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e firebase_auth: c2b8be95d602d4e8a9148fae72333ef78e69cc20 firebase_core: f1aafb21c14f497e5498f7ffc4dc63cbb52b2594 + firebase_crashlytics: 83c7467d7534975a4d779af43bd226d0a4616464 firebase_storage: 0ba617a05b24aec050395e4d5d3773c0d7518a15 FirebaseAppCheckInterop: f734c802f21fe1da0837708f0f9a27218c8a4ed0 FirebaseAuth: 4a2aed737c84114a9d9b33d11ae1b147d6b94889 @@ -1464,11 +1515,16 @@ SPEC CHECKSUMS: FirebaseCore: bb595f3114953664e3c1dc032f008a244147cfd3 FirebaseCoreExtension: 7e1f7118ee970e001a8013719fb90950ee5e0018 FirebaseCoreInternal: d7f5a043c2cd01a08103ab586587c1468047bca6 + FirebaseCrashlytics: a6ece278a837c7e88de2d9b5da0a3542f2342395 FirebaseFirestore: 2a6183381cf7679b1bb000eb76a8e3178e25dee2 FirebaseFirestoreInternal: 6577a27cd5dc3722b900042527f86d4ea1626134 + FirebaseInstallations: ae9f4902cb5bf1d0c5eaa31ec1f4e5495a0714e2 + FirebaseRemoteConfigInterop: 1e31ec72b89c9924367c59bfb5ec9ab60d1d6766 + FirebaseSessions: ba7c7a7ca8696a8d540eb3fe3800fbe98c79786d FirebaseSharedSwift: 93426a1de92f19e1199fac5295a4f8df16458daa FirebaseStorage: 20d6b56fb8a40ebaa03d6a2889fe33dac64adb73 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 + GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 "gRPC-C++": cc207623316fb041a7a3e774c252cf68a058b9e8 gRPC-Core: 860978b7db482de8b4f5e10677216309b5ff6330 @@ -1476,6 +1532,8 @@ SPEC CHECKSUMS: leveldb-library: cc8b8f8e013647a295ad3f8cd2ddf49a6f19be19 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 path_provider_foundation: bb55f6dbba17d0dccd6737fe6f7f34fbd0376880 + PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 + PromisesSwift: 9d77319bbe72ebf6d872900551f7eeba9bce2851 RecaptchaInterop: 11e0b637842dfb48308d242afc3f448062325aba shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0 diff --git a/lib/core/utils/logger/logger_setup.dart b/lib/core/utils/logger/logger_setup.dart index 9cdd252..6bb7a2e 100644 --- a/lib/core/utils/logger/logger_setup.dart +++ b/lib/core/utils/logger/logger_setup.dart @@ -20,6 +20,5 @@ Logger createLogger() { return Logger( level: isRelease ? Level.warning : Level.debug, printer: printer, - // TODO: later add custom LogOutput that duplicates in Crashlytics ); } diff --git a/lib/runner.dart b/lib/runner.dart index e5753a7..9e40e79 100644 --- a/lib/runner.dart +++ b/lib/runner.dart @@ -1,7 +1,8 @@ import 'dart:async'; -import 'dart:ui'; import 'package:firebase_core/firebase_core.dart'; +import 'package:firebase_crashlytics/firebase_crashlytics.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'core/di/di.dart'; @@ -18,6 +19,7 @@ Future run() async { await Firebase.initializeApp( options: DefaultFirebaseOptions.currentPlatform, ); + await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(kReleaseMode); // Setup dependency injection setupDI(); @@ -26,18 +28,21 @@ Future run() async { // Catch errors from Flutter framework FlutterError.onError = (details) { logger.e('FlutterError', details.exception, details.stack); - // TODO: forward to Crashlytics + FirebaseCrashlytics.instance.recordFlutterError(details); }; // Catch errors from outside Flutter framework PlatformDispatcher.instance.onError = (error, stack) { logger.f('PlatformDispatcherError', error, stack); - // TODO: forward to Crashlytics + FirebaseCrashlytics.instance.recordError(error, stack); return true; }; runApp(const FlutterStarterTemplate()); }, - (error, stackTrace) => di().f('UncaughtZoneError', error, stackTrace), + (error, stackTrace) { + di().f('UncaughtZoneError', error, stackTrace); + FirebaseCrashlytics.instance.recordError(error, stackTrace); + }, ); } diff --git a/pubspec.lock b/pubspec.lock index 459e49a..825d701 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -369,6 +369,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.3.0" + firebase_crashlytics: + dependency: "direct main" + description: + name: firebase_crashlytics + sha256: c3ebe3ed9f3b1d36c0864a4a28b041fcc2686f11fb2a4f7891319ea8d1d161cc + url: "https://pub.dev" + source: hosted + version: "5.0.4" + firebase_crashlytics_platform_interface: + dependency: transitive + description: + name: firebase_crashlytics_platform_interface + sha256: a8ca502fe3aa48b4f0b9e6e3bc0019085a247b5d1214cd342a189457975662db + url: "https://pub.dev" + source: hosted + version: "3.8.15" firebase_storage: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index a96b4d1..f29d36d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,6 +16,7 @@ dependencies: equatable: ^2.0.7 firebase_auth: ^6.1.1 firebase_core: ^4.2.0 + firebase_crashlytics: ^5.0.4 firebase_storage: ^13.0.3 flutter: sdk: flutter From e5d5eba3b112952a86d585fedb63d235c635ffcf Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Sat, 15 Nov 2025 18:45:09 +0700 Subject: [PATCH 13/32] feat(core): implement AppFailure base class and UnknownFailure subclass --- CHANGELOG.md | 1 + lib/core/failures/app_failure.dart | 17 +++++++++++++++++ lib/core/{errors => failures/feature}/.gitkeep | 0 lib/core/failures/unknown_failure.dart | 7 +++++++ 4 files changed, 25 insertions(+) create mode 100644 lib/core/failures/app_failure.dart rename lib/core/{errors => failures/feature}/.gitkeep (100%) create mode 100644 lib/core/failures/unknown_failure.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 006f077..83d5198 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Integrated `logger` package with `AppLogger` abstraction, DI setup, and global error handling (Flutter & platform-level). - Integrated `Firebase Core` for authentication and analytics features. - Integrated `Firebase Crashlytics` for global error handling. +- Added `AppFailure` base class and `UnknownFailure` implementation for unified domain-level error handling. ### Changed - Added `public_member_api_docs` rule to `analysis_options.yaml`. diff --git a/lib/core/failures/app_failure.dart b/lib/core/failures/app_failure.dart new file mode 100644 index 0000000..9e25e02 --- /dev/null +++ b/lib/core/failures/app_failure.dart @@ -0,0 +1,17 @@ +/// A base failure type used in the business logic layer. +/// +/// This class represents domain-level errors that wrap lower-level exceptions +/// and provide meaningful context for application logic. +abstract class AppFailure implements Exception { + /// A message describing the failure. + final String message; + + /// The original exception that caused this failure, if any. + final T? parentException; + + /// The stack trace at the point where the failure was created, if any. + final StackTrace? stackTrace; + + /// Creates an instance of [AppFailure]. + const AppFailure(this.parentException, this.message, this.stackTrace); +} diff --git a/lib/core/errors/.gitkeep b/lib/core/failures/feature/.gitkeep similarity index 100% rename from lib/core/errors/.gitkeep rename to lib/core/failures/feature/.gitkeep diff --git a/lib/core/failures/unknown_failure.dart b/lib/core/failures/unknown_failure.dart new file mode 100644 index 0000000..c47ec52 --- /dev/null +++ b/lib/core/failures/unknown_failure.dart @@ -0,0 +1,7 @@ +import 'app_failure.dart'; + +/// Unknown application error. +final class UnknownFailure extends AppFailure { + /// Creates an instance of [UnknownFailure]. + const UnknownFailure(super.parentException, super.message, super.stackTrace); +} From a6326fab8514b1b7a62e2a82560bc2a81b2d099e Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Sat, 15 Nov 2025 19:03:26 +0700 Subject: [PATCH 14/32] feat(core): introduce Result model with Success, Failure and AsyncResult types --- CHANGELOG.md | 1 + lib/core/result/.gitkeep | 0 lib/core/result/async_result.dart | 5 ++++ lib/core/result/result.dart | 44 +++++++++++++++++++++++++++++++ 4 files changed, 50 insertions(+) delete mode 100644 lib/core/result/.gitkeep create mode 100644 lib/core/result/async_result.dart create mode 100644 lib/core/result/result.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 83d5198..1a83240 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Integrated `Firebase Core` for authentication and analytics features. - Integrated `Firebase Crashlytics` for global error handling. - Added `AppFailure` base class and `UnknownFailure` implementation for unified domain-level error handling. + - Added sealed `Result` abstraction with `Success` and `Failure` implementations, plus `AsyncResult` type alias for standardizing async operation handling. ### Changed - Added `public_member_api_docs` rule to `analysis_options.yaml`. diff --git a/lib/core/result/.gitkeep b/lib/core/result/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/lib/core/result/async_result.dart b/lib/core/result/async_result.dart new file mode 100644 index 0000000..b5a096b --- /dev/null +++ b/lib/core/result/async_result.dart @@ -0,0 +1,5 @@ +import '../failures/app_failure.dart'; +import 'result.dart'; + +/// A type alias for an asynchronous [Result]. +typedef AsyncResult = Future>; diff --git a/lib/core/result/result.dart b/lib/core/result/result.dart new file mode 100644 index 0000000..2537c29 --- /dev/null +++ b/lib/core/result/result.dart @@ -0,0 +1,44 @@ +/// A sealed abstraction representing either a [Success] or a [Failure] result. +sealed class Result { + /// Creates a new [Result] instance. + const Result(); + + /// Creates a successful result containing the given [data]. + const factory Result.success(T data) = Success; + + /// Creates a failed result containing the given [error] and optional [stackTrace]. + const factory Result.failure(E error, [StackTrace? stackTrace]) = Failure; + + /// Whether this result represents a [Success]. + bool get isSuccess => this is Success; + + /// Whether this result represents a [Failure]. + bool get isFailure => this is Failure; + + /// Returns the success value if this is [Success], otherwise `null`. + T? get success => isSuccess ? (this as Success).data : null; + + /// Returns the failure value if this is [Failure], otherwise `null`. + E? get failure => isFailure ? (this as Failure).error : null; +} + +/// A successful result containing a value of type [T]. +final class Success extends Result { + /// The value returned in a successful result. + final T data; + + /// Creates a new [Success] containing the provided [data]. + const Success(this.data); +} + +/// A failed result containing an error of type [E] and optional stack trace. +final class Failure extends Result { + /// The error value describing the failure. + final E error; + + /// The associated stack trace for debugging, if available. + final StackTrace? stackTrace; + + /// Creates a new [Failure] with the given [error] and optional [stackTrace]. + const Failure(this.error, [this.stackTrace]); +} From de6d14912c0c71ca9b197e588c01606a939f12e3 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Fri, 21 Nov 2025 13:48:04 +0700 Subject: [PATCH 15/32] chore(linter): remove `always_put_control_body_on_new_line` rule from analysis options --- CHANGELOG.md | 1 + analysis_options.yaml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a83240..9c8c94d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Added `public_member_api_docs` rule to `analysis_options.yaml`. - Extracted main application widget to `app.dart`, added `runner.dart` for app initialization with error handling, and simplified `main.dart`. +- Removed `always_put_control_body_on_new_line` rule from `analysis_options.yaml`. ### Fixed - Moved `WidgetsFlutterBinding.ensureInitialized()` inside `runZonedGuarded` in `runner.dart` to ensure proper app initialization and error handling. diff --git a/analysis_options.yaml b/analysis_options.yaml index 2f5712c..da296b7 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -31,7 +31,6 @@ linter: rules: # --- Style & Formatting --- - always_declare_return_types - - always_put_control_body_on_new_line - annotate_overrides - avoid_print - avoid_redundant_argument_values From 14232a7b61805957e890275e269a234cda9c9f85 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Fri, 21 Nov 2025 13:53:40 +0700 Subject: [PATCH 16/32] refactor (runner): add DI-independent fallback error handler in runZonedGuarded --- CHANGELOG.md | 3 ++- lib/runner.dart | 13 +++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c8c94d..1ec37e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,7 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Integrated `Firebase Core` for authentication and analytics features. - Integrated `Firebase Crashlytics` for global error handling. - Added `AppFailure` base class and `UnknownFailure` implementation for unified domain-level error handling. - - Added sealed `Result` abstraction with `Success` and `Failure` implementations, plus `AsyncResult` type alias for standardizing async operation handling. +- Added sealed `Result` abstraction with `Success` and `Failure` implementations, plus `AsyncResult` type alias for standardizing async operation handling. +- Added DI-independent fallback error handler inside runZonedGuarded to ensure reliable error reporting even if DI or Firebase are not ready. ### Changed - Added `public_member_api_docs` rule to `analysis_options.yaml`. diff --git a/lib/runner.dart b/lib/runner.dart index 9e40e79..2a9e434 100644 --- a/lib/runner.dart +++ b/lib/runner.dart @@ -41,8 +41,17 @@ Future run() async { runApp(const FlutterStarterTemplate()); }, (error, stackTrace) { - di().f('UncaughtZoneError', error, stackTrace); - FirebaseCrashlytics.instance.recordError(error, stackTrace); + // Fallback handler (without DI) + debugPrint('UncaughtZoneError: $error'); + debugPrintStack(stackTrace: stackTrace); + + try { + FirebaseCrashlytics.instance.recordError(error, stackTrace); + } catch (e, s) { + // In case Firebase/Crashlytics aren't ready yet + debugPrint('Error while reporting to Crashlytics: $e'); + debugPrintStack(stackTrace: s); + } }, ); } From ee93044df7a4a7bae25fdfb00e3316cee03f93c8 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Mon, 24 Nov 2025 15:25:31 +0700 Subject: [PATCH 17/32] fix(deps): resolve build_runner failure by updating retrofit dependencies - Upgraded `retrofit` and `retrofit_generator` packages to fix compatibility issues with the latest `analyzer`. - Resolved build runner compilation errors due to removed `InterfaceElement2.representation` getter. --- CHANGELOG.md | 1 + pubspec.lock | 8 ++++---- pubspec.yaml | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ec37e0..c46d9df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Moved `WidgetsFlutterBinding.ensureInitialized()` inside `runZonedGuarded` in `runner.dart` to ensure proper app initialization and error handling. +- Fixed code generation failure by updating `retrofit_generator` (compatibility with `analyzer` 7.7.1+). --- diff --git a/pubspec.lock b/pubspec.lock index 825d701..966ed57 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -905,18 +905,18 @@ packages: dependency: "direct main" description: name: retrofit - sha256: "661ec56ecb891dfb8bbfad40827ad012daca7c69d6693b1703d72eb1ae1a5ec5" + sha256: "84063c18a00d55af41d6b8401edf8473e8c215bd7068ef7ec5e34c60657ffdbe" url: "https://pub.dev" source: hosted - version: "4.8.0" + version: "4.9.1" retrofit_generator: dependency: "direct dev" description: name: retrofit_generator - sha256: "37d74a07075b57d02772e5082610fe9f97d799de92a3aeb1417ec5e43c35c78d" + sha256: "7ec323f3329ad2ca0bcdc96fe02ec7f2486ecfac6cd2d035b03c398ef6f42308" url: "https://pub.dev" source: hosted - version: "10.1.1" + version: "10.2.0" rxdart: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index f29d36d..6e106d8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -35,7 +35,7 @@ dependencies: path: ^1.9.1 path_provider: ^2.1.5 provider: ^6.1.5+1 - retrofit: ^4.7.3 + retrofit: ^4.9.1 shared_preferences: ^2.5.3 dev_dependencies: @@ -45,7 +45,7 @@ dev_dependencies: flutter_test: sdk: flutter json_serializable: ^6.11.1 - retrofit_generator: ^10.0.6 + retrofit_generator: ^10.2.0 flutter: uses-material-design: true From 4629d92b9aedabf0e23be76571c0233aa1a54e18 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Mon, 24 Nov 2025 16:26:09 +0700 Subject: [PATCH 18/32] chore(xcode): update project settings and remove deprecated workspace files --- ios/Runner.xcodeproj/project.pbxproj | 4 +++- .../xcshareddata/IDEWorkspaceChecks.plist | 8 -------- .../xcshareddata/WorkspaceSettings.xcsettings | 8 -------- 3 files changed, 3 insertions(+), 17 deletions(-) delete mode 100644 ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index c15b095..86ec58e 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -164,7 +164,6 @@ D07CA0E6FCECE164629DBA3A /* Pods-RunnerTests.release.xcconfig */, 6AF687E372B820F28E6339F0 /* Pods-RunnerTests.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -437,6 +436,7 @@ /* Begin XCBuildConfiguration section */ 249021D3217E4FDB00AE95B9 /* Profile */ = { isa = XCBuildConfiguration; + baseConfigurationReference = A3F457A9E261AAC5A4F510DE /* Pods-Runner.profile.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -562,6 +562,7 @@ }; 97C147031CF9000F007C117D /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 219192A994BE2E810D21CF3E /* Pods-Runner.debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -619,6 +620,7 @@ }; 97C147041CF9000F007C117D /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = E1FE5A7588919AB0262267D9 /* Pods-Runner.release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; diff --git a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c..0000000 --- a/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - From 2e335adc74cf7f66bb864cae7286e7b321ef1f3e Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Mon, 24 Nov 2025 16:43:04 +0700 Subject: [PATCH 19/32] feat(analytics): integrate Firebase Analytics with a custom analytics interface and route observer. - Added `AppAnalytics` abstraction. - Implemented `FirebaseAnalyticsImpl` for release and `DebugAnalyticsImpl` for debug mode. - Added `AnalyticsRouteObserver` for automatic screen tracking via GoRouter. - Configure DI to switch implementations based on `kReleaseMode`. - Added unit tests for `AnalyticsRouteObserver`. - Add FIRDebugEnabled flag to Runner.xcsheme to enable DebugView on iOS. --- CHANGELOG.md | 1 + ios/Podfile.lock | 62 +++++++++++++ .../xcshareddata/xcschemes/Runner.xcscheme | 6 ++ lib/core/di/di.dart | 13 +++ lib/core/router/analytics_route_observer.dart | 33 +++++++ lib/core/router/router.dart | 6 ++ lib/core/utils/analytics/.gitkeep | 0 lib/core/utils/analytics/app_analytics.dart | 8 ++ .../utils/analytics/debug_analytics_impl.dart | 17 ++++ .../analytics/firebase_analytics_impl.dart | 19 ++++ lib/runner.dart | 2 + pubspec.lock | 24 +++++ pubspec.yaml | 1 + test/.gitkeep | 0 .../router/analytics_route_onserver_test.dart | 88 +++++++++++++++++++ .../analytics_route_onserver_test.mocks.dart | 47 ++++++++++ 16 files changed, 327 insertions(+) create mode 100644 lib/core/router/analytics_route_observer.dart delete mode 100644 lib/core/utils/analytics/.gitkeep create mode 100644 lib/core/utils/analytics/app_analytics.dart create mode 100644 lib/core/utils/analytics/debug_analytics_impl.dart create mode 100644 lib/core/utils/analytics/firebase_analytics_impl.dart delete mode 100644 test/.gitkeep create mode 100644 test/core/router/analytics_route_onserver_test.dart create mode 100644 test/core/router/analytics_route_onserver_test.mocks.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index c46d9df..b5adc57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `AppFailure` base class and `UnknownFailure` implementation for unified domain-level error handling. - Added sealed `Result` abstraction with `Success` and `Failure` implementations, plus `AsyncResult` type alias for standardizing async operation handling. - Added DI-independent fallback error handler inside runZonedGuarded to ensure reliable error reporting even if DI or Firebase are not ready. +- Added unified analytics architecture with `Firebase Analytics` integration, debug logging, and automatic screen view tracking via `AnalyticsRouteObserver`. ### Changed - Added `public_member_api_docs` rule to `analysis_options.yaml`. diff --git a/ios/Podfile.lock b/ios/Podfile.lock index e8923fb..2c50f2a 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1212,6 +1212,10 @@ PODS: - Firebase/Storage (12.4.0): - Firebase/CoreOnly - FirebaseStorage (~> 12.4.0) + - firebase_analytics (12.0.4): + - firebase_core + - FirebaseAnalytics (= 12.4.0) + - Flutter - firebase_auth (6.1.1): - Firebase/Auth (= 12.4.0) - firebase_core @@ -1227,6 +1231,24 @@ PODS: - Firebase/Storage (= 12.4.0) - firebase_core - Flutter + - FirebaseAnalytics (12.4.0): + - FirebaseAnalytics/Default (= 12.4.0) + - FirebaseCore (~> 12.4.0) + - FirebaseInstallations (~> 12.4.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - FirebaseAnalytics/Default (12.4.0): + - FirebaseCore (~> 12.4.0) + - FirebaseInstallations (~> 12.4.0) + - GoogleAppMeasurement/Default (= 12.4.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) - FirebaseAppCheckInterop (12.4.0) - FirebaseAuth (12.4.0): - FirebaseAppCheckInterop (~> 12.4.0) @@ -1299,6 +1321,33 @@ PODS: - GoogleUtilities/Environment (~> 8.1) - GTMSessionFetcher/Core (< 6.0, >= 3.4) - Flutter (1.0.0) + - GoogleAdsOnDeviceConversion (3.1.0): + - GoogleUtilities/Environment (~> 8.1) + - GoogleUtilities/Logger (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - nanopb (~> 3.30910.0) + - GoogleAppMeasurement/Core (12.4.0): + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - GoogleAppMeasurement/Default (12.4.0): + - GoogleAdsOnDeviceConversion (~> 3.1.0) + - GoogleAppMeasurement/Core (= 12.4.0) + - GoogleAppMeasurement/IdentitySupport (= 12.4.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) + - GoogleAppMeasurement/IdentitySupport (12.4.0): + - GoogleAppMeasurement/Core (= 12.4.0) + - GoogleUtilities/AppDelegateSwizzler (~> 8.1) + - GoogleUtilities/MethodSwizzler (~> 8.1) + - GoogleUtilities/Network (~> 8.1) + - "GoogleUtilities/NSData+zlib (~> 8.1)" + - nanopb (~> 3.30910.0) - GoogleDataTransport (10.1.0): - nanopb (~> 3.30910.0) - PromisesObjC (~> 2.4) @@ -1312,6 +1361,9 @@ PODS: - GoogleUtilities/Logger (8.1.0): - GoogleUtilities/Environment - GoogleUtilities/Privacy + - GoogleUtilities/MethodSwizzler (8.1.0): + - GoogleUtilities/Logger + - GoogleUtilities/Privacy - GoogleUtilities/Network (8.1.0): - GoogleUtilities/Logger - "GoogleUtilities/NSData+zlib" @@ -1441,6 +1493,7 @@ PODS: DEPENDENCIES: - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`) + - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) - firebase_crashlytics (from `.symlinks/plugins/firebase_crashlytics/ios`) @@ -1455,6 +1508,7 @@ SPEC REPOS: - abseil - BoringSSL-GRPC - Firebase + - FirebaseAnalytics - FirebaseAppCheckInterop - FirebaseAuth - FirebaseAuthInterop @@ -1469,6 +1523,8 @@ SPEC REPOS: - FirebaseSessions - FirebaseSharedSwift - FirebaseStorage + - GoogleAdsOnDeviceConversion + - GoogleAppMeasurement - GoogleDataTransport - GoogleUtilities - "gRPC-C++" @@ -1483,6 +1539,8 @@ SPEC REPOS: EXTERNAL SOURCES: cloud_firestore: :path: ".symlinks/plugins/cloud_firestore/ios" + firebase_analytics: + :path: ".symlinks/plugins/firebase_analytics/ios" firebase_auth: :path: ".symlinks/plugins/firebase_auth/ios" firebase_core: @@ -1505,10 +1563,12 @@ SPEC CHECKSUMS: BoringSSL-GRPC: dded2a44897e45f28f08ae87a55ee4bcd19bc508 cloud_firestore: 79014bb3b303d451717ed5fe69fded8a2b2e8dc2 Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e + firebase_analytics: 67fbdd9f3c04e55048024f3da21cfc36f05e56cf firebase_auth: c2b8be95d602d4e8a9148fae72333ef78e69cc20 firebase_core: f1aafb21c14f497e5498f7ffc4dc63cbb52b2594 firebase_crashlytics: 83c7467d7534975a4d779af43bd226d0a4616464 firebase_storage: 0ba617a05b24aec050395e4d5d3773c0d7518a15 + FirebaseAnalytics: 0fc2b20091f0ddd21bf73397cf8f0eb5346dc24f FirebaseAppCheckInterop: f734c802f21fe1da0837708f0f9a27218c8a4ed0 FirebaseAuth: 4a2aed737c84114a9d9b33d11ae1b147d6b94889 FirebaseAuthInterop: 858e6b754966e70740a4370dd1503dfffe6dbb49 @@ -1524,6 +1584,8 @@ SPEC CHECKSUMS: FirebaseSharedSwift: 93426a1de92f19e1199fac5295a4f8df16458daa FirebaseStorage: 20d6b56fb8a40ebaa03d6a2889fe33dac64adb73 Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467 + GoogleAdsOnDeviceConversion: e03a386840803ea7eef3fd22a061930142c039c1 + GoogleAppMeasurement: 1e718274b7e015cefd846ac1fcf7820c70dc017d GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7 GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 "gRPC-C++": cc207623316fb041a7a3e774c252cf68a058b9e8 diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index e3773d4..2ee0045 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -73,6 +73,12 @@ ReferencedContainer = "container:Runner.xcodeproj"> + + + + (() => logger); di.registerLazySingleton(() => AppLoggerImpl(di())); + + // Firebase Analytics + di.registerLazySingleton(() => FirebaseAnalytics.instance); + di.registerLazySingleton( + () => kReleaseMode + ? FirebaseAnalyticsImpl(di()) + : DebugAnalyticsImpl(di()), + ); } diff --git a/lib/core/router/analytics_route_observer.dart b/lib/core/router/analytics_route_observer.dart new file mode 100644 index 0000000..ecaa616 --- /dev/null +++ b/lib/core/router/analytics_route_observer.dart @@ -0,0 +1,33 @@ +import 'package:flutter/material.dart'; + +import '../utils/analytics/app_analytics.dart'; + +/// A universal navigation observer that sends screen view events +/// to the application's analytics abstraction. +class AnalyticsRouteObserver extends NavigatorObserver { + final AppAnalytics _analytics; + + /// Creates an instance of [AnalyticsRouteObserver]. + AnalyticsRouteObserver(this._analytics); + + @override + void didPush(Route route, Route? previousRoute) { + _logScreen(route); + super.didPush(route, previousRoute); + } + + @override + void didPop(Route route, Route? previousRoute) { + if (previousRoute != null) _logScreen(previousRoute); + + super.didPop(route, previousRoute); + } + + void _logScreen(Route route) { + final screenName = route.settings.name; + + if (screenName == null) return; + + _analytics.logScreenView(screenName); + } +} diff --git a/lib/core/router/router.dart b/lib/core/router/router.dart index 47fd912..4612b0c 100644 --- a/lib/core/router/router.dart +++ b/lib/core/router/router.dart @@ -1,11 +1,17 @@ import 'package:go_router/go_router.dart'; import '../../features/debug/presentation/debug_screen.dart'; +import '../di/di.dart'; +import '../utils/analytics/app_analytics.dart'; +import 'analytics_route_observer.dart'; import 'router_paths.dart'; /// The application's router using GoRouter. final router = GoRouter( initialLocation: AppRoutePaths.debugPath, + observers: [ + AnalyticsRouteObserver(di()), + ], routes: [ // Define your app's routes here GoRoute( diff --git a/lib/core/utils/analytics/.gitkeep b/lib/core/utils/analytics/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/lib/core/utils/analytics/app_analytics.dart b/lib/core/utils/analytics/app_analytics.dart new file mode 100644 index 0000000..e492537 --- /dev/null +++ b/lib/core/utils/analytics/app_analytics.dart @@ -0,0 +1,8 @@ +/// Abstract class for app analytics functionality. +abstract class AppAnalytics { + /// Logs an event with the given name and parameters. + Future logEvent(String name, {Map? parameters}); + + /// Logs a screen view with the given screen name. + Future logScreenView(String screenName); +} diff --git a/lib/core/utils/analytics/debug_analytics_impl.dart b/lib/core/utils/analytics/debug_analytics_impl.dart new file mode 100644 index 0000000..9f74dd5 --- /dev/null +++ b/lib/core/utils/analytics/debug_analytics_impl.dart @@ -0,0 +1,17 @@ +import '../logger/app_logger.dart'; +import 'app_analytics.dart'; + +/// Implementation of [AppAnalytics]. +class DebugAnalyticsImpl implements AppAnalytics { + final AppLogger _logger; + + /// Creates an instance of [DebugAnalyticsImpl]. + DebugAnalyticsImpl(this._logger); + + @override + Future logEvent(String name, {Map? parameters}) async => + _logger.i('Event: $name, params: $parameters'); + + @override + Future logScreenView(String screenName) async => _logger.i('ScreenView: $screenName'); +} diff --git a/lib/core/utils/analytics/firebase_analytics_impl.dart b/lib/core/utils/analytics/firebase_analytics_impl.dart new file mode 100644 index 0000000..c5a03cd --- /dev/null +++ b/lib/core/utils/analytics/firebase_analytics_impl.dart @@ -0,0 +1,19 @@ +import 'package:firebase_analytics/firebase_analytics.dart'; + +import 'app_analytics.dart'; + +/// Implementation of [AppAnalytics]. +class FirebaseAnalyticsImpl implements AppAnalytics { + final FirebaseAnalytics _analytics; + + /// Creates an instance of [FirebaseAnalyticsImpl]. + FirebaseAnalyticsImpl(this._analytics); + + @override + Future logEvent(String name, {Map? parameters}) async => + await _analytics.logEvent(name: name, parameters: parameters); + + @override + Future logScreenView(String screenName) async => + await _analytics.logScreenView(screenName: screenName); +} diff --git a/lib/runner.dart b/lib/runner.dart index 2a9e434..be468c6 100644 --- a/lib/runner.dart +++ b/lib/runner.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_crashlytics/firebase_crashlytics.dart'; import 'package:flutter/foundation.dart'; @@ -20,6 +21,7 @@ Future run() async { options: DefaultFirebaseOptions.currentPlatform, ); await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(kReleaseMode); + await FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(true); // Setup dependency injection setupDI(); diff --git a/pubspec.lock b/pubspec.lock index 966ed57..1063b82 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -321,6 +321,30 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.1" + firebase_analytics: + dependency: "direct main" + description: + name: firebase_analytics + sha256: bfb80d92eee10a6585ebd5a7e60de5caf0f2c06329e5676c0578130aea1bfe85 + url: "https://pub.dev" + source: hosted + version: "12.0.4" + firebase_analytics_platform_interface: + dependency: transitive + description: + name: firebase_analytics_platform_interface + sha256: "3b803077907def997044774f6c022d8e9204e9c0f5e205e3572d887c93dafd72" + url: "https://pub.dev" + source: hosted + version: "5.0.4" + firebase_analytics_web: + dependency: transitive + description: + name: firebase_analytics_web + sha256: "0dbd96dbe77b51185319000c0078477fdcffb4abb0018c362dd9afb9845c1e06" + url: "https://pub.dev" + source: hosted + version: "0.6.1" firebase_auth: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 6e106d8..551c505 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,6 +14,7 @@ dependencies: cloud_firestore: ^6.0.3 dio: ^5.9.0 equatable: ^2.0.7 + firebase_analytics: ^12.0.4 firebase_auth: ^6.1.1 firebase_core: ^4.2.0 firebase_crashlytics: ^5.0.4 diff --git a/test/.gitkeep b/test/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/test/core/router/analytics_route_onserver_test.dart b/test/core/router/analytics_route_onserver_test.dart new file mode 100644 index 0000000..3424a17 --- /dev/null +++ b/test/core/router/analytics_route_onserver_test.dart @@ -0,0 +1,88 @@ +// ignore_for_file: unawaited_futures + +import 'package:flutter/material.dart'; +import 'package:flutter_starter_template/core/router/analytics_route_observer.dart'; +import 'package:flutter_starter_template/core/utils/analytics/app_analytics.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'analytics_route_onserver_test.mocks.dart'; + +@GenerateNiceMocks([MockSpec()]) +void main() { + late AppAnalytics mockAnalytics; + late AnalyticsRouteObserver analyticsRouteObserver; + + Future pumpNavigator(WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + navigatorObservers: [analyticsRouteObserver], + home: const Scaffold(body: Text('Home Screen')), + routes: {'/test': (context) => const Scaffold(body: Text('Test Screen'))}, + ), + ); + } + + setUp(() { + mockAnalytics = MockAppAnalytics(); + analyticsRouteObserver = AnalyticsRouteObserver(mockAnalytics); + }); + + group('AnalyticsRouteObserver', () { + group('didPush', () { + testWidgets('should log screen view when pushing a named route', (tester) async { + // Arrange + await pumpNavigator(tester); + clearInteractions(mockAnalytics); + + // Act + final context = tester.element(find.text('Home Screen')); + Navigator.pushNamed(context, '/test'); + await tester.pumpAndSettle(); + + // Assert + verify(mockAnalytics.logScreenView('/test')).called(1); + }); + + testWidgets('should not log screen view when pushing a route without name', (tester) async { + // Arrange + await pumpNavigator(tester); + clearInteractions(mockAnalytics); + + // Act + final context = tester.element(find.text('Home Screen')); + Navigator.push(context, MaterialPageRoute(builder: (context) => const SizedBox())); + await tester.pumpAndSettle(); + + // Assert + verifyNever(mockAnalytics.logScreenView('/test')); + }); + }); + + group('didPop', () { + testWidgets('should log previous route name when popping current route', (tester) async { + // Arrange + await pumpNavigator(tester); + final context = tester.element(find.text('Home Screen')); + Navigator.pushNamed(context, '/test'); + await tester.pumpAndSettle(); + clearInteractions(mockAnalytics); + + // Act + Navigator.pop(context, tester.element(find.text('Test Screen'))); + await tester.pumpAndSettle(); + + // Assert + verify(mockAnalytics.logScreenView('/')).called(1); + }); + }); + + group('Initialization', () { + testWidgets('should log initial route on app start', (tester) async { + await pumpNavigator(tester); + verify(mockAnalytics.logScreenView('/')).called(1); + }); + }); + }); +} diff --git a/test/core/router/analytics_route_onserver_test.mocks.dart b/test/core/router/analytics_route_onserver_test.mocks.dart new file mode 100644 index 0000000..6968f91 --- /dev/null +++ b/test/core/router/analytics_route_onserver_test.mocks.dart @@ -0,0 +1,47 @@ +// Mocks generated by Mockito 5.4.6 from annotations +// in flutter_starter_template/test/core/router/analytics_route_onserver_test.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i3; + +import 'package:flutter_starter_template/core/utils/analytics/app_analytics.dart' + as _i2; +import 'package:mockito/mockito.dart' as _i1; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: must_be_immutable +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +/// A class which mocks [AppAnalytics]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockAppAnalytics extends _i1.Mock implements _i2.AppAnalytics { + @override + _i3.Future logEvent(String? name, {Map? parameters}) => + (super.noSuchMethod( + Invocation.method(#logEvent, [name], {#parameters: parameters}), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) + as _i3.Future); + + @override + _i3.Future logScreenView(String? screenName) => + (super.noSuchMethod( + Invocation.method(#logScreenView, [screenName]), + returnValue: _i3.Future.value(), + returnValueForMissingStub: _i3.Future.value(), + ) + as _i3.Future); +} From aa95cf9cef90fe5d202deba35184ebf28c2084b7 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Tue, 25 Nov 2025 13:02:34 +0700 Subject: [PATCH 20/32] refactor(di): streamline logger registration in dependency injection setup --- CHANGELOG.md | 1 + lib/core/di/di.dart | 7 +------ 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5adc57..52d9f82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `public_member_api_docs` rule to `analysis_options.yaml`. - Extracted main application widget to `app.dart`, added `runner.dart` for app initialization with error handling, and simplified `main.dart`. - Removed `always_put_control_body_on_new_line` rule from `analysis_options.yaml`. +- Streamlined logger registration in dependency injection setup ### Fixed - Moved `WidgetsFlutterBinding.ensureInitialized()` inside `runZonedGuarded` in `runner.dart` to ensure proper app initialization and error handling. diff --git a/lib/core/di/di.dart b/lib/core/di/di.dart index 63a8f83..896b188 100644 --- a/lib/core/di/di.dart +++ b/lib/core/di/di.dart @@ -15,13 +15,8 @@ final di = GetIt.instance; /// Initializes the application's dependencies using GetIt. void setupDI() { - // Register your dependencies here, for example: - // di.registerLazySingleton(() => SomeServiceImpl()); - // Logger - final logger = createLogger(); - - di.registerLazySingleton(() => logger); + di.registerLazySingleton(() => createLogger()); di.registerLazySingleton(() => AppLoggerImpl(di())); // Firebase Analytics From fc0bc6e8ebc3bd08336b1aa13bb0e82a79e27989 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Tue, 25 Nov 2025 14:43:07 +0700 Subject: [PATCH 21/32] feat(logger): add AppLoggerMixin for convenient logger access in Bloc/Cubit --- CHANGELOG.md | 1 + lib/core/utils/logger/logger_mixin.dart | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 lib/core/utils/logger/logger_mixin.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 52d9f82..c4a4a05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added sealed `Result` abstraction with `Success` and `Failure` implementations, plus `AsyncResult` type alias for standardizing async operation handling. - Added DI-independent fallback error handler inside runZonedGuarded to ensure reliable error reporting even if DI or Firebase are not ready. - Added unified analytics architecture with `Firebase Analytics` integration, debug logging, and automatic screen view tracking via `AnalyticsRouteObserver`. +- Added `AppLoggerMixin` for convenient logger access in Bloc/Cubit ### Changed - Added `public_member_api_docs` rule to `analysis_options.yaml`. diff --git a/lib/core/utils/logger/logger_mixin.dart b/lib/core/utils/logger/logger_mixin.dart new file mode 100644 index 0000000..45ec89d --- /dev/null +++ b/lib/core/utils/logger/logger_mixin.dart @@ -0,0 +1,12 @@ +import '../../di/di.dart'; +import 'app_logger.dart'; + +/// Mixin for convenient access to AppLogger through GetIt. +/// +/// Used in Bloc/Cubit for debug logging. +/// For critical components (Repository, UseCase), dependency injection +/// through the constructor is preferable for easier testing. +mixin AppLoggerMixin { + /// Gets the [AppLogger] instance from the global DI container. + AppLogger get logger => di(); +} From f8da6f765b1f5ad7a078bbe9d2a7e43ff006d7d0 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Tue, 25 Nov 2025 14:59:52 +0700 Subject: [PATCH 22/32] chore(auth): remove unused .gitkeep file from auth domain sources --- lib/features/auth/domain/sources/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 lib/features/auth/domain/sources/.gitkeep diff --git a/lib/features/auth/domain/sources/.gitkeep b/lib/features/auth/domain/sources/.gitkeep deleted file mode 100644 index e69de29..0000000 From 41a54102258c83a2ff8159dd3bfb90f37e6b6479 Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Wed, 26 Nov 2025 14:02:34 +0700 Subject: [PATCH 23/32] chore: update Dart and Flutter SDK requirements to >=3.10.0 --- CHANGELOG.md | 2 + pubspec.lock | 174 +++++++++++++++++++++------------------------------ pubspec.yaml | 2 +- 3 files changed, 74 insertions(+), 104 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4a4a05..20db947 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Extracted main application widget to `app.dart`, added `runner.dart` for app initialization with error handling, and simplified `main.dart`. - Removed `always_put_control_body_on_new_line` rule from `analysis_options.yaml`. - Streamlined logger registration in dependency injection setup +- Updated Dart SDK requirement to `>=3.10.0` (was `>=3.9.2`) +- Updated Flutter SDK requirement to `>=3.10.0` ### Fixed - Moved `WidgetsFlutterBinding.ensureInitialized()` inside `runZonedGuarded` in `runner.dart` to ensure proper app initialization and error handling. diff --git a/pubspec.lock b/pubspec.lock index 1063b82..dfdb3df 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f + sha256: c209688d9f5a5f26b2fb47a188131a6fb9e876ae9e47af3737c0b4f58a93470d url: "https://pub.dev" source: hosted - version: "85.0.0" + version: "91.0.0" _flutterfire_internals: dependency: transitive description: @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: analyzer - sha256: "974859dc0ff5f37bc4313244b3218c791810d03ab3470a579580279ba971a48d" + sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08 url: "https://pub.dev" source: hosted - version: "7.7.1" + version: "8.4.1" ansicolor: dependency: transitive description: @@ -77,10 +77,10 @@ packages: dependency: transitive description: name: build - sha256: ce76b1d48875e3233fde17717c23d1f60a91cc631597e49a400c89b475395b1d + sha256: c1668065e9ba04752570ad7e038288559d1e2ca5c6d0131c0f5f55e39e777413 url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "4.0.3" build_config: dependency: transitive description: @@ -93,34 +93,18 @@ packages: dependency: transitive description: name: build_daemon - sha256: "409002f1adeea601018715d613115cfaf0e31f512cb80ae4534c79867ae2363d" + sha256: bf05f6e12cfea92d3c09308d7bcdab1906cd8a179b023269eed00c071004b957 url: "https://pub.dev" source: hosted - version: "4.1.0" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: d1d57f7807debd7349b4726a19fd32ec8bc177c71ad0febf91a20f84cd2d4b46 - url: "https://pub.dev" - source: hosted - version: "3.0.3" + version: "4.1.1" build_runner: dependency: "direct dev" description: name: build_runner - sha256: b24597fceb695969d47025c958f3837f9f0122e237c6a22cb082a5ac66c3ca30 + sha256: "110c56ef29b5eb367b4d17fc79375fa8c18a6cd7acd92c05bb3986c17a079057" url: "https://pub.dev" source: hosted - version: "2.7.1" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: "066dda7f73d8eb48ba630a55acb50c4a84a2e6b453b1cb4567f581729e794f7b" - url: "https://pub.dev" - source: hosted - version: "9.3.1" + version: "2.10.4" built_collection: dependency: transitive description: @@ -133,10 +117,10 @@ packages: dependency: transitive description: name: built_value - sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d + sha256: "426cf75afdb23aa74bd4e471704de3f9393f3c7b04c1e2d9c6f1073ae0b8b139" url: "https://pub.dev" source: hosted - version: "8.12.0" + version: "8.12.1" cached_network_image: dependency: "direct main" description: @@ -197,26 +181,26 @@ packages: dependency: "direct main" description: name: cloud_firestore - sha256: ea3fe98eaf0c3b675ab2a242d98430518da60f89d5cc9774df7b2d9110aaf160 + sha256: fc1de79a62fe21615e9012f396070e6121838ef0d879475a4ec8320e79378208 url: "https://pub.dev" source: hosted - version: "6.0.3" + version: "6.1.0" cloud_firestore_platform_interface: dependency: transitive description: name: cloud_firestore_platform_interface - sha256: b5592862c451ebfd229b430907b6feb2082333d6e5a61d0eafd547c2afc6f68e + sha256: "2d2ee96a32ec3dd22fb682295e9bed6336e49a43f056d7841690228adca3ee7d" url: "https://pub.dev" source: hosted - version: "7.0.3" + version: "7.0.4" cloud_firestore_web: dependency: transitive description: name: cloud_firestore_web - sha256: eca0337985e0eedcef8a3e5380950af01c0853951b6b8427e7909115d81d7b9c + sha256: "28c39f3d050bf669787ef13fa0890df2b4af236de864e2db0cc3897b857066cb" url: "https://pub.dev" source: hosted - version: "5.0.3" + version: "5.1.0" code_builder: dependency: transitive description: @@ -253,18 +237,18 @@ packages: dependency: transitive description: name: crypto - sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" + sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf url: "https://pub.dev" source: hosted - version: "3.0.6" + version: "3.0.7" dart_style: dependency: transitive description: name: dart_style - sha256: "8a0e5fba27e8ee025d2ffb4ee820b4e6e2cf5e4246a6b1a477eb66866947e0bb" + sha256: a9c30492da18ff84efe2422ba2d319a89942d93e58eb0b73d32abe822ef54b7b url: "https://pub.dev" source: hosted - version: "3.1.1" + version: "3.1.3" diff_match_patch: dependency: transitive description: @@ -349,26 +333,26 @@ packages: dependency: "direct main" description: name: firebase_auth - sha256: "3150a56fc0f20b4b926e1343bfdca3acb591a0aa1c95bae5426353f384085352" + sha256: e54fb3ba57de041d832574126a37726eedf0f57400869f1942b0ca8ce4a6e209 url: "https://pub.dev" source: hosted - version: "6.1.1" + version: "6.1.2" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - sha256: "7bc50c0d74dd8f4c72d7840ae64ea7b638f203d089e5c4a90a157b2f2ead1963" + sha256: "421f95dc553cb283ed9d4d140e719800c0331d49ed37b962e513c9d1d61b090b" url: "https://pub.dev" source: hosted - version: "8.1.3" + version: "8.1.4" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - sha256: "351dcb82bc542e21a426cd97ffcc40d7232981dafc3fd89a6c932876a09240e1" + sha256: a064ffee202f7d42d62e2c01775899d4ffcb83c602af07632f206acd46a0964e url: "https://pub.dev" source: hosted - version: "6.0.4" + version: "6.1.0" firebase_core: dependency: "direct main" description: @@ -397,10 +381,10 @@ packages: dependency: "direct main" description: name: firebase_crashlytics - sha256: c3ebe3ed9f3b1d36c0864a4a28b041fcc2686f11fb2a4f7891319ea8d1d161cc + sha256: bd4eadd620071ae48ef0e3617499be2a0ea4062d6d5c6fea6c1cec41ebbb2435 url: "https://pub.dev" source: hosted - version: "5.0.4" + version: "5.0.5" firebase_crashlytics_platform_interface: dependency: transitive description: @@ -413,26 +397,26 @@ packages: dependency: "direct main" description: name: firebase_storage - sha256: d740b9ea0105f27d7286d7ad5957d778bf7fa967796c6e3fc26491cf5245b486 + sha256: "3438f38590186010ce76ece683ebf3b842cd637f31f83a13620917d7438a58fd" url: "https://pub.dev" source: hosted - version: "13.0.3" + version: "13.0.4" firebase_storage_platform_interface: dependency: transitive description: name: firebase_storage_platform_interface - sha256: "52a1dfb2c93f49a8e800d4b9aed107d1c0f2f6dd3ebf10a5f7e2222442960e50" + sha256: "5d56021a9d30f7ca89559c96cc4c7250ce6ff8881382ff7238fde64a1f449e39" url: "https://pub.dev" source: hosted - version: "5.2.14" + version: "5.2.15" firebase_storage_web: dependency: transitive description: name: firebase_storage_web - sha256: d5ce115e3d5a58fddd7631d5246aa8389d8000fa7790ecb04f65bf751bbf6a92 + sha256: a06775d1df6dd90f5fa3fe9e221b988dcbc221e73a0f8951136536e6d5e548e6 url: "https://pub.dev" source: hosted - version: "3.10.21" + version: "3.11.0" fixnum: dependency: transitive description: @@ -479,10 +463,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: b9c2ad5872518a27507ab432d1fb97e8813b05f0fc693f9d40fad06d073e0678 + sha256: "87fbd7c534435b6c5d9d98b01e1fd527812b82e68ddd8bd35fc45ed0fa8f0a95" url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.2.3" flutter_test: dependency: "direct dev" description: flutter @@ -521,10 +505,10 @@ packages: dependency: "direct main" description: name: get_it - sha256: a4292e7cf67193f8e7c1258203104eb2a51ec8b3a04baa14695f4064c144297b + sha256: ae78de7c3f2304b8d81f2bb6e320833e5e81de942188542328f074978cc0efa9 url: "https://pub.dev" source: hosted - version: "8.2.0" + version: "8.3.0" glob: dependency: transitive description: @@ -561,10 +545,10 @@ packages: dependency: transitive description: name: http - sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007 + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" url: "https://pub.dev" source: hosted - version: "1.5.0" + version: "1.6.0" http_multi_server: dependency: transitive description: @@ -617,10 +601,10 @@ packages: dependency: "direct dev" description: name: json_serializable - sha256: "33a040668b31b320aafa4822b7b1e177e163fc3c1e835c6750319d4ab23aa6fe" + sha256: c5b2ee75210a0f263c6c7b9eeea80553dbae96ea1bf57f02484e806a3ffdffa3 url: "https://pub.dev" source: hosted - version: "6.11.1" + version: "6.11.2" leak_tracker: dependency: transitive description: @@ -649,10 +633,10 @@ packages: dependency: transitive description: name: lean_builder - sha256: ef5cd5f907157eb7aa87d1704504b5a6386d2cbff88a3c2b3344477bab323ee9 + sha256: "6511c632f60435fa824283a9660b94de1b8768c1501f6910b773034bf8af7035" url: "https://pub.dev" source: hosted - version: "0.1.2" + version: "0.1.3" lints: dependency: transitive description: @@ -697,10 +681,10 @@ packages: dependency: transitive description: name: meta - sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.16.0" + version: "1.17.0" mime: dependency: transitive description: @@ -713,10 +697,10 @@ packages: dependency: "direct main" description: name: mockito - sha256: "2314cbe9165bcd16106513df9cf3c3224713087f09723b128928dc11a4379f99" + sha256: dac24d461418d363778d53198d9ac0510b9d073869f078450f195766ec48d05e url: "https://pub.dev" source: hosted - version: "5.5.0" + version: "5.6.1" mocktail: dependency: transitive description: @@ -825,18 +809,18 @@ packages: dependency: transitive description: name: path_provider_android - sha256: e122c5ea805bb6773bb12ce667611265980940145be920cd09a4b0ec0285cb16 + sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e url: "https://pub.dev" source: hosted - version: "2.2.20" + version: "2.2.22" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: efaec349ddfc181528345c56f8eda9d6cccd71c177511b132c6a0ddaefaa2738 + sha256: "97390a0719146c7c3e71b6866c34f1cde92685933165c1c671984390d2aca776" url: "https://pub.dev" source: hosted - version: "2.4.3" + version: "2.4.4" path_provider_linux: dependency: transitive description: @@ -897,10 +881,10 @@ packages: dependency: transitive description: name: protobuf - sha256: "826d6a306be26f29e5cd9faeb0c97aad5897270341dab6dbd7b8acd675937006" + sha256: "2fcc8a202ca7ec17dab7c97d6b6d91cf03aa07fe6f65f8afbb6dfa52cc5bd902" url: "https://pub.dev" source: hosted - version: "5.0.0" + version: "5.1.0" provider: dependency: "direct main" description: @@ -961,18 +945,18 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "34266009473bf71d748912da4bf62d439185226c03e01e2d9687bc65bbfcb713" + sha256: "46a46fd64659eff15f4638bbe19de43f9483f0e0bf024a9fb6b3582064bacc7b" url: "https://pub.dev" source: hosted - version: "2.4.15" + version: "2.4.17" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "1c33a907142607c40a7542768ec9badfd16293bac51da3a4482623d15845f88b" + sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" url: "https://pub.dev" source: hosted - version: "2.5.5" + version: "2.5.6" shared_preferences_linux: dependency: transitive description: @@ -1046,10 +1030,10 @@ packages: dependency: transitive description: name: source_gen - sha256: "7b19d6ba131c6eb98bfcbf8d56c1a7002eba438af2e7ae6f8398b2b0f4f381e3" + sha256: "07b277b67e0096c45196cbddddf2d8c6ffc49342e88bf31d460ce04605ddac75" url: "https://pub.dev" source: hosted - version: "3.1.0" + version: "4.1.1" source_helper: dependency: transitive description: @@ -1082,14 +1066,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.1" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.dev" - source: hosted - version: "7.0.0" sqflite: dependency: transitive description: @@ -1182,34 +1158,26 @@ packages: dependency: transitive description: name: test - sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb" + sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7" url: "https://pub.dev" source: hosted - version: "1.26.2" + version: "1.26.3" test_api: dependency: transitive description: name: test_api - sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" + sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55 url: "https://pub.dev" source: hosted - version: "0.7.6" + version: "0.7.7" test_core: dependency: transitive description: name: test_core - sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a" - url: "https://pub.dev" - source: hosted - version: "0.6.11" - timing: - dependency: transitive - description: - name: timing - sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0" url: "https://pub.dev" source: hosted - version: "1.0.2" + version: "0.6.12" typed_data: dependency: transitive description: @@ -1222,10 +1190,10 @@ packages: dependency: transitive description: name: uuid - sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff + sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8 url: "https://pub.dev" source: hosted - version: "4.5.1" + version: "4.5.2" vector_graphics: dependency: transitive description: @@ -1339,5 +1307,5 @@ packages: source: hosted version: "3.1.3" sdks: - dart: ">=3.9.2 <4.0.0" + dart: ">=3.10.0 <4.0.0" flutter: ">=3.35.0" diff --git a/pubspec.yaml b/pubspec.yaml index 551c505..2b846de 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: 'none' version: 0.1.0 environment: - sdk: ^3.9.2 + sdk: '>=3.10.0 <4.0.0' license: "MIT" From 9b9a1e3c6868bdc960c0b96f721342e7b53c762e Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Tue, 9 Dec 2025 16:19:57 +0700 Subject: [PATCH 24/32] docs: update changelog and contributing guidelines for clarity and formatting consistency --- CHANGELOG.md | 8 ++++++- CONTRIBUTING.md | 64 ++++++++++++++++++++++++++++--------------------- README.md | 2 +- 3 files changed, 45 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 20db947..1cbd39b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- ## [Unreleased] + ### Added + - Implemented base Clean Architecture structure (`core`, `features`, `uikit`) with `data`, `domain`, and `presentation` layers. - Added all project dependencies from `pubspec.yaml`. - Integrated `GetIt` for dependency injection. @@ -24,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `AppLoggerMixin` for convenient logger access in Bloc/Cubit ### Changed + - Added `public_member_api_docs` rule to `analysis_options.yaml`. - Extracted main application widget to `app.dart`, added `runner.dart` for app initialization with error handling, and simplified `main.dart`. - Removed `always_put_control_body_on_new_line` rule from `analysis_options.yaml`. @@ -32,14 +35,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Updated Flutter SDK requirement to `>=3.10.0` ### Fixed + - Moved `WidgetsFlutterBinding.ensureInitialized()` inside `runZonedGuarded` in `runner.dart` to ensure proper app initialization and error handling. - Fixed code generation failure by updating `retrofit_generator` (compatibility with `analyzer` 7.7.1+). --- ## [0.1.0] - 2025-10-29 + ### Added + - Initial Flutter project created via `flutter create`. - Added core documentation files and base configuration. -*This changelog was initialized with the project setup (initial commit). Future changes will be tracked starting from version 0.1.0.* \ No newline at end of file +*This changelog was initialized with the project setup (initial commit). Future changes will be tracked starting from version 0.1.0.* diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 92a294a..7f44b6d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,46 +21,53 @@ Please take a few minutes to read through these guidelines before submitting you 1. **Fork** the repository. 2. **Clone** your fork locally: -```bash - git clone https://github.com/CowboyGH/flutter-starter-template.git - cd -``` + + ```bash + git clone https://github.com/CowboyGH/flutter-starter-template.git + cd + ``` 3. Install dependencies: -```bash - flutter pub get -``` + + ```bash + flutter pub get + ``` 4. Create a new branch for your feature or fix: -```bash - git checkout -b feature/my-new-feature -``` + + ```bash + git checkout -b feature/my-new-feature + ``` --- ## ๐Ÿงฐ Development Workflow 1. Make sure your Flutter SDK and packages are up to date: -```bash - flutter upgrade - flutter pub upgrade -``` + + ```bash + flutter upgrade + flutter pub upgrade + ``` 2. Run the project: -```bash - flutter run -``` + + ```bash + flutter run + ``` 3. Before committing, check formatting and analyze code: -```bash - flutter format . - flutter analyze -``` + + ```bash + flutter format . + flutter analyze + ``` 4. Run unit tests (if any): -```bash - flutter test -``` + + ```bash + flutter test + ``` --- @@ -77,12 +84,14 @@ Please take a few minutes to read through these guidelines before submitting you ## ๐Ÿ“ Commit Guidelines Follow [Conventional Commits](https://www.conventionalcommits.org/) format: -``` + +```text (optional scope): ``` **Example:** -``` + +```text feat(auth): add user login with token refresh fix(router): correct initial route for onboarding docs(readme): update setup instructions @@ -111,6 +120,7 @@ I use a simple branching model: - `docs/*` โ€” documentation updates **Example workflow:** + ```bash git checkout -b feature/add-user-profile # make changes @@ -142,4 +152,4 @@ If you have questions or run into issues: --- -Thank you for contributing and helping make this project better! ๐Ÿš€ \ No newline at end of file +Thank you for contributing and helping make this project better! ๐Ÿš€ diff --git a/README.md b/README.md index 712e067..e3d005a 100644 --- a/README.md +++ b/README.md @@ -13,4 +13,4 @@ This project is released under the [MIT License](LICENSE). ## โœจ Author -Developed by [Ryan Delaney](https://github.com/CowboyGH) \ No newline at end of file +Developed by [Ryan Delaney](https://github.com/CowboyGH) From 4ca3de21b0484732947f96ff682dc1aafe2f8119 Mon Sep 17 00:00:00 2001 From: Ryan Delaney <145113692+CowboyGH@users.noreply.github.com> Date: Sun, 14 Dec 2025 13:56:03 +0700 Subject: [PATCH 25/32] feat: implement comprehensive CI/CD pipeline (#1) * feat: add Docker configuration for Android build - Add multi-stage Dockerfile for Android APK build - Configure Flutter 3.38.4 and Android SDK 35 - Add .dockerignore for build optimization - Support for armeabi-v7a, arm64-v8a, x86_64 architectures - Update .gitignore to include Docker & APK related files * feat: add complete CI/CD workflow with Docker and iOS build - Add code analysis job with formatting, analyze, and tests - Add Android build job with Docker and DockerHub integration - Add iOS build job with macOS runner - Configure GitHub Actions cache for faster builds * chore: ignore generated build_runner files (*.g.dart, *.mocks.dart) * feat: add build_runner step to CI for code generation * feat: add code formatting step after build_runner in CI workflow * fix: add step to create fake firebase_options.dart for Firebase configuration * fix: update fake firebase_options.dart * fix: update Docker tags format and add fake firebase_options in CI * fix: add fake google-services.json and GoogleService-Info.plist for Firebase configuration and update configuration file creation steps * fix: update iOS dependency installation to remove existing Pods and Podfile.lock before reinstalling * fix: remove unused FlutterFire upload symbols script from iOS build phase and remove unused outputs from Android job * chore: reduce and optimize pipeline * fix: add step to format generated code in CI pipeline --- .dockerignore | 114 +++++++++ .github/workflows/main.yml | 218 ++++++++++++++++++ .gitignore | 15 +- CHANGELOG.md | 6 + Dockerfile | 180 +++++++++++++++ ios/Runner.xcodeproj/project.pbxproj | 25 +- .../analytics_route_onserver_test.mocks.dart | 47 ---- 7 files changed, 537 insertions(+), 68 deletions(-) create mode 100644 .dockerignore create mode 100644 .github/workflows/main.yml create mode 100644 Dockerfile delete mode 100644 test/core/router/analytics_route_onserver_test.mocks.dart diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..7eb3709 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,114 @@ +# Flutter/Dart related +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +build/ +*.lock.json +.metadata + +# Android related +android/.gradle/ +android/app/build/ +android/gradle/ +android/.idea/ +android/local.properties +android/key.properties +android/app/release/ +android/app/debug/ +android/captures/ +android/*.iml +*.jks +*.keystore +*.p12 +google-services.json + +# iOS related +ios/Pods/ +ios/.symlinks/ +ios/Flutter/Flutter.framework +ios/Flutter/Flutter.podspec +ios/.generated/ +ios/Runner.xcworkspace/ +ios/Runner.xcodeproj/xcuserdata/ +ios/Runner.xcodeproj/project.xcworkspace/xcuserdata/ +*.pbxuser +*.mode1v3 +*.mode2v3 +*.perspectivev3 +*.xcuserstate +xcuserdata/ +DerivedData/ +*.mobileprovision +*.cer +*.p12 +GoogleService-Info.plist + +# IDE related +.idea/ +.vscode/ +*.swp +*.swo +*~ +.DS_Store +Thumbs.db +desktop.ini + +# Git +.git/ +.gitignore +.gitattributes +.github/ + +# CI/CD +.gitlab-ci.yml +.circleci/ +.travis.yml +azure-pipelines.yml + +# Testing and coverage +coverage/ +test/ +integration_test/ +.test_coverage/ +*.lcov +*.info + +# Documentation +README.md +README*.md +LICENSE +CHANGELOG.md +docs/ +CONTRIBUTING.md + +# Docker files +Dockerfile +Dockerfile.* +.dockerignore +docker-compose.yml +docker-compose.*.yml + +# Local configurations +.env +.env.* +.env.local +.env.*.local +*.log +*.log.* + +# Temporary files +*.tmp +*.temp +*.bak +*.swp +*~ + +# Firebase +firebase.json +.firebaserc +firestore.rules +firestore.indexes.json +storage.rules diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..34a1348 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,218 @@ +name: Flutter CI/CD Pipeline + +on: + push: + branches: [main, develop] + pull_request: + branches: [main, develop] + workflow_dispatch: + +env: + FLUTTER_VERSION: '3.38.4' + ANDROID_SDK_VERSION: '35' + +jobs: + # ========================================== + # Job 1: Code Quality & Analysis + # ========================================== + analyze: + name: Code Analysis + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: ${{ env.FLUTTER_VERSION }} + channel: 'stable' + cache: true + + - name: Create fake firebase_options.dart + run: | + mkdir -p lib + cat < lib/firebase_options.dart + // ignore_for_file: type=lint + import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; + class DefaultFirebaseOptions { + static FirebaseOptions get currentPlatform { + if (kIsWeb) throw UnsupportedError('Web not configured'); + switch (defaultTargetPlatform) { + case TargetPlatform.android: return android; + case TargetPlatform.iOS: return ios; + default: throw UnsupportedError('Platform not configured'); + } + } + static const FirebaseOptions android = FirebaseOptions(apiKey: 'fake', appId: 'fake', messagingSenderId: 'fake', projectId: 'fake', storageBucket: 'fake'); + static const FirebaseOptions ios = FirebaseOptions(apiKey: 'fake', appId: 'fake', messagingSenderId: 'fake', projectId: 'fake', storageBucket: 'fake', iosBundleId: 'fake'); + } + EOF + + - name: Get dependencies + run: flutter pub get + + - name: Run build_runner + run: dart run build_runner build --delete-conflicting-outputs + + - name: Format generated code + run: dart format . + + - name: Verify formatting + run: dart format --set-exit-if-changed lib/ test/ + continue-on-error: false + + - name: Analyze code + run: flutter analyze --fatal-infos + + - name: Run tests + run: flutter test --coverage || echo "โš ๏ธ No tests found" + + - name: Upload coverage to artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: code-coverage + path: coverage/lcov.info + if-no-files-found: ignore + retention-days: 7 + + # ========================================== + # Job 2: Android Build (Docker) + # ========================================== + build-android: + name: Build Android APK + runs-on: ubuntu-latest + needs: analyze + timeout-minutes: 60 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Create mock configuration files + run: | + mkdir -p lib + # firebase_options.dart + echo "import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; class DefaultFirebaseOptions { static FirebaseOptions get currentPlatform { if (kIsWeb) throw UnsupportedError('Web not configured'); switch (defaultTargetPlatform) { case TargetPlatform.android: return android; case TargetPlatform.iOS: return ios; default: throw UnsupportedError('Platform not configured'); } } static const FirebaseOptions android = FirebaseOptions(apiKey: 'fake', appId: 'fake', messagingSenderId: 'fake', projectId: 'fake', storageBucket: 'fake'); static const FirebaseOptions ios = FirebaseOptions(apiKey: 'fake', appId: 'fake', messagingSenderId: 'fake', projectId: 'fake', storageBucket: 'fake', iosBundleId: 'fake'); }" > lib/firebase_options.dart + # google-services.json + echo '{"project_info":{"project_number":"123","project_id":"fake","storage_bucket":"fake"},"client":[{"client_info":{"mobilesdk_app_id":"1:123:android:123","android_client_info":{"package_name":"com.flutter.starter.template.flutter_starter_template"}},"api_key":[{"current_key":"fake"}]}]}' > android/app/google-services.json + + - name: Build Docker image + id: build + uses: docker/build-push-action@v5 + with: + context: . + push: false + load: true + tags: flutter-build:latest + build-args: | + BUILD_DATE=${{ github.event.repository.updated_at }} + VCS_REF=${{ github.sha }} + FLUTTER_VERSION=${{ env.FLUTTER_VERSION }} + ANDROID_SDK_VERSION=${{ env.ANDROID_SDK_VERSION }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Extract APK + run: | + docker create --name temp flutter-build:latest + mkdir -p apk-output + docker cp temp:/artifacts/. apk-output/ + docker rm temp + ls -lh apk-output/ + + - name: Upload Android APK artifacts + uses: actions/upload-artifact@v4 + with: + name: android-apk-${{ github.sha }} + path: apk-output/*.apk + retention-days: 30 + compression-level: 0 + + - name: Generate Summary + run: | + echo "## ๐Ÿค– Android APK Build Results" >> $GITHUB_STEP_SUMMARY + echo "| Architecture | File | Size |" >> $GITHUB_STEP_SUMMARY + echo "|--------------|----------|------|" >> $GITHUB_STEP_SUMMARY + for apk in apk-output/*.apk; do + name=$(basename "$apk") + size=$(du -h "$apk" | cut -f1) + arch="Universal" + if [[ "$name" == *"arm64"* ]]; then arch="ARM64"; fi + if [[ "$name" == *"armeabi"* ]]; then arch="ARMv7"; fi + if [[ "$name" == *"x86_64"* ]]; then arch="x86_64"; fi + echo "| $arch | \`$name\` | $size |" >> $GITHUB_STEP_SUMMARY + done + + # ========================================== + # Job 3: iOS Build (macOS runner) + # ========================================== + build-ios: + name: Build iOS IPA + runs-on: macos-latest + needs: analyze + timeout-minutes: 45 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Flutter + uses: subosito/flutter-action@v2 + with: + flutter-version: ${{ env.FLUTTER_VERSION }} + channel: 'stable' + cache: true + + - name: Create mock configuration files + run: | + mkdir -p lib ios/Runner + # Create firebase_options.dart + echo "import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/foundation.dart'; class DefaultFirebaseOptions { static FirebaseOptions get currentPlatform { if (kIsWeb) throw UnsupportedError('Web not configured'); switch (defaultTargetPlatform) { case TargetPlatform.android: return android; case TargetPlatform.iOS: return ios; default: throw UnsupportedError('Platform not configured'); } } static const FirebaseOptions android = FirebaseOptions(apiKey: 'fake', appId: 'fake', messagingSenderId: 'fake', projectId: 'fake', storageBucket: 'fake'); static const FirebaseOptions ios = FirebaseOptions(apiKey: 'fake', appId: 'fake', messagingSenderId: 'fake', projectId: 'fake', storageBucket: 'fake', iosBundleId: 'fake'); }" > lib/firebase_options.dart + # Create GoogleService-Info.plist + echo 'API_KEYfakeGCM_SENDER_ID123PLIST_VERSION1BUNDLE_IDcom.flutter.starter.template.flutterStarterTemplatePROJECT_IDfakeSTORAGE_BUCKETfakeIS_ADS_ENABLEDIS_ANALYTICS_ENABLEDIS_APPINVITE_ENABLEDIS_GCM_ENABLEDIS_SIGNIN_ENABLEDGOOGLE_APP_ID1:123:ios:123' > ios/Runner/GoogleService-Info.plist + + - name: Get dependencies + run: flutter pub get + + - name: Install Pods + run: | + cd ios + rm -rf Pods Podfile.lock + pod install --repo-update + + - name: Build iOS app + run: | + flutter build ios --release --no-codesign --build-number=${{ github.run_number }} + + - name: Create IPA + run: | + mkdir -p Payload + cp -R build/ios/iphoneos/Runner.app Payload/ + IPA_NAME="flutter-starter-ios-${{ github.run_number }}-unsigned.ipa" + zip -r "$IPA_NAME" Payload + rm -rf Payload + + - name: Upload iOS IPA artifact + uses: actions/upload-artifact@v4 + with: + name: ios-ipa-${{ github.sha }} + path: "*.ipa" + retention-days: 30 + compression-level: 0 + + - name: Generate Summary + run: | + echo "## ๐ŸŽ iOS IPA Build Results" >> $GITHUB_STEP_SUMMARY + echo "| File | Size |" >> $GITHUB_STEP_SUMMARY + echo "|------|------|" >> $GITHUB_STEP_SUMMARY + for ipa in *.ipa; do + size=$(du -h "$ipa" | cut -f1) + echo "| \`$ipa\` | $size |" >> $GITHUB_STEP_SUMMARY + done + echo "โš ๏ธ Unsigned IPA for testing only." >> $GITHUB_STEP_SUMMARY diff --git a/.gitignore b/.gitignore index d540810..c539165 100644 --- a/.gitignore +++ b/.gitignore @@ -59,9 +59,22 @@ linked_*.ds unlinked.ds unlinked_spec.ds +# Build Runner generated +*.g.dart +*.mocks.dart + # Firebase related firebase_options.dart +# Docker related +*.img +*.tar +*.tar.gz + +# APK related +*.apk +apk-output/ + # Symbolication related app.*.symbols @@ -130,4 +143,4 @@ ios/Runner/GoogleService-Info.plist android/app/google-services.json # Generated localization -lib/core/localization/generated/ \ No newline at end of file +lib/core/localization/generated/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cbd39b..f2e3dd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added DI-independent fallback error handler inside runZonedGuarded to ensure reliable error reporting even if DI or Firebase are not ready. - Added unified analytics architecture with `Firebase Analytics` integration, debug logging, and automatic screen view tracking via `AnalyticsRouteObserver`. - Added `AppLoggerMixin` for convenient logger access in Bloc/Cubit +- **CI/CD**: Complete automated build pipeline with GitHub Actions + - Multi-stage Dockerfile with Flutter 3.38.4 and Android SDK 35 + - Support for split APK by ABI (armeabi-v7a, arm64-v8a, x86_64) + - `.dockerignore` for optimized build context + - GitHub Actions workflow: code analysis, Android (Docker + DockerHub), iOS (macOS runner) + - Automated quality checks, artifact uploads, and build summaries ### Changed diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..4b784a6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,180 @@ +# ========================================== +# Global ARGs +# ========================================== +ARG TARGETPLATFORM=linux/amd64 +ARG FLUTTER_VERSION=3.38.4 +ARG ANDROID_SDK_VERSION=35 +ARG ANDROID_BUILD_TOOLS_VERSION=35.0.0 +ARG CMDLINE_TOOLS_VERSION=13114758 + + +# ========================================== +# Stage 1: Builder - Android APK build +# ========================================== +FROM --platform=$TARGETPLATFORM ubuntu:22.04 AS builder + + +# Use global ARGs +ARG FLUTTER_VERSION +ARG ANDROID_SDK_VERSION +ARG ANDROID_BUILD_TOOLS_VERSION +ARG CMDLINE_TOOLS_VERSION + + +# Disable interactive prompts +ENV DEBIAN_FRONTEND=noninteractive + + +# Install required dependencies +RUN apt-get update && apt-get install -y \ + curl \ + git \ + unzip \ + xz-utils \ + zip \ + libglu1-mesa \ + openjdk-17-jdk \ + wget \ + ca-certificates \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + + +# Detect actual Java path and create symlink if needed +RUN REAL_JAVA=$(dirname $(dirname $(readlink -f $(which javac)))) && \ + echo "Real Java path: ${REAL_JAVA}" && \ + if [ "${REAL_JAVA}" != "/usr/lib/jvm/java-17-openjdk-amd64" ]; then \ + mkdir -p /usr/lib/jvm && \ + ln -sf ${REAL_JAVA} /usr/lib/jvm/java-17-openjdk-amd64; \ + fi + + +# Set JAVA_HOME via ENV (works for all subsequent RUN commands) +ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 +ENV PATH=$JAVA_HOME/bin:$PATH + + +# Verify that Java is working +RUN java -version && javac -version && \ + echo "JAVA_HOME is set to: ${JAVA_HOME}" && \ + ls -la ${JAVA_HOME}/bin/java + + +# Configure Android SDK +ENV ANDROID_SDK_ROOT=/opt/android-sdk +ENV ANDROID_HOME=$ANDROID_SDK_ROOT +ENV PATH=$PATH:$ANDROID_SDK_ROOT/cmdline-tools/latest/bin:$ANDROID_SDK_ROOT/platform-tools:$ANDROID_SDK_ROOT/build-tools/${ANDROID_BUILD_TOOLS_VERSION} + + +# Create directory for Android SDK +RUN mkdir -p $ANDROID_SDK_ROOT/cmdline-tools + + +# Download and install Android Command Line Tools +RUN wget -q https://dl.google.com/android/repository/commandlinetools-linux-${CMDLINE_TOOLS_VERSION}_latest.zip -O /tmp/cmdline-tools.zip && \ + unzip -q /tmp/cmdline-tools.zip -d $ANDROID_SDK_ROOT/cmdline-tools && \ + mv $ANDROID_SDK_ROOT/cmdline-tools/cmdline-tools $ANDROID_SDK_ROOT/cmdline-tools/latest && \ + rm /tmp/cmdline-tools.zip + + +# Install required Android SDK components (JAVA_HOME already set via ENV) +RUN echo "Current JAVA_HOME: ${JAVA_HOME}" && \ + yes | sdkmanager --licenses && \ + sdkmanager --install \ + "platform-tools" \ + "platforms;android-${ANDROID_SDK_VERSION}" \ + "build-tools;${ANDROID_BUILD_TOOLS_VERSION}" \ + "cmdline-tools;latest" + + +# Download Flutter SDK (release archive instead of git clone for speed) +RUN wget -q https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_${FLUTTER_VERSION}-stable.tar.xz -O /tmp/flutter.tar.xz && \ + tar xf /tmp/flutter.tar.xz -C /opt && \ + rm /tmp/flutter.tar.xz + + +# Configure environment variables for Flutter +ENV FLUTTER_ROOT=/opt/flutter +ENV PATH="${FLUTTER_ROOT}/bin:${FLUTTER_ROOT}/bin/cache/dart-sdk/bin:$PATH" +ENV PUB_CACHE=/root/.pub-cache + + +# Fix Git safe.directory warning +RUN git config --global --add safe.directory /opt/flutter && \ + git config --global --add safe.directory "${FLUTTER_ROOT}" + + +# Initial Flutter setup +RUN flutter doctor -v && \ + flutter config --no-analytics && \ + flutter config --android-sdk $ANDROID_SDK_ROOT && \ + flutter precache --android --no-ios --no-web + + +# Create directories for caching +RUN mkdir -p $PUB_CACHE && \ + mkdir -p /root/.gradle + + +# Set working directory +WORKDIR /app + + +# Copy dependency files to leverage layer caching +COPY pubspec.yaml pubspec.lock ./ + + +# Install dependencies +RUN flutter pub get + + +# Copy entire project +COPY . . + + +# Build Android APK in release mode +RUN flutter build apk --release --split-per-abi --no-tree-shake-icons + + +# Verify that the build succeeded +RUN test -f /app/build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk || \ + test -f /app/build/app/outputs/flutter-apk/app-arm64-v8a-release.apk || \ + (echo "ERROR: APK files not found!" && exit 1) + + +# ========================================== +# Stage 2: Runtime - Artifact storage +# ========================================== +FROM alpine:latest AS runtime + + +# Set image metadata +ARG FLUTTER_VERSION +ARG BUILD_DATE +ARG VCS_REF + + +LABEL maintainer="cowboycloud@icloud.com" \ + description="Flutter Android APK Build Artifacts" \ + version="1.0.0" \ + flutter.version="${FLUTTER_VERSION}" \ + org.opencontainers.image.created="${BUILD_DATE}" \ + org.opencontainers.image.revision="${VCS_REF}" \ + org.opencontainers.image.title="Flutter Starter Template Android Build" \ + org.opencontainers.image.description="Production-ready Android APK artifacts" + + +# Create directory for APKs +RUN mkdir -p /artifacts + + +# Copy built APKs from builder stage +COPY --from=builder /app/build/app/outputs/flutter-apk/*.apk /artifacts/ + + +# Set working directory +WORKDIR /artifacts + + +# Default command - list files with sizes +CMD ["ls", "-lh"] diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 86ec58e..0439783 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -201,7 +201,6 @@ 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 3D0E01DF18316EDF773EAB0D /* [CP] Embed Pods Frameworks */, - 05D396F55CC39F0934932997 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */, ); buildRules = ( ); @@ -274,24 +273,6 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 05D396F55CC39F0934932997 /* FlutterFire: "flutterfire upload-crashlytics-symbols" */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = "FlutterFire: \"flutterfire upload-crashlytics-symbols\""; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\n#!/bin/bash\nPATH=\"${PATH}:$FLUTTER_ROOT/bin:${PUB_CACHE}/bin:$HOME/.pub-cache/bin\"\n\nif [ -z \"$PODS_ROOT\" ] || [ ! -d \"$PODS_ROOT/FirebaseCrashlytics\" ]; then\n # Cannot use \"BUILD_DIR%/Build/*\" as per Firebase documentation, it points to \"flutter-project/build/ios/*\" path which doesn't have run script\n DERIVED_DATA_PATH=$(echo \"$BUILD_ROOT\" | sed -E 's|(.*DerivedData/[^/]+).*|\\1|')\n PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT=\"${DERIVED_DATA_PATH}/SourcePackages/checkouts/firebase-ios-sdk/Crashlytics/run\"\nelse\n PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT=\"$PODS_ROOT/FirebaseCrashlytics/run\"\nfi\n\n# Command to upload symbols script used to upload symbols to Firebase server\nflutterfire upload-crashlytics-symbols --upload-symbols-script-path=\"$PATH_TO_CRASHLYTICS_UPLOAD_SCRIPT\" --platform=ios --apple-project-path=\"${SRCROOT}\" --env-platform-name=\"${PLATFORM_NAME}\" --env-configuration=\"${CONFIGURATION}\" --env-project-dir=\"${PROJECT_DIR}\" --env-built-products-dir=\"${BUILT_PRODUCTS_DIR}\" --env-dwarf-dsym-folder-path=\"${DWARF_DSYM_FOLDER_PATH}\" --env-dwarf-dsym-file-name=\"${DWARF_DSYM_FILE_NAME}\" --env-infoplist-path=\"${INFOPLIST_PATH}\" --default-config=default\n"; - }; 1D144EDA24AD260BDCD8A5DC /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -360,10 +341,14 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); + inputPaths = ( + ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); + outputPaths = ( + ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; @@ -382,7 +367,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build\n"; }; /* End PBXShellScriptBuildPhase section */ diff --git a/test/core/router/analytics_route_onserver_test.mocks.dart b/test/core/router/analytics_route_onserver_test.mocks.dart deleted file mode 100644 index 6968f91..0000000 --- a/test/core/router/analytics_route_onserver_test.mocks.dart +++ /dev/null @@ -1,47 +0,0 @@ -// Mocks generated by Mockito 5.4.6 from annotations -// in flutter_starter_template/test/core/router/analytics_route_onserver_test.dart. -// Do not manually edit this file. - -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i3; - -import 'package:flutter_starter_template/core/utils/analytics/app_analytics.dart' - as _i2; -import 'package:mockito/mockito.dart' as _i1; - -// ignore_for_file: type=lint -// ignore_for_file: avoid_redundant_argument_values -// ignore_for_file: avoid_setters_without_getters -// ignore_for_file: comment_references -// ignore_for_file: deprecated_member_use -// ignore_for_file: deprecated_member_use_from_same_package -// ignore_for_file: implementation_imports -// ignore_for_file: invalid_use_of_visible_for_testing_member -// ignore_for_file: must_be_immutable -// ignore_for_file: prefer_const_constructors -// ignore_for_file: unnecessary_parenthesis -// ignore_for_file: camel_case_types -// ignore_for_file: subtype_of_sealed_class - -/// A class which mocks [AppAnalytics]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockAppAnalytics extends _i1.Mock implements _i2.AppAnalytics { - @override - _i3.Future logEvent(String? name, {Map? parameters}) => - (super.noSuchMethod( - Invocation.method(#logEvent, [name], {#parameters: parameters}), - returnValue: _i3.Future.value(), - returnValueForMissingStub: _i3.Future.value(), - ) - as _i3.Future); - - @override - _i3.Future logScreenView(String? screenName) => - (super.noSuchMethod( - Invocation.method(#logScreenView, [screenName]), - returnValue: _i3.Future.value(), - returnValueForMissingStub: _i3.Future.value(), - ) - as _i3.Future); -} From 970f1be427bf87b50652a2b9ece2477196d1d01a Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Sun, 14 Dec 2025 14:17:25 +0700 Subject: [PATCH 26/32] refactor(core): use interface contracts for analytics/logger and seal implementations --- lib/core/utils/analytics/app_analytics.dart | 2 +- lib/core/utils/analytics/debug_analytics_impl.dart | 2 +- lib/core/utils/analytics/firebase_analytics_impl.dart | 2 +- lib/core/utils/logger/app_logger.dart | 2 +- lib/core/utils/logger/app_logger_impl.dart | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/core/utils/analytics/app_analytics.dart b/lib/core/utils/analytics/app_analytics.dart index e492537..c62ec37 100644 --- a/lib/core/utils/analytics/app_analytics.dart +++ b/lib/core/utils/analytics/app_analytics.dart @@ -1,5 +1,5 @@ /// Abstract class for app analytics functionality. -abstract class AppAnalytics { +abstract interface class AppAnalytics { /// Logs an event with the given name and parameters. Future logEvent(String name, {Map? parameters}); diff --git a/lib/core/utils/analytics/debug_analytics_impl.dart b/lib/core/utils/analytics/debug_analytics_impl.dart index 9f74dd5..e7571c0 100644 --- a/lib/core/utils/analytics/debug_analytics_impl.dart +++ b/lib/core/utils/analytics/debug_analytics_impl.dart @@ -2,7 +2,7 @@ import '../logger/app_logger.dart'; import 'app_analytics.dart'; /// Implementation of [AppAnalytics]. -class DebugAnalyticsImpl implements AppAnalytics { +final class DebugAnalyticsImpl implements AppAnalytics { final AppLogger _logger; /// Creates an instance of [DebugAnalyticsImpl]. diff --git a/lib/core/utils/analytics/firebase_analytics_impl.dart b/lib/core/utils/analytics/firebase_analytics_impl.dart index c5a03cd..8fadf8c 100644 --- a/lib/core/utils/analytics/firebase_analytics_impl.dart +++ b/lib/core/utils/analytics/firebase_analytics_impl.dart @@ -3,7 +3,7 @@ import 'package:firebase_analytics/firebase_analytics.dart'; import 'app_analytics.dart'; /// Implementation of [AppAnalytics]. -class FirebaseAnalyticsImpl implements AppAnalytics { +final class FirebaseAnalyticsImpl implements AppAnalytics { final FirebaseAnalytics _analytics; /// Creates an instance of [FirebaseAnalyticsImpl]. diff --git a/lib/core/utils/logger/app_logger.dart b/lib/core/utils/logger/app_logger.dart index 26c3c31..f10fa74 100644 --- a/lib/core/utils/logger/app_logger.dart +++ b/lib/core/utils/logger/app_logger.dart @@ -1,5 +1,5 @@ /// Abstract class for application logging. -abstract class AppLogger { +abstract interface class AppLogger { /// Log a message at trace level. void t(String message, [Object? error, StackTrace? stackTrace]); diff --git a/lib/core/utils/logger/app_logger_impl.dart b/lib/core/utils/logger/app_logger_impl.dart index 4b05457..879e7aa 100644 --- a/lib/core/utils/logger/app_logger_impl.dart +++ b/lib/core/utils/logger/app_logger_impl.dart @@ -3,7 +3,7 @@ import 'package:logger/logger.dart'; import 'app_logger.dart'; /// Implementation of [AppLogger]. -class AppLoggerImpl implements AppLogger { +final class AppLoggerImpl implements AppLogger { final Logger _logger; /// Creates an instance of [AppLoggerImpl] with the provided [Logger]. From 65b0adf203cf4d4d1d5dcc39d7b9e66519f6742c Mon Sep 17 00:00:00 2001 From: Ryan Delaney <145113692+CowboyGH@users.noreply.github.com> Date: Fri, 19 Dec 2025 14:53:34 +0700 Subject: [PATCH 27/32] feat(network): add NetworkService for network availability monitoring (#2) * feat(network): add NetworkService for network availability monitoring * chore(ios): update Podfile.lock and project.pbxproj for dependency version upgrades * fix(ci): free disk space before docker build --- .github/workflows/main.yml | 3 + CHANGELOG.md | 1 + android/app/src/main/AndroidManifest.xml | 2 + ios/Podfile.lock | 22 ++- ios/Runner.xcodeproj/project.pbxproj | 4 - lib/core/di/di.dart | 10 ++ .../services/network/network_service.dart | 17 ++ .../network/network_service_impl.dart | 53 ++++++ pubspec.lock | 32 ++++ pubspec.yaml | 1 + .../network/network_service_test.dart | 165 ++++++++++++++++++ 11 files changed, 298 insertions(+), 12 deletions(-) create mode 100644 lib/core/services/network/network_service.dart create mode 100644 lib/core/services/network/network_service_impl.dart create mode 100644 test/core/services/network/network_service_test.dart diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 34a1348..8917208 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -90,6 +90,9 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + + - name: Free disk space + uses: endersonmenezes/free-disk-space@v2 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 diff --git a/CHANGELOG.md b/CHANGELOG.md index f2e3dd1..544c109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `.dockerignore` for optimized build context - GitHub Actions workflow: code analysis, Android (Docker + DockerHub), iOS (macOS runner) - Automated quality checks, artifact uploads, and build summaries +- Added `NetworkService` for monitoring network availability with `connectivity_plus` integration and unit tests. ### Changed diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index e10f231..ccaa432 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,6 @@ + + 12.4.0) @@ -1216,18 +1218,18 @@ PODS: - firebase_core - FirebaseAnalytics (= 12.4.0) - Flutter - - firebase_auth (6.1.1): + - firebase_auth (6.1.2): - Firebase/Auth (= 12.4.0) - firebase_core - Flutter - firebase_core (4.2.1): - Firebase/CoreOnly (= 12.4.0) - Flutter - - firebase_crashlytics (5.0.4): + - firebase_crashlytics (5.0.5): - Firebase/Crashlytics (= 12.4.0) - firebase_core - Flutter - - firebase_storage (13.0.3): + - firebase_storage (13.0.4): - Firebase/Storage (= 12.4.0) - firebase_core - Flutter @@ -1493,6 +1495,7 @@ PODS: DEPENDENCIES: - cloud_firestore (from `.symlinks/plugins/cloud_firestore/ios`) + - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) - firebase_analytics (from `.symlinks/plugins/firebase_analytics/ios`) - firebase_auth (from `.symlinks/plugins/firebase_auth/ios`) - firebase_core (from `.symlinks/plugins/firebase_core/ios`) @@ -1539,6 +1542,8 @@ SPEC REPOS: EXTERNAL SOURCES: cloud_firestore: :path: ".symlinks/plugins/cloud_firestore/ios" + connectivity_plus: + :path: ".symlinks/plugins/connectivity_plus/ios" firebase_analytics: :path: ".symlinks/plugins/firebase_analytics/ios" firebase_auth: @@ -1561,13 +1566,14 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: abseil: a05cc83bf02079535e17169a73c5be5ba47f714b BoringSSL-GRPC: dded2a44897e45f28f08ae87a55ee4bcd19bc508 - cloud_firestore: 79014bb3b303d451717ed5fe69fded8a2b2e8dc2 + cloud_firestore: 7a6d8a533ec7418a7fe46b3a5dabf55661a5b298 + connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd Firebase: f07b15ae5a6ec0f93713e30b923d9970d144af3e firebase_analytics: 67fbdd9f3c04e55048024f3da21cfc36f05e56cf - firebase_auth: c2b8be95d602d4e8a9148fae72333ef78e69cc20 + firebase_auth: 9225db04db5d8e3b46dc8940e04bc6aec6833e27 firebase_core: f1aafb21c14f497e5498f7ffc4dc63cbb52b2594 - firebase_crashlytics: 83c7467d7534975a4d779af43bd226d0a4616464 - firebase_storage: 0ba617a05b24aec050395e4d5d3773c0d7518a15 + firebase_crashlytics: c039028126cb45e32f4c217aa392408b0963d081 + firebase_storage: d558bbfa99449fff39352db83c466c8515fa1afa FirebaseAnalytics: 0fc2b20091f0ddd21bf73397cf8f0eb5346dc24f FirebaseAppCheckInterop: f734c802f21fe1da0837708f0f9a27218c8a4ed0 FirebaseAuth: 4a2aed737c84114a9d9b33d11ae1b147d6b94889 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 0439783..fd06209 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -341,14 +341,10 @@ inputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); - outputPaths = ( - ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; diff --git a/lib/core/di/di.dart b/lib/core/di/di.dart index 896b188..44d6726 100644 --- a/lib/core/di/di.dart +++ b/lib/core/di/di.dart @@ -1,8 +1,11 @@ +import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:flutter/foundation.dart'; import 'package:get_it/get_it.dart'; import 'package:logger/logger.dart'; +import '../services/network/network_service.dart'; +import '../services/network/network_service_impl.dart'; import '../utils/analytics/app_analytics.dart'; import '../utils/analytics/debug_analytics_impl.dart'; import '../utils/analytics/firebase_analytics_impl.dart'; @@ -26,4 +29,11 @@ void setupDI() { ? FirebaseAnalyticsImpl(di()) : DebugAnalyticsImpl(di()), ); + + /// Connectivity + di.registerLazySingleton(() => Connectivity()); + di.registerLazySingleton( + () => NetworkServiceImpl(di()), + dispose: (param) => param.dispose(), + ); } diff --git a/lib/core/services/network/network_service.dart b/lib/core/services/network/network_service.dart new file mode 100644 index 0000000..3553987 --- /dev/null +++ b/lib/core/services/network/network_service.dart @@ -0,0 +1,17 @@ +/// Abstract interface for monitoring network availability. +abstract interface class NetworkService { + /// Stream that emits network availability changes. + Stream get connectionStream; + + /// Latest known network availability value. + bool get isConnected; + + /// Initializes the service, performing the initial connectivity check. + Future init(); + + /// Performs a one-shot check of current network availability. + Future hasNetwork(); + + /// Releases resources (stream subscription, controllers). + Future dispose(); +} diff --git a/lib/core/services/network/network_service_impl.dart b/lib/core/services/network/network_service_impl.dart new file mode 100644 index 0000000..742277b --- /dev/null +++ b/lib/core/services/network/network_service_impl.dart @@ -0,0 +1,53 @@ +import 'dart:async'; + +import 'package:connectivity_plus/connectivity_plus.dart'; + +import 'network_service.dart'; + +/// Implementation of [NetworkService] using connectivity_plus package. +final class NetworkServiceImpl implements NetworkService { + final Connectivity _connectivity; + + final _controller = StreamController.broadcast(); + late final StreamSubscription> _subscription; + bool? _lastHasNetwork; + + /// Creates an instance of [NetworkServiceImpl]. + NetworkServiceImpl(this._connectivity) { + _subscription = _connectivity.onConnectivityChanged.listen(_onConnectivityChanged); + } + + void _onConnectivityChanged(List results) { + _emit(!results.contains(ConnectivityResult.none)); + } + + /// Emits [hasNetwork] to [connectionStream] when it changes. + void _emit(bool hasNetwork) { + if (_lastHasNetwork == hasNetwork) return; + _lastHasNetwork = hasNetwork; + if (!_controller.isClosed) _controller.add(hasNetwork); + } + + @override + Future init() async { + _emit(await hasNetwork()); + } + + @override + Future hasNetwork() async { + final results = await _connectivity.checkConnectivity(); + return !results.contains(ConnectivityResult.none); + } + + @override + Stream get connectionStream => _controller.stream; + + @override + bool get isConnected => _lastHasNetwork ?? false; + + @override + Future dispose() async { + await _subscription.cancel(); + await _controller.close(); + } +} diff --git a/pubspec.lock b/pubspec.lock index dfdb3df..605d50b 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -217,6 +217,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.1" + connectivity_plus: + dependency: "direct main" + description: + name: connectivity_plus + sha256: "33bae12a398f841c6cda09d1064212957265869104c478e5ad51e2fb26c3973c" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + connectivity_plus_platform_interface: + dependency: transitive + description: + name: connectivity_plus_platform_interface + sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204" + url: "https://pub.dev" + source: hosted + version: "2.0.1" convert: dependency: transitive description: @@ -249,6 +265,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.3" + dbus: + dependency: transitive + description: + name: dbus + sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c" + url: "https://pub.dev" + source: hosted + version: "0.7.11" diff_match_patch: dependency: transitive description: @@ -757,6 +781,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + nm: + dependency: transitive + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" node_preamble: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2b846de..1d27e02 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -12,6 +12,7 @@ dependencies: bloc_test: ^10.0.0 cached_network_image: ^3.4.1 cloud_firestore: ^6.0.3 + connectivity_plus: ^7.0.0 dio: ^5.9.0 equatable: ^2.0.7 firebase_analytics: ^12.0.4 diff --git a/test/core/services/network/network_service_test.dart b/test/core/services/network/network_service_test.dart new file mode 100644 index 0000000..5392946 --- /dev/null +++ b/test/core/services/network/network_service_test.dart @@ -0,0 +1,165 @@ +import 'dart:async'; + +import 'package:connectivity_plus/connectivity_plus.dart'; +import 'package:flutter_starter_template/core/services/network/network_service_impl.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/annotations.dart'; +import 'package:mockito/mockito.dart'; + +import 'network_service_test.mocks.dart'; + +@GenerateNiceMocks([MockSpec()]) +void main() { + late Connectivity connectivity; + + setUp(() { + connectivity = MockConnectivity(); + + when( + connectivity.onConnectivityChanged, + ).thenAnswer((_) => const Stream>.empty()); + }); + + group('NetworkServiceImpl', () { + test('init sets isConnected=true when network is available (wifi)', () async { + // Arrange + when( + connectivity.checkConnectivity(), + ).thenAnswer((_) async => [ConnectivityResult.wifi]); + + final service = NetworkServiceImpl(connectivity); + addTearDown(() async => await service.dispose()); + + // Act + await service.init(); + + // Assert + expect(service.isConnected, isTrue); + verify(connectivity.checkConnectivity()).called(1); + }); + + test('init sets isConnected=false when no network is available (none)', () async { + // Arrange + when( + connectivity.checkConnectivity(), + ).thenAnswer((_) async => [ConnectivityResult.none]); + + final service = NetworkServiceImpl(connectivity); + addTearDown(() async => await service.dispose()); + + // Act + await service.init(); + + // Assert + expect(service.isConnected, isFalse); + verify(connectivity.checkConnectivity()).called(1); + }); + + test( + 'connectionStream emits true when connectivity changes to wifi', + () async { + // Arrange + final controller = StreamController>(); + addTearDown(() async => await controller.close()); + + when(connectivity.onConnectivityChanged).thenAnswer((_) => controller.stream); + + final service = NetworkServiceImpl(connectivity); + addTearDown(() async => await service.dispose()); + + final events = []; + final sub = service.connectionStream.listen(events.add); + addTearDown(() async => await sub.cancel()); + + // Act + controller.add([ConnectivityResult.wifi]); + await pumpEventQueue(); + + // Assert + expect(events, [true]); + }, + ); + + test( + 'connectionStream emits values in order for multiple connectivity changes', + () async { + // Arrange + final controller = StreamController>(); + + addTearDown(() async => await controller.close()); + when(connectivity.onConnectivityChanged).thenAnswer((_) => controller.stream); + + final service = NetworkServiceImpl(connectivity); + addTearDown(() async => await service.dispose()); + + final events = []; + final sub = service.connectionStream.listen(events.add); + addTearDown(() async => await sub.cancel()); + + // Act + controller.add([ConnectivityResult.none]); + await pumpEventQueue(); + + controller.add([ConnectivityResult.wifi]); + await pumpEventQueue(); + + controller.add([ConnectivityResult.none]); + await pumpEventQueue(); + + // Assert + expect(events, [false, true, false]); + }, + ); + + test('connectionStream does not emit duplicates when state does not change', () async { + // Arrange + final controller = StreamController>(); + addTearDown(() async => await controller.close()); + + when(connectivity.onConnectivityChanged).thenAnswer((_) => controller.stream); + + final service = NetworkServiceImpl(connectivity); + addTearDown(() async => await service.dispose()); + + final events = []; + final sub = service.connectionStream.listen(events.add); + addTearDown(() async => await sub.cancel()); + + // Act + controller.add([ConnectivityResult.wifi]); + await pumpEventQueue(); + + controller.add([ConnectivityResult.mobile]); + await pumpEventQueue(); + + controller.add([ConnectivityResult.wifi]); + await pumpEventQueue(); + + // Assert + expect(events, [true]); + }); + + test('dispose cancels subscription and stops emitting further updates', () async { + // Arrange + final controller = StreamController>(); + addTearDown(() async => await controller.close()); + + when(connectivity.onConnectivityChanged).thenAnswer((_) => controller.stream); + + final service = NetworkServiceImpl(connectivity); + + final events = []; + final subscription = service.connectionStream.listen(events.add); + addTearDown(() async => await subscription.cancel()); + + // Act + await service.dispose(); + + controller.add([ConnectivityResult.wifi]); + await pumpEventQueue(); + + // Assert + expect(events, isEmpty); + }); + }); +} From 6c9e5cf0d748fbcfb1bc8b1227c4df2e8c69a02a Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Fri, 19 Dec 2025 15:29:39 +0700 Subject: [PATCH 28/32] chore(deps): update dependencies --- pubspec.lock | 96 ++++++++++++++++++++++++++-------------------------- pubspec.yaml | 24 ++++++------- 2 files changed, 60 insertions(+), 60 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index 605d50b..c9372de 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: "8a1f5f3020ef2a74fb93f7ab3ef127a8feea33a7a2276279113660784ee7516a" + sha256: e4a1b612fd2955908e26116075b3a4baf10c353418ca645b4deae231c82bf144 url: "https://pub.dev" source: hosted - version: "1.3.64" + version: "1.3.65" analyzer: dependency: transitive description: @@ -181,26 +181,26 @@ packages: dependency: "direct main" description: name: cloud_firestore - sha256: fc1de79a62fe21615e9012f396070e6121838ef0d879475a4ec8320e79378208 + sha256: "88abd6dc7786c23c8b5e434901bb0d79176414e52e9ab50a8e52552ff6148d7a" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.1.1" cloud_firestore_platform_interface: dependency: transitive description: name: cloud_firestore_platform_interface - sha256: "2d2ee96a32ec3dd22fb682295e9bed6336e49a43f056d7841690228adca3ee7d" + sha256: "573d4b4ebc56ba573dc9bac88b65bdb991cc5b66a885a62c7ab8dd1e2eaa0944" url: "https://pub.dev" source: hosted - version: "7.0.4" + version: "7.0.5" cloud_firestore_web: dependency: transitive description: name: cloud_firestore_web - sha256: "28c39f3d050bf669787ef13fa0890df2b4af236de864e2db0cc3897b857066cb" + sha256: c1ef53308a09d475503f75b658b65fae32af24047d03bba0713098f882ed187f url: "https://pub.dev" source: hosted - version: "5.1.0" + version: "5.1.1" code_builder: dependency: transitive description: @@ -333,58 +333,58 @@ packages: dependency: "direct main" description: name: firebase_analytics - sha256: bfb80d92eee10a6585ebd5a7e60de5caf0f2c06329e5676c0578130aea1bfe85 + sha256: "8ca4832c7a6d145ce987fd07d6dfbb8c91d9058178342f20de6305fb77b1b40d" url: "https://pub.dev" source: hosted - version: "12.0.4" + version: "12.1.0" firebase_analytics_platform_interface: dependency: transitive description: name: firebase_analytics_platform_interface - sha256: "3b803077907def997044774f6c022d8e9204e9c0f5e205e3572d887c93dafd72" + sha256: d00234716f415f89eb5c2cefb1238d7fd2f3120275d71414b84ae434dcdb7a19 url: "https://pub.dev" source: hosted - version: "5.0.4" + version: "5.0.5" firebase_analytics_web: dependency: transitive description: name: firebase_analytics_web - sha256: "0dbd96dbe77b51185319000c0078477fdcffb4abb0018c362dd9afb9845c1e06" + sha256: e42b294e51aedb4bd4b761a886c8d6b473c44b44aa4c0b47cab06b2c66ac3fba url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.6.1+1" firebase_auth: dependency: "direct main" description: name: firebase_auth - sha256: e54fb3ba57de041d832574126a37726eedf0f57400869f1942b0ca8ce4a6e209 + sha256: "060a9bfc9877538dfdc69f2fdc1e0bf068439a7e9f22da6513f7b9bde308bb93" url: "https://pub.dev" source: hosted - version: "6.1.2" + version: "6.1.3" firebase_auth_platform_interface: dependency: transitive description: name: firebase_auth_platform_interface - sha256: "421f95dc553cb283ed9d4d140e719800c0331d49ed37b962e513c9d1d61b090b" + sha256: e91c9aadf9944e8319855fa752fd6c664bade2bbd6e7697a5142113c319a4288 url: "https://pub.dev" source: hosted - version: "8.1.4" + version: "8.1.5" firebase_auth_web: dependency: transitive description: name: firebase_auth_web - sha256: a064ffee202f7d42d62e2c01775899d4ffcb83c602af07632f206acd46a0964e + sha256: "155145fd84f311e50eb2bfeb892f74b0d1b30936f40765a051b70110270cb6d1" url: "https://pub.dev" source: hosted - version: "6.1.0" + version: "6.1.1" firebase_core: dependency: "direct main" description: name: firebase_core - sha256: "1f2dfd9f535d81f8b06d7a50ecda6eac1e6922191ed42e09ca2c84bd2288927c" + sha256: "29cfa93c771d8105484acac340b5ea0835be371672c91405a300303986f4eba9" url: "https://pub.dev" source: hosted - version: "4.2.1" + version: "4.3.0" firebase_core_platform_interface: dependency: transitive description: @@ -397,50 +397,50 @@ packages: dependency: transitive description: name: firebase_core_web - sha256: ff18fabb0ad0ed3595d2f2c85007ecc794aadecdff5b3bb1460b7ee47cded398 + sha256: a631bbfbfa26963d68046aed949df80b228964020e9155b086eff94f462bbf1f url: "https://pub.dev" source: hosted - version: "3.3.0" + version: "3.3.1" firebase_crashlytics: dependency: "direct main" description: name: firebase_crashlytics - sha256: bd4eadd620071ae48ef0e3617499be2a0ea4062d6d5c6fea6c1cec41ebbb2435 + sha256: "8d52022ee6fdd224e92c042f297d1fd0ec277195c49f39fa61b8cc500a639f00" url: "https://pub.dev" source: hosted - version: "5.0.5" + version: "5.0.6" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: a8ca502fe3aa48b4f0b9e6e3bc0019085a247b5d1214cd342a189457975662db + sha256: "97c6a97b35e3d3dafe38fb053a65086a1efb125022d292161405848527cc25a4" url: "https://pub.dev" source: hosted - version: "3.8.15" + version: "3.8.16" firebase_storage: dependency: "direct main" description: name: firebase_storage - sha256: "3438f38590186010ce76ece683ebf3b842cd637f31f83a13620917d7438a58fd" + sha256: "5321a88518d992d03f8482bc48ede9cfb315b465bd59f1587468bc2f4a809d08" url: "https://pub.dev" source: hosted - version: "13.0.4" + version: "13.0.5" firebase_storage_platform_interface: dependency: transitive description: name: firebase_storage_platform_interface - sha256: "5d56021a9d30f7ca89559c96cc4c7250ce6ff8881382ff7238fde64a1f449e39" + sha256: dbcd5363a6b2edf2d1238fc22f10f4a39f0ea991a432b4fc43871ea31647cf5a url: "https://pub.dev" source: hosted - version: "5.2.15" + version: "5.2.16" firebase_storage_web: dependency: transitive description: name: firebase_storage_web - sha256: a06775d1df6dd90f5fa3fe9e221b988dcbc221e73a0f8951136536e6d5e548e6 + sha256: "141ada1185fbd4a982d7cdd4486d1e42fe616db6024e22f7fcb3e9df388be8b4" url: "https://pub.dev" source: hosted - version: "3.11.0" + version: "3.11.1" fixnum: dependency: transitive description: @@ -529,10 +529,10 @@ packages: dependency: "direct main" description: name: get_it - sha256: ae78de7c3f2304b8d81f2bb6e320833e5e81de942188542328f074978cc0efa9 + sha256: "1d648d2dd2047d7f7450d5727ca24ee435f240385753d90b49650e3cdff32e56" url: "https://pub.dev" source: hosted - version: "8.3.0" + version: "9.2.0" glob: dependency: transitive description: @@ -545,10 +545,10 @@ packages: dependency: "direct main" description: name: go_router - sha256: d8f590a69729f719177ea68eb1e598295e8dbc41bbc247fed78b2c8a25660d7c + sha256: eff94d2a6fc79fa8b811dde79c7549808c2346037ee107a1121b4a644c745f2a url: "https://pub.dev" source: hosted - version: "16.3.0" + version: "17.0.1" graphs: dependency: transitive description: @@ -657,10 +657,10 @@ packages: dependency: transitive description: name: lean_builder - sha256: "6511c632f60435fa824283a9660b94de1b8768c1501f6910b773034bf8af7035" + sha256: "4f3d70c34c52cc5034e8cc6f53d35aa3a32fb373b78fb4c29cf45cd1dcf06942" url: "https://pub.dev" source: hosted - version: "0.1.3" + version: "0.1.5" lints: dependency: transitive description: @@ -849,10 +849,10 @@ packages: dependency: transitive description: name: path_provider_foundation - sha256: "97390a0719146c7c3e71b6866c34f1cde92685933165c1c671984390d2aca776" + sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4" url: "https://pub.dev" source: hosted - version: "2.4.4" + version: "2.5.1" path_provider_linux: dependency: transitive description: @@ -969,18 +969,18 @@ packages: dependency: "direct main" description: name: shared_preferences - sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" url: "https://pub.dev" source: hosted - version: "2.5.3" + version: "2.5.4" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "46a46fd64659eff15f4638bbe19de43f9483f0e0bf024a9fb6b3582064bacc7b" + sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc" url: "https://pub.dev" source: hosted - version: "2.4.17" + version: "2.4.18" shared_preferences_foundation: dependency: transitive description: @@ -1270,10 +1270,10 @@ packages: dependency: transitive description: name: watcher - sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a" + sha256: f52385d4f73589977c80797e60fe51014f7f2b957b5e9a62c3f6ada439889249 url: "https://pub.dev" source: hosted - version: "1.1.4" + version: "1.2.0" web: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 1d27e02..62d2b85 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -11,37 +11,37 @@ license: "MIT" dependencies: bloc_test: ^10.0.0 cached_network_image: ^3.4.1 - cloud_firestore: ^6.0.3 + cloud_firestore: ^6.1.1 connectivity_plus: ^7.0.0 dio: ^5.9.0 equatable: ^2.0.7 - firebase_analytics: ^12.0.4 - firebase_auth: ^6.1.1 - firebase_core: ^4.2.0 - firebase_crashlytics: ^5.0.4 - firebase_storage: ^13.0.3 + firebase_analytics: ^12.1.0 + firebase_auth: ^6.1.3 + firebase_core: ^4.3.0 + firebase_crashlytics: ^5.0.6 + firebase_storage: ^13.0.5 flutter: sdk: flutter flutter_bloc: ^9.1.1 flutter_localizations: sdk: flutter - flutter_svg: ^2.2.1 + flutter_svg: ^2.2.3 freezed_annotation: ^3.1.0 - get_it: ^8.2.0 - go_router: ^16.3.0 + get_it: ^9.2.0 + go_router: ^17.0.1 intl: ^0.20.2 json_annotation: ^4.9.0 logger: ^2.6.2 - mockito: ^5.5.0 + mockito: ^5.6.1 native_splash_screen: ^3.0.0 path: ^1.9.1 path_provider: ^2.1.5 provider: ^6.1.5+1 retrofit: ^4.9.1 - shared_preferences: ^2.5.3 + shared_preferences: ^2.5.4 dev_dependencies: - build_runner: ^2.7.1 + build_runner: ^2.10.4 freezed: ^3.2.3 flutter_lints: ^6.0.0 flutter_test: From 7c494af1a05604724eb441e920806535260aa76a Mon Sep 17 00:00:00 2001 From: Ryan Delaney <145113692+CowboyGH@users.noreply.github.com> Date: Sat, 20 Dec 2025 10:54:40 +0700 Subject: [PATCH 29/32] fix(ci): update free disk space action to v3 and add additional cleanup options (#3) --- .github/workflows/main.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8917208..3e4ede7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -92,7 +92,13 @@ jobs: uses: actions/checkout@v4 - name: Free disk space - uses: endersonmenezes/free-disk-space@v2 + uses: endersonmenezes/free-disk-space@v3 + with: + remove_android: true + remove_dotnet: true + remove_haskell: true + remove_tool_cache: true + remove_swap: true - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 From 194e8a8ea16d7e34b085e885d8b1e8302220bb0c Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Sat, 20 Dec 2025 14:18:53 +0700 Subject: [PATCH 30/32] fix(ci): remove push trigger from CI/CD pipeline --- .github/workflows/main.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3e4ede7..9de6c81 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,8 +1,6 @@ name: Flutter CI/CD Pipeline on: - push: - branches: [main, develop] pull_request: branches: [main, develop] workflow_dispatch: From 7f4deee50d2765370d75a960ba69b7d9a8060b4d Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Sat, 20 Dec 2025 14:19:57 +0700 Subject: [PATCH 31/32] docs: update README.md --- README.md | 246 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 243 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index e3d005a..505b275 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,253 @@ # ๐Ÿงฉ Flutter Starter Template -A scalable **Flutter Starter Template** for building production-ready business applications. -Includes preconfigured architecture, dependency injection, routing, and modular feature structure. +A pragmatic **Flutter Starter Template** for personal and academic projects, built with **Clean Architecture**, **Firebase integration**, and **best practices** out of the box. + +[![Flutter Version](https://img.shields.io/badge/Flutter-3.10.0+-blue.svg)](https://flutter.dev/) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) + +--- + +## ๐Ÿ“š Table of Contents + +- [๐Ÿš€ Highlights](#-highlights) +- [๐Ÿ“ฆ Tech Stack](#-tech-stack) +- [๐Ÿ—๏ธ Architecture](#๏ธ-architecture) +- [๐Ÿ“‚ Project Structure](#-project-structure) +- [๐Ÿ› ๏ธ Getting Started](#๏ธ-getting-started) +- [๐Ÿงช Testing](#-testing) +- [๐Ÿ‹ Docker Build](#-docker-build) +- [๐Ÿง  Why This Template?](#-why-this-template) +- [๐Ÿ“ Documentation](#-documentation) +- [๐Ÿค Contributing](#-contributing) +- [๐Ÿ“œ License](#-license) +- [โœจ Author](#-author) + +--- + +## ๐Ÿš€ Highlights + +- ๐Ÿ›๏ธ **Clean Architecture** (Data โ†’ Domain โ†’ Presentation) +- ๐Ÿ’‰ **Dependency Injection** (GetIt + Provider) +- ๐Ÿ”ฅ **Firebase** integration (Core, Crashlytics, Analytics) +- โš ๏ธ **Error Handling** (Result pattern + Failure hierarchy) +- ๐Ÿ“ **Logging** with AppLogger abstraction and Mixin for Bloc/Cubit +- ๐Ÿงญ **Navigation** with GoRouter and automatic screen tracking +- ๐Ÿ“ฆ **Modular Structure** for scalable feature development +- ๐ŸŽจ **Shared UIKit** (buttons, images, themes) + +--- + +## ๐Ÿ“ฆ Tech Stack + +| Category | Technology | +| ---------- | ----------- | +| **Framework** | Flutter 3.10.0+ | +| **State Management** | flutter_bloc | +| **Dependency Injection** | get_it, provider | +| **Backend** | Firebase (Core, Auth, Crashlytics, Analytics, Firestore, Storage) | +| **Navigation** | go_router | +| **Connectivity Tracking** | connectivity_plus | +| **HTTP Client** | dio, retrofit | +| **UI Components** | flutter_svg, cached_network_image | +| **Code Generation** | freezed, json_serializable, build_runner | +| **Logging** | logger | +| **Localization** | intl, flutter_localizations | +| **Testing** | mockito, bloc_test, flutter_test | +| **Local Storage** | shared_preferences | + +--- + +## ๐Ÿ—๏ธ Architecture + +This template follows **Clean Architecture** principles: + +```text +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Presentation Layer โ”‚ +โ”‚ (Pages, Widgets, BLoC/Cubit) โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ”‚ uses +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Domain Layer โ”‚ +โ”‚ (Entities, UseCases, Repositories*) โ”‚ * interfaces only +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ฒโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ”‚ + โ”‚ implements +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Data Layer โ”‚ +โ”‚ (DTOs, DataSources, Repositories Impl) โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +``` + +### Dependency Rule + +`Presentation โ†’ Domain โ† Data` + +**Domain Layer** does NOT know about: + +- Flutter framework (`import 'package:flutter/...'` โŒ) +- Firebase, Dio, SharedPreferences +- JSON serialization (`@JsonSerializable` only in Data layer) + +--- + +## ๐Ÿ“‚ Project Structure + +```text +lib/ +โ”œโ”€โ”€ core/ # Shared infrastructure +โ”‚ โ”œโ”€โ”€ di/ # Dependency injection setup +โ”‚ โ”œโ”€โ”€ failures/ # Error handling (Failure) +โ”‚ โ”œโ”€โ”€ localization/ # Localization files +โ”‚ โ”œโ”€โ”€ result/ # Result-pattern +โ”‚ โ”œโ”€โ”€ router/ # Navigation (GoRouter, paths) +โ”‚ โ”œโ”€โ”€ services/ # Services (Network, etc.) +โ”‚ โ””โ”€โ”€ utils/ # Utilities (Logger, Analytics) +โ”œโ”€โ”€ features/ # Feature modules (Clean Architecture) +โ”‚ โ””โ”€โ”€ [feature_name]/ +โ”‚ โ”œโ”€โ”€ data/ # DTOs, DataSources, Repository Impl +โ”‚ โ”œโ”€โ”€ domain/ # Entities, UseCases, Repository Interfaces +โ”‚ โ””โ”€โ”€ presentation/ # Pages, Widgets, BLoC/Cubit +โ”œโ”€โ”€ uikit/ # Shared UI components +โ””โ”€โ”€ main.dart # App entry point +``` + +--- + +## ๐Ÿ› ๏ธ Getting Started + +### Prerequisites + +- Flutter SDK: `>=3.10.0` +- Dart SDK: `>=3.10.0` +- Firebase project (for Analytics, Crashlytics) + +### Installation + +1. **Clone or use this template:** + + ```bash + # Option 1: Clone + git clone https://github.com/CowboyGH/flutter_starter_template.git + + # Option 2: Use template (on GitHub: "Use this template" button) + ``` + +2. **Install dependencies:** + + ```bash + flutter pub get + ``` + +3. **Configure Firebase:** + + ```bash + # Install FlutterFire CLI + dart pub global activate flutterfire_cli + + # Configure Firebase for your project + flutterfire configure + ``` + +4. **Generate localization files:** + + ```bash + flutter gen-l10n + ``` + +5. **Run code generation:** + + ```bash + flutter pub run build_runner build --delete-conflicting-outputs + ``` + +6. **Run the app:** + + ```bash + flutter run + ``` + +--- + +## ๐Ÿงช Testing + +Run tests: + +```bash +# Unit tests +flutter test + +# Widget tests +flutter test test/features/*/presentation/ + +# Coverage (requires lcov) +flutter test --coverage +genhtml coverage/lcov.info -o coverage/html +``` + +--- + +## ๐Ÿ‹ Docker Build + +Build Android APK using Docker: + +```bash +docker build + --build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') + --build-arg VCS_REF=$(git rev-parse --short HEAD) + --progress=plain + --no-cache + -t flutter-starter-android:latest . +``` + +Extract APK files: + +```bash +docker create --name temp-flutter flutter-starter-android:latest +docker cp temp-flutter:/artifacts/. ./apk-output/ +docker rm temp-flutter +``` + +--- + +## ๐Ÿง  Why This Template? + +This repository is primarily a **personal starter** used to move faster on personal and academic projects. + +### Goals + +- Provide a stable baseline with **Clean Architecture** and strict layer boundaries. +- Have a ready-to-use infrastructure for **errors**, **logging**, **navigation**, and **CI/CD**. +- Reduce recurring setup work so feature development starts from a consistent foundation. + +### Non-goals (at least for now) + +- Competing with popular community templates. +- Shipping a complete UI kit/design system out of the box. +- Covering every possible app scenario or advanced product requirements. + +If you find it useful โ€” great! If not, treat it as a reference implementation of the patterns listed above. + +--- + +## ๐Ÿ“ Documentation + +- [CHANGELOG](CHANGELOG.md) - Version history and release notes +- [CONTRIBUTING](CONTRIBUTING.md) - Development guidelines and coding standards +- [LICENSE](LICENSE) - MIT License + +--- + +## ๐Ÿค Contributing + +Issues and PRs are welcome, but the template is primarily maintained for personal use. See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines. --- ## ๐Ÿ“œ License -This project is released under the [MIT License](LICENSE). +This project is licensed under the [MIT License](LICENSE). --- From a65b1a52adfe6cf5d3d862324b652ed8b5d00cea Mon Sep 17 00:00:00 2001 From: CowboyGH Date: Sat, 20 Dec 2025 14:20:48 +0700 Subject: [PATCH 32/32] chore(release): bump version to 0.2.0 in pubspec.yaml and update changelog --- CHANGELOG.md | 39 +++++++++++++++++++-------------------- pubspec.yaml | 2 +- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 544c109..859871f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.2.0] - 2025-12-20 + ### Added - Implemented base Clean Architecture structure (`core`, `features`, `uikit`) with `data`, `domain`, and `presentation` layers. -- Added all project dependencies from `pubspec.yaml`. +- Added project dependencies from `pubspec.yaml`. - Integrated `GetIt` for dependency injection. - Added `DebugScreen` widget for testing and debugging purposes. - Added application router with `GoRouter` and defined route paths for navigation. @@ -21,32 +23,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Integrated `Firebase Crashlytics` for global error handling. - Added `AppFailure` base class and `UnknownFailure` implementation for unified domain-level error handling. - Added sealed `Result` abstraction with `Success` and `Failure` implementations, plus `AsyncResult` type alias for standardizing async operation handling. -- Added DI-independent fallback error handler inside runZonedGuarded to ensure reliable error reporting even if DI or Firebase are not ready. +- Added DI-independent fallback error handler inside `runZonedGuarded` to ensure reliable error reporting even if DI or Firebase are not ready. - Added unified analytics architecture with `Firebase Analytics` integration, debug logging, and automatic screen view tracking via `AnalyticsRouteObserver`. -- Added `AppLoggerMixin` for convenient logger access in Bloc/Cubit -- **CI/CD**: Complete automated build pipeline with GitHub Actions - - Multi-stage Dockerfile with Flutter 3.38.4 and Android SDK 35 - - Support for split APK by ABI (armeabi-v7a, arm64-v8a, x86_64) - - `.dockerignore` for optimized build context - - GitHub Actions workflow: code analysis, Android (Docker + DockerHub), iOS (macOS runner) - - Automated quality checks, artifact uploads, and build summaries +- Added `AppLoggerMixin` for convenient logger access in Bloc/Cubit. +- Added CI/CD pipeline with GitHub Actions: + - Multi-stage Dockerfile with Flutter 3.38.4 and Android SDK 35. + - Support for split APK by ABI (armeabi-v7a, arm64-v8a, x86_64). + - `.dockerignore` for optimized build context. + - Workflow jobs: code analysis, Android build (Docker + DockerHub), iOS build (macOS runner). + - Artifact uploads and build summaries. - Added `NetworkService` for monitoring network availability with `connectivity_plus` integration and unit tests. ### Changed -- Added `public_member_api_docs` rule to `analysis_options.yaml`. -- Extracted main application widget to `app.dart`, added `runner.dart` for app initialization with error handling, and simplified `main.dart`. -- Removed `always_put_control_body_on_new_line` rule from `analysis_options.yaml`. -- Streamlined logger registration in dependency injection setup -- Updated Dart SDK requirement to `>=3.10.0` (was `>=3.9.2`) -- Updated Flutter SDK requirement to `>=3.10.0` +- Updated `analysis_options.yaml` (including enabling `public_member_api_docs`). +- Refactored app startup: + - Extracted main application widget to `app.dart`. + - Added `runner.dart` for app initialization and error handling. + - Simplified `main.dart`. +- Streamlined logger registration in dependency injection setup. +- Updated Dart SDK constraint to `>=3.10.0 <4.0.0`. ### Fixed - Moved `WidgetsFlutterBinding.ensureInitialized()` inside `runZonedGuarded` in `runner.dart` to ensure proper app initialization and error handling. -- Fixed code generation failure by updating `retrofit_generator` (compatibility with `analyzer` 7.7.1+). - ---- +- Fixed code generation failure by updating `retrofit_generator` for compatibility with newer `analyzer` versions. ## [0.1.0] - 2025-10-29 @@ -54,5 +55,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Initial Flutter project created via `flutter create`. - Added core documentation files and base configuration. - -*This changelog was initialized with the project setup (initial commit). Future changes will be tracked starting from version 0.1.0.* diff --git a/pubspec.yaml b/pubspec.yaml index 62d2b85..1d83386 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_starter_template description: A scalable Flutter Starter Template for building production-ready business applications. publish_to: 'none' -version: 0.1.0 +version: 0.2.0 environment: sdk: '>=3.10.0 <4.0.0'