Compare commits

...
Author SHA1 Message Date
hugo 2a4e29f38c chore(release): publish packages
- wyatt_architecture@0.1.0
 - wyatt_authentication_bloc@0.4.0+2
 - wyatt_crud_bloc@0.1.0+1
 - wyatt_form_bloc@0.2.0+1
2022-12-03 20:40:23 -05:00
hugo ca70f2eb1d feat(authentication)!: add repo in callback usecases (close #55) 2022-12-03 18:53:20 -05:00
hugo f123e537ae fix(authentication): allow email/password validators customization (closes #57) 2022-12-03 18:41:04 -05:00
AN12345 c3315d968c refactor(bloc_helper)!: update onWrap label (close #46) 2022-11-24 18:16:20 -05:00
AN12345 45312473d1 feat: add gen-class-models in melos scripts 2022-11-24 17:55:07 -05:00
AN12345 17e7bce9e6 refactor(architecture): update naming methodes (close #47) 2022-11-24 17:25:44 -05:00
hugo 3886ef080e fix(crud)!: use FutureOrResult extension (close #42) 2022-11-24 15:24:59 -05:00
hugo 626d951443 fix(authentication)!: use FutureOrResult extension (close #41) 2022-11-24 11:18:16 -05:00
hugo f36e0b51e0 feat(type): add Result extension on FutureOr type (close #40) 2022-11-23 19:13:04 -05:00
AN12345 80ce7aff91 fix(architecture): fix execute method on error handler. (#38) 2022-11-23 23:11:04 +00:00
AN12345 a9121b6862 refactor(architecture): update example. (close #38) 2022-11-23 23:11:04 +00:00
AN12345 54f92e8042 docs(architecture): generate class model. (#38) 2022-11-23 23:11:04 +00:00
AN12345 00393a6ba7 docs(architecture): update Readme. (#38) 2022-11-23 23:11:04 +00:00
AN12345 136e0e8c98 chore(architecture)!: update exports. (#38) 2022-11-23 23:11:04 +00:00
AN12345 500a6ae51d feat(architecture): update usecase en add & sepcific case. (#38) 2022-11-23 23:11:04 +00:00
AN12345 79fdde25fc feat(architecture): add obsevers for alternative usecase scenarions (#38) 2022-11-23 23:11:04 +00:00
Malo Léon 742bf1d614 Merge pull request 'config/update_project_management' (#32) from config/update_project_management into master
Reviewed-on: #32
Reviewed-by: Hugo Pointcheval <hugo@noreply@wyatt-studio.fr>
2022-11-22 02:33:48 +00:00
AN12345 0dbde68d7d typo: fix readme typo error 2022-11-21 21:32:36 -05:00
AN12345 cd881da072 doc: update readme 2022-11-21 21:20:25 -05:00
AN12345 5918a5cf1f doc: add simple workflow diagramm 2022-11-21 21:20:01 -05:00
AN12345 775d766d14 build: update melos configuration and update targets 2022-11-21 20:59:36 -05:00
AN12345 383098b147 build: add mason configuration files 2022-11-21 20:59:02 -05:00
AN12345 4d51e2b786 build: add coverage script 2022-11-21 20:58:18 -05:00
AN12345 32cc1f55d9 chore: update gitignore 2022-11-21 20:57:52 -05:00
AN12345 6ee101bf36 doc: update readme 2022-11-21 20:54:57 -05:00
hugo f0ed08bd49 feat(auth): add reloadCache method in AuthenticationCubit 2022-11-16 19:52:24 -05:00
hugo 390fb81b1e fix(auth): fix stream account, now including correctly extra data 2022-11-16 19:51:45 -05:00
hugo 279d451cd8 fix(auth): fix mock isNewUser 2022-11-16 19:48:03 -05:00
hugo a66db23ace chore(release): publish packages
- wyatt_authentication_bloc@0.4.0+1
2022-11-16 17:20:57 -05:00
hugo 3d7af98ac9 fix(auth): add sort of mutex on onAuthChanges callback trigger 2022-11-16 17:16:41 -05:00
AN12345 b033b97917 feat(notification_bloc) add new package for push notification management 2022-11-16 16:58:19 -05:00
hugo 14b7f31b53 chore(form): use hard link for vscode folder 2022-11-16 16:32:05 -05:00
hugo 403bcafe93 chore(form): remove vscode symlink 2022-11-16 16:30:49 -05:00
hugo 21ab6f6021 chore(auth): use hardlink for vscode folder 2022-11-16 16:27:51 -05:00
hugo 198ecac5da chore(auth): remove vscode symlink to fix publish 2022-11-16 16:18:23 -05:00
hugo c945cd435e chore(release): publish packages
- wyatt_authentication_bloc@0.4.0
 - wyatt_form_bloc@0.2.0
2022-11-16 16:03:13 -05:00
hugo 852d29ef0a refactor(auth): update example with new input builders 2022-11-16 16:02:18 -05:00
hugo 08383ec03b refactor(form)!: input builders provides FormInput 2022-11-16 16:00:13 -05:00
hugo ee8ce76839 fix(form): disable status selection and rebuild SubmitBuilder on every state changes #28 2022-11-16 15:51:34 -05:00
hugo f05f2230e9 fix(auth): add initial null account event in stream in the mock authentication 2022-11-16 15:46:07 -05:00
hugo 54002beaab test(auth): add tests for the SignUpCubit update method 2022-11-16 15:45:11 -05:00
hugo d5698fbffc test(form): add formReplace test case #27 2022-11-16 15:43:52 -05:00
hugo 31ad460757 fix(auth)!: add signInWithEmailAndPassword and signInAnonymously methods in SignInCubit #26 2022-11-15 19:25:20 -05:00
hugo 4fd2a2a16c chore(auth): remove old example and rename actual 2022-11-15 18:52:10 -05:00
hugo 6eb40de72c feat(form): add metadata tag to bypass validation #29 2022-11-15 18:48:02 -05:00
hugo cecbc94a95 chore(auth): add global vscode folder symlink 2022-11-15 18:13:56 -05:00
hugo 3820fc4764 chore(auth): remove local vscode folder 2022-11-15 18:13:30 -05:00
hugo 1b893a5b84 test(form): add some tests 2022-11-15 18:10:50 -05:00
hugo 72661a343f fix(form): fix set operations behaviors 2022-11-15 18:10:30 -05:00
hugo 9ebe8e21dc fix(form): add nullable validator on boolean value 2022-11-15 18:09:39 -05:00
hugo 141f15b444 feat(form): add shorthands to access validator status 2022-11-15 18:09:13 -05:00
hugo 3a8a604a48 chore(form): add vscode folder symlink 2022-11-15 18:06:13 -05:00
hugo 41493207b3 chore(form): remove local vscode folder 2022-11-15 18:05:18 -05:00
hugo cbc4a2e149 chore: change vscode global settings 2022-11-15 18:03:30 -05:00
hugo 7345e415a7 chore(release): publish packages
- wyatt_crud_bloc@0.1.0
2022-11-13 20:29:19 -05:00
hugo ff680614aa typo(crud): fix line length 2022-11-13 20:21:20 -05:00
hugo 3e110e2eb5 fix(crud): update example 2022-11-13 20:19:21 -05:00
hugo 1ccb0a540e refactor(crud)!: migrate to wyatt architecture 2022-11-13 20:19:10 -05:00
hugo f9da719b91 refactor(crud): add vscode config 2022-11-13 20:18:10 -05:00
hugo 95f41025f4 chore(release): publish packages
- wyatt_authentication_bloc@0.3.0+1
2022-11-11 19:09:01 -05:00
hugo eceda684f0 fix(auth): fix some bugs to validate publishing 2022-11-11 19:08:28 -05:00
hugo 291c195ceb chore(release): publish packages
- wyatt_authentication_bloc@0.3.0
2022-11-11 19:01:49 -05:00
hugo 1896497c1a refactor(auth): use correct deps 2022-11-11 19:01:00 -05:00
hugo 87254ef547 feat(auth): add fully functionnal mock data source 2022-11-11 18:48:30 -05:00
hugo da34acd35b fix(auth): return exception and not throwing it anymore 2022-11-11 18:47:59 -05:00
hugo d9d0625c67 fix(auth): exception returns msg as message 2022-11-11 18:47:16 -05:00
hugo 08f789725b feat(auth): add toString and equality on entities and models 2022-11-11 18:46:37 -05:00
hugo 33ac5c6280 doc(auth): update pubspec and readme 2022-11-11 16:54:58 -05:00
hugo 762b9bcd11 refactor(auth): update example with new listeners 2022-11-11 16:54:40 -05:00
hugo 38480d84f4 test(auth): add tests for all features 2022-11-11 16:54:08 -05:00
hugo 03a51b97ad feat(auth)!: add email verification, password reset support 2022-11-11 16:53:36 -05:00
hugo 1dd49fa080 chore(release): publish packages
- wyatt_architecture@0.0.2
 - wyatt_authentication_bloc@0.2.1+6
 - wyatt_form_bloc@0.1.0+1
2022-11-10 22:59:51 -05:00
hugo 0219e3d557 doc(arch): update readme 2022-11-10 22:58:35 -05:00
hugo 3de7d01173 feat(arch): add new typedef for stream and simple result 2022-11-10 22:58:19 -05:00
hugo 4183da6259 feat(arch): make base class const 2022-11-10 22:57:51 -05:00
hugo 1f24a76717 chore(form): add hosted dependecy 2022-11-10 13:51:37 -05:00
hugo 0a4a9c2feb chore(release): publish packages
- wyatt_form_bloc@0.1.0
 - wyatt_authentication_bloc@0.2.1+5
2022-11-10 13:43:55 -05:00
hugo 92f8b2916c Merge pull request 'auth/wyatt_arch_migration' (#25) from auth/wyatt_arch_migration into master
Reviewed-on: #25
2022-11-10 18:40:27 +00:00
hugo 3588c3241b chore(auth): update pubspec 2022-11-10 13:39:07 -05:00
hugo 9b87eb7ed9 chore(form): update pubspec 2022-11-10 13:38:55 -05:00
hugo 6802a56bfc doc(auth): update example router 2022-11-10 13:31:15 -05:00
hugo b2a9dac7c6 refactor(auth): rename local to cache auth data source 2022-11-10 13:31:00 -05:00
hugo c0a91d6437 feat!(auth): use form repository 2022-11-10 13:30:13 -05:00
hugo 59bd3edb0d feat!(auth): use new form features 2022-11-10 13:29:30 -05:00
hugo 0c920e8245 feat(form): clean and add toString and equality 2022-11-10 13:24:29 -05:00
hugo ae3711a136 fix!(form): fix states with required form 2022-11-10 13:24:00 -05:00
hugo f031b3cbf5 fix: make cubit custom implementation simpler 2022-11-10 01:28:30 -05:00
hugo 68a582c3ad refactor(form): refactor simple example 2022-11-10 01:06:25 -05:00
hugo 7a056ac38e feat!(form): add repository feature, and controller for inputs 2022-11-10 01:05:12 -05:00
hugo b89ef3de8a feat!(form): migrate to wyatt architecture 2022-11-09 22:35:41 -05:00
hugo 79fdd3c837 doc(auth): update example 2022-11-09 01:48:32 -05:00
hugo 0d00a67b7c fix: rework presentation features 2022-11-09 01:47:57 -05:00
hugo d2d8acdafb fix: rework core and entities 2022-11-09 01:47:37 -05:00
hugo 2f572db4a4 feat: add data sources 2022-11-09 01:47:09 -05:00
hugo 3972c56e55 feat: rewrite authentication repository 2022-11-09 01:46:28 -05:00
hugo 0bc2aa3512 feat: update dependencies 2022-11-09 01:44:52 -05:00
hugo 94f21a664f chore(release): publish packages
- wyatt_architecture@0.0.2-dev.0
2022-11-08 21:45:28 -05:00
hugo 6602db215f doc(arch): add fully featured example 2022-11-08 20:09:18 -05:00
hugo db67491876 doc(arch): update readme 2022-11-08 20:08:55 -05:00
hugo 35aa6bb7c2 feat(arch): add exceptions, datasources, repositories, and usecases 2022-11-08 20:07:55 -05:00
hugo 067ca4df5d chore(release): publish packages
- wyatt_type_utils@0.0.3+1
 - wyatt_architecture@0.0.1+2
2022-11-07 17:42:41 -05:00
hugo f3c17ad387 refactor(types): rename future result extension 2022-11-07 17:31:33 -05:00
hugo 492d95b164 chore(release): publish packages
- wyatt_bloc_helper@1.2.0
2022-11-06 20:37:11 -05:00
hugo c568a05654 chore(release): publish packages
- wyatt_type_utils@0.0.3
 - wyatt_architecture@0.0.1+1
2022-11-06 19:38:31 -05:00
hugo aa721afcaa chore: chang melos config and fix ambiguous readme character 2022-11-06 19:35:39 -05:00
hugo 5f7efddb86 feat(types): add object, string, datetime, iterable extensions 2022-11-06 19:32:40 -05:00
hugo 46f5960d43 test(types): add pair tests 2022-11-06 19:32:05 -05:00
hugo d63e7a8a07 feat(types): add new pair type 2022-11-06 19:31:52 -05:00
hugo 597ecf23e2 refactor(types): move either folder 2022-11-06 19:31:36 -05:00
565 changed files with 20346 additions and 5338 deletions
+5 -1
View File
@@ -196,4 +196,8 @@ $RECYCLE.BIN/
.idea/ .idea/
*.iml *.iml
google-services.json google-services.json
pubspec_overrides.yaml
# Mason
.mason/
+7 -2
View File
@@ -1,4 +1,10 @@
{ {
// Dart
"dart.runPubGetOnPubspecChanges": "never",
// Gitea
"gitea.owner": "Wyatt-FOSS",
"gitea.repo": "wyatt-packages",
// Header
"psi-header.changes-tracking": { "psi-header.changes-tracking": {
"isActive": true "isActive": true
}, },
@@ -67,5 +73,4 @@
], ],
} }
], ],
"dart.runPubGetOnPubspecChanges": false, }
}
+330
View File
@@ -3,6 +3,336 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## 2022-12-03
### Changes
---
Packages with breaking changes:
- [`wyatt_architecture` - `v0.1.0`](#wyatt_architecture---v010)
Packages with other changes:
- [`wyatt_authentication_bloc` - `v0.4.0+2`](#wyatt_authentication_bloc---v0402)
- [`wyatt_crud_bloc` - `v0.1.0+1`](#wyatt_crud_bloc---v0101)
- [`wyatt_form_bloc` - `v0.2.0+1`](#wyatt_form_bloc---v0201)
Packages with dependency updates only:
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
- `wyatt_authentication_bloc` - `v0.4.0+2`
- `wyatt_crud_bloc` - `v0.1.0+1`
- `wyatt_form_bloc` - `v0.2.0+1`
---
#### `wyatt_architecture` - `v0.1.0`
- **REFACTOR**: update naming methodes (close #47).
- **REFACTOR**: update example. (close #38).
- **FIX**: fix execute method on error handler. (#38).
- **FEAT**: update usecase en add & sepcific case. (#38).
- **FEAT**: add obsevers for alternative usecase scenarions (#38).
- **DOCS**: generate class model. (#38).
- **DOCS**: update Readme. (#38).
- **BREAKING** **CHORE**: update exports. (#38).
## 2022-11-16
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`wyatt_authentication_bloc` - `v0.4.0+1`](#wyatt_authentication_bloc---v0401)
---
#### `wyatt_authentication_bloc` - `v0.4.0+1`
- **FIX**: add sort of mutex on onAuthChanges callback trigger.
## 2022-11-16
### Changes
---
Packages with breaking changes:
- [`wyatt_authentication_bloc` - `v0.4.0`](#wyatt_authentication_bloc---v040)
- [`wyatt_form_bloc` - `v0.2.0`](#wyatt_form_bloc---v020)
Packages with other changes:
- There are no other changes in this release.
---
#### `wyatt_authentication_bloc` - `v0.4.0`
- **REFACTOR**: update example with new input builders.
- **FIX**: add initial null account event in stream in the mock authentication.
- **BREAKING** **FIX**: add signInWithEmailAndPassword and signInAnonymously methods in SignInCubit #26.
#### `wyatt_form_bloc` - `v0.2.0`
- **FIX**: disable status selection and rebuild SubmitBuilder on every state changes #28.
- **FIX**: fix set operations behaviors.
- **FIX**: add nullable validator on boolean value.
- **FEAT**: add metadata tag to bypass validation #29.
- **FEAT**: add shorthands to access validator status.
- **BREAKING** **REFACTOR**: input builders provides FormInput.
## 2022-11-13
### Changes
---
Packages with breaking changes:
- [`wyatt_crud_bloc` - `v0.1.0`](#wyatt_crud_bloc---v010)
Packages with other changes:
- There are no other changes in this release.
---
#### `wyatt_crud_bloc` - `v0.1.0`
- **REFACTOR**: add vscode config.
- **FIX**: update example.
- **BREAKING** **REFACTOR**: migrate to wyatt architecture.
## 2022-11-11
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`wyatt_authentication_bloc` - `v0.3.0+1`](#wyatt_authentication_bloc---v0301)
---
#### `wyatt_authentication_bloc` - `v0.3.0+1`
- **FIX**: fix some bugs to validate publishing.
## 2022-11-11
### Changes
---
Packages with breaking changes:
- [`wyatt_authentication_bloc` - `v0.3.0`](#wyatt_authentication_bloc---v030)
Packages with other changes:
- There are no other changes in this release.
---
#### `wyatt_authentication_bloc` - `v0.3.0`
- **REFACTOR**: use correct deps.
- **REFACTOR**: update example with new listeners.
- **FIX**: return exception and not throwing it anymore.
- **FIX**: exception returns msg as message.
- **FEAT**: add fully functionnal mock data source.
- **FEAT**: add toString and equality on entities and models.
- **BREAKING** **FEAT**: add email verification, password reset support.
## 2022-11-10
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`wyatt_architecture` - `v0.0.2`](#wyatt_architecture---v002)
- [`wyatt_authentication_bloc` - `v0.2.1+6`](#wyatt_authentication_bloc---v0216)
- [`wyatt_form_bloc` - `v0.1.0+1`](#wyatt_form_bloc---v0101)
Packages graduated to a stable release (see pre-releases prior to the stable version for changelog entries):
- `wyatt_architecture` - `v0.0.2`
Packages with dependency updates only:
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
- `wyatt_authentication_bloc` - `v0.2.1+6`
- `wyatt_form_bloc` - `v0.1.0+1`
---
#### `wyatt_architecture` - `v0.0.2`
## 2022-11-10
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`wyatt_form_bloc` - `v0.1.0`](#wyatt_form_bloc---v010)
- [`wyatt_authentication_bloc` - `v0.2.1+5`](#wyatt_authentication_bloc---v0215)
Packages with dependency updates only:
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
- `wyatt_authentication_bloc` - `v0.2.1+5`
---
#### `wyatt_form_bloc` - `v0.1.0`
- **REFACTOR**: refactor simple example.
- **FIX**: make cubit custom implementation simpler.
- **FEAT**: clean and add toString and equality.
## 2022-11-08
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`wyatt_architecture` - `v0.0.2-dev.0`](#wyatt_architecture---v002-dev0)
---
#### `wyatt_architecture` - `v0.0.2-dev.0`
- **FEAT**: add exceptions, datasources, repositories, and usecases.
## 2022-11-07
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`wyatt_type_utils` - `v0.0.3+1`](#wyatt_type_utils---v0031)
- [`wyatt_architecture` - `v0.0.1+2`](#wyatt_architecture---v0012)
Packages with dependency updates only:
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
- `wyatt_architecture` - `v0.0.1+2`
---
#### `wyatt_type_utils` - `v0.0.3+1`
- **REFACTOR**: rename future result extension.
## 2022-11-06
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`wyatt_bloc_helper` - `v1.2.0`](#wyatt_bloc_helper---v120)
---
#### `wyatt_bloc_helper` - `v1.2.0`
- **REFACTOR**: general code update.
- **FEAT**: add init function and multi provider.
- **FEAT**: add a lot of docs, fix `onWrap`, and add repo provider.
- **FEAT**: add wrap feature.
- **DOCS**: add ios example.
- **DOCS**: update license.
## 2022-11-06
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`wyatt_type_utils` - `v0.0.3`](#wyatt_type_utils---v003)
- [`wyatt_architecture` - `v0.0.1+1`](#wyatt_architecture---v0011)
Packages with dependency updates only:
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
- `wyatt_architecture` - `v0.0.1+1`
---
#### `wyatt_type_utils` - `v0.0.3`
- **REFACTOR**: move either folder.
- **FEAT**: add object, string, datetime, iterable extensions.
- **FEAT**: add new pair type.
## 2022-11-06 ## 2022-11-06
### Changes ### Changes
+132 -24
View File
@@ -1,17 +1,17 @@
<!-- <!--
* Copyright (C) 2022 WYATT GROUP * Copyright (C) 2022 WYATT GROUP
* Please see the AUTHORS file for details. * Please see the AUTHORS file for details.
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* any later version. * any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
@@ -46,44 +46,59 @@
Here is it a set of [Flutter plugins](https://flutter.io/platform-plugins/) that power up your applications. Here is it a set of [Flutter plugins](https://flutter.io/platform-plugins/) that power up your applications.
[Flutter](https://flutter.dev) is Googles UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase. Flutter is used by developers and organizations around the world, and is free [Flutter](https://flutter.dev) is Google's UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase. Flutter is used by developers and organizations around the world, and is free
and open source. and open source.
--- ---
## Development ## Contribution
Clone this repo. Clone this repo.
You have to install [melos](https://github.com/invertase/melos). ```shell
git clone ssh://git@git.wyatt-studio.fr:993/Wyatt-FOSS/wyatt-packages.git
```
### Prerequisite
- [Flutter](https://github.com/flutter/flutter).
- [Melos](https://github.com/invertase/melos). Used to manage Dart projects with multiple packages.
```shell ```shell
dart pub global activate melos dart pub global activate melos
``` ```
Then bootstrap with `melos bs`. - [Mason](https://github.com/felangel/mason). Used for code generation.
```shell
dart pub global activate mason_cli
```
- [Lcov](https://github.com/linux-test-project/lcov). Used to generate coverage data.
```shell
# on macos
brew install lcov
```
- genhtml. Used to convert coverage data into html pages.
After installing all these packages, bootstrap with `melos bs`.
### Create a new package ### Create a new package
Create a new package in `packages/` folder. Create a new package in `packages/` folder.
```shell ```shell
dart create -t package-simple wyatt_<name> mason make wyatt_package --package_name wyatt_<name> --description A new Wyatt package --flutter_only false
```
Remove any `example/` subfolder. Then create a new sample project.
```shell
flutter create --platforms android --project-name <name>_example example
``` ```
Then bootstrap project with `melos bs` command. Then bootstrap project with `melos bs` command.
### Convention
#### Naming #### Naming
In the previous instructions `<name>` variable is important. In the previous instructions `<name>` variable is important.
It have to be clear and intelligible. It have to be clear and intelligible.
You **MUST** use underscores. You **MUST** use underscores.
@@ -93,15 +108,44 @@ You **MUST** use `wyatt` prefix for package.
You **MUST** name example with specific name. You **MUST** name example with specific name.
For example, if name is CRUD BLOC For example, if name is CRUD BLOC
- name will be crud_bloc - name will be crud_bloc
- so the package will be: `wyatt_crud_bloc` - so the package will be: `wyatt_crud_bloc`
- and the example will be: `crud_bloc_example` - and the example will be: `crud_bloc_example`
#### Commits ### Create issues
Add the issues directly related to what you want to develop and label them correctly.
```
Type(scope): issue.
```
For example, on notification bloc package :
- `Feature(notification_bloc): add firebase messaging data source.`
- `Test(notification_bloc): add test for pushwoosh datasource.`
### Branches
Master is protected. You can't push on it. Please develop your feature on another branch. The name of your branch has to match with the issue opened.
`type(scope):issue`
Example :
Issue :
- `Feature(notification_bloc): add firebase messaging data source.`
Branch related to this issue :
- `feature(notification_bloc)/add_firebase_messaging_data_source`
### Commits
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
tl;dr : `type(scope): description`. tl;dr : `type(scope): description #issue`.
Here allowed <type> values: Here allowed <type> values:
- **feat** for a new feature for the user, not a new feature for build script. Such commit will trigger a release bumping a MINOR version. - **feat** for a new feature for the user, not a new feature for build script. Such commit will trigger a release bumping a MINOR version.
@@ -115,11 +159,68 @@ Here allowed <type> values:
Some examples : Some examples :
- `feat(auth): add AWS support.` = add a feature in authentication_bloc package. - `feat(auth): add AWS support. (#31)` = add a feature in authentication_bloc package, linked to the 31st issue.
- `docs: update readme.` = update **this** readme file. - `docs: update readme.` = update **this** readme file.
- `fix(crud)!: fix bug in awesome() function.` = fix a bug, `!` is important and indicate `BREAKING CHANGES`. - `fix(crud)!: fix bug in awesome() function. (#32)` = fix a bug, `!` is important and indicate `BREAKING CHANGES` linked with the 32nd issue.
Conventional commits are important for `melos version` command ! When you have finished developing, and are ready to close the issue, close it via your commit :
`feat(auth): add AWS support. (close #31)`
Note that your issue will be close after merging on master.
Before closing the issue, please check tests and update coverage. You might run :
```shell
melos run test
melos run gen_coverage
```
#### Merge your work
After closing your issue, some work may have been done on master in the meantime. To keep a clean git history, please rebase before opening a change request.
Two situations :
- You have not yet created your branch and have committed locally :
```shell
git pull --rebase
```
- You are already working on your branch :
```shell
git rebase -i master
```
If possible, please use `--fixup` on your commit in interactive rebase.
Then, opend your PR, get the necessary approval and merge your work. Then you're done ✅.
#### Update version.
Once your work is on the master, you can update your package version. Please filter packages to update only your package. `--scope` is used to do it.
For example, after merging changes on `wyatt_otification_bloc` package, you might run :
```shell
melos version --scope="*wyatt_notification_bloc*"
```
In fact, melos will filter all packages that match the `wyatt_notification_block` pattern.
You can update check all packages tests by running :
```shell
melos run test
```
#### Publish your package
If package is ready for procution, publish it runnig :
```shell
melos publish --scope"*package*"
```
#### Badging #### Badging
@@ -130,7 +231,8 @@ In the package `readme.md` file, please specify the supported SDK:
```markdown ```markdown
![SDK: Dart & Flutter](https://img.shields.io/badge/SDK-Dart%20%7C%20Flutter-blue?style=flat-square) ![SDK: Dart & Flutter](https://img.shields.io/badge/SDK-Dart%20%7C%20Flutter-blue?style=flat-square)
``` ```
or
or
![SDK: Dart](https://img.shields.io/badge/SDK-Dart-blue?style=flat-square) ![SDK: Dart](https://img.shields.io/badge/SDK-Dart-blue?style=flat-square)
@@ -161,4 +263,10 @@ Here you can change `package name` and `package version`.
![Status: Experimental](https://img.shields.io/badge/Status-WIP-red?style=flat-square) ![Status: Experimental](https://img.shields.io/badge/Status-WIP-red?style=flat-square)
This repository is maintained by Wyatt Studio but work is in progress. This repository is maintained by Wyatt Studio but work is in progress.
## Simple work flow diagramm
To sum up, here a simple work flow diagramm.
![Dev Wokflow](doc/workflow.svg)
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env bash
escapedPath="$(echo `pwd` | sed 's/\//\\\//g')"
if grep flutter pubspec.yaml > /dev/null; then
if [ -d "coverage" ]; then
# combine line coverage info from package tests to a common file
if [ ! -d "$MELOS_ROOT_PATH/coverage" ]; then
mkdir "$MELOS_ROOT_PATH/coverage"
fi
sed "s/^SF:lib/SF:$escapedPath\/lib/g" coverage/lcov.info >> "$MELOS_ROOT_PATH/coverage/lcov.info"
rm -rf "coverage"
fi
fi
+15
View File
@@ -0,0 +1,15 @@
@startuml Simple Developpment Workflow
start
:Create an issue;
:Create branch related to the issue;
repeat :Commit related to the issue;
repeat while (feature is done)
:Update coverage;
:Close issue via last commit;
:Rebase from `master`;
:Create pull request;
:Merge branches;
:Update version;
:Publish package;
stop
@enduml
+25
View File
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 7.5 KiB

+1
View File
@@ -0,0 +1 @@
{"bricks":{"wyatt_clean_code":{"git":{"url":"ssh://git@git.wyatt-studio.fr:993/Wyatt-FOSS/wyatt-bricks.git","path":"bricks/wyatt_clean_code","ref":"033baa65408ec6b68e6a2ef43e64c24708ef16a1"}},"wyatt_feature_brick":{"git":{"url":"ssh://git@git.wyatt-studio.fr:993/Wyatt-FOSS/wyatt-bricks.git","path":"bricks/wyatt_feature_brick","ref":"033baa65408ec6b68e6a2ef43e64c24708ef16a1"}},"wyatt_package":{"git":{"url":"ssh://git@git.wyatt-studio.fr:993/Wyatt-FOSS/wyatt-bricks.git","path":"bricks/wyatt_package","ref":"033baa65408ec6b68e6a2ef43e64c24708ef16a1"}}}}
+13
View File
@@ -0,0 +1,13 @@
bricks:
wyatt_clean_code:
git:
url: ssh://git@git.wyatt-studio.fr:993/Wyatt-FOSS/wyatt-bricks.git
path: bricks/wyatt_clean_code
wyatt_feature_brick:
git:
url: ssh://git@git.wyatt-studio.fr:993/Wyatt-FOSS/wyatt-bricks.git
path: bricks/wyatt_feature_brick
wyatt_package:
git:
url: ssh://git@git.wyatt-studio.fr:993/Wyatt-FOSS/wyatt-bricks.git
path: bricks/wyatt_package
+17
View File
@@ -5,6 +5,9 @@ packages:
- packages/** - packages/**
command: command:
bootstrap:
usePubspecOverrides: true
version: version:
updateGitTagRefs: true updateGitTagRefs: true
linkToCommits: false # Gitea not yet supported linkToCommits: false # Gitea not yet supported
@@ -33,6 +36,20 @@ scripts:
clean:deep: clean:deep:
run: git clean -x -d -f -q run: git clean -x -d -f -q
description: Clean things very deeply with `git clean`. description: Clean things very deeply with `git clean`.
test:selective_unit_test:
run: melos exec --dir-exists="test" -- flutter test --no-pub --coverage
description: Run Flutter tests for a specific package in this project.
select-package:
flutter: true
test:
run: melos run test:selective_unit_test --no-select
description: Run all Flutter tests in this project.
gen-coverage: melos exec -- sh "\$MELOS_ROOT_PATH/combine_coverage.sh" && genhtml coverage/lcov.info -o coverage/html
gen-class-models: melos exec --scope="*wyatt_*" -- dart pub global run dcdg -o models/class-models.puml
# Additional cleanup lifecycle script, executed when `melos clean` is run. # Additional cleanup lifecycle script, executed when `melos clean` is run.
postclean: > postclean: >
+29
View File
@@ -1,3 +1,32 @@
## 0.1.0
> Note: This release has breaking changes.
- **REFACTOR**: update naming methodes (close #47).
- **REFACTOR**: update example. (close #38).
- **FIX**: fix execute method on error handler. (#38).
- **FEAT**: update usecase en add & sepcific case. (#38).
- **FEAT**: add obsevers for alternative usecase scenarions (#38).
- **DOCS**: generate class model. (#38).
- **DOCS**: update Readme. (#38).
- **BREAKING** **CHORE**: update exports. (#38).
## 0.0.2
- Graduate package to a stable release. See pre-releases prior to this version for changelog entries.
## 0.0.2-dev.0
- **FEAT**: add exceptions, datasources, repositories, and usecases.
## 0.0.1+2
- Update a dependency to the latest release.
## 0.0.1+1
- Update a dependency to the latest release.
# 0.0.1 # 0.0.1
- TODO: Describe initial release. - TODO: Describe initial release.
+190 -10
View File
@@ -16,28 +16,208 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
--> -->
# Flutter - Wyatt Architecture # Flutter - Wyatt Architecture
<p align="left"> <p align="left">
<a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis"> <a href="https://git.wyatt-studio.fr/Wyatt-FOSS/wyatt-packages/src/branch/master/packages/wyatt_analysis">
<img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" /> <img src="https://img.shields.io/badge/Style-Wyatt%20Analysis-blue.svg?style=flat-square" alt="Style: Wyatt Analysis" />
</a> </a>
<img src="https://img.shields.io/badge/SDK-Flutter-blue?style=flat-square" alt="SDK: Flutter" /> <img src="https://img.shields.io/badge/SDK-Flutter-blue?style=flat-square" alt="SDK: Flutter" />
</p> </p>
Architecture for Flutter. The Wyatt Architecture for Flutter.
## Features ## Features
<!-- TODO --> - Usecase
- Repository
## Getting started - DataSource
- Entity
<!-- TODO -->
## Usage ## Usage
<!-- TODO --> ### Domain
Create your entities by extending `Entity` :
```dart
class Photo extends Entity {
final int id;
final String url;
const Photo({
required this.id,
required this.url,
});
}
```
Then create the data sources by extending `BaseLocalDataSource` or `BaseRemoteDataSource` depending the type of data source.
```dart
abstract class PhotoRemoteDataSource extends BaseRemoteDataSource {
Future<Photo> getPhoto(int id);
Future<List<Photo>> getAllPhotos({int? start, int? limit});
}
```
Then you can create your repositories by extenting `BaseRepository` :
```dart
abstract class PhotoRepository extends BaseRepository {
FutureResult<Photo> getPhoto(int id);
FutureResult<List<Photo>> getAllPhotos({int? start, int? limit});
}
```
> Here the repository is just a proxy of the data sources with result type (to have beautiful error handling).
And finaly create your different usecases :
Several use cases are supported :
- Classic usecase :
```dart
class Test extends AsyncUseCase<QueryParameters, List<Photo>>> {
@override
FuturOrResult<List<Photo>>> call(QueryParameters? params) {
final photos = _photoRepository.getAllPhotos(
start: params.start,
limit: params.limit,
);
return photos;
}
}
```
You can add alternatve scenarios and check pre/post conditions using our extensions :
```dart
class SearchPhotos extends AsyncUseCase<QueryParameters, List<Photo>>> {
@override
FutureOrResult<List<Photo>>> call(QueryParameters? params) {
final photos = _photoRepository.getAllPhotos(
start: params.start,
limit: params.limit,
);
return photos;
}
@override
FutureOr<void> onStart(QueryParameters? params) {
if(params.start == null || params.limit == null){
throw ClientException('Préconndition non valides');
}
}
}
```
You can implement error scenarios overriding `onError`, or check postconditions by overriding `onComplete` .
- Stream usecase :
```dart
class SearchPhotos extends StreamUseCase<QueryParameters, List<Photo>>> {
@override
FutureOrResult<Stream<List<Photo>>>> call(QueryParameters? params) {
final photos = _photoRepository.getAllPhotos(
start: params.start,
limit: params.limit,
);
return photos;
}
}
```
On this case, observers allow you to add alternative scénarios when data changed, overriding `onData` or `onDone`.
Please note that to use handlers, call `call` method and not `execute`.
> In fact, here we need a new parameter object, so let's create it:
```dart
class QueryParameters {
final int? start;
final int? limit;
QueryParameters(this.start, this.limit);
}
```
### Data
We start by creating models for photos and list of photos. You can use `freezed`. The `PhotoModel` extends `Photo` with some de/serializer capabilities. And those are used only in data layer.
Then implements your data sources:
```dart
class PhotoApiDataSourceImpl extends PhotoRemoteDataSource {
final MiddlewareClient _client;
PhotoApiDataSourceImpl(this._client);
@override
Future<Photo> getPhoto(int id) async {
final response = await _client.get(Uri.parse('/photos/$id'));
final photo =
PhotoModel.fromJson(jsonDecode(response.body) as Map<String, Object?>);
return photo;
}
@override
Future<List<Photo>> getAllPhotos({int? start, int? limit}) async {
final startQuery = start.isNotNull ? '_start=$start' : '';
final limitQuery = limit.isNotNull ? '_limit=$limit' : '';
final delimiter1 =
(startQuery.isNotEmpty || limitQuery.isNotEmpty) ? '?' : '';
final delimiter2 =
(startQuery.isNotEmpty && limitQuery.isNotEmpty) ? '&' : '';
final url = '/photos$delimiter1$startQuery$delimiter2$limitQuery';
final response = await _client.get(Uri.parse(url));
final photos =
ListPhotoModel.fromJson({'photos': jsonDecode(response.body)});
return photos.photos;
}
}
```
> 1: Note that here we use `MiddlewareClient` from our http package.
> 2: You can create multiple implementations (one real and one mock for example).
And implement the repositories:
```dart
class PhotoRepositoryImpl extends PhotoRepository {
final PhotoRemoteDataSource _photoRemoteDataSource;
PhotoRepositoryImpl(
this._photoRemoteDataSource,
);
@override
FutureResult<Photo> getPhoto(int id) => Result.tryCatchAsync(
() => _photoRemoteDataSource.getPhoto(id),
(error) => ServerException('Cannot retrieve photo $id.'),
);
@override
FutureResult<List<Photo>> getAllPhotos({int? start, int? limit}) async =>
Result.tryCatchAsync(
() => _photoRemoteDataSource.getAllPhotos(start: start, limit: limit),
(error) => ServerException('Cannot retrieve all photos.'),
);
}
```
That's all.
@@ -15,5 +15,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
include: package:wyatt_analysis/analysis_options.flutter.experimental.yaml include: package:wyatt_analysis/analysis_options.flutter.yaml
analyzer:
exclude: "!example/**"
@@ -32,9 +32,6 @@ migrate_working_dir/
.pub/ .pub/
/build/ /build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related # Symbolication related
app.*.symbols app.*.symbols
@@ -4,7 +4,7 @@
# This file should be version controlled. # This file should be version controlled.
version: version:
revision: f1875d570e39de09040c8f79aa13cc56baab8db1 revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4
channel: stable channel: stable
project_type: app project_type: app
@@ -13,11 +13,11 @@ project_type: app
migration: migration:
platforms: platforms:
- platform: root - platform: root
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 create_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 base_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4
- platform: android - platform: ios
create_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 create_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4
base_revision: f1875d570e39de09040c8f79aa13cc56baab8db1 base_revision: e99c9c7cd9f6c0b2f8ae6e3ebfd585239f5568f4
# User provided section # User provided section
@@ -1,4 +1,4 @@
# example_router # example
A new Flutter project. A new Flutter project.
@@ -0,0 +1,15 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:wyatt_analysis/analysis_options.flutter.yaml
analyzer:
exclude:
- lib/**/*.g.dart
- lib/**/*.freezed.dart
@@ -0,0 +1,34 @@
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>11.0</string>
</dict>
</plist>
@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
@@ -0,0 +1,2 @@
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
@@ -0,0 +1,41 @@
# Uncomment this line to define a global platform for your project
# platform :ios, '11.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
@@ -0,0 +1,552 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
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 */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
9D08BF82920EBA33F23464F5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E946B0F0AD8828F4A7866A40 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
06DDDDC67790BA3B67292B88 /* 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 = "<group>"; };
082A1BEB8CE5B02C4AABB40D /* 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 = "<group>"; };
0B7103FC187D9782DF68E292 /* 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 = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E946B0F0AD8828F4A7866A40 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
9D08BF82920EBA33F23464F5 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
2C514ED236D54DCA1D9DFFC4 /* Frameworks */ = {
isa = PBXGroup;
children = (
E946B0F0AD8828F4A7866A40 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
AD70810057DA8269974A316F /* Pods */,
2C514ED236D54DCA1D9DFFC4 /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
AD70810057DA8269974A316F /* Pods */ = {
isa = PBXGroup;
children = (
0B7103FC187D9782DF68E292 /* Pods-Runner.debug.xcconfig */,
082A1BEB8CE5B02C4AABB40D /* Pods-Runner.release.xcconfig */,
06DDDDC67790BA3B67292B88 /* Pods-Runner.profile.xcconfig */,
);
name = Pods;
path = Pods;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
FBD8EC5BAF7097A0A31AD63E /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
374960F8815E0EA685AB7576 /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
374960F8815E0EA685AB7576 /* [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;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
FBD8EC5BAF7097A0A31AD63E /* [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;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 6Z5P8GG96U;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 6Z5P8GG96U;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 6Z5P8GG96U;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = com.example.example;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
@@ -0,0 +1,13 @@
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
@@ -0,0 +1,122 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 564 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

@@ -0,0 +1,23 @@
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 B

@@ -0,0 +1,5 @@
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Example</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>example</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
</dict>
</plist>
@@ -0,0 +1 @@
#import "GeneratedPluginRegistrant.h"
@@ -0,0 +1,34 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:architecture_example/core/dependency_injection/get_it.dart';
import 'package:architecture_example/core/utils/app_bloc_observer.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hive_flutter/hive_flutter.dart';
Future<void> bootstrap(FutureOr<Widget> Function() builder) async {
WidgetsFlutterBinding.ensureInitialized();
Bloc.observer = AppBlocObserver();
await GetItInitializer.run();
await Hive.initFlutter();
runApp(
await builder.call(),
);
}
@@ -0,0 +1,19 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
abstract class HiveBoxes {
static const String favorites = 'favorites';
}
@@ -0,0 +1,78 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:architecture_example/data/data_sources/local/favorite_hive_data_source.dart';
import 'package:architecture_example/data/data_sources/local/favorite_mock_data_source.dart';
import 'package:architecture_example/data/data_sources/remote/album_api_data_source_impl.dart';
import 'package:architecture_example/data/data_sources/remote/album_mock_data_source_impl.dart';
import 'package:architecture_example/data/data_sources/remote/photo_api_data_source_impl.dart';
import 'package:architecture_example/data/data_sources/remote/photo_mock_data_source_impl.dart';
import 'package:architecture_example/domain/data_sources/local/favorite_local_data_source.dart';
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
import 'package:get_it/get_it.dart';
import 'package:wyatt_http_client/wyatt_http_client.dart';
final getIt = GetIt.I;
abstract class GetItInitializer {
static Future<void> _initGlobal() async {}
static Future<void> _initMocks() async {
getIt
..registerLazySingleton<FavoriteLocalDataSource>(
FavoriteMockDataSource.new,
)
..registerLazySingleton<PhotoRemoteDataSource>(
PhotoMockDataSourceImpl.new,
)
..registerLazySingleton<AlbumRemoteDataSource>(
AlbumMockDataSourceImpl.new,
);
}
static Future<void> _initReal() async {
getIt
..registerLazySingleton<FavoriteLocalDataSource>(
FavoriteHiveDataSource.new,
)
..registerLazySingleton<MiddlewareClient>(() {
final Pipeline pipeline = Pipeline()
.addMiddleware(
UriPrefixMiddleware(
protocol: Protocols.https,
authority: 'jsonplaceholder.typicode.com',
),
)
.addMiddleware(BodyToJsonMiddleware());
return MiddlewareClient(pipeline: pipeline);
})
..registerLazySingleton<PhotoRemoteDataSource>(
() => PhotoApiDataSourceImpl(getIt()),
)
..registerLazySingleton<AlbumRemoteDataSource>(
() => AlbumApiDataSourceImpl(getIt()),
);
}
static Future<void> run({bool enableMocks = false}) async {
await _initGlobal();
enableMocks ? await _initMocks() : await _initReal();
await getIt.allReady();
}
}
@@ -14,5 +14,8 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
export 'queries_firestore.dart'; enum FetchStatus {
export 'queries_interface.dart'; initial,
success,
failure,
}
@@ -14,31 +14,22 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:bloc/bloc.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class AppBlocObserver extends BlocObserver { class AppBlocObserver extends BlocObserver {
@override @override
void onEvent(Bloc bloc, Object? event) { void onTransition(
super.onEvent(bloc, event); Bloc<dynamic, dynamic> bloc,
debugPrint(event.toString()); Transition<dynamic, dynamic> transition,
} ) {
@override
void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
debugPrint(error.toString());
super.onError(bloc, error, stackTrace);
}
@override
void onChange(BlocBase bloc, Change change) {
super.onChange(bloc, change);
debugPrint('curr:\t${change.currentState}\nnext:\t${change.nextState}');
}
@override
void onTransition(Bloc bloc, Transition transition) {
super.onTransition(bloc, transition); super.onTransition(bloc, transition);
debugPrint(transition.toString()); debugPrint(transition.toString());
} }
@override
void onError(BlocBase<dynamic> bloc, Object error, StackTrace stackTrace) {
debugPrint(error.toString());
super.onError(bloc, error, stackTrace);
}
} }
@@ -0,0 +1,52 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/core/constants/hive_boxes.dart';
import 'package:architecture_example/domain/data_sources/local/favorite_local_data_source.dart';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:hive/hive.dart';
class FavoriteHiveDataSource extends FavoriteLocalDataSource {
@override
Future<void> addPhotoToFavorites(Photo photo) async {
final box = await Hive.openBox<bool>(HiveBoxes.favorites);
if (box.containsKey(photo.id)) {
throw Exception('Photo already in favorites');
}
await box.put(photo.id, true);
}
@override
Future<bool> checkIfPhotoIsInFavorites(int id) async {
final box = await Hive.openBox<bool>(HiveBoxes.favorites);
return box.containsKey(id);
}
@override
Future<void> deletePhotoFromFavorites(int id) async {
final box = await Hive.openBox<bool>(HiveBoxes.favorites);
if (!box.containsKey(id)) {
throw Exception('Unknown photo');
}
await box.delete(id);
}
@override
Future<List<int>> getAllPhotosFromFavorites() async {
final box = await Hive.openBox<bool>(HiveBoxes.favorites);
return box.keys.cast<int>().toList();
}
}
@@ -0,0 +1,56 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/data/models/photo_model.dart';
import 'package:architecture_example/domain/data_sources/local/favorite_local_data_source.dart';
import 'package:architecture_example/domain/entities/photo.dart';
class FavoriteMockDataSource extends FavoriteLocalDataSource {
final Map<int, Photo> _mock = {
2: const PhotoModel(
albumId: 1,
id: 2,
title: 'Photo 2',
url: 'https://via.placeholder.com/600/771796',
thumbnailUrl: 'https://via.placeholder.com/150/771796',
),
};
@override
Future<void> addPhotoToFavorites(Photo photo) async {
if (_mock.containsKey(photo.id)) {
throw Exception('Photo already in favorites');
}
_mock.putIfAbsent(photo.id, () => photo);
}
@override
Future<bool> checkIfPhotoIsInFavorites(int id) async => _mock.containsKey(id);
@override
Future<void> deletePhotoFromFavorites(int id) async {
if (!_mock.containsKey(id)) {
throw Exception('Unknown photo');
}
_mock.remove(id);
}
@override
Future<List<int>> getAllPhotosFromFavorites() async {
final result = _mock.keys;
return result.toList();
}
}
@@ -0,0 +1,53 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'package:architecture_example/data/models/album_model.dart';
import 'package:architecture_example/data/models/list_album_model.dart';
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
import 'package:architecture_example/domain/entities/album.dart';
import 'package:wyatt_http_client/wyatt_http_client.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class AlbumApiDataSourceImpl extends AlbumRemoteDataSource {
final MiddlewareClient _client;
AlbumApiDataSourceImpl(this._client);
@override
Future<Album> getAlbum(int id) async {
final response = await _client.get(Uri.parse('/albums/$id'));
final album =
AlbumModel.fromJson(jsonDecode(response.body) as Map<String, Object?>);
return album;
}
@override
Future<List<Album>> getAllAlbums({int? start, int? limit}) async {
final startQuery = start.isNotNull ? '_start=$start' : '';
final limitQuery = limit.isNotNull ? '_limit=$limit' : '';
final delimiter1 =
(startQuery.isNotEmpty || limitQuery.isNotEmpty) ? '?' : '';
final delimiter2 =
(startQuery.isNotEmpty && limitQuery.isNotEmpty) ? '&' : '';
final url = '/albums$delimiter1$startQuery$delimiter2$limitQuery';
final response = await _client.get(Uri.parse(url));
final albums =
ListAlbumModel.fromJson({'albums': jsonDecode(response.body)});
return albums.albums;
}
}
@@ -0,0 +1,46 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/data/models/album_model.dart';
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
import 'package:architecture_example/domain/entities/album.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class AlbumMockDataSourceImpl extends AlbumRemoteDataSource {
final Map<int, Album> _mock = {
1: const AlbumModel(
id: 1,
userId: 1,
title: 'Album 1',
)
};
@override
Future<Album> getAlbum(int id) async {
final response = _mock[id];
if (response.isNull) {
throw Exception('Unknown album');
}
return response!;
}
@override
Future<List<Album>> getAllAlbums({int? start, int? limit}) async {
final response = _mock.values;
final nullableEnd = (limit != null) ? start ?? 0 + limit : null;
return response.toList().sublist(start ?? 0, nullableEnd);
}
}
@@ -0,0 +1,70 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:convert';
import 'package:architecture_example/data/models/list_photo_model.dart';
import 'package:architecture_example/data/models/photo_model.dart';
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:wyatt_http_client/wyatt_http_client.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class PhotoApiDataSourceImpl extends PhotoRemoteDataSource {
final MiddlewareClient _client;
PhotoApiDataSourceImpl(this._client);
@override
Future<Photo> getPhoto(int id) async {
final response = await _client.get(Uri.parse('/photos/$id'));
final photo =
PhotoModel.fromJson(jsonDecode(response.body) as Map<String, Object?>);
return photo;
}
@override
Future<List<Photo>> getAllPhotos({int? start, int? limit}) async {
final startQuery = start.isNotNull ? '_start=$start' : '';
final limitQuery = limit.isNotNull ? '_limit=$limit' : '';
final delimiter1 =
(startQuery.isNotEmpty || limitQuery.isNotEmpty) ? '?' : '';
final delimiter2 =
(startQuery.isNotEmpty && limitQuery.isNotEmpty) ? '&' : '';
final url = '/photos$delimiter1$startQuery$delimiter2$limitQuery';
final response = await _client.get(Uri.parse(url));
final photos =
ListPhotoModel.fromJson({'photos': jsonDecode(response.body)});
return photos.photos;
}
@override
Future<List<Photo>> getPhotosFromAlbum(
int albumId, {
int? start,
int? limit,
}) async {
final startQuery = start.isNotNull ? '_start=$start' : '';
final limitQuery = limit.isNotNull ? '_limit=$limit' : '';
final delimiter =
(startQuery.isNotEmpty && limitQuery.isNotEmpty) ? '&' : '';
final url = '/photos?albumId=$albumId&$startQuery$delimiter$limitQuery';
final response = await _client.get(Uri.parse(url));
final photos =
ListPhotoModel.fromJson({'photos': jsonDecode(response.body)});
return photos.photos;
}
}
@@ -0,0 +1,67 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/data/models/photo_model.dart';
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class PhotoMockDataSourceImpl extends PhotoRemoteDataSource {
final Map<int, Photo> _mock = {
1: const PhotoModel(
albumId: 1,
id: 1,
title: 'Photo 1',
url: 'https://via.placeholder.com/600/92c952',
thumbnailUrl: 'https://via.placeholder.com/150/92c952',
),
2: const PhotoModel(
albumId: 1,
id: 2,
title: 'Photo 2',
url: 'https://via.placeholder.com/600/771796',
thumbnailUrl: 'https://via.placeholder.com/150/771796',
),
};
@override
Future<Photo> getPhoto(int id) async {
final response = _mock[id];
if (response.isNull) {
throw Exception('Unknown photo');
}
return response!;
}
@override
Future<List<Photo>> getAllPhotos({int? start, int? limit}) async {
final response = _mock.values;
final nullableEnd = (limit != null) ? start ?? 0 + limit : null;
return response.toList().sublist(start ?? 0, nullableEnd);
}
@override
Future<List<Photo>> getPhotosFromAlbum(
int albumId, {
int? start,
int? limit,
}) async {
// TODO(hpcl): use start and limit
final response = _mock.values
..where((element) => element.albumId == albumId);
return response.toList();
}
}
@@ -0,0 +1,33 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/album.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'album_model.freezed.dart';
part 'album_model.g.dart';
@freezed
class AlbumModel extends Album with _$AlbumModel {
const factory AlbumModel({
required int userId,
required int id,
required String title,
}) = _AlbumModel;
factory AlbumModel.fromJson(Map<String, Object?> json) =>
_$AlbumModelFromJson(json);
}
@@ -0,0 +1,173 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
part of 'album_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
AlbumModel _$AlbumModelFromJson(Map<String, dynamic> json) {
return _AlbumModel.fromJson(json);
}
/// @nodoc
mixin _$AlbumModel {
int get userId => throw _privateConstructorUsedError;
int get id => throw _privateConstructorUsedError;
String get title => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$AlbumModelCopyWith<AlbumModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $AlbumModelCopyWith<$Res> {
factory $AlbumModelCopyWith(
AlbumModel value, $Res Function(AlbumModel) then) =
_$AlbumModelCopyWithImpl<$Res, AlbumModel>;
@useResult
$Res call({int userId, int id, String title});
}
/// @nodoc
class _$AlbumModelCopyWithImpl<$Res, $Val extends AlbumModel>
implements $AlbumModelCopyWith<$Res> {
_$AlbumModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? userId = null,
Object? id = null,
Object? title = null,
}) {
return _then(_value.copyWith(
userId: null == userId
? _value.userId
: userId // ignore: cast_nullable_to_non_nullable
as int,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
title: null == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$_AlbumModelCopyWith<$Res>
implements $AlbumModelCopyWith<$Res> {
factory _$$_AlbumModelCopyWith(
_$_AlbumModel value, $Res Function(_$_AlbumModel) then) =
__$$_AlbumModelCopyWithImpl<$Res>;
@override
@useResult
$Res call({int userId, int id, String title});
}
/// @nodoc
class __$$_AlbumModelCopyWithImpl<$Res>
extends _$AlbumModelCopyWithImpl<$Res, _$_AlbumModel>
implements _$$_AlbumModelCopyWith<$Res> {
__$$_AlbumModelCopyWithImpl(
_$_AlbumModel _value, $Res Function(_$_AlbumModel) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? userId = null,
Object? id = null,
Object? title = null,
}) {
return _then(_$_AlbumModel(
userId: null == userId
? _value.userId
: userId // ignore: cast_nullable_to_non_nullable
as int,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
title: null == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$_AlbumModel implements _AlbumModel {
const _$_AlbumModel(
{required this.userId, required this.id, required this.title});
factory _$_AlbumModel.fromJson(Map<String, dynamic> json) =>
_$$_AlbumModelFromJson(json);
@override
final int userId;
@override
final int id;
@override
final String title;
@override
String toString() {
return 'AlbumModel(userId: $userId, id: $id, title: $title)';
}
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_AlbumModelCopyWith<_$_AlbumModel> get copyWith =>
__$$_AlbumModelCopyWithImpl<_$_AlbumModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$_AlbumModelToJson(
this,
);
}
}
abstract class _AlbumModel implements AlbumModel {
const factory _AlbumModel(
{required final int userId,
required final int id,
required final String title}) = _$_AlbumModel;
factory _AlbumModel.fromJson(Map<String, dynamic> json) =
_$_AlbumModel.fromJson;
@override
int get userId;
@override
int get id;
@override
String get title;
@override
@JsonKey(ignore: true)
_$$_AlbumModelCopyWith<_$_AlbumModel> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -0,0 +1,21 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'album_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$_AlbumModel _$$_AlbumModelFromJson(Map<String, dynamic> json) =>
_$_AlbumModel(
userId: json['userId'] as int,
id: json['id'] as int,
title: json['title'] as String,
);
Map<String, dynamic> _$$_AlbumModelToJson(_$_AlbumModel instance) =>
<String, dynamic>{
'userId': instance.userId,
'id': instance.id,
'title': instance.title,
};
@@ -0,0 +1,32 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/data/models/album_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'list_album_model.freezed.dart';
part 'list_album_model.g.dart';
@freezed
class ListAlbumModel with _$ListAlbumModel {
const factory ListAlbumModel({
required List<AlbumModel> albums,
}) = _ListAlbumModel;
factory ListAlbumModel.fromJson(Map<String, Object?> json) =>
_$ListAlbumModelFromJson(json);
}
@@ -0,0 +1,158 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
part of 'list_album_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
ListAlbumModel _$ListAlbumModelFromJson(Map<String, dynamic> json) {
return _ListAlbumModel.fromJson(json);
}
/// @nodoc
mixin _$ListAlbumModel {
List<AlbumModel> get albums => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$ListAlbumModelCopyWith<ListAlbumModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ListAlbumModelCopyWith<$Res> {
factory $ListAlbumModelCopyWith(
ListAlbumModel value, $Res Function(ListAlbumModel) then) =
_$ListAlbumModelCopyWithImpl<$Res, ListAlbumModel>;
@useResult
$Res call({List<AlbumModel> albums});
}
/// @nodoc
class _$ListAlbumModelCopyWithImpl<$Res, $Val extends ListAlbumModel>
implements $ListAlbumModelCopyWith<$Res> {
_$ListAlbumModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? albums = null,
}) {
return _then(_value.copyWith(
albums: null == albums
? _value.albums
: albums // ignore: cast_nullable_to_non_nullable
as List<AlbumModel>,
) as $Val);
}
}
/// @nodoc
abstract class _$$_ListAlbumModelCopyWith<$Res>
implements $ListAlbumModelCopyWith<$Res> {
factory _$$_ListAlbumModelCopyWith(
_$_ListAlbumModel value, $Res Function(_$_ListAlbumModel) then) =
__$$_ListAlbumModelCopyWithImpl<$Res>;
@override
@useResult
$Res call({List<AlbumModel> albums});
}
/// @nodoc
class __$$_ListAlbumModelCopyWithImpl<$Res>
extends _$ListAlbumModelCopyWithImpl<$Res, _$_ListAlbumModel>
implements _$$_ListAlbumModelCopyWith<$Res> {
__$$_ListAlbumModelCopyWithImpl(
_$_ListAlbumModel _value, $Res Function(_$_ListAlbumModel) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? albums = null,
}) {
return _then(_$_ListAlbumModel(
albums: null == albums
? _value._albums
: albums // ignore: cast_nullable_to_non_nullable
as List<AlbumModel>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$_ListAlbumModel implements _ListAlbumModel {
const _$_ListAlbumModel({required final List<AlbumModel> albums})
: _albums = albums;
factory _$_ListAlbumModel.fromJson(Map<String, dynamic> json) =>
_$$_ListAlbumModelFromJson(json);
final List<AlbumModel> _albums;
@override
List<AlbumModel> get albums {
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_albums);
}
@override
String toString() {
return 'ListAlbumModel(albums: $albums)';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_ListAlbumModel &&
const DeepCollectionEquality().equals(other._albums, _albums));
}
@JsonKey(ignore: true)
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(_albums));
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_ListAlbumModelCopyWith<_$_ListAlbumModel> get copyWith =>
__$$_ListAlbumModelCopyWithImpl<_$_ListAlbumModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$_ListAlbumModelToJson(
this,
);
}
}
abstract class _ListAlbumModel implements ListAlbumModel {
const factory _ListAlbumModel({required final List<AlbumModel> albums}) =
_$_ListAlbumModel;
factory _ListAlbumModel.fromJson(Map<String, dynamic> json) =
_$_ListAlbumModel.fromJson;
@override
List<AlbumModel> get albums;
@override
@JsonKey(ignore: true)
_$$_ListAlbumModelCopyWith<_$_ListAlbumModel> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -0,0 +1,19 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'list_album_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$_ListAlbumModel _$$_ListAlbumModelFromJson(Map<String, dynamic> json) =>
_$_ListAlbumModel(
albums: (json['albums'] as List<dynamic>)
.map((e) => AlbumModel.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$$_ListAlbumModelToJson(_$_ListAlbumModel instance) =>
<String, dynamic>{
'albums': instance.albums,
};
@@ -0,0 +1,34 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/data/models/photo_model.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'list_photo_model.freezed.dart';
part 'list_photo_model.g.dart';
@freezed
class ListPhotoModel with _$ListPhotoModel {
const factory ListPhotoModel({
required List<PhotoModel> photos,
}) = _ListPhotoModel;
factory ListPhotoModel.fromJson(Map<String, Object?> json) =>
_$ListPhotoModelFromJson(json);
}
@@ -0,0 +1,158 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
part of 'list_photo_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
ListPhotoModel _$ListPhotoModelFromJson(Map<String, dynamic> json) {
return _ListPhotoModel.fromJson(json);
}
/// @nodoc
mixin _$ListPhotoModel {
List<PhotoModel> get photos => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$ListPhotoModelCopyWith<ListPhotoModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ListPhotoModelCopyWith<$Res> {
factory $ListPhotoModelCopyWith(
ListPhotoModel value, $Res Function(ListPhotoModel) then) =
_$ListPhotoModelCopyWithImpl<$Res, ListPhotoModel>;
@useResult
$Res call({List<PhotoModel> photos});
}
/// @nodoc
class _$ListPhotoModelCopyWithImpl<$Res, $Val extends ListPhotoModel>
implements $ListPhotoModelCopyWith<$Res> {
_$ListPhotoModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? photos = null,
}) {
return _then(_value.copyWith(
photos: null == photos
? _value.photos
: photos // ignore: cast_nullable_to_non_nullable
as List<PhotoModel>,
) as $Val);
}
}
/// @nodoc
abstract class _$$_ListPhotoModelCopyWith<$Res>
implements $ListPhotoModelCopyWith<$Res> {
factory _$$_ListPhotoModelCopyWith(
_$_ListPhotoModel value, $Res Function(_$_ListPhotoModel) then) =
__$$_ListPhotoModelCopyWithImpl<$Res>;
@override
@useResult
$Res call({List<PhotoModel> photos});
}
/// @nodoc
class __$$_ListPhotoModelCopyWithImpl<$Res>
extends _$ListPhotoModelCopyWithImpl<$Res, _$_ListPhotoModel>
implements _$$_ListPhotoModelCopyWith<$Res> {
__$$_ListPhotoModelCopyWithImpl(
_$_ListPhotoModel _value, $Res Function(_$_ListPhotoModel) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? photos = null,
}) {
return _then(_$_ListPhotoModel(
photos: null == photos
? _value._photos
: photos // ignore: cast_nullable_to_non_nullable
as List<PhotoModel>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$_ListPhotoModel implements _ListPhotoModel {
const _$_ListPhotoModel({required final List<PhotoModel> photos})
: _photos = photos;
factory _$_ListPhotoModel.fromJson(Map<String, dynamic> json) =>
_$$_ListPhotoModelFromJson(json);
final List<PhotoModel> _photos;
@override
List<PhotoModel> get photos {
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_photos);
}
@override
String toString() {
return 'ListPhotoModel(photos: $photos)';
}
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$_ListPhotoModel &&
const DeepCollectionEquality().equals(other._photos, _photos));
}
@JsonKey(ignore: true)
@override
int get hashCode =>
Object.hash(runtimeType, const DeepCollectionEquality().hash(_photos));
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_ListPhotoModelCopyWith<_$_ListPhotoModel> get copyWith =>
__$$_ListPhotoModelCopyWithImpl<_$_ListPhotoModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$_ListPhotoModelToJson(
this,
);
}
}
abstract class _ListPhotoModel implements ListPhotoModel {
const factory _ListPhotoModel({required final List<PhotoModel> photos}) =
_$_ListPhotoModel;
factory _ListPhotoModel.fromJson(Map<String, dynamic> json) =
_$_ListPhotoModel.fromJson;
@override
List<PhotoModel> get photos;
@override
@JsonKey(ignore: true)
_$$_ListPhotoModelCopyWith<_$_ListPhotoModel> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -0,0 +1,19 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'list_photo_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$_ListPhotoModel _$$_ListPhotoModelFromJson(Map<String, dynamic> json) =>
_$_ListPhotoModel(
photos: (json['photos'] as List<dynamic>)
.map((e) => PhotoModel.fromJson(e as Map<String, dynamic>))
.toList(),
);
Map<String, dynamic> _$$_ListPhotoModelToJson(_$_ListPhotoModel instance) =>
<String, dynamic>{
'photos': instance.photos,
};
@@ -0,0 +1,35 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'photo_model.freezed.dart';
part 'photo_model.g.dart';
@freezed
class PhotoModel extends Photo with _$PhotoModel {
const factory PhotoModel({
required int albumId,
required int id,
required String title,
required String url,
required String thumbnailUrl,
}) = _PhotoModel;
factory PhotoModel.fromJson(Map<String, Object?> json) =>
_$PhotoModelFromJson(json);
}
@@ -0,0 +1,211 @@
// coverage:ignore-file
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: type=lint
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target
part of 'photo_model.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(T value) => value;
final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
PhotoModel _$PhotoModelFromJson(Map<String, dynamic> json) {
return _PhotoModel.fromJson(json);
}
/// @nodoc
mixin _$PhotoModel {
int get albumId => throw _privateConstructorUsedError;
int get id => throw _privateConstructorUsedError;
String get title => throw _privateConstructorUsedError;
String get url => throw _privateConstructorUsedError;
String get thumbnailUrl => throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$PhotoModelCopyWith<PhotoModel> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $PhotoModelCopyWith<$Res> {
factory $PhotoModelCopyWith(
PhotoModel value, $Res Function(PhotoModel) then) =
_$PhotoModelCopyWithImpl<$Res, PhotoModel>;
@useResult
$Res call(
{int albumId, int id, String title, String url, String thumbnailUrl});
}
/// @nodoc
class _$PhotoModelCopyWithImpl<$Res, $Val extends PhotoModel>
implements $PhotoModelCopyWith<$Res> {
_$PhotoModelCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
@pragma('vm:prefer-inline')
@override
$Res call({
Object? albumId = null,
Object? id = null,
Object? title = null,
Object? url = null,
Object? thumbnailUrl = null,
}) {
return _then(_value.copyWith(
albumId: null == albumId
? _value.albumId
: albumId // ignore: cast_nullable_to_non_nullable
as int,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
title: null == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String,
url: null == url
? _value.url
: url // ignore: cast_nullable_to_non_nullable
as String,
thumbnailUrl: null == thumbnailUrl
? _value.thumbnailUrl
: thumbnailUrl // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$_PhotoModelCopyWith<$Res>
implements $PhotoModelCopyWith<$Res> {
factory _$$_PhotoModelCopyWith(
_$_PhotoModel value, $Res Function(_$_PhotoModel) then) =
__$$_PhotoModelCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{int albumId, int id, String title, String url, String thumbnailUrl});
}
/// @nodoc
class __$$_PhotoModelCopyWithImpl<$Res>
extends _$PhotoModelCopyWithImpl<$Res, _$_PhotoModel>
implements _$$_PhotoModelCopyWith<$Res> {
__$$_PhotoModelCopyWithImpl(
_$_PhotoModel _value, $Res Function(_$_PhotoModel) _then)
: super(_value, _then);
@pragma('vm:prefer-inline')
@override
$Res call({
Object? albumId = null,
Object? id = null,
Object? title = null,
Object? url = null,
Object? thumbnailUrl = null,
}) {
return _then(_$_PhotoModel(
albumId: null == albumId
? _value.albumId
: albumId // ignore: cast_nullable_to_non_nullable
as int,
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as int,
title: null == title
? _value.title
: title // ignore: cast_nullable_to_non_nullable
as String,
url: null == url
? _value.url
: url // ignore: cast_nullable_to_non_nullable
as String,
thumbnailUrl: null == thumbnailUrl
? _value.thumbnailUrl
: thumbnailUrl // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$_PhotoModel implements _PhotoModel {
const _$_PhotoModel(
{required this.albumId,
required this.id,
required this.title,
required this.url,
required this.thumbnailUrl});
factory _$_PhotoModel.fromJson(Map<String, dynamic> json) =>
_$$_PhotoModelFromJson(json);
@override
final int albumId;
@override
final int id;
@override
final String title;
@override
final String url;
@override
final String thumbnailUrl;
@override
String toString() {
return 'PhotoModel(albumId: $albumId, id: $id, title: $title, url: $url, thumbnailUrl: $thumbnailUrl)';
}
@JsonKey(ignore: true)
@override
@pragma('vm:prefer-inline')
_$$_PhotoModelCopyWith<_$_PhotoModel> get copyWith =>
__$$_PhotoModelCopyWithImpl<_$_PhotoModel>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$_PhotoModelToJson(
this,
);
}
}
abstract class _PhotoModel implements PhotoModel {
const factory _PhotoModel(
{required final int albumId,
required final int id,
required final String title,
required final String url,
required final String thumbnailUrl}) = _$_PhotoModel;
factory _PhotoModel.fromJson(Map<String, dynamic> json) =
_$_PhotoModel.fromJson;
@override
int get albumId;
@override
int get id;
@override
String get title;
@override
String get url;
@override
String get thumbnailUrl;
@override
@JsonKey(ignore: true)
_$$_PhotoModelCopyWith<_$_PhotoModel> get copyWith =>
throw _privateConstructorUsedError;
}
@@ -0,0 +1,25 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'photo_model.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$_PhotoModel _$$_PhotoModelFromJson(Map<String, dynamic> json) =>
_$_PhotoModel(
albumId: json['albumId'] as int,
id: json['id'] as int,
title: json['title'] as String,
url: json['url'] as String,
thumbnailUrl: json['thumbnailUrl'] as String,
);
Map<String, dynamic> _$$_PhotoModelToJson(_$_PhotoModel instance) =>
<String, dynamic>{
'albumId': instance.albumId,
'id': instance.id,
'title': instance.title,
'url': instance.url,
'thumbnailUrl': instance.thumbnailUrl,
};
@@ -0,0 +1,119 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/data_sources/local/favorite_local_data_source.dart';
import 'package:architecture_example/domain/data_sources/remote/album_remote_data_source.dart';
import 'package:architecture_example/domain/data_sources/remote/photo_remote_data_source.dart';
import 'package:architecture_example/domain/entities/album.dart';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
import 'package:wyatt_type_utils/wyatt_type_utils.dart';
class PhotoRepositoryImpl extends PhotoRepository {
final PhotoRemoteDataSource _photoRemoteDataSource;
final AlbumRemoteDataSource _albumRemoteDataSource;
final FavoriteLocalDataSource _favoriteLocalDataSource;
PhotoRepositoryImpl(
this._photoRemoteDataSource,
this._albumRemoteDataSource,
this._favoriteLocalDataSource,
);
@override
FutureOrResult<void> addPhotoToFavorites(Photo photo) => Result.tryCatchAsync(
() => _favoriteLocalDataSource.addPhotoToFavorites(photo),
(error) => ClientException('Cannot add photo to favorites.'),
);
@override
FutureOrResult<bool> checkIfPhotoIsInFavorites(int id) =>
Result.tryCatchAsync(
() => _favoriteLocalDataSource.checkIfPhotoIsInFavorites(id),
(error) =>
ClientException('Cannot check if photo `$id` is in favorites.'),
);
@override
FutureOrResult<void> deletePhotoFromFavorites(int id) => Result.tryCatchAsync(
() => _favoriteLocalDataSource.deletePhotoFromFavorites(id),
(error) => ClientException('Cannot delete photo `$id` from favorites.'),
);
@override
FutureOrResult<Album> getAlbum(int id) => Result.tryCatchAsync(
() => _albumRemoteDataSource.getAlbum(id),
(error) => ServerException('Cannot retrieve album $id.'),
);
@override
FutureOrResult<List<Album>> getAllAlbums({int? start, int? limit}) =>
Result.tryCatchAsync(
() => _albumRemoteDataSource.getAllAlbums(start: start, limit: limit),
(error) => ServerException('Cannot retrieve all albums.'),
);
@override
FutureOrResult<List<Photo>> getAllPhotos({int? start, int? limit}) async =>
Result.tryCatchAsync(
() => _photoRemoteDataSource.getAllPhotos(start: start, limit: limit),
(error) => ServerException('Cannot retrieve all photos.'),
);
@override
FutureOrResult<List<Photo>> getAllPhotosFromFavorites() async {
try {
final response = <Photo>[];
final favorites =
await _favoriteLocalDataSource.getAllPhotosFromFavorites();
final photos = await _photoRemoteDataSource.getAllPhotos();
for (final photo in photos) {
if (favorites.any((favId) => photo.id == favId)) {
response.add(photo);
}
}
return Ok(response);
} catch (_) {
return Err(
ClientException('Cannot retrieve all photos from favorites.'),
);
}
}
@override
FutureOrResult<Photo> getPhoto(int id) => Result.tryCatchAsync(
() => _photoRemoteDataSource.getPhoto(id),
(error) => ServerException('Cannot retrieve photo $id.'),
);
@override
FutureOrResult<List<Photo>> getPhotosFromAlbum(
int albumId, {
int? start,
int? limit,
}) async =>
Result.tryCatchAsync(
() => _photoRemoteDataSource.getPhotosFromAlbum(
albumId,
start: start,
limit: limit,
),
(error) =>
ServerException('Cannot retrieve all photos from album $albumId'),
);
}
@@ -0,0 +1,25 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
abstract class FavoriteLocalDataSource extends BaseLocalDataSource {
Future<void> addPhotoToFavorites(Photo photo);
Future<void> deletePhotoFromFavorites(int id);
Future<List<int>> getAllPhotosFromFavorites();
Future<bool> checkIfPhotoIsInFavorites(int id);
}
@@ -0,0 +1,23 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/album.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
abstract class AlbumRemoteDataSource extends BaseRemoteDataSource {
Future<Album> getAlbum(int id);
Future<List<Album>> getAllAlbums({int? start, int? limit});
}
@@ -14,11 +14,11 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const String formFieldName = 'name'; import 'package:architecture_example/domain/entities/photo.dart';
const String formFieldPhone = 'phone'; import 'package:wyatt_architecture/wyatt_architecture.dart';
const String formFieldPro = 'isPro';
const String formFieldConfirmedPassword = 'confirmedPassword';
const String formFieldSiren = 'siren';
const String formFieldIban = 'iban';
const String firestoreCollectionUsers = 'authentication_bloc_users'; abstract class PhotoRemoteDataSource extends BaseRemoteDataSource {
Future<Photo> getPhoto(int id);
Future<List<Photo>> getAllPhotos({int? start, int? limit});
Future<List<Photo>> getPhotosFromAlbum(int albumId, {int? start, int? limit});
}
@@ -0,0 +1,44 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/foundation.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
@immutable
class Album extends Entity {
final int userId;
final int id;
final String title;
const Album({
required this.userId,
required this.id,
required this.title,
});
@override
bool operator ==(covariant Album other) {
if (identical(this, other)) {
return true;
}
return other.userId == userId && other.id == id && other.title == title;
}
@override
int get hashCode => userId.hashCode ^ id.hashCode ^ title.hashCode;
}
@@ -0,0 +1,57 @@
// ignore_for_file: public_member_api_docs, sort_constructors_first
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:flutter/foundation.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
@immutable
class Photo extends Entity {
final int albumId;
final int id;
final String title;
final String url;
final String thumbnailUrl;
const Photo({
required this.albumId,
required this.id,
required this.title,
required this.url,
required this.thumbnailUrl,
});
@override
bool operator ==(covariant Photo other) {
if (identical(this, other)) {
return true;
}
return other.albumId == albumId &&
other.id == id &&
other.title == title &&
other.url == url &&
other.thumbnailUrl == thumbnailUrl;
}
@override
int get hashCode =>
albumId.hashCode ^
id.hashCode ^
title.hashCode ^
url.hashCode ^
thumbnailUrl.hashCode;
}
@@ -0,0 +1,35 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/domain/entities/album.dart';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
abstract class PhotoRepository extends BaseRepository {
FutureOrResult<Album> getAlbum(int id);
FutureOrResult<List<Album>> getAllAlbums({int? start, int? limit});
FutureOrResult<Photo> getPhoto(int id);
FutureOrResult<List<Photo>> getAllPhotos({int? start, int? limit});
FutureOrResult<List<Photo>> getPhotosFromAlbum(
int albumId, {
int? start,
int? limit,
});
FutureOrResult<void> addPhotoToFavorites(Photo photo);
FutureOrResult<void> deletePhotoFromFavorites(int id);
FutureOrResult<List<Photo>> getAllPhotosFromFavorites();
FutureOrResult<bool> checkIfPhotoIsInFavorites(int id);
}
@@ -0,0 +1,40 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
class AddPhotoToFavorites extends AsyncUseCase<Photo, List<Photo>> {
final PhotoRepository _photoRepository;
AddPhotoToFavorites(this._photoRepository);
@override
FutureOrResult<List<Photo>> execute(Photo? params) async {
await _photoRepository.addPhotoToFavorites(params!);
return _photoRepository.getAllPhotosFromFavorites();
}
@override
FutureOr<void> onStart(Photo? params) {
if (params == null) {
throw ClientException('Photo cannot be null');
}
}
}
@@ -0,0 +1,37 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
class CheckIfPhotoIsInFavorites extends AsyncUseCase<int, bool> {
final PhotoRepository _photoRepository;
CheckIfPhotoIsInFavorites(this._photoRepository);
@override
FutureOrResult<bool> execute(int? params) async =>
_photoRepository.checkIfPhotoIsInFavorites(params!);
@override
FutureOr<void> onStart(int? params) {
if (params == null) {
throw ClientException('id cannot be null');
}
}
}
@@ -14,24 +14,18 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:authentication_bloc_example/login/widgets/login_form.dart'; import 'package:architecture_example/domain/entities/photo.dart';
import 'package:flutter/material.dart'; import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
class LoginPage extends StatelessWidget { class DisplayFavorites extends AsyncUseCase<NoParam, List<Photo>> {
const LoginPage({Key? key}) : super(key: key); final PhotoRepository _photoRepository;
static Route route() { DisplayFavorites(this._photoRepository);
return MaterialPageRoute<void>(builder: (_) => const LoginPage());
}
@override @override
Widget build(BuildContext context) { FutureOrResult<List<Photo>> execute(void params) {
return Scaffold( final photos = _photoRepository.getAllPhotosFromFavorites();
appBar: AppBar(title: const Text('Login')), return photos;
body: const Padding(
padding: EdgeInsets.all(8),
child: LoginForm(),
),
);
} }
} }
@@ -0,0 +1,40 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
class DisplayPhoto extends AsyncUseCase<int, Photo> {
final PhotoRepository _photoRepository;
DisplayPhoto(this._photoRepository);
@override
FutureOrResult<Photo> execute(int? params) {
final photo = _photoRepository.getPhoto(params!);
return photo;
}
@override
FutureOr<void> onStart(int? params) {
if (params == null) {
throw ClientException('id cannot be null');
}
}
}
@@ -0,0 +1,46 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:architecture_example/domain/usecases/photos/params/query_parameters.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
class OpenAlbum extends AsyncUseCase<QueryParameters, List<Photo>> {
final PhotoRepository _photoRepository;
OpenAlbum(this._photoRepository);
@override
FutureOrResult<List<Photo>> execute(QueryParameters? params) {
final photos = _photoRepository.getPhotosFromAlbum(
params!.albumId,
start: params.start,
limit: params.limit,
);
return photos;
}
@override
FutureOr<void> onStart(QueryParameters? params) {
if (params == null) {
throw ClientException('params cannot be null');
}
}
}
@@ -0,0 +1,23 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
class QueryParameters {
final int albumId;
final int? start;
final int? limit;
QueryParameters(this.start, this.limit, {this.albumId = -1});
}
@@ -0,0 +1,40 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:architecture_example/domain/entities/photo.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
class RemovePhotoFromFavorites extends AsyncUseCase<int, List<Photo>> {
final PhotoRepository _photoRepository;
RemovePhotoFromFavorites(this._photoRepository);
@override
FutureOrResult<List<Photo>> execute(int? params) async {
await _photoRepository.deletePhotoFromFavorites(params!);
return _photoRepository.getAllPhotosFromFavorites();
}
@override
FutureOr<void> onStart(int? params) {
if (params == null) {
throw ClientException('id cannot be null');
}
}
}
@@ -0,0 +1,44 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'dart:async';
import 'package:architecture_example/domain/entities/album.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:architecture_example/domain/usecases/photos/params/query_parameters.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
class RetrieveAllAlbums extends AsyncUseCase<QueryParameters, List<Album>> {
final PhotoRepository _photoRepository;
RetrieveAllAlbums(this._photoRepository);
@override
FutureOrResult<List<Album>> execute(QueryParameters? params) {
final albums = _photoRepository.getAllAlbums(
start: params!.start,
limit: params.limit,
);
return albums;
}
@override
FutureOr<void> onStart(QueryParameters? params) {
if (params == null) {
throw ClientException('params cannot be null');
}
}
}
@@ -14,34 +14,9 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/bootstrap.dart';
import 'package:flutter/material.dart'; import 'package:architecture_example/presentation/features/app/app.dart';
import 'package:wyatt_architecture/wyatt_architecture.dart';
void main() { void main() {
runApp(const MyApp()); bootstrap(() => const App());
} }
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Architecture Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
appBar: AppBar(
title: const Text('Architecture Example'),
),
body: Center(child: Text(wyatt())),
),
);
}
}
@@ -0,0 +1,25 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/presentation/features/albums/state_management/albums_screen.dart';
import 'package:flutter/material.dart';
class Albums extends StatelessWidget {
const Albums({super.key});
@override
Widget build(BuildContext context) => const AlbumsScreen();
}
@@ -0,0 +1,94 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/core/enums/fetch_status.dart';
import 'package:architecture_example/domain/entities/album.dart';
import 'package:architecture_example/domain/usecases/photos/params/query_parameters.dart';
import 'package:architecture_example/domain/usecases/photos/retrieve_all_albums.dart';
import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:stream_transform/stream_transform.dart';
part 'album_event.dart';
part 'album_state.dart';
const _albumLimit = 20;
const throttleDuration = Duration(milliseconds: 100);
EventTransformer<E> throttleDroppable<E>(Duration duration) =>
(events, mapper) => droppable<E>().call(events.throttle(duration), mapper);
class AlbumBloc extends Bloc<AlbumEvent, AlbumState> {
final RetrieveAllAlbums _retrieveAllAlbums;
AlbumBloc(this._retrieveAllAlbums) : super(const AlbumState()) {
on<AlbumFetched>(
_onAlbumFetched,
transformer: throttleDroppable(throttleDuration),
);
}
Future<void> _onAlbumFetched(
AlbumFetched event,
Emitter<AlbumState> emit,
) async {
if (state.hasReachedMax) {
return;
}
if (state.status == FetchStatus.initial) {
final albums =
await _retrieveAllAlbums.call(QueryParameters(0, _albumLimit));
return emit(
albums.fold(
(value) => state.copyWith(
status: FetchStatus.success,
albums: value,
hasReachedMax: false,
),
(error) => state.copyWith(
status: FetchStatus.failure,
albums: [],
hasReachedMax: false,
),
),
);
}
final albums = await _retrieveAllAlbums
.call(QueryParameters(state.albums.length, _albumLimit));
return emit(
albums.fold(
(value) {
if (value.isEmpty) {
return state.copyWith(
hasReachedMax: true,
);
}
return state.copyWith(
status: FetchStatus.success,
albums: List.of(state.albums)..addAll(value),
hasReachedMax: false,
);
},
(error) => state.copyWith(
status: FetchStatus.failure,
albums: List.of(state.albums),
hasReachedMax: false,
),
),
);
}
}
@@ -0,0 +1,26 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'album_bloc.dart';
abstract class AlbumEvent extends Equatable {
const AlbumEvent();
@override
List<Object> get props => [];
}
class AlbumFetched extends AlbumEvent {}
@@ -0,0 +1,43 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
part of 'album_bloc.dart';
class AlbumState extends Equatable {
const AlbumState({
this.status = FetchStatus.initial,
this.albums = const <Album>[],
this.hasReachedMax = false,
});
final FetchStatus status;
final List<Album> albums;
final bool hasReachedMax;
AlbumState copyWith({
FetchStatus? status,
List<Album>? albums,
bool? hasReachedMax,
}) =>
AlbumState(
status: status ?? this.status,
albums: albums ?? this.albums,
hasReachedMax: hasReachedMax ?? this.hasReachedMax,
);
@override
List<Object> get props => [status, albums, hasReachedMax];
}
@@ -0,0 +1,58 @@
// Copyright (C) 2022 WYATT GROUP
// Please see the AUTHORS file for details.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
import 'package:architecture_example/core/enums/fetch_status.dart';
import 'package:architecture_example/domain/repositories/photo_repository.dart';
import 'package:architecture_example/domain/usecases/photos/retrieve_all_albums.dart';
import 'package:architecture_example/presentation/features/albums/blocs/album/album_bloc.dart';
import 'package:architecture_example/presentation/features/albums/state_management/albums_wrapper_widget.dart';
import 'package:architecture_example/presentation/features/albums/state_management/widgets/albums_list.dart';
import 'package:flutter/material.dart';
import 'package:wyatt_bloc_helper/wyatt_bloc_helper.dart';
class AlbumsScreen extends BlocScreen<AlbumBloc, AlbumEvent, AlbumState> {
const AlbumsScreen({super.key});
@override
AlbumBloc create(BuildContext context) =>
AlbumBloc(RetrieveAllAlbums(repo<PhotoRepository>(context)));
@override
AlbumBloc init(BuildContext context, AlbumBloc bloc) =>
bloc..add(AlbumFetched());
@override
Widget onWrap(BuildContext context, Widget child) => AlbumsWrapperWidget(
child: child,
);
@override
Widget onBuild(BuildContext context, AlbumState state) {
switch (state.status) {
case FetchStatus.initial:
return const Center(child: CircularProgressIndicator());
case FetchStatus.failure:
return const Center(
child: Text('failed to fetch albums'),
);
case FetchStatus.success:
return AlbumsList(
albums: state.albums,
hasReachedMax: state.hasReachedMax,
);
}
}
}

Some files were not shown because too many files have changed in this diff Show More