├── .eslintignore
├── .gitignore
├── .metadata
├── README.md
├── amplify
├── .config
│ └── project-config.json
├── README.md
├── backend
│ ├── analytics
│ │ └── amplifyrecipe
│ │ │ ├── parameters.json
│ │ │ └── pinpoint-cloudformation-template.json
│ ├── api
│ │ └── amplifyrecipe
│ │ │ ├── cli-inputs.json
│ │ │ ├── parameters.json
│ │ │ ├── resolvers
│ │ │ └── README.md
│ │ │ ├── schema.graphql
│ │ │ ├── stacks
│ │ │ └── CustomResources.json
│ │ │ └── transform.conf.json
│ ├── auth
│ │ └── amplifyrecipe86a8efe0
│ │ │ └── cli-inputs.json
│ ├── backend-config.json
│ ├── function
│ │ └── amplifyrecipefb7252f0
│ │ │ ├── amplify.state
│ │ │ ├── amplifyrecipefb7252f0-cloudformation-template.json
│ │ │ ├── custom-policies.json
│ │ │ ├── function-parameters.json
│ │ │ └── src
│ │ │ ├── event.json
│ │ │ ├── index.js
│ │ │ ├── package-lock.json
│ │ │ └── package.json
│ ├── storage
│ │ ├── Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev
│ │ │ └── parameters.json
│ │ └── s3b350e034
│ │ │ └── cli-inputs.json
│ ├── tags.json
│ └── types
│ │ └── amplify-dependent-resources-ref.d.ts
├── cli.json
├── hooks
│ └── README.md
└── team-provider-info.json
├── analysis_options.yaml
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ ├── google-services.json
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── amplify_recipe
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable-v21
│ │ │ └── launch_background.xml
│ │ │ ├── drawable
│ │ │ └── launch_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── values-night
│ │ │ └── styles.xml
│ │ │ └── values
│ │ │ └── styles.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
└── settings.gradle
├── assets
├── icons
│ ├── Add Friends.svg
│ ├── Apple.svg
│ ├── Arrow.svg
│ ├── At.svg
│ ├── Back.svg
│ ├── Bookmark.svg
│ ├── Calendar.svg
│ ├── Camera.svg
│ ├── Checklist.svg
│ ├── Collaboration.svg
│ ├── Comment.svg
│ ├── Delete.svg
│ ├── Eyes Close.svg
│ ├── Eyes Open.svg
│ ├── Faceboook.svg
│ ├── Google.svg
│ ├── Help.svg
│ ├── Home.svg
│ ├── Kebab.svg
│ ├── Language.svg
│ ├── Logout.svg
│ ├── Moon.svg
│ ├── Notification.svg
│ ├── Outlined Bookmark.svg
│ ├── Pencil.svg
│ ├── Plus in Box.svg
│ ├── Plus with container.svg
│ ├── Plus.svg
│ ├── Profile.svg
│ ├── Remove.svg
│ ├── Search.svg
│ ├── Trash.svg
│ └── clock.svg
└── images
│ └── onboard_image.png
├── binary
└── macos
│ ├── librealm_dart.dylib
│ └── realm_version.txt
├── ios
├── .gitignore
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── Podfile
├── Podfile.lock
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
└── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── Icon-App-1024x1024@1x.png
│ │ ├── Icon-App-20x20@1x.png
│ │ ├── Icon-App-20x20@2x.png
│ │ ├── Icon-App-20x20@3x.png
│ │ ├── Icon-App-29x29@1x.png
│ │ ├── Icon-App-29x29@2x.png
│ │ ├── Icon-App-29x29@3x.png
│ │ ├── Icon-App-40x40@1x.png
│ │ ├── Icon-App-40x40@2x.png
│ │ ├── Icon-App-40x40@3x.png
│ │ ├── Icon-App-60x60@2x.png
│ │ ├── Icon-App-60x60@3x.png
│ │ ├── Icon-App-76x76@1x.png
│ │ ├── Icon-App-76x76@2x.png
│ │ └── Icon-App-83.5x83.5@2x.png
│ └── LaunchImage.imageset
│ │ ├── Contents.json
│ │ ├── LaunchImage.png
│ │ ├── LaunchImage@2x.png
│ │ ├── LaunchImage@3x.png
│ │ └── README.md
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ ├── Info.plist
│ ├── Runner-Bridging-Header.h
│ ├── Runner.entitlements
│ └── RunnerDebug.entitlements
├── lib
├── features
│ ├── authentication
│ │ ├── screens
│ │ │ ├── forgot_password_screen.dart
│ │ │ ├── onboarding_screen.dart
│ │ │ ├── register_screen.dart
│ │ │ ├── resend_email_screen.dart
│ │ │ └── sign_in_screen.dart
│ │ └── widgets
│ │ │ ├── forgot_password_form.dart
│ │ │ ├── onboard_content.dart
│ │ │ ├── password_confirmation_form.dart
│ │ │ ├── sign_in_form.dart
│ │ │ ├── sign_up_form.dart
│ │ │ ├── socal_login_buttons.dart
│ │ │ ├── terms_and_policy.dart
│ │ │ └── user_confirmation_form.dart
│ ├── details
│ │ ├── components
│ │ │ ├── ingredient_tile.dart
│ │ │ └── ingredients.dart
│ │ └── screens
│ │ │ └── recipe_details_screen.dart
│ ├── entry_point.dart
│ ├── favorite
│ │ └── screens
│ │ │ └── favorite_screen.dart
│ ├── home
│ │ ├── screens
│ │ │ └── home_screen.dart
│ │ └── widgets
│ │ │ └── search_container.dart
│ ├── notifications
│ │ └── screens
│ │ │ └── notifications_screen.dart
│ ├── profile
│ │ ├── components
│ │ │ └── settings.dart
│ │ └── screens
│ │ │ ├── edit_profile_screen.dart
│ │ │ └── profile_screen.dart
│ └── recipes
│ │ └── screens
│ │ └── search
│ │ ├── screen
│ │ ├── add_recipe_screen.dart
│ │ ├── all_recipes_screen.dart
│ │ └── search_screen.dart
│ │ └── widgets
│ │ └── recent_search_tile.dart
├── main.dart
├── models
│ ├── Category.dart
│ ├── Duration.dart
│ ├── ModelProvider.dart
│ └── Recipe.dart
├── shared
│ ├── constants
│ │ ├── constants.dart
│ │ └── gaps.dart
│ ├── data
│ │ ├── authentication_repository.dart
│ │ ├── database
│ │ │ └── isar_provider.dart
│ │ ├── implementation
│ │ │ ├── cognito_authentication_repository.dart
│ │ │ ├── local_notification_repository.dart
│ │ │ ├── local_remote_recipe_repository.dart
│ │ │ ├── local_search_repository.dart
│ │ │ └── s3_storage_repository.dart
│ │ ├── model
│ │ │ ├── notification.dart
│ │ │ ├── notification.g.dart
│ │ │ ├── recipe.dart
│ │ │ ├── recipe.g.dart
│ │ │ ├── search_item.dart
│ │ │ ├── search_item.g.dart
│ │ │ ├── user.dart
│ │ │ └── user.g.dart
│ │ ├── notification_repository.dart
│ │ ├── recipe_repository.dart
│ │ ├── search_repository.dart
│ │ └── storage_repository.dart
│ ├── navigation
│ │ └── routes.dart
│ ├── utils
│ │ └── form_utils.dart
│ └── widgets
│ │ ├── async_image_loader.dart
│ │ ├── frosted_glass_container.dart
│ │ ├── recipe_card.dart
│ │ └── section_list_tile.dart
└── themes
│ ├── app_colors.dart
│ └── app_theme.dart
├── pubspec.lock
└── pubspec.yaml
/.eslintignore:
--------------------------------------------------------------------------------
1 | amplify-codegen-temp/models
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | #.vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | **/doc/api/
26 | **/ios/Flutter/.last_build_id
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .packages
31 | .pub-cache/
32 | .pub/
33 | /build/
34 |
35 | # Symbolication related
36 | app.*.symbols
37 |
38 | # Obfuscation related
39 | app.*.map.json
40 |
41 | # Android Studio will place build artifacts here
42 | /android/app/debug
43 | /android/app/profile
44 | /android/app/release
45 |
46 | #amplify-do-not-edit-begin
47 | amplify/\#current-cloud-backend
48 | amplify/.config/local-*
49 | amplify/logs
50 | amplify/mock-data
51 | amplify/mock-api-resources
52 | amplify/backend/amplify-meta.json
53 | amplify/backend/.temp
54 | build/
55 | dist/
56 | node_modules/
57 | aws-exports.js
58 | awsconfiguration.json
59 | amplifyconfiguration.json
60 | amplifyconfiguration.dart
61 | amplify-build-config.json
62 | amplify-gradle-config.json
63 | amplifytools.xcconfig
64 | .secret-*
65 | **.sample
66 | #amplify-do-not-edit-end
67 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled.
5 |
6 | version:
7 | revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
8 | channel: stable
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
17 | base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
18 | - platform: android
19 | create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
20 | base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
21 | - platform: ios
22 | create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
23 | base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
24 | - platform: linux
25 | create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
26 | base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
27 | - platform: macos
28 | create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
29 | base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
30 | - platform: web
31 | create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
32 | base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
33 | - platform: windows
34 | create_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
35 | base_revision: 12cb4eb7a009f52b347b62ade7cb4854b926af72
36 |
37 | # User provided section
38 |
39 | # List of Local paths (relative to this file) that should be
40 | # ignored by the migrate tool.
41 | #
42 | # Files that are not part of the templates will be ignored by default.
43 | unmanaged_files:
44 | - 'lib/main.dart'
45 | - 'ios/Runner.xcodeproj/project.pbxproj'
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Complete Flutter Course: Take Your Flutter Skills to the Next Level for FREE!
2 |
3 | Welcome to our Complete Flutter Course. If you've been searching for the best Flutter course online, look no further. This comprehensive guide will not only teach you the basics but will elevate your Flutter skills to the next level.
4 |
5 | ### [Watch the full course on YouTube for FREE!](https://youtu.be/2j5ONMbX7xE)
6 |
7 |
8 | This is your one-stop spot to master on:\
9 | 📁 Project Structure \
10 | 🔐 Email & Social Logins \
11 | 🔔 Push Notifications \
12 | 🛠 CRUD Operations \
13 | ☁️ AWSAmplify \
14 | ...and that's just scratching the surface! We cover many more advanced topics to give you an edge in Flutter app development.
15 |
16 |
17 | **Useful links:**
18 |
19 | [🔗 Function link: ](https://gist.github.com/salihgueler/e9be9fa6cc9ece30ba05764146850f74) \
20 | [🔗 Custom policy:](https://gist.github.com/salihgueler/2b44dd5494bdbfbd288a033af21bff9d) \
21 | [🔥 Complate source code:](https://cutt.ly/PwkRT3yZ)
22 |
23 | [☁️ AWS Amplify:](https://aws.amazon.com/amplify/) \
24 | [📄 AWS Amplify Doc:](https://docs.amplify.aws/lib/q/platform/flutter/)
25 |
26 | **👨💻 Instructor:**
27 |
28 | Muhammed Salih Guler (Sr. Dev Advocate at AWS Amplify | Flutter/Dart GDE) \
29 | Follow him on [Twitter](https://twitter.com/salihgueler) & [Linkedin](https://www.linkedin.com/in/salihgueler/)
30 |
--------------------------------------------------------------------------------
/amplify/.config/project-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "projectName": "amplifyrecipe",
3 | "version": "3.1",
4 | "frontend": "flutter",
5 | "flutter": {
6 | "config": {
7 | "ResDir": "./lib/"
8 | }
9 | },
10 | "providers": [
11 | "awscloudformation"
12 | ]
13 | }
--------------------------------------------------------------------------------
/amplify/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Amplify CLI
2 | This directory was generated by [Amplify CLI](https://docs.amplify.aws/cli).
3 |
4 | Helpful resources:
5 | - Amplify documentation: https://docs.amplify.aws
6 | - Amplify CLI documentation: https://docs.amplify.aws/cli
7 | - More details on this folder & generated files: https://docs.amplify.aws/cli/reference/files
8 | - Join Amplify's community: https://amplify.aws/community/
9 |
--------------------------------------------------------------------------------
/amplify/backend/analytics/amplifyrecipe/parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "appName": "amplifyrecipe",
3 | "roleName": "pinpointLambdaRolec51987f6",
4 | "cloudformationPolicyName": "cloudformationPolicyc51987f6",
5 | "cloudWatchPolicyName": "cloudWatchPolicyc51987f6",
6 | "pinpointPolicyName": "pinpointPolicyc51987f6",
7 | "authPolicyName": "pinpoint_amplify_c51987f6",
8 | "unauthPolicyName": "pinpoint_amplify_c51987f6",
9 | "authRoleName": {
10 | "Ref": "AuthRoleName"
11 | },
12 | "unauthRoleName": {
13 | "Ref": "UnauthRoleName"
14 | },
15 | "authRoleArn": {
16 | "Fn::GetAtt": [
17 | "AuthRole",
18 | "Arn"
19 | ]
20 | }
21 | }
--------------------------------------------------------------------------------
/amplify/backend/api/amplifyrecipe/cli-inputs.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "serviceConfiguration": {
4 | "apiName": "amplifyrecipe",
5 | "serviceName": "AppSync",
6 | "defaultAuthType": {
7 | "mode": "AMAZON_COGNITO_USER_POOLS",
8 | "cognitoUserPoolId": "authamplifyrecipe86a8efe0"
9 | }
10 | }
11 | }
--------------------------------------------------------------------------------
/amplify/backend/api/amplifyrecipe/parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "AppSyncApiName": "amplifyrecipe",
3 | "DynamoDBBillingMode": "PAY_PER_REQUEST",
4 | "DynamoDBEnableServerSideEncryption": false,
5 | "AuthCognitoUserPoolId": {
6 | "Fn::GetAtt": [
7 | "authamplifyrecipe86a8efe0",
8 | "Outputs.UserPoolId"
9 | ]
10 | }
11 | }
--------------------------------------------------------------------------------
/amplify/backend/api/amplifyrecipe/resolvers/README.md:
--------------------------------------------------------------------------------
1 | Any resolvers that you add in this directory will override the ones automatically generated by Amplify CLI and will be directly copied to the cloud.
2 | For more information, visit [https://docs.amplify.aws/cli/graphql-transformer/resolvers](https://docs.amplify.aws/cli/graphql-transformer/resolvers)
--------------------------------------------------------------------------------
/amplify/backend/api/amplifyrecipe/schema.graphql:
--------------------------------------------------------------------------------
1 | enum Category {
2 | SOUP
3 | DESSERT
4 | SALAD
5 | APPETIZER
6 | FISH
7 | MAIN_COURSE
8 | }
9 |
10 | enum Duration {
11 | MINUTE
12 | HOUR
13 | }
14 |
15 | type Recipe @model @auth(rules: [{allow: private}]) {
16 | id: ID!
17 | title: String!
18 | serve: Int!
19 | duration: Duration!
20 | category: Category!
21 | description: String!
22 | image: String!
23 | duration_unit: Int!
24 | ingredients: [String!]!
25 | }
--------------------------------------------------------------------------------
/amplify/backend/api/amplifyrecipe/stacks/CustomResources.json:
--------------------------------------------------------------------------------
1 | {
2 | "AWSTemplateFormatVersion": "2010-09-09",
3 | "Description": "An auto-generated nested stack.",
4 | "Metadata": {},
5 | "Parameters": {
6 | "AppSyncApiId": {
7 | "Type": "String",
8 | "Description": "The id of the AppSync API associated with this project."
9 | },
10 | "AppSyncApiName": {
11 | "Type": "String",
12 | "Description": "The name of the AppSync API",
13 | "Default": "AppSyncSimpleTransform"
14 | },
15 | "env": {
16 | "Type": "String",
17 | "Description": "The environment name. e.g. Dev, Test, or Production",
18 | "Default": "NONE"
19 | },
20 | "S3DeploymentBucket": {
21 | "Type": "String",
22 | "Description": "The S3 bucket containing all deployment assets for the project."
23 | },
24 | "S3DeploymentRootKey": {
25 | "Type": "String",
26 | "Description": "An S3 key relative to the S3DeploymentBucket that points to the root\nof the deployment directory."
27 | }
28 | },
29 | "Resources": {
30 | "EmptyResource": {
31 | "Type": "Custom::EmptyResource",
32 | "Condition": "AlwaysFalse"
33 | }
34 | },
35 | "Conditions": {
36 | "HasEnvironmentParameter": {
37 | "Fn::Not": [
38 | {
39 | "Fn::Equals": [
40 | {
41 | "Ref": "env"
42 | },
43 | "NONE"
44 | ]
45 | }
46 | ]
47 | },
48 | "AlwaysFalse": {
49 | "Fn::Equals": ["true", "false"]
50 | }
51 | },
52 | "Outputs": {
53 | "EmptyOutput": {
54 | "Description": "An empty output. You may delete this if you have at least one resource above.",
55 | "Value": ""
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/amplify/backend/api/amplifyrecipe/transform.conf.json:
--------------------------------------------------------------------------------
1 | {
2 | "Version": 5,
3 | "ElasticsearchWarning": true
4 | }
--------------------------------------------------------------------------------
/amplify/backend/auth/amplifyrecipe86a8efe0/cli-inputs.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1",
3 | "cognitoConfig": {
4 | "identityPoolName": "amplifyrecipe86a8efe0_identitypool_86a8efe0",
5 | "allowUnauthenticatedIdentities": true,
6 | "resourceNameTruncated": "amplif86a8efe0",
7 | "userPoolName": "amplifyrecipe86a8efe0_userpool_86a8efe0",
8 | "autoVerifiedAttributes": [
9 | "email"
10 | ],
11 | "mfaConfiguration": "OFF",
12 | "mfaTypes": [
13 | "SMS Text Message"
14 | ],
15 | "smsAuthenticationMessage": "Your authentication code is {####}",
16 | "smsVerificationMessage": "Your verification code is {####}",
17 | "emailVerificationSubject": "Your verification code",
18 | "emailVerificationMessage": "Your verification code is {####}",
19 | "defaultPasswordPolicy": false,
20 | "passwordPolicyMinLength": 8,
21 | "passwordPolicyCharacters": [],
22 | "requiredAttributes": [
23 | "email"
24 | ],
25 | "aliasAttributes": [],
26 | "userpoolClientGenerateSecret": false,
27 | "userpoolClientRefreshTokenValidity": 30,
28 | "userpoolClientWriteAttributes": [
29 | "email"
30 | ],
31 | "userpoolClientReadAttributes": [
32 | "email"
33 | ],
34 | "userpoolClientLambdaRole": "amplif86a8efe0_userpoolclient_lambda_role",
35 | "userpoolClientSetAttributes": false,
36 | "sharedId": "86a8efe0",
37 | "resourceName": "amplifyrecipe86a8efe0",
38 | "authSelections": "identityPoolAndUserPool",
39 | "useDefault": "defaultSocial",
40 | "usernameAttributes": [
41 | "email"
42 | ],
43 | "userPoolGroupList": [],
44 | "serviceName": "Cognito",
45 | "usernameCaseSensitive": false,
46 | "useEnabledMfas": true,
47 | "authRoleArn": {
48 | "Fn::GetAtt": [
49 | "AuthRole",
50 | "Arn"
51 | ]
52 | },
53 | "unauthRoleArn": {
54 | "Fn::GetAtt": [
55 | "UnauthRole",
56 | "Arn"
57 | ]
58 | },
59 | "breakCircularDependency": true,
60 | "dependsOn": [],
61 | "hostedUI": true,
62 | "hostedUIDomainName": "amplifyrecipe752a9874-752a9874",
63 | "authProvidersUserPool": [
64 | "Facebook",
65 | "Google"
66 | ],
67 | "hostedUIProviderMeta": "[{\"ProviderName\":\"Facebook\",\"authorize_scopes\":\"email,public_profile\",\"AttributeMapping\":{\"email\":\"email\",\"username\":\"id\"}},{\"ProviderName\":\"Google\",\"authorize_scopes\":\"openid email profile\",\"AttributeMapping\":{\"email\":\"email\",\"username\":\"sub\"}}]",
68 | "authProviders": [],
69 | "oAuthMetadata": "{\"AllowedOAuthFlows\":[\"code\"],\"AllowedOAuthScopes\":[\"phone\",\"email\",\"openid\",\"profile\",\"aws.cognito.signin.user.admin\"],\"CallbackURLs\":[\"amplifyrecipe://\"],\"LogoutURLs\":[\"amplifyrecipe://\"]}"
70 | }
71 | }
--------------------------------------------------------------------------------
/amplify/backend/backend-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "analytics": {
3 | "amplifyrecipe": {
4 | "providerPlugin": "awscloudformation",
5 | "service": "Pinpoint"
6 | }
7 | },
8 | "api": {
9 | "amplifyrecipe": {
10 | "dependsOn": [
11 | {
12 | "attributes": [
13 | "UserPoolId"
14 | ],
15 | "category": "auth",
16 | "resourceName": "amplifyrecipe86a8efe0"
17 | }
18 | ],
19 | "output": {
20 | "authConfig": {
21 | "additionalAuthenticationProviders": [],
22 | "defaultAuthentication": {
23 | "authenticationType": "AMAZON_COGNITO_USER_POOLS",
24 | "userPoolConfig": {
25 | "userPoolId": "authamplifyrecipe86a8efe0"
26 | }
27 | }
28 | }
29 | },
30 | "providerPlugin": "awscloudformation",
31 | "service": "AppSync"
32 | }
33 | },
34 | "auth": {
35 | "amplifyrecipe86a8efe0": {
36 | "customAuth": false,
37 | "dependsOn": [],
38 | "frontendAuthConfig": {
39 | "mfaConfiguration": "OFF",
40 | "mfaTypes": [
41 | "SMS"
42 | ],
43 | "passwordProtectionSettings": {
44 | "passwordPolicyCharacters": [],
45 | "passwordPolicyMinLength": 8
46 | },
47 | "signupAttributes": [
48 | "EMAIL"
49 | ],
50 | "socialProviders": [
51 | "FACEBOOK",
52 | "GOOGLE"
53 | ],
54 | "usernameAttributes": [
55 | "EMAIL"
56 | ],
57 | "verificationMechanisms": [
58 | "EMAIL"
59 | ]
60 | },
61 | "providerPlugin": "awscloudformation",
62 | "service": "Cognito"
63 | }
64 | },
65 | "function": {
66 | "amplifyrecipefb7252f0": {
67 | "build": true,
68 | "dependsOn": [
69 | {
70 | "attributes": [
71 | "GraphQLAPIIdOutput",
72 | "GraphQLAPIEndpointOutput"
73 | ],
74 | "category": "api",
75 | "resourceName": "amplifyrecipe"
76 | }
77 | ],
78 | "providerPlugin": "awscloudformation",
79 | "service": "Lambda"
80 | }
81 | },
82 | "notifications": {
83 | "amplifyrecipe": {
84 | "channels": [
85 | "APNS",
86 | "FCM"
87 | ],
88 | "service": "Pinpoint"
89 | }
90 | },
91 | "parameters": {
92 | "AMPLIFY_analytics_Pinpoint_Id": {
93 | "usedBy": [
94 | {
95 | "category": "analytics",
96 | "resourceName": "Pinpoint"
97 | }
98 | ]
99 | },
100 | "AMPLIFY_analytics_Pinpoint_Name": {
101 | "usedBy": [
102 | {
103 | "category": "analytics",
104 | "resourceName": "Pinpoint"
105 | }
106 | ]
107 | },
108 | "AMPLIFY_analytics_Pinpoint_Region": {
109 | "usedBy": [
110 | {
111 | "category": "analytics",
112 | "resourceName": "Pinpoint"
113 | }
114 | ]
115 | },
116 | "AMPLIFY_function_amplifyrecipefb7252f0_deploymentBucketName": {
117 | "usedBy": [
118 | {
119 | "category": "function",
120 | "resourceName": "amplifyrecipefb7252f0"
121 | }
122 | ]
123 | },
124 | "AMPLIFY_function_amplifyrecipefb7252f0_s3Key": {
125 | "usedBy": [
126 | {
127 | "category": "function",
128 | "resourceName": "amplifyrecipefb7252f0"
129 | }
130 | ]
131 | },
132 | "AMPLIFY_notifications_Pinpoint_Id": {
133 | "usedBy": [
134 | {
135 | "category": "notifications",
136 | "resourceName": "Pinpoint"
137 | }
138 | ]
139 | },
140 | "AMPLIFY_notifications_Pinpoint_Name": {
141 | "usedBy": [
142 | {
143 | "category": "notifications",
144 | "resourceName": "Pinpoint"
145 | }
146 | ]
147 | },
148 | "AMPLIFY_notifications_Pinpoint_Region": {
149 | "usedBy": [
150 | {
151 | "category": "notifications",
152 | "resourceName": "Pinpoint"
153 | }
154 | ]
155 | },
156 | "AMPLIFY_storage_Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev_arn": {
157 | "usedBy": [
158 | {
159 | "category": "storage",
160 | "resourceName": "Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev"
161 | }
162 | ]
163 | },
164 | "AMPLIFY_storage_Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev_partitionKeyName": {
165 | "usedBy": [
166 | {
167 | "category": "storage",
168 | "resourceName": "Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev"
169 | }
170 | ]
171 | },
172 | "AMPLIFY_storage_Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev_partitionKeyType": {
173 | "usedBy": [
174 | {
175 | "category": "storage",
176 | "resourceName": "Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev"
177 | }
178 | ]
179 | },
180 | "AMPLIFY_storage_Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev_region": {
181 | "usedBy": [
182 | {
183 | "category": "storage",
184 | "resourceName": "Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev"
185 | }
186 | ]
187 | },
188 | "AMPLIFY_storage_Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev_streamArn": {
189 | "usedBy": [
190 | {
191 | "category": "storage",
192 | "resourceName": "Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev"
193 | }
194 | ]
195 | },
196 | "AMPLIFY_storage_Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev_tableName": {
197 | "usedBy": [
198 | {
199 | "category": "storage",
200 | "resourceName": "Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev"
201 | }
202 | ]
203 | }
204 | },
205 | "storage": {
206 | "Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev": {
207 | "dependsOn": [],
208 | "providerPlugin": "awscloudformation",
209 | "service": "DynamoDB",
210 | "serviceType": "imported"
211 | },
212 | "s3b350e034": {
213 | "dependsOn": [],
214 | "providerPlugin": "awscloudformation",
215 | "service": "S3"
216 | }
217 | }
218 | }
--------------------------------------------------------------------------------
/amplify/backend/function/amplifyrecipefb7252f0/amplify.state:
--------------------------------------------------------------------------------
1 | {
2 | "pluginId": "amplify-nodejs-function-runtime-provider",
3 | "functionRuntime": "nodejs",
4 | "useLegacyBuild": true,
5 | "defaultEditorFile": "src/index.js"
6 | }
--------------------------------------------------------------------------------
/amplify/backend/function/amplifyrecipefb7252f0/custom-policies.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Effect": "Allow",
4 | "Action": [
5 | "mobiletargeting:GetCampaigns",
6 | "mobiletargeting:CreateCampaign",
7 | "mobiletargeting:SendMessages"
8 | ],
9 | "Resource": [
10 | "arn:aws:mobiletargeting:*:345161578575:apps/*"
11 | ]
12 | }
13 | ]
--------------------------------------------------------------------------------
/amplify/backend/function/amplifyrecipefb7252f0/function-parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "lambdaLayers": []
3 | }
--------------------------------------------------------------------------------
/amplify/backend/function/amplifyrecipefb7252f0/src/event.json:
--------------------------------------------------------------------------------
1 | {
2 | "key1": "value1",
3 | "key2": "value2",
4 | "key3": "value3"
5 | }
6 |
--------------------------------------------------------------------------------
/amplify/backend/function/amplifyrecipefb7252f0/src/index.js:
--------------------------------------------------------------------------------
1 | const { PinpointClient, CreateCampaignCommand } = require("@aws-sdk/client-pinpoint");
2 |
3 | const pinpointClient = new PinpointClient({ region: "eu-central-1" });
4 |
5 | /*
6 | * @type { import('@types/aws-lambda').APIGatewayProxyHandler }
7 | */
8 |
9 | exports.handler = async (event) => {
10 | console.log(`EVENT: ${JSON.stringify(event)}`);
11 | for (const record of event.Records) {
12 | if (record.eventName === 'INSERT') {
13 | try {
14 | let recipeTitle = record.dynamodb.NewImage.title.S;
15 | let recipeDescription = record.dynamodb.NewImage.description.S;
16 | let recipeId = record.dynamodb.NewImage.id.S;
17 | const createCampaingCommand = new CreateCampaignCommand({
18 | ApplicationId: "357c824c055f4213a6632f9486666550",
19 | WriteCampaignRequest: {
20 | AdditionalTreatments: [],
21 | Name: "Campaign: " + recipeId.substring(0, 50) + "!",
22 | SegmentId: "afced6a65fdd467d8e11e6848331e1ee",
23 | SegmentVersion: 1,
24 | HoldoutPercent: 0,
25 | TemplateConfiguration: {},
26 | Limits: {
27 | MessagesPerSecond: 20000,
28 | MaximumDuration: 10800,
29 | Daily: 0,
30 | Total: 0,
31 | },
32 | Schedule: {
33 | IsLocalTime: false,
34 | QuietTime: {},
35 | StartTime: "IMMEDIATE",
36 | Timezone: "UTC",
37 | },
38 | MessageConfiguration: {
39 | DefaultMessage: {
40 | Action: "OPEN_APP",
41 | Title: "New Recipe!",
42 | Body: "Check out: " + recipeTitle + "!",
43 | JsonBody: JSON.stringify({
44 | recipeId: recipeId,
45 | recipeTitle: recipeTitle,
46 | recipeDescription: recipeDescription,
47 | }),
48 | },
49 | APNSMessage: {
50 | Action: "OPEN_APP",
51 | Title: "New Recipe!",
52 | Body: "Check out: " + recipeTitle + "!",
53 | JsonBody: JSON.stringify({
54 | recipeId: recipeId,
55 | recipeTitle: recipeTitle,
56 | recipeDescription: recipeDescription,
57 | }),
58 | },
59 | GCMMessage: {
60 | Action: "OPEN_APP",
61 | Title: "New Recipe!",
62 | Body: "Check out: " + recipeTitle + "!",
63 | JsonBody: JSON.stringify({
64 | recipeId: recipeId,
65 | recipeTitle: recipeTitle,
66 | recipeDescription: recipeDescription,
67 | }),
68 | },
69 | BaiduMessage: {
70 | Action: "OPEN_APP",
71 | Title: "New Recipe!",
72 | Body: "Check out: " + recipeTitle + "!",
73 | JsonBody: JSON.stringify({
74 | recipeId: recipeId,
75 | recipeTitle: recipeTitle,
76 | recipeDescription: recipeDescription,
77 | }),
78 | },
79 | ADMMessage: {
80 | Action: "OPEN_APP",
81 | Title: "New Recipe!",
82 | Body: "Check out: " + recipeTitle + "!",
83 | JsonBody: JSON.stringify({
84 | recipeId: recipeId,
85 | recipeTitle: recipeTitle,
86 | recipeDescription: recipeDescription,
87 | }),
88 | }
89 | }
90 | },
91 | });
92 | const data = await pinpointClient.send(createCampaingCommand);
93 | console.log('Send Message Respond: %j', data);
94 | } catch (error) {
95 | console.log('Pinpoint error: %j', error);
96 | } finally {
97 | console.log('Pinpoint finally finished.');
98 | }
99 | }
100 | }
101 | };
--------------------------------------------------------------------------------
/amplify/backend/function/amplifyrecipefb7252f0/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "amplifyrecipefb7252f0",
3 | "version": "2.0.0",
4 | "description": "Lambda function generated by Amplify",
5 | "main": "index.js",
6 | "license": "Apache-2.0",
7 | "devDependencies": {
8 | "@types/aws-lambda": "^8.10.92"
9 | },
10 | "dependencies": {
11 | "@aws-sdk/client-pinpoint": "^3.391.0"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/amplify/backend/storage/Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev/parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "resourceName": "Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev",
3 | "serviceType": "imported"
4 | }
--------------------------------------------------------------------------------
/amplify/backend/storage/s3b350e034/cli-inputs.json:
--------------------------------------------------------------------------------
1 | {
2 | "resourceName": "s3b350e034",
3 | "policyUUID": "b350e034",
4 | "bucketName": "amplifyrecipe108a68fcdfe84b1ebda1a9542810e694",
5 | "storageAccess": "auth",
6 | "guestAccess": [],
7 | "authAccess": [
8 | "CREATE_AND_UPDATE",
9 | "READ",
10 | "DELETE"
11 | ],
12 | "triggerFunction": "NONE",
13 | "groupAccess": {}
14 | }
--------------------------------------------------------------------------------
/amplify/backend/tags.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Key": "user:Stack",
4 | "Value": "{project-env}"
5 | },
6 | {
7 | "Key": "user:Application",
8 | "Value": "{project-name}"
9 | }
10 | ]
--------------------------------------------------------------------------------
/amplify/backend/types/amplify-dependent-resources-ref.d.ts:
--------------------------------------------------------------------------------
1 | export type AmplifyDependentResourcesAttributes = {
2 | "analytics": {
3 | "amplifyrecipe": {
4 | "Id": "string",
5 | "Region": "string",
6 | "appName": "string"
7 | }
8 | },
9 | "api": {
10 | "amplifyrecipe": {
11 | "GraphQLAPIEndpointOutput": "string",
12 | "GraphQLAPIIdOutput": "string"
13 | }
14 | },
15 | "auth": {
16 | "amplifyrecipe86a8efe0": {
17 | "AppClientID": "string",
18 | "AppClientIDWeb": "string",
19 | "HostedUIDomain": "string",
20 | "IdentityPoolId": "string",
21 | "IdentityPoolName": "string",
22 | "OAuthMetadata": "string",
23 | "UserPoolArn": "string",
24 | "UserPoolId": "string",
25 | "UserPoolName": "string"
26 | }
27 | },
28 | "function": {
29 | "amplifyrecipefb7252f0": {
30 | "Arn": "string",
31 | "LambdaExecutionRole": "string",
32 | "LambdaExecutionRoleArn": "string",
33 | "Name": "string",
34 | "Region": "string"
35 | }
36 | },
37 | "storage": {
38 | "s3b350e034": {
39 | "BucketName": "string",
40 | "Region": "string"
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/amplify/cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "features": {
3 | "graphqltransformer": {
4 | "addmissingownerfields": true,
5 | "improvepluralization": false,
6 | "validatetypenamereservedwords": true,
7 | "useexperimentalpipelinedtransformer": true,
8 | "enableiterativegsiupdates": true,
9 | "secondarykeyasgsi": true,
10 | "skipoverridemutationinputtypes": true,
11 | "transformerversion": 2,
12 | "suppressschemamigrationprompt": true,
13 | "securityenhancementnotification": false,
14 | "showfieldauthnotification": false,
15 | "usesubusernamefordefaultidentityclaim": true,
16 | "usefieldnameforprimarykeyconnectionfield": false,
17 | "enableautoindexquerynames": true,
18 | "respectprimarykeyattributesonconnectionfield": true,
19 | "shoulddeepmergedirectiveconfigdefaults": false,
20 | "populateownerfieldforstaticgroupauth": true
21 | },
22 | "frontend-ios": {
23 | "enablexcodeintegration": true
24 | },
25 | "auth": {
26 | "enablecaseinsensitivity": true,
27 | "useinclusiveterminology": true,
28 | "breakcirculardependency": true,
29 | "forcealiasattributes": false,
30 | "useenabledmfas": true
31 | },
32 | "codegen": {
33 | "useappsyncmodelgenplugin": true,
34 | "usedocsgeneratorplugin": true,
35 | "usetypesgeneratorplugin": true,
36 | "cleangeneratedmodelsdirectory": true,
37 | "retaincasestyle": true,
38 | "addtimestampfields": true,
39 | "handlelistnullabilitytransparently": true,
40 | "emitauthprovider": true,
41 | "generateindexrules": true,
42 | "enabledartnullsafety": true,
43 | "generatemodelsforlazyloadandcustomselectionset": false
44 | },
45 | "appsync": {
46 | "generategraphqlpermissions": true
47 | },
48 | "latestregionsupport": {
49 | "pinpoint": 1,
50 | "translate": 1,
51 | "transcribe": 1,
52 | "rekognition": 1,
53 | "textract": 1,
54 | "comprehend": 1
55 | },
56 | "project": {
57 | "overrides": true
58 | }
59 | },
60 | "debug": {
61 | "shareProjectConfig": false
62 | }
63 | }
--------------------------------------------------------------------------------
/amplify/hooks/README.md:
--------------------------------------------------------------------------------
1 | # Command Hooks
2 |
3 | Command hooks can be used to run custom scripts upon Amplify CLI lifecycle events like pre-push, post-add-function, etc.
4 |
5 | To get started, add your script files based on the expected naming convention in this directory.
6 |
7 | Learn more about the script file naming convention, hook parameters, third party dependencies, and advanced configurations at https://docs.amplify.aws/cli/usage/command-hooks
8 |
--------------------------------------------------------------------------------
/amplify/team-provider-info.json:
--------------------------------------------------------------------------------
1 | {
2 | "dev": {
3 | "awscloudformation": {
4 | "AuthRoleName": "amplify-amplifyrecipe-dev-145708-authRole",
5 | "UnauthRoleArn": "arn:aws:iam::345161578575:role/amplify-amplifyrecipe-dev-145708-unauthRole",
6 | "AuthRoleArn": "arn:aws:iam::345161578575:role/amplify-amplifyrecipe-dev-145708-authRole",
7 | "Region": "eu-central-1",
8 | "DeploymentBucketName": "amplify-amplifyrecipe-dev-145708-deployment",
9 | "UnauthRoleName": "amplify-amplifyrecipe-dev-145708-unauthRole",
10 | "StackName": "amplify-amplifyrecipe-dev-145708",
11 | "StackId": "arn:aws:cloudformation:eu-central-1:345161578575:stack/amplify-amplifyrecipe-dev-145708/34142f90-383e-11ee-b9da-069104f8e03c",
12 | "AmplifyAppId": "d1ni8t9fi3je8z"
13 | },
14 | "categories": {
15 | "auth": {
16 | "amplifyrecipe86a8efe0": {}
17 | },
18 | "api": {
19 | "amplifyrecipe": {}
20 | },
21 | "storage": {
22 | "s3b350e034": {},
23 | "Recipe3dqfxi2ts5g6lnj72cqbgk72dqdev": {
24 | "tableName": "Recipe-3dqfxi2ts5g6lnj72cqbgk72dq-dev",
25 | "region": "eu-central-1",
26 | "arn": "arn:aws:dynamodb:eu-central-1:345161578575:table/Recipe-3dqfxi2ts5g6lnj72cqbgk72dq-dev",
27 | "streamArn": "arn:aws:dynamodb:eu-central-1:345161578575:table/Recipe-3dqfxi2ts5g6lnj72cqbgk72dq-dev/stream/2023-08-14T14:17:44.962",
28 | "partitionKeyName": "id",
29 | "partitionKeyType": "S"
30 | }
31 | },
32 | "analytics": {
33 | "Pinpoint": {
34 | "Name": "amplifyrecipe-dev",
35 | "Id": "357c824c055f4213a6632f9486666550",
36 | "Region": "eu-central-1"
37 | },
38 | "amplifyrecipe": {}
39 | },
40 | "notifications": {
41 | "Pinpoint": {
42 | "Name": "amplifyrecipe-dev",
43 | "Id": "357c824c055f4213a6632f9486666550",
44 | "Region": "eu-central-1"
45 | }
46 | },
47 | "function": {
48 | "amplifyrecipefb7252f0": {
49 | "deploymentBucketName": "amplify-amplifyrecipe-dev-145708-deployment",
50 | "s3Key": "amplify-builds/amplifyrecipefb7252f0-66684f49457461496359-build.zip"
51 | }
52 | }
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | # This file configures the analyzer, which statically analyzes Dart code to
2 | # check for errors, warnings, and lints.
3 | #
4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled
5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
6 | # invoked from the command line by running `flutter analyze`.
7 |
8 | # The following line activates a set of recommended lints for Flutter apps,
9 | # packages, and plugins designed to encourage good coding practices.
10 | include: package:flutter_lints/flutter.yaml
11 |
12 | linter:
13 | # The lint rules applied to this project can be customized in the
14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml`
15 | # included above or to enable additional rules. A list of all available lints
16 | # and their documentation is published at
17 | # https://dart-lang.github.io/linter/lints/index.html.
18 | #
19 | # Instead of disabling a lint rule for the entire project in the
20 | # section below, it can also be suppressed for a single line of code
21 | # or a specific dart file by using the `// ignore: name_of_lint` and
22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file
23 | # producing the lint.
24 | rules:
25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule
26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
27 |
28 | # Additional information about this file can be found at
29 | # https://dart.dev/guides/language/analysis-options
30 |
31 | analyzer:
32 | exclude:
33 | - lib/models
34 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 | apply plugin: 'com.android.application'
24 | apply plugin: 'kotlin-android'
25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
26 |
27 | android {
28 | compileSdkVersion flutter.compileSdkVersion
29 | ndkVersion flutter.ndkVersion
30 |
31 | compileOptions {
32 | sourceCompatibility JavaVersion.VERSION_1_8
33 | targetCompatibility JavaVersion.VERSION_1_8
34 | }
35 |
36 | kotlinOptions {
37 | jvmTarget = '1.8'
38 | }
39 |
40 | sourceSets {
41 | main.java.srcDirs += 'src/main/kotlin'
42 | }
43 |
44 | defaultConfig {
45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
46 | applicationId "com.example.amplify_recipe"
47 | // You can update the following values to match your application needs.
48 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
49 | minSdkVersion 24
50 | targetSdkVersion flutter.targetSdkVersion
51 | versionCode flutterVersionCode.toInteger()
52 | versionName flutterVersionName
53 | }
54 |
55 | buildTypes {
56 | release {
57 | // TODO: Add your own signing config for the release build.
58 | // Signing with the debug keys for now, so `flutter run --release` works.
59 | signingConfig signingConfigs.debug
60 | }
61 | }
62 | }
63 |
64 | flutter {
65 | source '../..'
66 | }
67 |
68 | dependencies {
69 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
70 | }
71 |
72 | apply plugin: 'com.google.gms.google-services'
--------------------------------------------------------------------------------
/android/app/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_info": {
3 | "project_number": "1035948754199",
4 | "project_id": "amplifyrecipe",
5 | "storage_bucket": "amplifyrecipe.appspot.com"
6 | },
7 | "client": [
8 | {
9 | "client_info": {
10 | "mobilesdk_app_id": "1:1035948754199:android:46a58667cbed85e6606352",
11 | "android_client_info": {
12 | "package_name": "com.example.amplify_recipe"
13 | }
14 | },
15 | "oauth_client": [
16 | {
17 | "client_id": "1035948754199-ur7h7cfojm47lfpjeer2nn66vji9gi1t.apps.googleusercontent.com",
18 | "client_type": 1,
19 | "android_info": {
20 | "package_name": "com.example.amplify_recipe",
21 | "certificate_hash": "647e2c23cb7c558f1f95b00276ac206c23bc1bef"
22 | }
23 | },
24 | {
25 | "client_id": "1035948754199-99c4efv7utj1t2fmsd7dkeuhb11dsbed.apps.googleusercontent.com",
26 | "client_type": 3
27 | }
28 | ],
29 | "api_key": [
30 | {
31 | "current_key": "AIzaSyCTwkU7yRa6KHgqReIpYTWF3_MoyZbtwI8"
32 | }
33 | ],
34 | "services": {
35 | "appinvite_service": {
36 | "other_platform_oauth_client": [
37 | {
38 | "client_id": "1035948754199-99c4efv7utj1t2fmsd7dkeuhb11dsbed.apps.googleusercontent.com",
39 | "client_type": 3
40 | }
41 | ]
42 | }
43 | }
44 | }
45 | ],
46 | "configuration_version": "1"
47 | }
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
7 |
8 |
9 |
13 |
21 |
25 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
42 |
45 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/amplify_recipe/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.amplify_recipe
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.7.10'
3 | repositories {
4 | google()
5 | mavenCentral()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:7.4.2'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | classpath 'com.google.gms:google-services:4.3.15'
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | google()
18 | mavenCentral()
19 | }
20 | }
21 |
22 | rootProject.buildDir = '../build'
23 | subprojects {
24 | project.buildDir = "${rootProject.buildDir}/${project.name}"
25 | }
26 | subprojects {
27 | project.evaluationDependsOn(':app')
28 | }
29 |
30 | tasks.register("clean", Delete) {
31 | delete rootProject.buildDir
32 | }
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/assets/icons/Add Friends.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icons/Apple.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/assets/icons/Arrow.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/At.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icons/Back.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/Bookmark.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/Calendar.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/Camera.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/Checklist.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/Collaboration.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icons/Comment.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icons/Delete.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/Eyes Close.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/Eyes Open.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/Faceboook.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/assets/icons/Google.svg:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/assets/icons/Help.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/Home.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icons/Kebab.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/Language.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/Logout.svg:
--------------------------------------------------------------------------------
1 |
13 |
--------------------------------------------------------------------------------
/assets/icons/Moon.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/Notification.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/Outlined Bookmark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icons/Pencil.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icons/Plus in Box.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/assets/icons/Plus with container.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/assets/icons/Plus.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icons/Profile.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/assets/icons/Remove.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/Search.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/Trash.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/icons/clock.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/assets/images/onboard_image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/assets/images/onboard_image.png
--------------------------------------------------------------------------------
/binary/macos/librealm_dart.dylib:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/binary/macos/librealm_dart.dylib
--------------------------------------------------------------------------------
/binary/macos/realm_version.txt:
--------------------------------------------------------------------------------
1 | 1.0.3
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 11.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '11.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | # post_install do |installer|
38 | # installer.pods_project.targets.each do |target|
39 | # flutter_additional_ios_build_settings(target)
40 | # end
41 | # end
42 |
43 | post_install do |installer|
44 | installer.generated_projects.each do |project|
45 | project.targets.each do |target|
46 | target.build_configurations.each do |config|
47 | config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
48 | end
49 | end
50 | end
51 | installer.pods_project.targets.each do |target|
52 | flutter_additional_ios_build_settings(target)
53 | end
54 | end
55 |
--------------------------------------------------------------------------------
/ios/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - amplify_auth_cognito (0.0.1):
3 | - Flutter
4 | - FlutterMacOS
5 | - amplify_push_notifications (0.0.1):
6 | - AmplifyUtilsNotifications
7 | - Flutter
8 | - amplify_secure_storage (0.0.1):
9 | - Flutter
10 | - AmplifyUtilsNotifications (1.1.0)
11 | - connectivity_plus (0.0.1):
12 | - Flutter
13 | - ReachabilitySwift
14 | - device_info_plus (0.0.1):
15 | - Flutter
16 | - Flutter (1.0.0)
17 | - image_picker_ios (0.0.1):
18 | - Flutter
19 | - isar_flutter_libs (1.0.0):
20 | - Flutter
21 | - package_info_plus (0.4.5):
22 | - Flutter
23 | - path_provider_foundation (0.0.1):
24 | - Flutter
25 | - FlutterMacOS
26 | - ReachabilitySwift (5.0.0)
27 | - shared_preferences_foundation (0.0.1):
28 | - Flutter
29 | - FlutterMacOS
30 |
31 | DEPENDENCIES:
32 | - amplify_auth_cognito (from `.symlinks/plugins/amplify_auth_cognito/darwin`)
33 | - amplify_push_notifications (from `.symlinks/plugins/amplify_push_notifications/ios`)
34 | - amplify_secure_storage (from `.symlinks/plugins/amplify_secure_storage/ios`)
35 | - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
36 | - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
37 | - Flutter (from `Flutter`)
38 | - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
39 | - isar_flutter_libs (from `.symlinks/plugins/isar_flutter_libs/ios`)
40 | - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
41 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
42 | - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
43 |
44 | SPEC REPOS:
45 | trunk:
46 | - AmplifyUtilsNotifications
47 | - ReachabilitySwift
48 |
49 | EXTERNAL SOURCES:
50 | amplify_auth_cognito:
51 | :path: ".symlinks/plugins/amplify_auth_cognito/darwin"
52 | amplify_push_notifications:
53 | :path: ".symlinks/plugins/amplify_push_notifications/ios"
54 | amplify_secure_storage:
55 | :path: ".symlinks/plugins/amplify_secure_storage/ios"
56 | connectivity_plus:
57 | :path: ".symlinks/plugins/connectivity_plus/ios"
58 | device_info_plus:
59 | :path: ".symlinks/plugins/device_info_plus/ios"
60 | Flutter:
61 | :path: Flutter
62 | image_picker_ios:
63 | :path: ".symlinks/plugins/image_picker_ios/ios"
64 | isar_flutter_libs:
65 | :path: ".symlinks/plugins/isar_flutter_libs/ios"
66 | package_info_plus:
67 | :path: ".symlinks/plugins/package_info_plus/ios"
68 | path_provider_foundation:
69 | :path: ".symlinks/plugins/path_provider_foundation/darwin"
70 | shared_preferences_foundation:
71 | :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
72 |
73 | SPEC CHECKSUMS:
74 | amplify_auth_cognito: 76b5a54f05f66f69966b468b8121a0dd33a32c70
75 | amplify_push_notifications: 694e708ce7b2a9f39ff6cd2d4de69005040011ca
76 | amplify_secure_storage: 501cbdac671a050090d48dfcc002d5637d3f4a55
77 | AmplifyUtilsNotifications: 56221a17675c052b82d06981797aae8e0735820e
78 | connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a
79 | device_info_plus: 7545d84d8d1b896cb16a4ff98c19f07ec4b298ea
80 | Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
81 | image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5
82 | isar_flutter_libs: b69f437aeab9c521821c3f376198c4371fa21073
83 | package_info_plus: fd030dabf36271f146f1f3beacd48f564b0f17f7
84 | path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
85 | ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
86 | shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
87 |
88 | PODFILE CHECKSUM: fa61b0820d8a526e68a48f3d621f636f34ba4f43
89 |
90 | COCOAPODS: 1.12.1
91 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abuanwar072/Flutter-Course-Amplify-Recipe-App/d3925995bb4b069d9ba2de8c109a6cf1407f5778/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | 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.
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CADisableMinimumFrameDurationOnPhone
6 |
7 | CFBundleDevelopmentRegion
8 | $(DEVELOPMENT_LANGUAGE)
9 | CFBundleDisplayName
10 | Amplify Recipe
11 | CFBundleExecutable
12 | $(EXECUTABLE_NAME)
13 | CFBundleIdentifier
14 | $(PRODUCT_BUNDLE_IDENTIFIER)
15 | CFBundleInfoDictionaryVersion
16 | 6.0
17 | CFBundleName
18 | amplify_recipe
19 | CFBundlePackageType
20 | APPL
21 | CFBundleShortVersionString
22 | $(FLUTTER_BUILD_NAME)
23 | CFBundleSignature
24 | ????
25 | CFBundleVersion
26 | $(FLUTTER_BUILD_NUMBER)
27 | LSRequiresIPhoneOS
28 |
29 | NSCameraUsageDescription
30 | This app requires access to the camera.
31 | NSMicrophoneUsageDescription
32 | This app requires access to the microphone.
33 | NSPhotoLibraryUsageDescription
34 | This app requires access to the photo library.
35 | UIApplicationSupportsIndirectInputEvents
36 |
37 | UIBackgroundModes
38 |
39 | fetch
40 | remote-notification
41 |
42 | UILaunchStoryboardName
43 | LaunchScreen
44 | UIMainStoryboardFile
45 | Main
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UISupportedInterfaceOrientations~ipad
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationPortraitUpsideDown
56 | UIInterfaceOrientationLandscapeLeft
57 | UIInterfaceOrientationLandscapeRight
58 |
59 | UIViewControllerBasedStatusBarAppearance
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/ios/Runner/Runner.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | aps-environment
6 | development
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/RunnerDebug.entitlements:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | aps-environment
6 | development
7 |
8 |
9 |
--------------------------------------------------------------------------------
/lib/features/authentication/screens/forgot_password_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/features/authentication/widgets/forgot_password_form.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import '../../../shared/constants/constants.dart';
5 | import '../../../shared/constants/gaps.dart';
6 | import '../../../themes/app_colors.dart';
7 |
8 | class ForgotPasswordScreen extends StatelessWidget {
9 | const ForgotPasswordScreen({super.key});
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return Scaffold(
14 | appBar: AppBar(
15 | title: const Text('Forgot Password'),
16 | ),
17 | body: SafeArea(
18 | child: SingleChildScrollView(
19 | padding: const EdgeInsets.all(defaultPadding),
20 | child: Column(
21 | crossAxisAlignment: CrossAxisAlignment.start,
22 | children: [
23 | Text(
24 | 'Forgot Password',
25 | style: Theme.of(context).textTheme.headlineSmall!.copyWith(
26 | color: AppColors.text,
27 | ),
28 | ),
29 | gapH16,
30 | const Text(
31 | 'Enter your email address and we will send you a reset instructions.',
32 | ),
33 | gapH24,
34 | const ForgotPassForm(),
35 | ],
36 | ),
37 | ),
38 | ),
39 | );
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib/features/authentication/screens/onboarding_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 |
4 | import '../widgets/onboard_content.dart';
5 |
6 | class OnboardingScreen extends StatelessWidget {
7 | const OnboardingScreen({super.key});
8 |
9 | @override
10 | Widget build(BuildContext context) {
11 | return Scaffold(
12 | extendBodyBehindAppBar: true,
13 | appBar: AppBar(
14 | systemOverlayStyle: SystemUiOverlayStyle.light,
15 | backgroundColor: Colors.transparent,
16 | ),
17 | body: Stack(
18 | fit: StackFit.expand,
19 | children: [
20 | Image.asset(
21 | 'assets/images/onboard_image.png',
22 | fit: BoxFit.fitHeight,
23 | ),
24 | Container(
25 | decoration: const BoxDecoration(
26 | gradient: LinearGradient(
27 | begin: Alignment.topCenter,
28 | end: Alignment.bottomCenter,
29 | stops: [0.35, 1],
30 | colors: [
31 | Colors.transparent,
32 | Colors.black,
33 | ],
34 | ),
35 | ),
36 | ),
37 | const OnboardContent(),
38 | ],
39 | ),
40 | );
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/lib/features/authentication/screens/register_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/shared/constants/constants.dart';
2 | import 'package:amplify_recipe/shared/constants/gaps.dart';
3 | import 'package:amplify_recipe/themes/app_colors.dart';
4 | import 'package:flutter/gestures.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:go_router/go_router.dart';
7 |
8 | import '../widgets/sign_up_form.dart';
9 | import '../widgets/socal_login_buttons.dart';
10 | import '../widgets/terms_and_policy.dart';
11 |
12 | class RegisterScreen extends StatelessWidget {
13 | const RegisterScreen({super.key});
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return Scaffold(
18 | body: SafeArea(
19 | child: SingleChildScrollView(
20 | padding: const EdgeInsets.all(defaultPadding),
21 | child: Column(
22 | crossAxisAlignment: CrossAxisAlignment.start,
23 | children: [
24 | Text(
25 | 'Create Account',
26 | style: Theme.of(context).textTheme.headlineSmall!.copyWith(
27 | color: AppColors.text,
28 | ),
29 | ),
30 | gapH16,
31 | Text.rich(
32 | TextSpan(
33 | text: 'Enter your name, email and password for sign up. ',
34 | children: [
35 | TextSpan(
36 | text: 'Already have an account?',
37 | recognizer: TapGestureRecognizer()
38 | ..onTap = () {
39 | context.push('/sign-in');
40 | },
41 | style: const TextStyle(
42 | color: AppColors.primary,
43 | height: 1.5,
44 | ),
45 | ),
46 | ],
47 | ),
48 | ),
49 | gapH24,
50 | const SignUpForm(),
51 | gapH20,
52 | const TermsAndPolicy(),
53 | gapH16,
54 | const SocalLoginButtons()
55 | ],
56 | ),
57 | ),
58 | ),
59 | );
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/lib/features/authentication/screens/resend_email_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/gestures.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import '../../../shared/constants/constants.dart';
5 | import '../../../shared/constants/gaps.dart';
6 | import '../../../themes/app_colors.dart';
7 |
8 | class EmailResendScreen extends StatelessWidget {
9 | const EmailResendScreen({super.key});
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return Scaffold(
14 | appBar: AppBar(
15 | title: const Text('Forgot Password'),
16 | ),
17 | body: SafeArea(
18 | child: SingleChildScrollView(
19 | padding: const EdgeInsets.all(defaultPadding),
20 | child: Column(
21 | crossAxisAlignment: CrossAxisAlignment.start,
22 | children: [
23 | Text(
24 | 'Email Sent',
25 | style: Theme.of(context).textTheme.headlineSmall!.copyWith(
26 | color: AppColors.text,
27 | ),
28 | ),
29 | gapH16,
30 | Text.rich(
31 | TextSpan(
32 | text: 'Not receiving email? check on promotion page, spam. ',
33 | children: [
34 | TextSpan(
35 | text: 'Having problem?',
36 | recognizer: TapGestureRecognizer()..onTap = () {},
37 | style: const TextStyle(
38 | color: AppColors.primary,
39 | height: 1.5,
40 | ),
41 | ),
42 | ],
43 | ),
44 | ),
45 | gapH24,
46 | ElevatedButton(
47 | onPressed: () {},
48 | child: const Text('Resend'),
49 | ),
50 | ],
51 | ),
52 | ),
53 | ),
54 | );
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/lib/features/authentication/screens/sign_in_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/features/authentication/widgets/sign_in_form.dart';
2 | import 'package:amplify_recipe/shared/constants/constants.dart';
3 | import 'package:amplify_recipe/shared/constants/gaps.dart';
4 | import 'package:amplify_recipe/themes/app_colors.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:go_router/go_router.dart';
7 |
8 | import '../widgets/socal_login_buttons.dart';
9 |
10 | class SignInScreen extends StatelessWidget {
11 | const SignInScreen({super.key});
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return Scaffold(
16 | body: SafeArea(
17 | child: SingleChildScrollView(
18 | padding: const EdgeInsets.all(defaultPadding),
19 | child: Column(
20 | crossAxisAlignment: CrossAxisAlignment.start,
21 | children: [
22 | Text(
23 | 'Welcome',
24 | style: Theme.of(context).textTheme.headlineSmall!.copyWith(
25 | color: AppColors.text,
26 | ),
27 | ),
28 | gapH16,
29 | const Text('Enter your email address for sign in'),
30 | gapH24,
31 | const SignInForm(),
32 | Align(
33 | alignment: Alignment.centerRight,
34 | child: TextButton(
35 | onPressed: () {
36 | context.push('/forgot-password');
37 | },
38 | child: Text(
39 | 'Forgot password?',
40 | style: Theme.of(context).textTheme.bodyMedium,
41 | ),
42 | ),
43 | ),
44 | gapH16,
45 | const SocalLoginButtons()
46 | ],
47 | ),
48 | ),
49 | ),
50 | );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/lib/features/authentication/widgets/forgot_password_form.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/main.dart';
2 | import 'package:amplify_recipe/shared/data/authentication_repository.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:go_router/go_router.dart';
5 |
6 | import '../../../shared/constants/gaps.dart';
7 | import '../../../shared/utils/form_utils.dart';
8 |
9 | class ForgotPassForm extends StatefulWidget {
10 | const ForgotPassForm({
11 | super.key,
12 | });
13 |
14 | @override
15 | State createState() => _ForgotPassFormState();
16 | }
17 |
18 | class _ForgotPassFormState extends State {
19 | final TextEditingController _emailController = TextEditingController();
20 |
21 | @override
22 | void dispose() {
23 | _emailController.dispose();
24 | super.dispose();
25 | }
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | return Form(
30 | child: Column(
31 | crossAxisAlignment: CrossAxisAlignment.start,
32 | children: [
33 | Text(
34 | 'Email address',
35 | style: Theme.of(context).textTheme.titleSmall,
36 | ),
37 | gapH8,
38 | TextFormField(
39 | controller: _emailController,
40 | validator: FormUtils.emailValidator,
41 | keyboardType: TextInputType.emailAddress,
42 | decoration: const InputDecoration(hintText: 'test@mail.com'),
43 | ),
44 | gapH24,
45 | ElevatedButton(
46 | onPressed: () async {
47 | await getIt
48 | .get()
49 | .forgotPassword(_emailController.text);
50 | if (mounted) {
51 | context.push('/password-confirmation/${_emailController.text}');
52 | }
53 | },
54 | child: const Text('Reset Password'),
55 | ),
56 | ],
57 | ),
58 | );
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/lib/features/authentication/widgets/onboard_content.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/main.dart';
2 | import 'package:amplify_recipe/shared/data/authentication_repository.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:go_router/go_router.dart';
5 |
6 | import '../../../shared/constants/constants.dart';
7 | import '../../../shared/constants/gaps.dart';
8 |
9 | class OnboardContent extends StatefulWidget {
10 | const OnboardContent({
11 | super.key,
12 | });
13 |
14 | @override
15 | State createState() => _OnboardContentState();
16 | }
17 |
18 | class _OnboardContentState extends State {
19 | late final Future futureOperation;
20 |
21 | @override
22 | void initState() {
23 | super.initState();
24 | futureOperation = getIt.get().isUserLoggedIn();
25 | }
26 |
27 | @override
28 | Widget build(BuildContext context) {
29 | return SafeArea(
30 | child: Padding(
31 | padding: const EdgeInsets.all(defaultPadding * 2),
32 | child: Column(
33 | mainAxisAlignment: MainAxisAlignment.end,
34 | children: [
35 | Text(
36 | 'Cooking with \ngreat experiences',
37 | textAlign: TextAlign.center,
38 | style: Theme.of(context).textTheme.headlineMedium!.copyWith(
39 | color: Colors.white,
40 | fontWeight: FontWeight.w600,
41 | ),
42 | ),
43 | gapH16,
44 | const SizedBox(
45 | width: 250,
46 | child: Text(
47 | 'The best experience is given based on the ingredients you have at home',
48 | textAlign: TextAlign.center,
49 | style: TextStyle(color: Color(0xFFADADAD)),
50 | ),
51 | ),
52 | gapH24,
53 | FutureBuilder(
54 | future: futureOperation,
55 | builder: (context, snapshot) {
56 | if (snapshot.data == true) {
57 | WidgetsBinding.instance.addPostFrameCallback((_) {
58 | context.go('/entry-point');
59 | });
60 | }
61 | return AnimatedSwitcher(
62 | duration: const Duration(milliseconds: 300),
63 | child: snapshot.data == false
64 | ? Column(
65 | children: [
66 | ElevatedButton(
67 | onPressed: () {
68 | context.push('/register');
69 | },
70 | child: const Text('Register'),
71 | ),
72 | gapH8,
73 | OutlinedButton(
74 | onPressed: () {
75 | context.push('/sign-in');
76 | },
77 | style: OutlinedButton.styleFrom(
78 | foregroundColor: Colors.white,
79 | side:
80 | const BorderSide(color: Colors.transparent),
81 | ),
82 | child: const Text('Sign in'),
83 | ),
84 | ],
85 | )
86 | : const SizedBox.shrink(),
87 | );
88 | },
89 | ),
90 | ],
91 | ),
92 | ),
93 | );
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/lib/features/authentication/widgets/password_confirmation_form.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/shared/data/authentication_repository.dart';
2 | import 'package:amplify_recipe/main.dart';
3 | import 'package:flutter/gestures.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:go_router/go_router.dart';
6 |
7 | import '../../../shared/constants/constants.dart';
8 | import '../../../shared/constants/gaps.dart';
9 | import '../../../shared/utils/form_utils.dart';
10 | import '../../../themes/app_colors.dart';
11 |
12 | class PasswordConfirmationForm extends StatefulWidget {
13 | const PasswordConfirmationForm({
14 | required this.email,
15 | super.key,
16 | });
17 |
18 | final String email;
19 |
20 | @override
21 | State createState() => _PasswordConfirmationFormState();
22 | }
23 |
24 | class _PasswordConfirmationFormState extends State {
25 | late final TextEditingController confirmationCodeController =
26 | TextEditingController();
27 | late final TextEditingController newPasswordController =
28 | TextEditingController();
29 | final _formKey = GlobalKey();
30 | bool _isEnabled = true;
31 |
32 | @override
33 | Widget build(BuildContext context) {
34 | return Scaffold(
35 | body: SafeArea(
36 | child: Padding(
37 | padding: const EdgeInsets.all(defaultPadding),
38 | child: Form(
39 | key: _formKey,
40 | child: Column(
41 | crossAxisAlignment: CrossAxisAlignment.start,
42 | children: [
43 | Text(
44 | 'Confirmation Code',
45 | style: Theme.of(context).textTheme.titleSmall,
46 | ),
47 | gapH8,
48 | TextFormField(
49 | controller: confirmationCodeController,
50 | validator: FormUtils.requireFieldValidator,
51 | decoration:
52 | const InputDecoration(hintText: 'Confirmation Code'),
53 | ),
54 | gapH16,
55 | Text(
56 | 'New Password',
57 | style: Theme.of(context).textTheme.titleSmall,
58 | ),
59 | gapH8,
60 | TextFormField(
61 | controller: newPasswordController,
62 | validator: FormUtils.requireFieldValidator,
63 | obscureText: true,
64 | decoration:
65 | const InputDecoration(hintText: 'New Password'),
66 | ),
67 | gapH24,
68 | ElevatedButton(
69 | onPressed: _isEnabled
70 | ? () {
71 | if (_formKey.currentState!.validate()) {
72 | setState(() {
73 | _isEnabled = false;
74 | });
75 | getIt
76 | .get()
77 | .confirmPasswordReset(
78 | widget.email,
79 | newPasswordController.text,
80 | confirmationCodeController.text,
81 | )
82 | .then((value) {
83 | context.push('/sign-in');
84 | }).onError((error, stackTrace) {
85 | setState(() {
86 | _isEnabled = true;
87 | });
88 | ScaffoldMessenger.of(context).showSnackBar(
89 | SnackBar(
90 | content: Text(error.toString()),
91 | ),
92 | );
93 | });
94 | }
95 | }
96 | : null,
97 | child: const Text('Confirm New Password'),
98 | ),
99 | gapH24,
100 | Text.rich(
101 | TextSpan(
102 | text:
103 | 'Not receiving email? check on promotion page or spam.\n',
104 | children: [
105 | TextSpan(
106 | text: 'Or click here to send the code again.',
107 | recognizer: TapGestureRecognizer()..onTap = () {},
108 | style: const TextStyle(
109 | color: AppColors.primary,
110 | height: 1.5,
111 | ),
112 | ),
113 | ],
114 | ),
115 | ),
116 | ],
117 | ),
118 | ),
119 | ),
120 | ),
121 | );
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/lib/features/authentication/widgets/sign_in_form.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: unused_import
2 |
3 | import 'package:amplify_recipe/features/authentication/widgets/user_confirmation_form.dart';
4 | import 'package:amplify_recipe/features/entry_point.dart';
5 | import 'package:amplify_recipe/main.dart';
6 | import 'package:amplify_recipe/shared/data/authentication_repository.dart';
7 | import 'package:amplify_recipe/shared/data/notification_repository.dart';
8 | import 'package:amplify_recipe/shared/data/recipe_repository.dart';
9 | import 'package:flutter/material.dart';
10 | import 'package:go_router/go_router.dart';
11 |
12 | import '../../../shared/constants/gaps.dart';
13 | import '../../../shared/utils/form_utils.dart';
14 |
15 | class SignInForm extends StatefulWidget {
16 | const SignInForm({
17 | super.key,
18 | });
19 |
20 | @override
21 | State createState() => _SignInFormState();
22 | }
23 |
24 | class _SignInFormState extends State {
25 | late final TextEditingController emailController = TextEditingController();
26 | late final TextEditingController passwordController = TextEditingController();
27 | late final _formKey = GlobalKey();
28 | bool _isEnabled = true;
29 |
30 | @override
31 | void dispose() {
32 | emailController.dispose();
33 | passwordController.dispose();
34 | super.dispose();
35 | }
36 |
37 | @override
38 | Widget build(BuildContext context) {
39 | return Form(
40 | key: _formKey,
41 | child: Column(
42 | crossAxisAlignment: CrossAxisAlignment.start,
43 | children: [
44 | Text(
45 | 'Email address',
46 | style: Theme.of(context).textTheme.titleSmall,
47 | ),
48 | gapH8,
49 | TextFormField(
50 | controller: emailController,
51 | validator: FormUtils.emailValidator,
52 | keyboardType: TextInputType.emailAddress,
53 | textInputAction: TextInputAction.next,
54 | decoration: const InputDecoration(hintText: 'user@example.com'),
55 | ),
56 | gapH16,
57 | Text(
58 | 'Password',
59 | style: Theme.of(context).textTheme.titleSmall,
60 | ),
61 | gapH8,
62 | TextFormField(
63 | controller: passwordController,
64 | validator: FormUtils.passwordValidator,
65 | obscureText: true,
66 | decoration: const InputDecoration(hintText: 'Enter your password'),
67 | ),
68 | gapH24,
69 | ElevatedButton(
70 | onPressed: _isEnabled
71 | ? () {
72 | if (_formKey.currentState!.validate()) {
73 | setState(() {
74 | _isEnabled = false;
75 | });
76 | getIt
77 | .get()
78 | .logInWithCredentials(
79 | emailController.text,
80 | passwordController.text,
81 | )
82 | .then((value) {
83 | context.go('/entry-point');
84 | }).onError((error, stackTrace) {
85 | setState(() {
86 | _isEnabled = true;
87 | });
88 | });
89 | }
90 | }
91 | : null,
92 | child: const Text('Login'),
93 | ),
94 | ],
95 | ),
96 | );
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/lib/features/authentication/widgets/sign_up_form.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/shared/data/authentication_repository.dart';
2 | import 'package:amplify_recipe/main.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:go_router/go_router.dart';
5 |
6 | import '../../../shared/constants/gaps.dart';
7 | import '../../../shared/utils/form_utils.dart';
8 |
9 | class SignUpForm extends StatefulWidget {
10 | const SignUpForm({
11 | super.key,
12 | });
13 |
14 | @override
15 | State createState() => _SignUpFormState();
16 | }
17 |
18 | class _SignUpFormState extends State {
19 | late final TextEditingController nameController = TextEditingController();
20 | late final TextEditingController emailController = TextEditingController();
21 | late final TextEditingController passwordController = TextEditingController();
22 | final _formKey = GlobalKey();
23 | bool _isEnabled = true;
24 |
25 | @override
26 | void dispose() {
27 | nameController.dispose();
28 | emailController.dispose();
29 | passwordController.dispose();
30 | super.dispose();
31 | }
32 |
33 | @override
34 | Widget build(BuildContext context) {
35 | return Form(
36 | key: _formKey,
37 | child: Column(
38 | crossAxisAlignment: CrossAxisAlignment.start,
39 | children: [
40 | Text(
41 | 'Full Name',
42 | style: Theme.of(context).textTheme.titleSmall,
43 | ),
44 | gapH8,
45 | TextFormField(
46 | controller: nameController,
47 | validator: FormUtils.requireFieldValidator,
48 | textInputAction: TextInputAction.next,
49 | decoration: const InputDecoration(hintText: 'Enter your name'),
50 | ),
51 | gapH16,
52 | Text(
53 | 'Email address',
54 | style: Theme.of(context).textTheme.titleSmall,
55 | ),
56 | gapH8,
57 | TextFormField(
58 | controller: emailController,
59 | validator: FormUtils.emailValidator,
60 | keyboardType: TextInputType.emailAddress,
61 | textInputAction: TextInputAction.next,
62 | decoration: const InputDecoration(hintText: 'test@mail.com'),
63 | ),
64 | gapH16,
65 | Text(
66 | 'Password',
67 | style: Theme.of(context).textTheme.titleSmall,
68 | ),
69 | gapH8,
70 | TextFormField(
71 | controller: passwordController,
72 | validator: FormUtils.passwordValidator,
73 | obscureText: true,
74 | decoration: const InputDecoration(hintText: 'Enter your password'),
75 | ),
76 | gapH24,
77 | ElevatedButton(
78 | onPressed: _isEnabled
79 | ? () {
80 | if (_formKey.currentState!.validate()) {
81 | setState(() {
82 | _isEnabled = false;
83 | });
84 | getIt
85 | .get()
86 | .signUp(
87 | emailController.text,
88 | passwordController.text,
89 | nameController.text,
90 | )
91 | .then((value) {
92 | context
93 | .go('/user-confirmation/${emailController.text}');
94 | }).onError(
95 | (error, stackTrace) {
96 | setState(() {
97 | _isEnabled = true;
98 | });
99 | ScaffoldMessenger.of(context).showSnackBar(
100 | SnackBar(
101 | content: Text(error.toString()),
102 | ),
103 | );
104 | },
105 | );
106 | }
107 | }
108 | : null,
109 | child: const Text('Sign up'),
110 | ),
111 | ],
112 | ),
113 | );
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/lib/features/authentication/widgets/socal_login_buttons.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: unused_import
2 |
3 | import 'package:amplify_recipe/shared/data/authentication_repository.dart';
4 | import 'package:amplify_recipe/features/entry_point.dart';
5 | import 'package:amplify_recipe/main.dart';
6 | import 'package:flutter/material.dart';
7 | import 'package:flutter_svg/flutter_svg.dart';
8 | import 'package:go_router/go_router.dart';
9 |
10 | import '../../../shared/constants/constants.dart';
11 | import '../../../shared/constants/gaps.dart';
12 |
13 | class SocalLoginButtons extends StatelessWidget {
14 | const SocalLoginButtons({
15 | super.key,
16 | });
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return Column(
21 | children: [
22 | const Row(
23 | children: [
24 | Expanded(
25 | child: Divider(),
26 | ),
27 | Padding(
28 | padding: EdgeInsets.symmetric(horizontal: defaultPadding / 1.5),
29 | child: Text('or continue with'),
30 | ),
31 | Expanded(
32 | child: Divider(),
33 | ),
34 | ],
35 | ),
36 | gapH16,
37 | OutlinedButton.icon(
38 | onPressed: () {
39 | getIt.get().logInWithGoogle().then(
40 | (value) {
41 | context.go('/entry-point');
42 | },
43 | ).onError((error, stackTrace) {
44 | ScaffoldMessenger.of(context).showSnackBar(
45 | SnackBar(
46 | content: Text(error.toString()),
47 | ),
48 | );
49 | });
50 | },
51 | icon: SvgPicture.asset('assets/icons/Google.svg'),
52 | label: const Text('Sign Up with Google'),
53 | ),
54 | gapH16,
55 | OutlinedButton.icon(
56 | onPressed: () {
57 | getIt.get().logInWithFacebook().then(
58 | (value) {
59 | context.go('/entry-point');
60 | },
61 | ).onError((error, stackTrace) {
62 | ScaffoldMessenger.of(context).showSnackBar(
63 | SnackBar(
64 | content: Text(error.toString()),
65 | ),
66 | );
67 | });
68 | },
69 | icon: SvgPicture.asset('assets/icons/Faceboook.svg'),
70 | label: const Text('Sign Up with Facebook'),
71 | ),
72 | ],
73 | );
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/lib/features/authentication/widgets/terms_and_policy.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/gestures.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import '../../../themes/app_colors.dart';
5 |
6 | class TermsAndPolicy extends StatelessWidget {
7 | const TermsAndPolicy({
8 | super.key,
9 | });
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return Text.rich(
14 | TextSpan(text: 'By signing up, you agree to our ', children: [
15 | TextSpan(
16 | text: 'Terms of Service',
17 | recognizer: TapGestureRecognizer()..onTap = () {},
18 | style: const TextStyle(
19 | color: AppColors.primary,
20 | height: 1.5,
21 | ),
22 | ),
23 | const TextSpan(text: ' and '),
24 | TextSpan(
25 | text: 'Privacy Policy',
26 | recognizer: TapGestureRecognizer()..onTap = () {},
27 | style: const TextStyle(
28 | color: AppColors.primary,
29 | height: 1.5,
30 | ),
31 | ),
32 | ]),
33 | );
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib/features/authentication/widgets/user_confirmation_form.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/shared/data/authentication_repository.dart';
2 | import 'package:amplify_recipe/main.dart';
3 | import 'package:flutter/gestures.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:go_router/go_router.dart';
6 |
7 | import '../../../shared/constants/constants.dart';
8 | import '../../../shared/constants/gaps.dart';
9 | import '../../../shared/utils/form_utils.dart';
10 | import '../../../themes/app_colors.dart';
11 |
12 | class UserConfirmationForm extends StatefulWidget {
13 | const UserConfirmationForm({
14 | required this.email,
15 | super.key,
16 | });
17 |
18 | final String email;
19 |
20 | @override
21 | State createState() => _UserConfirmationFormState();
22 | }
23 |
24 | class _UserConfirmationFormState extends State {
25 | late final TextEditingController confirmationCodeController =
26 | TextEditingController();
27 | final _formKey = GlobalKey();
28 | bool _isEnabled = true;
29 |
30 | @override
31 | Widget build(BuildContext context) {
32 | return Scaffold(
33 | body: SafeArea(
34 | child: Padding(
35 | padding: const EdgeInsets.all(defaultPadding),
36 | child: Form(
37 | key: _formKey,
38 | child: Column(
39 | crossAxisAlignment: CrossAxisAlignment.start,
40 | children: [
41 | Text(
42 | 'Confirmation Code',
43 | style: Theme.of(context).textTheme.titleSmall,
44 | ),
45 | gapH8,
46 | TextFormField(
47 | controller: confirmationCodeController,
48 | validator: FormUtils.requireFieldValidator,
49 | keyboardType: TextInputType.emailAddress,
50 | decoration:
51 | const InputDecoration(hintText: 'Confirmation Code'),
52 | ),
53 | gapH24,
54 | ElevatedButton(
55 | onPressed: _isEnabled
56 | ? () {
57 | if (_formKey.currentState!.validate()) {
58 | setState(() {
59 | _isEnabled = false;
60 | });
61 | getIt
62 | .get()
63 | .confirmUser(
64 | widget.email,
65 | confirmationCodeController.text,
66 | )
67 | .then((value) {
68 | context.push('/sign-in');
69 | }).onError((error, stackTrace) {
70 | setState(() {
71 | _isEnabled = true;
72 | });
73 | ScaffoldMessenger.of(context).showSnackBar(
74 | SnackBar(
75 | content: Text(error.toString()),
76 | ),
77 | );
78 | });
79 | }
80 | }
81 | : null,
82 | child: const Text('Confirm User'),
83 | ),
84 | gapH24,
85 | Text.rich(
86 | TextSpan(
87 | text:
88 | 'Not receiving email? check on promotion page or spam.\n',
89 | children: [
90 | TextSpan(
91 | text: 'Or click here to send the code again.',
92 | recognizer: TapGestureRecognizer()..onTap = () {},
93 | style: const TextStyle(
94 | color: AppColors.primary,
95 | height: 1.5,
96 | ),
97 | ),
98 | ],
99 | ),
100 | ),
101 | ],
102 | ),
103 | ),
104 | ),
105 | ),
106 | );
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/lib/features/details/components/ingredient_tile.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../../shared/constants/constants.dart';
4 | import '../../../themes/app_colors.dart';
5 |
6 | class IngredientTile extends StatelessWidget {
7 | const IngredientTile({
8 | super.key,
9 | required this.name,
10 | required this.quantity,
11 | });
12 |
13 | final String name, quantity;
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return Container(
18 | decoration: const BoxDecoration(
19 | color: AppColors.inputfieldBg,
20 | borderRadius: BorderRadius.all(Radius.circular(defaultBorderRadius)),
21 | ),
22 | child: ListTile(
23 | title: Text(name),
24 | trailing: Text(quantity),
25 | ),
26 | );
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/lib/features/details/components/ingredients.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/shared/constants/constants.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import '../../../shared/constants/gaps.dart';
5 | import 'ingredient_tile.dart';
6 |
7 | class Ingredients extends StatelessWidget {
8 | const Ingredients({
9 | required this.ingredients,
10 | super.key,
11 | });
12 |
13 | final List ingredients;
14 |
15 | @override
16 | Widget build(BuildContext context) {
17 | return Column(
18 | crossAxisAlignment: CrossAxisAlignment.start,
19 | children: [
20 | gapH24,
21 | Text(
22 | 'Ingredients ${ingredients.length}',
23 | style: Theme.of(context).textTheme.titleLarge,
24 | ),
25 | ...ingredients.map((ingredient) {
26 | final ingredientSplit = ingredient.split('-');
27 | return Padding(
28 | padding: const EdgeInsets.only(top: defaultPadding),
29 | child: IngredientTile(
30 | name: ingredientSplit[1],
31 | quantity: ingredientSplit[0],
32 | ),
33 | );
34 | })
35 | ],
36 | );
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/lib/features/entry_point.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/features/favorite/screens/favorite_screen.dart';
2 | import 'package:amplify_recipe/features/home/screens/home_screen.dart';
3 | import 'package:amplify_recipe/features/profile/screens/profile_screen.dart';
4 | import 'package:amplify_recipe/themes/app_colors.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:flutter_svg/flutter_svg.dart';
7 |
8 | class EntryPoint extends StatefulWidget {
9 | const EntryPoint({super.key});
10 |
11 | @override
12 | State createState() => _EntryPointState();
13 | }
14 |
15 | class _EntryPointState extends State {
16 | int _currentPage = 0;
17 | final List _pages = [
18 | const HomeScreen(),
19 | const FavoriteScreen(),
20 | const ProfileScreen(),
21 | ];
22 | static const _iconColor = Color(0xffADADAD);
23 |
24 | @override
25 | Widget build(BuildContext context) {
26 | return Scaffold(
27 | body: _pages[_currentPage],
28 | bottomNavigationBar: BottomNavigationBar(
29 | currentIndex: _currentPage,
30 | onTap: (index) {
31 | setState(() {
32 | _currentPage = index;
33 | });
34 | },
35 | showSelectedLabels: false,
36 | showUnselectedLabels: false,
37 | items: [
38 | BottomNavigationBarItem(
39 | icon: SvgPicture.asset(
40 | 'assets/icons/Home.svg',
41 | colorFilter: const ColorFilter.mode(
42 | _iconColor,
43 | BlendMode.srcIn,
44 | ),
45 | ),
46 | activeIcon: SvgPicture.asset(
47 | 'assets/icons/Home.svg',
48 | colorFilter: const ColorFilter.mode(
49 | AppColors.primary,
50 | BlendMode.srcIn,
51 | ),
52 | ),
53 | label: 'Home',
54 | ),
55 | BottomNavigationBarItem(
56 | icon: SvgPicture.asset(
57 | 'assets/icons/Bookmark.svg',
58 | colorFilter: const ColorFilter.mode(
59 | _iconColor,
60 | BlendMode.srcIn,
61 | ),
62 | ),
63 | activeIcon: SvgPicture.asset(
64 | 'assets/icons/Bookmark.svg',
65 | colorFilter: const ColorFilter.mode(
66 | AppColors.primary,
67 | BlendMode.srcIn,
68 | ),
69 | ),
70 | label: 'Bookmarked',
71 | ),
72 | BottomNavigationBarItem(
73 | icon: SvgPicture.asset(
74 | 'assets/icons/Profile.svg',
75 | colorFilter: const ColorFilter.mode(
76 | _iconColor,
77 | BlendMode.srcIn,
78 | ),
79 | ),
80 | activeIcon: SvgPicture.asset(
81 | 'assets/icons/Profile.svg',
82 | colorFilter: const ColorFilter.mode(
83 | AppColors.primary,
84 | BlendMode.srcIn,
85 | ),
86 | ),
87 | label: 'Profile',
88 | ),
89 | ],
90 | ),
91 | );
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/lib/features/favorite/screens/favorite_screen.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: unused_import
2 |
3 | import 'package:amplify_recipe/shared/data/model/recipe.dart';
4 | import 'package:amplify_recipe/shared/data/recipe_repository.dart';
5 | import 'package:amplify_recipe/features/details/screens/recipe_details_screen.dart';
6 | import 'package:amplify_recipe/main.dart';
7 | import 'package:amplify_recipe/shared/constants/gaps.dart';
8 | import 'package:amplify_recipe/shared/widgets/recipe_card.dart';
9 | import 'package:flutter/material.dart';
10 | import 'package:go_router/go_router.dart';
11 |
12 | import '../../../shared/constants/constants.dart';
13 |
14 | class FavoriteScreen extends StatelessWidget {
15 | const FavoriteScreen({super.key});
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return Scaffold(
20 | body: SafeArea(
21 | child: Padding(
22 | padding: const EdgeInsets.symmetric(horizontal: defaultPadding),
23 | child: Column(
24 | crossAxisAlignment: CrossAxisAlignment.start,
25 | children: [
26 | gapH24,
27 | Text(
28 | 'Saved 😍',
29 | style: Theme.of(context).textTheme.titleLarge,
30 | ),
31 | gapH8,
32 | Expanded(
33 | child: StreamBuilder>(
34 | stream:
35 | getIt.get().listenFavoritedRecipes(),
36 | builder: (context, snapshot) {
37 | if (snapshot.hasData) {
38 | final recipes = snapshot.data!;
39 | if (recipes.isEmpty) {
40 | return Center(
41 | child: Text(
42 | 'No saved recipes yet',
43 | style: Theme.of(context).textTheme.titleLarge,
44 | ),
45 | );
46 | }
47 | return ListView.builder(
48 | itemCount: recipes.length,
49 | itemBuilder: (context, index) {
50 | final recipe = recipes[index];
51 | return Padding(
52 | padding:
53 | const EdgeInsets.only(bottom: defaultPadding),
54 | child: RecipeCard(
55 | press: () {
56 | context.push(
57 | '/recipe/${recipe.id}',
58 | );
59 | },
60 | onBookmarked: () {
61 | getIt
62 | .get()
63 | .toggleFavoriteForRecipe(
64 | id: recipe.id,
65 | isFavorited: !recipe.isFavorited,
66 | );
67 | },
68 | title: recipe.title,
69 | image: recipe.image,
70 | category: recipe.category,
71 | duration: recipe.duration,
72 | serve: recipe.serve,
73 | isFavorited: recipe.isFavorited,
74 | ),
75 | );
76 | },
77 | );
78 | } else if (snapshot.hasError) {
79 | return Center(
80 | child: Text(
81 | snapshot.error.toString(),
82 | style: Theme.of(context).textTheme.bodyMedium,
83 | ),
84 | );
85 | } else {
86 | return const Center(
87 | child: CircularProgressIndicator(),
88 | );
89 | }
90 | },
91 | ),
92 | )
93 | ],
94 | ),
95 | ),
96 | ),
97 | );
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/lib/features/home/widgets/search_container.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_svg/flutter_svg.dart';
3 | import 'package:go_router/go_router.dart';
4 |
5 | import '../../../shared/constants/constants.dart';
6 | import '../../../shared/constants/gaps.dart';
7 | import '../../../themes/app_colors.dart';
8 |
9 | class SearchContaner extends StatelessWidget {
10 | const SearchContaner({
11 | super.key,
12 | });
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return GestureDetector(
17 | onTap: () {
18 | context.push('/search-recipes');
19 | },
20 | child: Container(
21 | height: 60,
22 | padding: const EdgeInsets.symmetric(horizontal: defaultPadding),
23 | decoration: const BoxDecoration(
24 | color: AppColors.inputfieldBg,
25 | borderRadius: BorderRadius.all(Radius.circular(defaultBorderRadius)),
26 | ),
27 | child: Row(
28 | children: [
29 | SvgPicture.asset('assets/icons/Search.svg'),
30 | gapW8,
31 | const Text('Type to find recipes..'),
32 | ],
33 | ),
34 | ),
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib/features/notifications/screens/notifications_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/main.dart';
2 | import 'package:amplify_recipe/shared/data/model/notification.dart';
3 | import 'package:amplify_recipe/shared/data/notification_repository.dart';
4 | import 'package:flutter/material.dart' hide Notification;
5 | import 'package:go_router/go_router.dart';
6 |
7 | class NotificationsScreen extends StatelessWidget {
8 | const NotificationsScreen({super.key});
9 |
10 | @override
11 | Widget build(BuildContext context) {
12 | return Scaffold(
13 | appBar: AppBar(
14 | title: const Text('Notifications'),
15 | ),
16 | body: FutureBuilder>(
17 | future: getIt.get().fetchAllNotifications(),
18 | builder: (context, snapshot) {
19 | if (snapshot.hasData) {
20 | if (snapshot.data!.isEmpty) {
21 | return Center(
22 | child: Text(
23 | 'You don\'t have any notifications (yet).',
24 | style: Theme.of(context).textTheme.titleLarge,
25 | ),
26 | );
27 | }
28 | return ListView(
29 | children: [
30 | for (final notification in snapshot.data!)
31 | ListTile(
32 | title: Text(notification.title),
33 | subtitle: Text(notification.description),
34 | tileColor: notification.isSeen
35 | ? Colors.transparent
36 | : Theme.of(context)
37 | .colorScheme
38 | .primary
39 | .withOpacity(0.1),
40 | onTap: () {
41 | getIt
42 | .get()
43 | .markAsSeen(notification.id);
44 | context.push('/recipe/${notification.recipeId}');
45 | },
46 | )
47 | ],
48 | );
49 | } else {
50 | return const Center(
51 | child: CircularProgressIndicator(),
52 | );
53 | }
54 | },
55 | ),
56 | );
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/lib/features/profile/components/settings.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/shared/data/authentication_repository.dart';
2 | import 'package:amplify_recipe/main.dart';
3 | import 'package:flutter/cupertino.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_svg/flutter_svg.dart';
6 | import 'package:go_router/go_router.dart';
7 |
8 | import '../../../shared/constants/constants.dart';
9 | import '../../../shared/constants/gaps.dart';
10 | import '../../../themes/app_colors.dart';
11 |
12 | class Settings extends StatelessWidget {
13 | const Settings({
14 | super.key,
15 | });
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | return Column(
20 | children: [
21 | gapH8,
22 | Container(
23 | color: const Color(0xFF757575).withOpacity(0.08),
24 | child: const ListTile(
25 | title: Text('Settings'),
26 | ),
27 | ),
28 | gapH16,
29 | SettingListTile(
30 | onTap: () {},
31 | title: 'Notifications',
32 | iconSrc: 'assets/icons/Notification.svg',
33 | ),
34 | SettingListTile(
35 | onTap: () {},
36 | title: 'Language',
37 | trailingText: 'English',
38 | iconSrc: 'assets/icons/Language.svg',
39 | ),
40 | SettingListTile(
41 | onTap: () {},
42 | title: 'Theme',
43 | trailingText: 'Light',
44 | iconSrc: 'assets/icons/Moon.svg',
45 | ),
46 | SettingListTile(
47 | onTap: () {},
48 | title: 'Help',
49 | iconSrc: 'assets/icons/Help.svg',
50 | ),
51 | gapH8,
52 | SettingListTile(
53 | onTap: () {
54 | getIt.get().signOut().then((value) {
55 | context.go('/');
56 | });
57 | },
58 | title: 'Log out',
59 | isLogout: true,
60 | iconSrc: 'assets/icons/Logout.svg',
61 | ),
62 | ],
63 | );
64 | }
65 | }
66 |
67 | class SettingListTile extends StatelessWidget {
68 | const SettingListTile({
69 | super.key,
70 | required this.title,
71 | required this.iconSrc,
72 | this.trailingText,
73 | this.isLogout = false,
74 | required this.onTap,
75 | });
76 |
77 | final String title, iconSrc;
78 | final String? trailingText;
79 | final bool isLogout;
80 | final VoidCallback onTap;
81 |
82 | @override
83 | Widget build(BuildContext context) {
84 | return Padding(
85 | padding: const EdgeInsets.only(bottom: defaultPadding / 2),
86 | child: ListTile(
87 | onTap: onTap,
88 | minLeadingWidth: 20,
89 | leading: SvgPicture.asset(
90 | iconSrc,
91 | colorFilter: ColorFilter.mode(
92 | isLogout ? AppColors.error : Colors.grey.shade500,
93 | BlendMode.srcIn),
94 | height: 24,
95 | width: 24,
96 | ),
97 | title: Text(
98 | title,
99 | style: TextStyle(color: isLogout ? AppColors.error : null),
100 | ),
101 | trailing: isLogout
102 | ? const SizedBox()
103 | : SizedBox(
104 | width: 100,
105 | child: Row(
106 | mainAxisAlignment: MainAxisAlignment.end,
107 | children: [
108 | trailingText != null
109 | ? Text(trailingText!)
110 | : const SizedBox(),
111 | gapW4,
112 | const Icon(CupertinoIcons.forward),
113 | ],
114 | ),
115 | ),
116 | ),
117 | );
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/lib/features/profile/screens/profile_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/shared/data/authentication_repository.dart';
2 | import 'package:amplify_recipe/main.dart';
3 | import 'package:amplify_recipe/shared/constants/constants.dart';
4 | import 'package:flutter/cupertino.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:go_router/go_router.dart';
7 |
8 | import '../components/settings.dart';
9 |
10 | class ProfileScreen extends StatelessWidget {
11 | const ProfileScreen({super.key});
12 |
13 | @override
14 | Widget build(BuildContext context) {
15 | return Scaffold(
16 | appBar: AppBar(
17 | title: const Text('Profile'),
18 | ),
19 | body: Column(
20 | children: [
21 | Container(
22 | color: const Color(0xFF757575).withOpacity(0.08),
23 | child: const ListTile(
24 | title: Text('Profile'),
25 | ),
26 | ),
27 | Padding(
28 | padding: const EdgeInsets.symmetric(vertical: defaultPadding),
29 | child: ListTile(
30 | onTap: () {
31 | context.push('/edit-profile');
32 | },
33 | minLeadingWidth: 60,
34 | leading: CircleAvatar(
35 | radius: 30,
36 | backgroundColor: Colors.grey.shade300,
37 | backgroundImage: NetworkImage(
38 | getIt
39 | .get()
40 | .currentUser
41 | ?.profilePicture ??
42 | 'https://picsum.photos/200',
43 | ),
44 | ),
45 | title: Text(getIt.get().fullName),
46 | trailing: const Icon(CupertinoIcons.forward),
47 | ),
48 | ),
49 | const Settings()
50 | ],
51 | ),
52 | );
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib/features/recipes/screens/search/screen/all_recipes_screen.dart:
--------------------------------------------------------------------------------
1 | // ignore_for_file: unused_import
2 |
3 | import 'package:amplify_recipe/shared/data/model/recipe.dart';
4 | import 'package:amplify_recipe/shared/data/recipe_repository.dart';
5 | import 'package:amplify_recipe/features/details/screens/recipe_details_screen.dart';
6 | import 'package:amplify_recipe/main.dart';
7 | import 'package:amplify_recipe/shared/constants/gaps.dart';
8 | import 'package:amplify_recipe/shared/widgets/recipe_card.dart';
9 | import 'package:flutter/material.dart';
10 | import 'package:go_router/go_router.dart';
11 |
12 | import '../../../../../shared/constants/constants.dart';
13 |
14 | class AllRecipesScreen extends StatelessWidget {
15 | const AllRecipesScreen({super.key});
16 |
17 | @override
18 | Widget build(BuildContext context) {
19 | final recipeRepository = getIt.get();
20 | return Scaffold(
21 | appBar: AppBar(
22 | title: Text(
23 | 'All Recipes 👾',
24 | style: Theme.of(context).textTheme.titleLarge,
25 | ),
26 | ),
27 | body: SafeArea(
28 | child: Padding(
29 | padding: const EdgeInsets.symmetric(horizontal: defaultPadding),
30 | child: Column(
31 | crossAxisAlignment: CrossAxisAlignment.start,
32 | children: [
33 | gapH8,
34 | Expanded(
35 | child: StreamBuilder>(
36 | stream: recipeRepository.listenRecipes(),
37 | builder: (context, snapshot) {
38 | if (snapshot.hasData) {
39 | final recipes = snapshot.data!;
40 | return ListView.builder(
41 | itemCount: recipes.length,
42 | itemBuilder: (context, index) {
43 | final recipe = recipes[index];
44 | return Padding(
45 | padding:
46 | const EdgeInsets.only(bottom: defaultPadding),
47 | child: RecipeCard(
48 | press: () {
49 | context.push(
50 | '/recipe/${recipe.id}',
51 | );
52 | },
53 | onBookmarked: () {
54 | recipeRepository.toggleFavoriteForRecipe(
55 | id: recipe.id,
56 | isFavorited: !recipe.isFavorited,
57 | );
58 | },
59 | title: recipe.title,
60 | image: recipe.image,
61 | category: recipe.category,
62 | duration: recipe.duration,
63 | serve: recipe.serve,
64 | isFavorited: recipe.isFavorited,
65 | ),
66 | );
67 | },
68 | );
69 | } else if (snapshot.hasError) {
70 | return Center(
71 | child: Text(
72 | snapshot.error.toString(),
73 | style: Theme.of(context).textTheme.bodyMedium,
74 | ),
75 | );
76 | } else {
77 | return const Center(
78 | child: CircularProgressIndicator(),
79 | );
80 | }
81 | },
82 | ),
83 | )
84 | ],
85 | ),
86 | ),
87 | ),
88 | );
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/lib/features/recipes/screens/search/widgets/recent_search_tile.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter_svg/svg.dart';
3 |
4 | import '../../../../../themes/app_colors.dart';
5 |
6 | class RecentSearchTile extends StatelessWidget {
7 | const RecentSearchTile({
8 | super.key,
9 | required this.title,
10 | required this.onDeleted,
11 | this.onTap,
12 | });
13 |
14 | final String title;
15 | final VoidCallback onDeleted;
16 | final VoidCallback? onTap;
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return SizedBox(
21 | height: 48,
22 | child: ListTile(
23 | onTap: onTap,
24 | contentPadding: EdgeInsets.zero,
25 | title: Text(
26 | title,
27 | style: const TextStyle(color: AppColors.bodyText),
28 | ),
29 | trailing: IconButton(
30 | onPressed: onDeleted,
31 | icon: SvgPicture.asset('assets/icons/Remove.svg',
32 | colorFilter: const ColorFilter.mode(
33 | AppColors.bodyText,
34 | BlendMode.srcIn,
35 | )),
36 | ),
37 | ),
38 | );
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_auth_cognito/amplify_auth_cognito.dart';
2 | import 'package:amplify_flutter/amplify_flutter.dart';
3 | import 'package:amplify_api/amplify_api.dart';
4 | import 'package:amplify_push_notifications_pinpoint/amplify_push_notifications_pinpoint.dart';
5 | import 'package:amplify_recipe/amplifyconfiguration.dart';
6 | import 'package:amplify_recipe/models/ModelProvider.dart';
7 | import 'package:amplify_recipe/shared/data/database/isar_provider.dart';
8 | import 'package:amplify_recipe/shared/data/implementation/local_notification_repository.dart';
9 | import 'package:amplify_recipe/shared/data/implementation/local_remote_recipe_repository.dart';
10 | import 'package:amplify_recipe/shared/data/implementation/local_search_repository.dart';
11 | import 'package:amplify_recipe/shared/data/implementation/cognito_authentication_repository.dart';
12 | import 'package:amplify_recipe/shared/data/implementation/s3_storage_repository.dart';
13 | import 'package:amplify_recipe/shared/data/notification_repository.dart';
14 | import 'package:amplify_recipe/shared/data/storage_repository.dart';
15 | import 'package:amplify_recipe/shared/data/authentication_repository.dart';
16 | import 'package:amplify_recipe/shared/data/recipe_repository.dart';
17 | import 'package:amplify_recipe/shared/data/search_repository.dart';
18 | import 'package:amplify_recipe/shared/navigation/routes.dart';
19 | import 'package:amplify_recipe/themes/app_theme.dart';
20 | import 'package:amplify_storage_s3/amplify_storage_s3.dart';
21 | import 'package:flutter/material.dart';
22 | import 'package:get_it/get_it.dart';
23 |
24 | final getIt = GetIt.instance;
25 |
26 | Future main() async {
27 | WidgetsFlutterBinding.ensureInitialized();
28 | await _configureAmplify();
29 | await _registerData();
30 | await getIt.get().handlePermissions();
31 | await getIt.get().listenNotifications();
32 | runApp(const MyApp());
33 | }
34 |
35 | Future _configureAmplify() async {
36 | try {
37 | await Amplify.addPlugins(
38 | [
39 | AmplifyAuthCognito(),
40 | AmplifyAPI(modelProvider: ModelProvider.instance),
41 | AmplifyStorageS3(),
42 | AmplifyPushNotificationsPinpoint(),
43 | ],
44 | );
45 | await Amplify.configure(amplifyconfig);
46 | } on AmplifyException catch (e) {
47 | safePrint(e);
48 | }
49 | }
50 |
51 | Future _registerData() async {
52 | await IsarProvider.open();
53 | getIt.registerSingleton(
54 | CognitoAuthenticationRepository(),
55 | );
56 | getIt.registerSingleton(
57 | S3StorageRepository(),
58 | );
59 | getIt.registerSingleton(
60 | LocalRemoteRecipeRepository(),
61 | );
62 | getIt.registerSingleton(
63 | LocalSearchRepository(),
64 | );
65 | getIt.registerSingleton(
66 | LocalNotificationRepository(),
67 | );
68 | }
69 |
70 | class MyApp extends StatelessWidget {
71 | const MyApp({super.key});
72 |
73 | // This widget is the root of your application.
74 | @override
75 | Widget build(BuildContext context) {
76 | return MaterialApp.router(
77 | title: 'AWS Amplify Recipe App',
78 | theme: AppTheme.lightTheme(context),
79 | debugShowCheckedModeBanner: false,
80 | routerConfig: routerConfig,
81 | );
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/lib/models/Category.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | // NOTE: This file is generated and may not follow lint rules defined in your app
17 | // Generated files can be excluded from analysis in analysis_options.yaml
18 | // For more info, see: https://dart.dev/guides/language/analysis-options#excluding-code-from-analysis
19 |
20 | // ignore_for_file: public_member_api_docs, annotate_overrides, dead_code, dead_codepublic_member_api_docs, depend_on_referenced_packages, file_names, library_private_types_in_public_api, no_leading_underscores_for_library_prefixes, no_leading_underscores_for_local_identifiers, non_constant_identifier_names, null_check_on_nullable_type_parameter, prefer_adjacent_string_concatenation, prefer_const_constructors, prefer_if_null_operators, prefer_interpolation_to_compose_strings, slash_for_doc_comments, sort_child_properties_last, unnecessary_const, unnecessary_constructor_name, unnecessary_late, unnecessary_new, unnecessary_null_aware_assignments, unnecessary_nullable_for_final_variable_declarations, unnecessary_string_interpolations, use_build_context_synchronously
21 |
22 | enum Category {
23 | SOUP,
24 | DESSERT,
25 | SALAD,
26 | APPETIZER,
27 | FISH,
28 | MAIN_COURSE
29 | }
--------------------------------------------------------------------------------
/lib/models/Duration.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | // NOTE: This file is generated and may not follow lint rules defined in your app
17 | // Generated files can be excluded from analysis in analysis_options.yaml
18 | // For more info, see: https://dart.dev/guides/language/analysis-options#excluding-code-from-analysis
19 |
20 | // ignore_for_file: public_member_api_docs, annotate_overrides, dead_code, dead_codepublic_member_api_docs, depend_on_referenced_packages, file_names, library_private_types_in_public_api, no_leading_underscores_for_library_prefixes, no_leading_underscores_for_local_identifiers, non_constant_identifier_names, null_check_on_nullable_type_parameter, prefer_adjacent_string_concatenation, prefer_const_constructors, prefer_if_null_operators, prefer_interpolation_to_compose_strings, slash_for_doc_comments, sort_child_properties_last, unnecessary_const, unnecessary_constructor_name, unnecessary_late, unnecessary_new, unnecessary_null_aware_assignments, unnecessary_nullable_for_final_variable_declarations, unnecessary_string_interpolations, use_build_context_synchronously
21 |
22 | enum Duration {
23 | MINUTE,
24 | HOUR
25 | }
--------------------------------------------------------------------------------
/lib/models/ModelProvider.dart:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License").
5 | * You may not use this file except in compliance with the License.
6 | * A copy of the License is located at
7 | *
8 | * http://aws.amazon.com/apache2.0
9 | *
10 | * or in the "license" file accompanying this file. This file is distributed
11 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 | * express or implied. See the License for the specific language governing
13 | * permissions and limitations under the License.
14 | */
15 |
16 | // NOTE: This file is generated and may not follow lint rules defined in your app
17 | // Generated files can be excluded from analysis in analysis_options.yaml
18 | // For more info, see: https://dart.dev/guides/language/analysis-options#excluding-code-from-analysis
19 |
20 | // ignore_for_file: public_member_api_docs, annotate_overrides, dead_code, dead_codepublic_member_api_docs, depend_on_referenced_packages, file_names, library_private_types_in_public_api, no_leading_underscores_for_library_prefixes, no_leading_underscores_for_local_identifiers, non_constant_identifier_names, null_check_on_nullable_type_parameter, prefer_adjacent_string_concatenation, prefer_const_constructors, prefer_if_null_operators, prefer_interpolation_to_compose_strings, slash_for_doc_comments, sort_child_properties_last, unnecessary_const, unnecessary_constructor_name, unnecessary_late, unnecessary_new, unnecessary_null_aware_assignments, unnecessary_nullable_for_final_variable_declarations, unnecessary_string_interpolations, use_build_context_synchronously
21 |
22 | import 'package:amplify_core/amplify_core.dart' as amplify_core;
23 | import 'Recipe.dart';
24 |
25 | export 'Category.dart';
26 | export 'Duration.dart';
27 | export 'Recipe.dart';
28 |
29 | class ModelProvider implements amplify_core.ModelProviderInterface {
30 | @override
31 | String version = "7d742fe4b47906e294c936dce271bc37";
32 | @override
33 | List modelSchemas = [Recipe.schema];
34 | @override
35 | List customTypeSchemas = [];
36 | static final ModelProvider _instance = ModelProvider();
37 |
38 | static ModelProvider get instance => _instance;
39 |
40 | amplify_core.ModelType getModelTypeByModelName(String modelName) {
41 | switch(modelName) {
42 | case "Recipe":
43 | return Recipe.classType;
44 | default:
45 | throw Exception("Failed to find model in model provider for model name: " + modelName);
46 | }
47 | }
48 | }
49 |
50 |
51 | class ModelFieldValue {
52 | const ModelFieldValue.value(this.value);
53 |
54 | final T value;
55 | }
56 |
--------------------------------------------------------------------------------
/lib/shared/constants/constants.dart:
--------------------------------------------------------------------------------
1 | const double defaultPadding = 16.0;
2 | const double defaultBorderRadius = 14.0;
3 |
--------------------------------------------------------------------------------
/lib/shared/constants/gaps.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | const gapH8 = SizedBox(height: 8);
4 | const gapH16 = SizedBox(height: 16);
5 | const gapH20 = SizedBox(height: 20);
6 | const gapH24 = SizedBox(height: 24);
7 |
8 | const gapW4 = SizedBox(width: 4);
9 | const gapW8 = SizedBox(width: 8);
10 | const gapW16 = SizedBox(width: 16);
11 | const gapW20 = SizedBox(width: 20);
12 | const gapW24 = SizedBox(width: 24);
13 |
--------------------------------------------------------------------------------
/lib/shared/data/authentication_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/shared/data/model/user.dart';
2 |
3 | abstract class AuthenticationRepository {
4 | late String name;
5 |
6 | late String fullName;
7 |
8 | late String email;
9 |
10 | User? currentUser;
11 |
12 | Future logInWithCredentials(String email, String password);
13 |
14 | Future logInWithGoogle();
15 |
16 | Future logInWithFacebook();
17 |
18 | Future signUp(String email, String password, String name);
19 |
20 | Future forgotPassword(String email);
21 |
22 | Future confirmPasswordReset(
23 | String email,
24 | String newPassword,
25 | String confirmationCode,
26 | );
27 |
28 | Future signOut();
29 |
30 | Future confirmUser(String email, String confirmationCode);
31 |
32 | Future isUserLoggedIn();
33 |
34 | Future generateCurrentUserInformation();
35 | }
36 |
--------------------------------------------------------------------------------
/lib/shared/data/database/isar_provider.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/shared/data/model/notification.dart';
2 | import 'package:amplify_recipe/shared/data/model/recipe.dart';
3 | import 'package:amplify_recipe/shared/data/model/search_item.dart';
4 | import 'package:isar/isar.dart';
5 | import 'package:path_provider/path_provider.dart';
6 |
7 | class IsarProvider {
8 | IsarProvider._();
9 |
10 | static late Isar isar;
11 |
12 | void close() {
13 | isar.close();
14 | }
15 |
16 | static Future open() async {
17 | final dir = await getApplicationDocumentsDirectory();
18 | isar = Isar.open(
19 | schemas: [
20 | NotificationSchema,
21 | RecipeSchema,
22 | SearchItemSchema,
23 | ],
24 | directory: dir.path,
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/shared/data/implementation/local_notification_repository.dart:
--------------------------------------------------------------------------------
1 | import 'dart:convert';
2 |
3 | import 'package:amplify_flutter/amplify_flutter.dart';
4 | import 'package:amplify_recipe/shared/data/database/isar_provider.dart';
5 | import 'package:amplify_recipe/shared/data/model/notification.dart';
6 | import 'package:amplify_recipe/shared/data/notification_repository.dart';
7 | import 'package:isar/isar.dart';
8 | import 'package:uuid/uuid.dart';
9 |
10 | class LocalNotificationRepository extends NotificationRepository {
11 | LocalNotificationRepository() {
12 | isar = IsarProvider.isar;
13 | }
14 |
15 | late Isar isar;
16 |
17 | @override
18 | Future> fetchAllNotifications() {
19 | return isar.readAsync>((isar) {
20 | return isar.notifications.where().findAll();
21 | });
22 | }
23 |
24 | @override
25 | Stream listenUnseenNotifications() {
26 | return isar.read>((isar) {
27 | return isar.notifications
28 | .where()
29 | .isSeenEqualTo(false)
30 | .build()
31 | .watch(fireImmediately: true)
32 | .map((notifications) => notifications.isNotEmpty);
33 | });
34 | }
35 |
36 | @override
37 | Future listenNotifications() async {
38 | Amplify.Notifications.Push.onNotificationReceivedInForeground.listen(
39 | (notification) {
40 | final pinpointBody = notification.data['pinpoint.jsonBody'] as String?;
41 | if (pinpointBody != null) {
42 | final recipeJson = json.decode(pinpointBody);
43 | final recipeTitle = recipeJson['recipeTitle'] as String;
44 | final recipeDescription = recipeJson['recipeDescription'] as String;
45 | final recipeId = recipeJson['recipeId'] as String;
46 | saveNotification(
47 | notification.title as String,
48 | notification.body as String,
49 | recipeId,
50 | recipeTitle,
51 | recipeDescription,
52 | null,
53 | );
54 | }
55 | },
56 | );
57 |
58 | Amplify.Notifications.Push.onNotificationOpened.listen(
59 | (notification) {
60 | final pinpointBody = notification.data['pinpoint.jsonBody'] as String?;
61 | if (pinpointBody != null) {
62 | final recipeJson = json.decode(pinpointBody);
63 | final recipeTitle = recipeJson['recipeTitle'] as String;
64 | final recipeDescription = recipeJson['recipeDescription'] as String;
65 | final recipeId = recipeJson['recipeId'] as String;
66 | saveNotification(
67 | notification.title as String,
68 | notification.body as String,
69 | recipeId,
70 | recipeTitle,
71 | recipeDescription,
72 | null,
73 | );
74 | }
75 | },
76 | );
77 | }
78 |
79 | @override
80 | Future markAllAsSeen() async {
81 | isar.write((isar) {
82 | isar.notifications
83 | .where()
84 | .isSeenEqualTo(false)
85 | .build()
86 | .findAll()
87 | .forEach((notification) {
88 | isar.notifications.update(
89 | id: notification.id,
90 | isSeen: true,
91 | );
92 | });
93 | });
94 | }
95 |
96 | @override
97 | Future markAsSeen(String id) async {
98 | isar.write((isar) {
99 | isar.notifications.update(id: id, isSeen: true);
100 | });
101 | }
102 |
103 | @override
104 | Future saveNotification(
105 | String title,
106 | String description,
107 | String recipeId,
108 | String recipeTitle,
109 | String recipeDescription,
110 | String? deepLink,
111 | ) async {
112 | isar.write((isar) {
113 | isar.notifications.put(
114 | Notification(
115 | id: const Uuid().v4(),
116 | title: title,
117 | description: description,
118 | recipeId: recipeId,
119 | recipeTitle: recipeTitle,
120 | recipeDescription: recipeDescription,
121 | deepLink: deepLink,
122 | isSeen: false,
123 | ),
124 | );
125 | });
126 | }
127 |
128 | @override
129 | Future handlePermissions() async {
130 | final status = await Amplify.Notifications.Push.getPermissionStatus();
131 | if (status == PushNotificationPermissionStatus.granted) {
132 | // no further action is required, user has already granted permissions
133 | return;
134 | }
135 | if (status == PushNotificationPermissionStatus.denied) {
136 | return;
137 | }
138 | if (status == PushNotificationPermissionStatus.shouldRequest) {
139 | // go ahead and request permissions from the user
140 | await Amplify.Notifications.Push.requestPermissions();
141 | }
142 | if (status == PushNotificationPermissionStatus.shouldExplainThenRequest) {
143 | // then request permissions
144 | await Amplify.Notifications.Push.requestPermissions();
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/lib/shared/data/implementation/local_search_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/shared/data/database/isar_provider.dart';
2 | import 'package:amplify_recipe/shared/data/model/recipe.dart';
3 | import 'package:amplify_recipe/shared/data/model/search_item.dart';
4 | import 'package:amplify_recipe/shared/data/search_repository.dart';
5 | import 'package:isar/isar.dart';
6 | import 'package:uuid/uuid.dart';
7 |
8 | class LocalSearchRepository extends SearchRepository {
9 | LocalSearchRepository() {
10 | isar = IsarProvider.isar;
11 | }
12 |
13 | late Isar isar;
14 |
15 | @override
16 | Future deleteAllSearchItems() async {
17 | isar.write((isar) => isar.searchItems.clear());
18 | }
19 |
20 | @override
21 | Future saveSearchItem(String searchItem) async {
22 | isar.write((isar) {
23 | isar.searchItems.put(
24 | SearchItem(
25 | id: const Uuid().v4(),
26 | searchItem: searchItem,
27 | createdAt: DateTime.now(),
28 | ),
29 | );
30 | });
31 | }
32 |
33 | @override
34 | Future deleteSearchItemWithId(String id) async {
35 | isar.write((isar) {
36 | isar.searchItems.delete(id);
37 | });
38 | }
39 |
40 | @override
41 | Future> getSearchItems() {
42 | return isar.readAsync((isar) =>
43 | isar.searchItems.where().sortByCreatedAtDesc().findAll(limit: 3));
44 | }
45 |
46 | @override
47 | Future> searchRecipes(String searchTerm) async {
48 | return isar.recipes
49 | .where()
50 | .titleContains(searchTerm, caseSensitive: false)
51 | .or()
52 | .descriptionContains(searchTerm, caseSensitive: false)
53 | .findAll();
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/lib/shared/data/implementation/s3_storage_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_flutter/amplify_flutter.dart';
2 | import 'package:amplify_recipe/shared/data/storage_repository.dart';
3 |
4 | class S3StorageRepository extends StorageRepository {
5 | @override
6 | Future generateDownloadUrl(String key) async {
7 | if (generatedImageLinksPerKey.containsKey(key)) {
8 | return generatedImageLinksPerKey[key]!;
9 | }
10 | final result = await Amplify.Storage.getUrl(
11 | key: key,
12 | options: const StorageGetUrlOptions(
13 | accessLevel: StorageAccessLevel.guest,
14 | ),
15 | ).result;
16 | final link = result.url.toString();
17 | generatedImageLinksPerKey.putIfAbsent(key, () => link);
18 | return link;
19 | }
20 |
21 | @override
22 | Future uploadImage(
23 | String path,
24 | void Function(double progress) onProgressUpdate,
25 | ) async {
26 | final key = UUID.getUUID();
27 | await Amplify.Storage.uploadFile(
28 | localFile: AWSFile.fromPath(path),
29 | key: '$key.jpg',
30 | options: const StorageUploadFileOptions(
31 | accessLevel: StorageAccessLevel.guest,
32 | ),
33 | onProgress: (progress) {
34 | onProgressUpdate(progress.fractionCompleted);
35 | },
36 | ).result;
37 | return key;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/lib/shared/data/model/notification.dart:
--------------------------------------------------------------------------------
1 | import 'package:isar/isar.dart';
2 |
3 | part 'notification.g.dart';
4 |
5 | @collection
6 | class Notification {
7 | Notification({
8 | required this.id,
9 | required this.title,
10 | required this.description,
11 | required this.recipeId,
12 | required this.recipeTitle,
13 | required this.recipeDescription,
14 | required this.isSeen,
15 | this.deepLink,
16 | });
17 |
18 | @Id()
19 | final String id;
20 | final String title;
21 | final String description;
22 | final String recipeId;
23 | final String recipeTitle;
24 | final String recipeDescription;
25 | final String? deepLink;
26 | final bool isSeen;
27 | }
28 |
--------------------------------------------------------------------------------
/lib/shared/data/model/recipe.dart:
--------------------------------------------------------------------------------
1 | import 'package:isar/isar.dart';
2 |
3 | part 'recipe.g.dart';
4 |
5 | @collection
6 | class Recipe {
7 | Recipe({
8 | required this.id,
9 | required this.title,
10 | required this.description,
11 | required this.serve,
12 | required this.duration,
13 | required this.category,
14 | required this.image,
15 | required this.isFavorited,
16 | required this.ingredients,
17 | required this.createdAt,
18 | this.isSynced = false,
19 | });
20 |
21 | @Id()
22 | final String id;
23 | final String title;
24 | final String description;
25 | final int serve;
26 | final String duration;
27 | final String category;
28 | final String image;
29 | final bool isFavorited;
30 | final List ingredients;
31 | final DateTime createdAt;
32 | final bool isSynced;
33 | }
34 |
--------------------------------------------------------------------------------
/lib/shared/data/model/search_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:isar/isar.dart';
2 |
3 | part 'search_item.g.dart';
4 |
5 | @collection
6 | class SearchItem {
7 | SearchItem({
8 | required this.id,
9 | required this.searchItem,
10 | required this.createdAt,
11 | });
12 |
13 | @Id()
14 | final String id;
15 | final String searchItem;
16 | final DateTime createdAt;
17 | }
18 |
--------------------------------------------------------------------------------
/lib/shared/data/model/user.dart:
--------------------------------------------------------------------------------
1 | import 'package:isar/isar.dart';
2 |
3 | part 'user.g.dart';
4 |
5 | @collection
6 | class User {
7 | User({
8 | required this.id,
9 | required this.name,
10 | required this.email,
11 | this.profilePicture,
12 | });
13 |
14 | @Id()
15 | final String id;
16 | final String name;
17 | final String email;
18 | final String? profilePicture;
19 | }
20 |
--------------------------------------------------------------------------------
/lib/shared/data/notification_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/shared/data/model/notification.dart';
2 |
3 | abstract class NotificationRepository {
4 | /// Save notification to the local database
5 | Future saveNotification(
6 | String title,
7 | String description,
8 | String recipeId,
9 | String recipeTitle,
10 | String recipeDescription,
11 | String? deepLink,
12 | );
13 |
14 | Future> fetchAllNotifications();
15 |
16 | /// Mark all notifications as seen
17 | Future markAllAsSeen();
18 |
19 | /// Mark the notification by id as seen
20 | Future markAsSeen(String id);
21 |
22 | /// Listens to all notifications
23 | Future listenNotifications();
24 |
25 | /// Checks if there is any unseen notification
26 | Stream listenUnseenNotifications();
27 |
28 | Future handlePermissions();
29 | }
30 |
--------------------------------------------------------------------------------
/lib/shared/data/recipe_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/shared/data/model/recipe.dart';
2 |
3 | abstract class RecipeRepository {
4 | Future toggleFavoriteForRecipe({
5 | required String id,
6 | required bool isFavorited,
7 | });
8 |
9 | Future deleteRecipe(String id);
10 |
11 | Future updateRecipe(Recipe recipe);
12 |
13 | Future getRecipe(String id);
14 |
15 | Future addRecipe(
16 | String title,
17 | String description,
18 | String duration,
19 | String durationUnit,
20 | String category,
21 | String serves,
22 | String imageKey,
23 | List<(String, String)> ingredients,
24 | );
25 |
26 | Stream> listenRecipes();
27 |
28 | Stream> listenLatestRecipes();
29 |
30 | Stream> listenFavoritedRecipes();
31 | }
32 |
--------------------------------------------------------------------------------
/lib/shared/data/search_repository.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/shared/data/model/recipe.dart';
2 | import 'package:amplify_recipe/shared/data/model/search_item.dart';
3 |
4 | abstract class SearchRepository {
5 | Future> getSearchItems();
6 |
7 | Future> searchRecipes(String searchTerm);
8 |
9 | Future deleteAllSearchItems();
10 |
11 | Future saveSearchItem(String searchItem);
12 |
13 | Future deleteSearchItemWithId(String id);
14 | }
15 |
--------------------------------------------------------------------------------
/lib/shared/data/storage_repository.dart:
--------------------------------------------------------------------------------
1 | abstract class StorageRepository {
2 |
3 | final generatedImageLinksPerKey = {};
4 |
5 | Future uploadImage(
6 | String path,
7 | void Function(double progress) onProgressUpdate,
8 | );
9 |
10 | Future generateDownloadUrl(String key);
11 | }
12 |
--------------------------------------------------------------------------------
/lib/shared/navigation/routes.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/features/authentication/screens/forgot_password_screen.dart';
2 | import 'package:amplify_recipe/features/authentication/screens/onboarding_screen.dart';
3 | import 'package:amplify_recipe/features/authentication/screens/register_screen.dart';
4 | import 'package:amplify_recipe/features/authentication/screens/resend_email_screen.dart';
5 | import 'package:amplify_recipe/features/authentication/screens/sign_in_screen.dart';
6 | import 'package:amplify_recipe/features/authentication/widgets/password_confirmation_form.dart';
7 | import 'package:amplify_recipe/features/authentication/widgets/user_confirmation_form.dart';
8 | import 'package:amplify_recipe/features/details/screens/recipe_details_screen.dart';
9 | import 'package:amplify_recipe/features/entry_point.dart';
10 | import 'package:amplify_recipe/features/favorite/screens/favorite_screen.dart';
11 | import 'package:amplify_recipe/features/notifications/screens/notifications_screen.dart';
12 | import 'package:amplify_recipe/features/profile/screens/edit_profile_screen.dart';
13 | import 'package:amplify_recipe/features/profile/screens/profile_screen.dart';
14 | import 'package:amplify_recipe/features/recipes/screens/search/screen/all_recipes_screen.dart';
15 | import 'package:amplify_recipe/features/recipes/screens/search/screen/search_screen.dart';
16 | import 'package:go_router/go_router.dart';
17 |
18 | final routerConfig = GoRouter(
19 | initialLocation: '/',
20 | routes: [
21 | GoRoute(
22 | path: '/',
23 | builder: (context, state) => const OnboardingScreen(),
24 | ),
25 | GoRoute(
26 | path: '/entry-point',
27 | builder: (context, state) => const EntryPoint(),
28 | ),
29 | GoRoute(
30 | path: '/sign-in',
31 | builder: (context, state) => const SignInScreen(),
32 | ),
33 | GoRoute(
34 | path: '/register',
35 | builder: (context, state) => const RegisterScreen(),
36 | ),
37 | GoRoute(
38 | path: '/forgot-password',
39 | builder: (context, state) => const ForgotPasswordScreen(),
40 | ),
41 | GoRoute(
42 | path: '/password-confirmation/:email',
43 | builder: (context, state) {
44 | final email = state.pathParameters['email'];
45 | if (email == null) {
46 | throw Exception('Recipe ID is missing');
47 | }
48 | return PasswordConfirmationForm(email: email);
49 | },
50 | ),
51 | GoRoute(
52 | path: '/resend-email-verification',
53 | builder: (context, state) => const EmailResendScreen(),
54 | ),
55 | GoRoute(
56 | path: '/user-confirmation/:email',
57 | builder: (context, state) {
58 | final email = state.pathParameters['email'];
59 | if (email == null) {
60 | throw Exception('Recipe ID is missing');
61 | }
62 | return UserConfirmationForm(email: email);
63 | },
64 | ),
65 | GoRoute(
66 | path: '/favorite',
67 | builder: (context, state) => const FavoriteScreen(),
68 | ),
69 | GoRoute(
70 | path: '/recipe/:id',
71 | builder: (context, state) {
72 | final id = state.pathParameters['id'];
73 | if (id == null) {
74 | throw Exception('Recipe ID or Favorite state is missing');
75 | }
76 | return RecipeDetailsScreen(
77 | id: id,
78 | );
79 | },
80 | ),
81 | GoRoute(
82 | path: '/profile',
83 | builder: (context, state) => const ProfileScreen(),
84 | ),
85 | GoRoute(
86 | path: '/edit-profile',
87 | builder: (context, state) => const EditProfileScreen(),
88 | ),
89 | GoRoute(
90 | path: '/all-recipes',
91 | builder: (context, state) => const AllRecipesScreen(),
92 | ),
93 | GoRoute(
94 | path: '/search-recipes',
95 | builder: (context, state) => const SearchScreen(),
96 | ),
97 | GoRoute(
98 | path: '/notifications',
99 | builder: (context, state) => const NotificationsScreen(),
100 | ),
101 | ],
102 | );
103 |
--------------------------------------------------------------------------------
/lib/shared/utils/form_utils.dart:
--------------------------------------------------------------------------------
1 | import 'package:form_field_validator/form_field_validator.dart';
2 |
3 | class FormUtils {
4 | static final requireFieldValidator =
5 | RequiredValidator(errorText: 'This field is required');
6 | static final emailValidator = MultiValidator(
7 | [
8 | RequiredValidator(errorText: 'Email is required'),
9 | EmailValidator(errorText: 'Enter a valid email address'),
10 | ],
11 | );
12 | static final passwordValidator = MultiValidator(
13 | [
14 | RequiredValidator(errorText: 'Password is required'),
15 | MinLengthValidator(8,
16 | errorText: 'Password must be at least 8 digits long'),
17 | ],
18 | );
19 | static MatchValidator matchPassValidator =
20 | MatchValidator(errorText: 'passwords do not match');
21 | }
22 |
--------------------------------------------------------------------------------
/lib/shared/widgets/async_image_loader.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:amplify_recipe/main.dart';
4 | import 'package:amplify_recipe/shared/constants/gaps.dart';
5 | import 'package:amplify_recipe/shared/data/storage_repository.dart';
6 | import 'package:amplify_recipe/themes/app_colors.dart';
7 | import 'package:flutter/material.dart';
8 | import 'package:go_router/go_router.dart';
9 | import 'package:image_picker/image_picker.dart';
10 |
11 | class AsyncImageLoader extends StatefulWidget {
12 | const AsyncImageLoader({
13 | required this.keyOrUrl,
14 | this.onImageChanged,
15 | super.key,
16 | });
17 |
18 | final String keyOrUrl;
19 | final ValueSetter? onImageChanged;
20 |
21 | @override
22 | State createState() => _AsyncImageLoaderState();
23 | }
24 |
25 | class _AsyncImageLoaderState extends State {
26 | String? updatedImage;
27 | final imagePicker = ImagePicker();
28 |
29 | @override
30 | Widget build(BuildContext context) {
31 | return WillPopScope(
32 | onWillPop: () {
33 | if (updatedImage != null) {
34 | showDialog(
35 | context: context,
36 | builder: (context) {
37 | return SimpleDialog(
38 | title: const Text('Updated Image'),
39 | children: [
40 | Padding(
41 | padding: const EdgeInsets.all(8.0),
42 | child: Text(
43 | 'Do you want to update the image?',
44 | style: Theme.of(context).textTheme.titleMedium,
45 | ),
46 | ),
47 | gapH16,
48 | Padding(
49 | padding: const EdgeInsets.all(8.0),
50 | child: ElevatedButton(
51 | onPressed: () {
52 | widget.onImageChanged?.call(updatedImage);
53 | context.pop('/entry-point');
54 | },
55 | child: const Text('Yes'),
56 | ),
57 | ),
58 | Padding(
59 | padding: const EdgeInsets.all(8.0),
60 | child: ElevatedButton(
61 | onPressed: () {
62 | context.go('/entry-point');
63 | },
64 | child: const Text('No'),
65 | ),
66 | ),
67 | ],
68 | );
69 | },
70 | );
71 | }
72 | return Future.value(true);
73 | },
74 | child: GestureDetector(
75 | onTap: () async {
76 | final image = await imagePicker.pickImage(
77 | source: ImageSource.gallery,
78 | );
79 | setState(() {
80 | updatedImage = image?.path;
81 | });
82 | },
83 | child: updatedImage != null
84 | ? Image.file(
85 | File(updatedImage!),
86 | fit: BoxFit.cover,
87 | )
88 | : widget.keyOrUrl.startsWith('http')
89 | ? Image.network(
90 | widget.keyOrUrl,
91 | fit: BoxFit.cover,
92 | )
93 | : FutureBuilder(
94 | future: getIt
95 | .get()
96 | .generateDownloadUrl(widget.keyOrUrl),
97 | builder: (context, snapshot) {
98 | if (snapshot.hasData) {
99 | return Image.network(
100 | snapshot.data!,
101 | fit: BoxFit.cover,
102 | );
103 | } else if (snapshot.hasError) {
104 | return Center(
105 | child: Column(
106 | mainAxisAlignment: MainAxisAlignment.center,
107 | children: [
108 | const Icon(
109 | Icons.error_outline,
110 | color: AppColors.error,
111 | ),
112 | gapH8,
113 | Text(
114 | 'Error loading image',
115 | style: Theme.of(context)
116 | .textTheme
117 | .bodyLarge!
118 | .copyWith(
119 | color: AppColors.error,
120 | ),
121 | ),
122 | ],
123 | ),
124 | );
125 | }
126 | return const Center(
127 | child: CircularProgressIndicator(),
128 | );
129 | },
130 | ),
131 | ),
132 | );
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/lib/shared/widgets/frosted_glass_container.dart:
--------------------------------------------------------------------------------
1 | import 'dart:ui';
2 |
3 | import 'package:flutter/material.dart';
4 |
5 | import '../constants/constants.dart';
6 |
7 | class FrostedGlassContainer extends StatelessWidget {
8 | const FrostedGlassContainer({
9 | super.key,
10 | this.size = const Size(40, 40),
11 | required this.child,
12 | this.borderRadius = defaultBorderRadius / 2,
13 | });
14 |
15 | final Size size;
16 | final Widget child;
17 | final double borderRadius;
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | return ClipRRect(
22 | borderRadius: BorderRadius.circular(borderRadius),
23 | child: BackdropFilter(
24 | filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
25 | child: Container(
26 | height: size.height,
27 | width: size.width,
28 | decoration: BoxDecoration(
29 | color: Colors.white.withOpacity(0.2),
30 | borderRadius: BorderRadius.circular(borderRadius),
31 | ),
32 | child: child,
33 | ),
34 | ),
35 | );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/lib/shared/widgets/recipe_card.dart:
--------------------------------------------------------------------------------
1 | import 'package:amplify_recipe/shared/widgets/async_image_loader.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | import '../../themes/app_colors.dart';
5 | import '../constants/constants.dart';
6 | import 'frosted_glass_container.dart';
7 |
8 | class RecipeCard extends StatelessWidget {
9 | const RecipeCard({
10 | super.key,
11 | required this.title,
12 | required this.image,
13 | required this.category,
14 | required this.duration,
15 | this.serve = 1,
16 | this.isFavorited = false,
17 | required this.press,
18 | required this.onBookmarked,
19 | this.onDismissed,
20 | });
21 |
22 | final String title, image, category, duration;
23 | final int serve;
24 | final bool isFavorited;
25 | final VoidCallback press;
26 | final VoidCallback onBookmarked;
27 | final DismissDirectionCallback? onDismissed;
28 |
29 | @override
30 | Widget build(BuildContext context) {
31 | return GestureDetector(
32 | onTap: press,
33 | child: Container(
34 | height: 200,
35 | width: double.infinity,
36 | clipBehavior: Clip.hardEdge,
37 | decoration: BoxDecoration(
38 | borderRadius: BorderRadius.circular(defaultBorderRadius),
39 | ),
40 | child: Dismissible(
41 | key: ValueKey(title),
42 | background: Container(
43 | padding: const EdgeInsets.only(
44 | right: 16,
45 | ),
46 | color: Theme.of(context).colorScheme.error,
47 | alignment: Alignment.centerRight,
48 | child: const Icon(
49 | Icons.delete,
50 | color: Colors.white,
51 | ),
52 | ),
53 | onDismissed: onDismissed,
54 | child: Stack(
55 | fit: StackFit.expand,
56 | children: [
57 | AsyncImageLoader(keyOrUrl: image),
58 | Container(
59 | decoration: const BoxDecoration(
60 | gradient: LinearGradient(
61 | begin: Alignment.topCenter,
62 | end: Alignment.bottomCenter,
63 | colors: [
64 | Colors.transparent,
65 | Colors.black54,
66 | ],
67 | ),
68 | ),
69 | ),
70 | Positioned(
71 | left: 0,
72 | right: 0,
73 | bottom: 0,
74 | child: ListTile(
75 | title: Text(
76 | title,
77 | style: Theme.of(context)
78 | .textTheme
79 | .titleLarge!
80 | .copyWith(color: Colors.white),
81 | ),
82 | subtitle: Text(
83 | '$duration | $serve Serve',
84 | style: const TextStyle(color: Colors.white),
85 | ),
86 | trailing: GestureDetector(
87 | onTap: onBookmarked,
88 | child: FrostedGlassContainer(
89 | child: Icon(
90 | Icons.bookmark_border,
91 | color: isFavorited ? AppColors.success : Colors.white,
92 | ),
93 | ),
94 | ),
95 | ),
96 | ),
97 | Positioned(
98 | top: 16,
99 | left: 16,
100 | height: 32,
101 | width: 72,
102 | child: FrostedGlassContainer(
103 | borderRadius: 20,
104 | child: Center(
105 | child: Text(
106 | category,
107 | style: const TextStyle(color: Colors.white),
108 | ),
109 | ),
110 | ),
111 | )
112 | ],
113 | ),
114 | ),
115 | ),
116 | );
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/lib/shared/widgets/section_list_tile.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../../themes/app_colors.dart';
4 |
5 | class SectionListTile extends StatelessWidget {
6 | const SectionListTile({
7 | super.key,
8 | required this.title,
9 | this.trailingText = 'See all',
10 | required this.press,
11 | });
12 |
13 | final String title, trailingText;
14 | final VoidCallback press;
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | return Row(
19 | children: [
20 | Text(
21 | title,
22 | style: Theme.of(context)
23 | .textTheme
24 | .titleMedium!
25 | .copyWith(fontWeight: FontWeight.w600),
26 | ),
27 | const Spacer(),
28 | TextButton(
29 | onPressed: press,
30 | style: TextButton.styleFrom(foregroundColor: AppColors.bodyText),
31 | child: Text(trailingText),
32 | )
33 | ],
34 | );
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lib/themes/app_colors.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class AppColors {
4 | static const Color primary = Color(0xFF22A45D);
5 | static const Color text = Color(0xFF010F07);
6 | static const Color bodyText = Color(0xFF757575);
7 | static const Color inputfieldBg = Color(0xFFF6F8FC);
8 | static const Color success = Color(0xFF43B726);
9 | static const Color error = Color(0xFFE23D24);
10 | }
11 |
--------------------------------------------------------------------------------
/lib/themes/app_theme.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 | import 'package:google_fonts/google_fonts.dart';
4 |
5 | import '../shared/constants/constants.dart';
6 | import 'app_colors.dart';
7 |
8 | class AppTheme {
9 | static ThemeData lightTheme(BuildContext context) {
10 | return ThemeData(
11 | primaryColor: AppColors.primary,
12 | scaffoldBackgroundColor: Colors.white,
13 | textTheme: GoogleFonts.poppinsTextTheme(Theme.of(context).textTheme)
14 | .copyWith(bodyMedium: const TextStyle(color: AppColors.bodyText)),
15 | appBarTheme: AppBarTheme(
16 | backgroundColor: Colors.white,
17 | elevation: 0,
18 | systemOverlayStyle: SystemUiOverlayStyle.dark,
19 | foregroundColor: AppColors.text,
20 | titleTextStyle: Theme.of(context).textTheme.titleLarge!.copyWith(
21 | fontWeight: FontWeight.normal,
22 | ),
23 | ),
24 | floatingActionButtonTheme: const FloatingActionButtonThemeData(
25 | backgroundColor: AppColors.primary,
26 | ),
27 | elevatedButtonTheme: ElevatedButtonThemeData(
28 | style: ElevatedButton.styleFrom(
29 | backgroundColor: AppColors.primary,
30 | minimumSize: const Size(double.infinity, 48),
31 | shape: RoundedRectangleBorder(
32 | borderRadius: BorderRadius.circular(defaultBorderRadius),
33 | ),
34 | ),
35 | ),
36 | outlinedButtonTheme: OutlinedButtonThemeData(
37 | style: OutlinedButton.styleFrom(
38 | minimumSize: const Size(double.infinity, 48),
39 | foregroundColor: AppColors.text,
40 | shape: RoundedRectangleBorder(
41 | borderRadius: BorderRadius.circular(defaultBorderRadius),
42 | ),
43 | side: const BorderSide(color: AppColors.bodyText),
44 | ),
45 | ),
46 | inputDecorationTheme: const InputDecorationTheme(
47 | filled: true,
48 | fillColor: AppColors.inputfieldBg,
49 | border: defaultOutlineInputBorder,
50 | enabledBorder: defaultOutlineInputBorder,
51 | focusedBorder: defaultOutlineInputBorder,
52 | ),
53 | );
54 | }
55 | }
56 |
57 | const defaultOutlineInputBorder = OutlineInputBorder(
58 | borderRadius: BorderRadius.all(Radius.circular(defaultBorderRadius)),
59 | borderSide: BorderSide.none,
60 | );
61 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: amplify_recipe
2 | description: A new Flutter project.
3 | publish_to: "none" # Remove this line if you wish to publish to pub.dev
4 |
5 | version: 1.0.0+1
6 |
7 | environment:
8 | sdk: ">=3.0.0 <4.0.0"
9 |
10 | dependencies:
11 | flutter:
12 | sdk: flutter
13 |
14 | cupertino_icons: ^1.0.5
15 | flutter_svg: ^2.0.5
16 | google_fonts: ^4.0.4
17 | form_field_validator: ^1.1.0
18 | intl: ^0.18.1
19 | image_picker: ^1.0.1
20 | get_it: ^7.6.0
21 | isar: ^4.0.0-0
22 | isar_flutter_libs: ^4.0.0-0
23 | path_provider: ^2.0.15
24 | go_router: ^9.1.0
25 | uuid: ^3.0.7
26 | amplify_flutter: ^1.3.1
27 | amplify_auth_cognito: ^1.3.1
28 | amplify_api: ^1.3.2
29 | amplify_storage_s3: ^1.3.2
30 | amplify_push_notifications_pinpoint: ^1.3.1
31 |
32 | dev_dependencies:
33 | flutter_lints: ^2.0.1
34 | build_runner: ^2.4.6
35 | flutter:
36 | uses-material-design: true
37 |
38 | assets:
39 | - assets/icons/
40 | - assets/images/
--------------------------------------------------------------------------------