├── .gitignore ├── .metadata ├── .vscode ├── launch.json └── settings.json ├── README.md ├── amplify ├── .config │ └── project-config.json ├── README.md ├── backend │ ├── api │ │ └── FlutterAmplifyApi │ │ │ ├── parameters.json │ │ │ ├── schema.graphql │ │ │ ├── stacks │ │ │ └── CustomResources.json │ │ │ └── transform.conf.json │ ├── auth │ │ └── flutteramplify586ac13a │ │ │ ├── flutteramplify586ac13a-cloudformation-template.yml │ │ │ └── parameters.json │ ├── backend-config.json │ ├── storage │ │ └── s324fe3c07 │ │ │ ├── parameters.json │ │ │ ├── s3-cloudformation-template.json │ │ │ └── storage-params.json │ └── tags.json ├── cli.json └── team-provider-info.json ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── flutter_amplify_demo │ │ │ │ └── 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 ├── demo.gif ├── fonts │ └── Poppins │ │ ├── OFL.txt │ │ ├── Poppins-Black.ttf │ │ ├── Poppins-Bold.ttf │ │ ├── Poppins-ExtraBold.ttf │ │ ├── Poppins-ExtraLight.ttf │ │ ├── Poppins-Italic.ttf │ │ ├── Poppins-Light.ttf │ │ ├── Poppins-Medium.ttf │ │ ├── Poppins-Regular.ttf │ │ ├── Poppins-SemiBold.ttf │ │ └── Poppins-Thin.ttf ├── images │ ├── bg_chat.jpg │ ├── splash.png │ └── splash.svg └── title_image.png ├── clean.sh ├── 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 ├── lib ├── components │ ├── get_name_bottom_sheet.dart │ ├── google_login_button.dart │ ├── login_text_field.dart │ ├── primary_button.dart │ ├── secondary_button.dart │ ├── show_snackbar.dart │ ├── textarea.dart │ └── update_message_bottom_sheet.dart ├── main.dart ├── models │ ├── ChatData.dart │ ├── ModelProvider.dart │ └── Users.dart ├── repositories │ ├── chat_repository.dart │ └── user_repository.dart ├── routes │ ├── routes_generator.dart │ └── routes_path.dart ├── screens │ ├── ChatScreen │ │ ├── ChatDetails │ │ │ └── appbar.dart │ │ ├── chat_detail_page.dart │ │ └── chat_page.dart │ ├── LoginScreen │ │ └── login_screen.dart │ ├── OtpScreen │ │ └── otp_screen.dart │ ├── RegisterScreen │ │ └── register_screen.dart │ ├── SettingsScreen │ │ ├── components │ │ │ ├── image_picker.dart │ │ │ └── users_list.dart │ │ └── settings_page.dart │ ├── SplashScreen │ │ └── splash_screen.dart │ ├── StatusScreens │ │ └── status_page.dart │ └── home_screen.dart ├── services │ ├── amplify_services.dart │ ├── get_it_service.dart │ ├── navigation_service.dart │ ├── size_config.dart │ ├── text_style.dart │ └── validations.dart ├── stores │ ├── auth.dart │ ├── chat.dart │ ├── state_keeper.dart │ └── user.dart ├── theme │ └── colors.dart └── wrapper.dart ├── pubspec.lock ├── pubspec.yaml ├── test └── widget_test.dart └── web ├── favicon.png ├── icons ├── Icon-192.png └── Icon-512.png ├── index.html └── manifest.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | 48 | #amplify 49 | amplify/\#current-cloud-backend 50 | amplify/.config/local-* 51 | amplify/logs 52 | amplify/mock-data 53 | amplify/backend/amplify-meta.json 54 | amplify/backend/awscloudformation 55 | amplify/backend/.temp 56 | build/ 57 | dist/ 58 | node_modules/ 59 | aws-exports.js 60 | awsconfiguration.json 61 | amplifyconfiguration.json 62 | amplify-build-config.json 63 | amplify-gradle-config.json 64 | amplifytools.xcconfig 65 | .secret-* 66 | 67 | creds 68 | 69 | lib/amplifyconfiguration.dart 70 | android/key.jks -------------------------------------------------------------------------------- /.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 and should not be manually edited. 5 | 6 | version: 7 | revision: c5a4b4029c0798f37c4a39b479d7cb75daa7b05c 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "flutter_amplify_demo", 9 | "request": "launch", 10 | "type": "dart" 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "amplify/.config": true, 4 | "amplify/**/*-parameters.json": true, 5 | "amplify/**/amplify.state": true, 6 | "amplify/**/transform.conf.json": true, 7 | "amplify/#current-cloud-backend": true, 8 | "amplify/backend/amplify-meta.json": true, 9 | "amplify/backend/awscloudformation": true 10 | } 11 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 |

Logo

Whatsapp Clone using Amplify DataStore in Flutter

7 | 8 | ## About The Project 9 | 10 | ![product-screenshot](./assets/title_image.png) 11 | 12 | ### Built With 13 | 14 | - [Flutter](https://flutter.dev/) 15 | - [AWS Amplify DataStore](https://docs.amplify.aws/lib/datastore/getting-started/q/platform/flutter) 16 | 17 | 18 | 19 | ## Getting Started 20 | 21 | To get a local copy up and running follow these simple steps. 22 | 23 | ### Prerequisites 24 | 25 | - You should have Flutter installed in your system. 26 | - AWS account is required to provision all the resources. 27 | - AWS Amplify CLI should be installed and configured with AWS Account. 28 | 29 | ### Installation 30 | 31 | 1. Clone the repo 32 | 33 | ```sh 34 | git clone https://github.com/GeekyAnts/flutter_amplify_datastore_demo 35 | ``` 36 | 37 | 2. Install NPM packages 38 | 39 | ```sh 40 | flutter pub get 41 | ``` 42 | 43 | 3. Run Amplify init to initialize Amplify project and app api to provision resource 44 | 45 | ```sh 46 | amplify init 47 | amplify add api 48 | amplify add auth 49 | // Choose email and password authentication. 50 | ``` 51 | 52 | 4. Replace graphql schema in `amplify/backend/api/FlutterAmplifyApi/schema.graphql` file with below content. 53 | 54 | ```graphql 55 | type ChatRoom 56 | @model 57 | @auth(rules: [{ allow: public }]) 58 | @key(name: "byUser", fields: ["userID"]) { 59 | id: ID! 60 | otherUserId: String 61 | otherUserName: String 62 | userID: ID 63 | chatId: String 64 | untitledfield: String 65 | } 66 | 67 | type ChatData @model @auth(rules: [{ allow: public }]) { 68 | id: ID! 69 | message: String 70 | createdAt: AWSDateTime 71 | chatRoomId: String 72 | senderId: String 73 | } 74 | 75 | type User @model @auth(rules: [{ allow: public }]) { 76 | id: ID! 77 | username: String! 78 | email: String 79 | bio: String 80 | profileImage: String 81 | isVerified: Boolean 82 | createdAt: AWSDateTime 83 | chats: AWSJSON 84 | ChatRooms: [ChatRoom] @connection(keyName: "byUser", fields: ["id"]) 85 | } 86 | ``` 87 | 88 | 89 | 90 | ## Contributing 91 | 92 | Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**. 93 | 94 | 1. Fork the Project 95 | 2. Create your Feature Branch (`git checkout -b feature/flutter_amplify_datastore_demo`) 96 | 3. Commit your Changes (`git commit -m 'Add some flutter_amplify_datastore_demo'`) 97 | 4. Push to the Branch (`git push origin feature/flutter_amplify_datastore_demo`) 98 | 5. Open a Pull Request 99 | -------------------------------------------------------------------------------- /amplify/.config/project-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "flutterAmplify", 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/api/FlutterAmplifyApi/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "AppSyncApiName": "FlutterAmplifyApi", 3 | "DynamoDBBillingMode": "PAY_PER_REQUEST", 4 | "DynamoDBEnableServerSideEncryption": false 5 | } -------------------------------------------------------------------------------- /amplify/backend/api/FlutterAmplifyApi/schema.graphql: -------------------------------------------------------------------------------- 1 | type Chatdata @model @auth(rules: [{allow: public}]) { 2 | id: ID! 3 | message: String 4 | createdAt: AWSTimestamp 5 | updatedAt: AWSTimestamp 6 | chatId: String 7 | senderId: String 8 | } 9 | 10 | type Users @model @auth(rules: [{allow: public}]) { 11 | id: ID! 12 | username: String 13 | email: AWSEmail 14 | bio: String 15 | profileImage: AWSURL 16 | isVerified: Boolean 17 | createdAt: AWSTimestamp 18 | chats: [AWSJSON] 19 | } 20 | -------------------------------------------------------------------------------- /amplify/backend/api/FlutterAmplifyApi/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/FlutterAmplifyApi/transform.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": 5, 3 | "ElasticsearchWarning": true, 4 | "ResolverConfig": { 5 | "project": { 6 | "ConflictHandler": "AUTOMERGE", 7 | "ConflictDetection": "VERSION" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /amplify/backend/auth/flutteramplify586ac13a/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "identityPoolName": "flutteramplify586ac13a_identitypool_586ac13a", 3 | "allowUnauthenticatedIdentities": false, 4 | "resourceNameTruncated": "flutte586ac13a", 5 | "userPoolName": "flutteramplify586ac13a_userpool_586ac13a", 6 | "autoVerifiedAttributes": [ 7 | "email" 8 | ], 9 | "mfaConfiguration": "OFF", 10 | "mfaTypes": [ 11 | "SMS Text Message" 12 | ], 13 | "smsAuthenticationMessage": "Your authentication code is {####}", 14 | "smsVerificationMessage": "Your verification code is {####}", 15 | "emailVerificationSubject": "Your verification code", 16 | "emailVerificationMessage": "Your verification code is {####}", 17 | "defaultPasswordPolicy": false, 18 | "passwordPolicyMinLength": 8, 19 | "passwordPolicyCharacters": [], 20 | "requiredAttributes": [ 21 | "email" 22 | ], 23 | "userpoolClientGenerateSecret": false, 24 | "userpoolClientRefreshTokenValidity": 30, 25 | "userpoolClientWriteAttributes": [], 26 | "userpoolClientReadAttributes": [], 27 | "userpoolClientLambdaRole": "flutte586ac13a_userpoolclient_lambda_role", 28 | "userpoolClientSetAttributes": false, 29 | "sharedId": "586ac13a", 30 | "resourceName": "flutteramplify586ac13a", 31 | "authSelections": "identityPoolAndUserPool", 32 | "authRoleArn": { 33 | "Fn::GetAtt": [ 34 | "AuthRole", 35 | "Arn" 36 | ] 37 | }, 38 | "unauthRoleArn": { 39 | "Fn::GetAtt": [ 40 | "UnauthRole", 41 | "Arn" 42 | ] 43 | }, 44 | "useDefault": "manual", 45 | "usernameAttributes": [ 46 | "email, phone_number" 47 | ], 48 | "triggers": "{}", 49 | "userPoolGroupList": [], 50 | "serviceName": "Cognito", 51 | "usernameCaseSensitive": false, 52 | "parentStack": { 53 | "Ref": "AWS::StackId" 54 | }, 55 | "permissions": [], 56 | "dependsOn": [], 57 | "hostedUI": true, 58 | "hostedUIDomainName": "flutteramplify70fe2805-70fe2805", 59 | "authProvidersUserPool": [ 60 | "Google" 61 | ], 62 | "hostedUIProviderMeta": "[{\"ProviderName\":\"Google\",\"authorize_scopes\":\"openid email profile\",\"AttributeMapping\":{\"email\":\"email\",\"username\":\"sub\"}}]", 63 | "oAuthMetadata": "{\"AllowedOAuthFlows\":[\"code\"],\"AllowedOAuthScopes\":[\"phone\",\"email\",\"openid\",\"profile\",\"aws.cognito.signin.user.admin\"],\"CallbackURLs\":[\"myapp://\"],\"LogoutURLs\":[\"myapp://\"]}", 64 | "authProviders": [], 65 | "userPoolGroups": false, 66 | "adminQueries": false, 67 | "thirdPartyAuth": false 68 | } -------------------------------------------------------------------------------- /amplify/backend/backend-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "api": { 3 | "FlutterAmplifyApi": { 4 | "service": "AppSync", 5 | "providerPlugin": "awscloudformation", 6 | "output": { 7 | "authConfig": { 8 | "defaultAuthentication": { 9 | "authenticationType": "API_KEY", 10 | "apiKeyConfig": { 11 | "apiKeyExpirationDays": 30, 12 | "description": "api key description" 13 | } 14 | }, 15 | "additionalAuthenticationProviders": [ 16 | { 17 | "authenticationType": "AWS_IAM" 18 | } 19 | ] 20 | } 21 | } 22 | } 23 | }, 24 | "auth": { 25 | "flutteramplify586ac13a": { 26 | "service": "Cognito", 27 | "providerPlugin": "awscloudformation", 28 | "dependsOn": [], 29 | "customAuth": false 30 | } 31 | }, 32 | "storage": { 33 | "s324fe3c07": { 34 | "service": "S3", 35 | "providerPlugin": "awscloudformation" 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /amplify/backend/storage/s324fe3c07/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "bucketName": "flutteramplify376c099e0a9a4a87979592a7eae6cb1d", 3 | "authPolicyName": "s3_amplify_24fe3c07", 4 | "unauthPolicyName": "s3_amplify_24fe3c07", 5 | "authRoleName": { 6 | "Ref": "AuthRoleName" 7 | }, 8 | "unauthRoleName": { 9 | "Ref": "UnauthRoleName" 10 | }, 11 | "selectedGuestPermissions": [ 12 | "s3:GetObject", 13 | "s3:ListBucket" 14 | ], 15 | "selectedAuthenticatedPermissions": [ 16 | "s3:PutObject", 17 | "s3:GetObject", 18 | "s3:ListBucket", 19 | "s3:DeleteObject" 20 | ], 21 | "s3PermissionsAuthenticatedPublic": "s3:PutObject,s3:GetObject,s3:DeleteObject", 22 | "s3PublicPolicy": "Public_policy_c94f4f45", 23 | "s3PermissionsAuthenticatedUploads": "s3:PutObject", 24 | "s3UploadsPolicy": "Uploads_policy_c94f4f45", 25 | "s3PermissionsAuthenticatedProtected": "s3:PutObject,s3:GetObject,s3:DeleteObject", 26 | "s3ProtectedPolicy": "Protected_policy_c94f4f45", 27 | "s3PermissionsAuthenticatedPrivate": "s3:PutObject,s3:GetObject,s3:DeleteObject", 28 | "s3PrivatePolicy": "Private_policy_c94f4f45", 29 | "AuthenticatedAllowList": "ALLOW", 30 | "s3ReadPolicy": "read_policy_c94f4f45", 31 | "s3PermissionsGuestPublic": "DISALLOW", 32 | "s3PermissionsGuestUploads": "DISALLOW", 33 | "GuestAllowList": "DISALLOW", 34 | "triggerFunction": "NONE" 35 | } -------------------------------------------------------------------------------- /amplify/backend/storage/s324fe3c07/s3-cloudformation-template.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Description": "S3 resource stack creation using Amplify CLI", 4 | "Parameters": { 5 | "bucketName": { 6 | "Type": "String" 7 | }, 8 | "authPolicyName": { 9 | "Type": "String" 10 | }, 11 | "unauthPolicyName": { 12 | "Type": "String" 13 | }, 14 | "authRoleName": { 15 | "Type": "String" 16 | }, 17 | "unauthRoleName": { 18 | "Type": "String" 19 | }, 20 | "s3PublicPolicy": { 21 | "Type": "String", 22 | "Default" : "NONE" 23 | }, 24 | "s3PrivatePolicy": { 25 | "Type": "String", 26 | "Default" : "NONE" 27 | }, 28 | "s3ProtectedPolicy": { 29 | "Type": "String", 30 | "Default" : "NONE" 31 | }, 32 | "s3UploadsPolicy": { 33 | "Type": "String", 34 | "Default" : "NONE" 35 | }, 36 | "s3ReadPolicy": { 37 | "Type": "String", 38 | "Default" : "NONE" 39 | }, 40 | "s3PermissionsAuthenticatedPublic": { 41 | "Type": "String", 42 | "Default" : "DISALLOW" 43 | }, 44 | "s3PermissionsAuthenticatedProtected": { 45 | "Type": "String", 46 | "Default" : "DISALLOW" 47 | }, 48 | "s3PermissionsAuthenticatedPrivate": { 49 | "Type": "String", 50 | "Default" : "DISALLOW" 51 | }, 52 | "s3PermissionsAuthenticatedUploads": { 53 | "Type": "String", 54 | "Default" : "DISALLOW" 55 | }, 56 | "s3PermissionsGuestPublic": { 57 | "Type": "String", 58 | "Default" : "DISALLOW" 59 | }, 60 | "s3PermissionsGuestUploads": { 61 | "Type": "String", 62 | "Default" : "DISALLOW" }, 63 | "AuthenticatedAllowList": { 64 | "Type": "String", 65 | "Default" : "DISALLOW" 66 | }, 67 | "GuestAllowList": { 68 | "Type": "String", 69 | "Default" : "DISALLOW" 70 | }, 71 | "selectedGuestPermissions": { 72 | "Type": "CommaDelimitedList", 73 | "Default" : "NONE" 74 | }, 75 | "selectedAuthenticatedPermissions": { 76 | "Type": "CommaDelimitedList", 77 | "Default" : "NONE" 78 | }, 79 | "env": { 80 | "Type": "String" 81 | }, 82 | "triggerFunction": { 83 | "Type": "String" 84 | } 85 | 86 | 87 | }, 88 | "Conditions": { 89 | "ShouldNotCreateEnvResources": { 90 | "Fn::Equals": [ 91 | { 92 | "Ref": "env" 93 | }, 94 | "NONE" 95 | ] 96 | }, 97 | "CreateAuthPublic": { 98 | "Fn::Not" : [{ 99 | "Fn::Equals" : [ 100 | {"Ref" : "s3PermissionsAuthenticatedPublic"}, 101 | "DISALLOW" 102 | ] 103 | }] 104 | }, 105 | "CreateAuthProtected": { 106 | "Fn::Not" : [{ 107 | "Fn::Equals" : [ 108 | {"Ref" : "s3PermissionsAuthenticatedProtected"}, 109 | "DISALLOW" 110 | ] 111 | }] 112 | }, 113 | "CreateAuthPrivate": { 114 | "Fn::Not" : [{ 115 | "Fn::Equals" : [ 116 | {"Ref" : "s3PermissionsAuthenticatedPrivate"}, 117 | "DISALLOW" 118 | ] 119 | }] 120 | }, 121 | "CreateAuthUploads": { 122 | "Fn::Not" : [{ 123 | "Fn::Equals" : [ 124 | {"Ref" : "s3PermissionsAuthenticatedUploads"}, 125 | "DISALLOW" 126 | ] 127 | }] 128 | }, 129 | "CreateGuestPublic": { 130 | "Fn::Not" : [{ 131 | "Fn::Equals" : [ 132 | {"Ref" : "s3PermissionsGuestPublic"}, 133 | "DISALLOW" 134 | ] 135 | }] 136 | }, 137 | "CreateGuestUploads": { 138 | "Fn::Not" : [{ 139 | "Fn::Equals" : [ 140 | {"Ref" : "s3PermissionsGuestUploads"}, 141 | "DISALLOW" 142 | ] 143 | }] 144 | }, 145 | "AuthReadAndList": { 146 | "Fn::Not" : [{ 147 | "Fn::Equals" : [ 148 | {"Ref" : "AuthenticatedAllowList"}, 149 | "DISALLOW" 150 | ] 151 | }] 152 | }, 153 | "GuestReadAndList": { 154 | "Fn::Not" : [{ 155 | "Fn::Equals" : [ 156 | {"Ref" : "GuestAllowList"}, 157 | "DISALLOW" 158 | ] 159 | }] 160 | } 161 | }, 162 | "Resources": { 163 | "S3Bucket": { 164 | "Type": "AWS::S3::Bucket", 165 | 166 | "DeletionPolicy" : "Retain", 167 | "Properties": { 168 | "BucketName": { 169 | "Fn::If": [ 170 | "ShouldNotCreateEnvResources", 171 | { 172 | "Ref": "bucketName" 173 | }, 174 | { 175 | "Fn::Join": [ 176 | "", 177 | [ 178 | { 179 | "Ref": "bucketName" 180 | }, 181 | { 182 | "Fn::Select": [ 183 | 3, 184 | { 185 | "Fn::Split": [ 186 | "-", 187 | { 188 | "Ref": "AWS::StackName" 189 | } 190 | ] 191 | } 192 | ] 193 | }, 194 | "-", 195 | { 196 | "Ref": "env" 197 | } 198 | ] 199 | ] 200 | } 201 | ] 202 | }, 203 | 204 | "CorsConfiguration": { 205 | "CorsRules": [ 206 | { 207 | "AllowedHeaders": [ 208 | "*" 209 | ], 210 | "AllowedMethods": [ 211 | "GET", 212 | "HEAD", 213 | "PUT", 214 | "POST", 215 | "DELETE" 216 | ], 217 | "AllowedOrigins": [ 218 | "*" 219 | ], 220 | "ExposedHeaders": [ 221 | "x-amz-server-side-encryption", 222 | "x-amz-request-id", 223 | "x-amz-id-2", 224 | "ETag" 225 | ], 226 | "Id": "S3CORSRuleId1", 227 | "MaxAge": "3000" 228 | } 229 | ] 230 | } 231 | } 232 | }, 233 | 234 | 235 | "S3AuthPublicPolicy": { 236 | "DependsOn": [ 237 | "S3Bucket" 238 | ], 239 | "Condition": "CreateAuthPublic", 240 | "Type": "AWS::IAM::Policy", 241 | "Properties": { 242 | "PolicyName": { 243 | "Ref": "s3PublicPolicy" 244 | }, 245 | "Roles": [ 246 | { 247 | "Ref": "authRoleName" 248 | } 249 | ], 250 | "PolicyDocument": { 251 | "Version": "2012-10-17", 252 | "Statement": [ 253 | { 254 | "Effect": "Allow", 255 | "Action": { 256 | "Fn::Split" : [ "," , { 257 | "Ref": "s3PermissionsAuthenticatedPublic" 258 | } ] 259 | }, 260 | "Resource": [ 261 | { 262 | "Fn::Join": [ 263 | "", 264 | [ 265 | "arn:aws:s3:::", 266 | { 267 | "Ref": "S3Bucket" 268 | }, 269 | "/public/*" 270 | ] 271 | ] 272 | } 273 | ] 274 | } 275 | ] 276 | } 277 | } 278 | }, 279 | "S3AuthProtectedPolicy": { 280 | "DependsOn": [ 281 | "S3Bucket" 282 | ], 283 | "Condition": "CreateAuthProtected", 284 | "Type": "AWS::IAM::Policy", 285 | "Properties": { 286 | "PolicyName": { 287 | "Ref": "s3ProtectedPolicy" 288 | }, 289 | "Roles": [ 290 | { 291 | "Ref": "authRoleName" 292 | } 293 | ], 294 | "PolicyDocument": { 295 | "Version": "2012-10-17", 296 | "Statement": [ 297 | { 298 | "Effect": "Allow", 299 | "Action": { 300 | "Fn::Split" : [ "," , { 301 | "Ref": "s3PermissionsAuthenticatedProtected" 302 | } ] 303 | }, 304 | "Resource": [ 305 | { 306 | "Fn::Join": [ 307 | "", 308 | [ 309 | "arn:aws:s3:::", 310 | { 311 | "Ref": "S3Bucket" 312 | }, 313 | "/protected/${cognito-identity.amazonaws.com:sub}/*" 314 | ] 315 | ] 316 | } 317 | ] 318 | } 319 | ] 320 | } 321 | } 322 | }, 323 | "S3AuthPrivatePolicy": { 324 | "DependsOn": [ 325 | "S3Bucket" 326 | ], 327 | "Condition": "CreateAuthPrivate", 328 | "Type": "AWS::IAM::Policy", 329 | "Properties": { 330 | "PolicyName": { 331 | "Ref": "s3PrivatePolicy" 332 | }, 333 | "Roles": [ 334 | { 335 | "Ref": "authRoleName" 336 | } 337 | ], 338 | "PolicyDocument": { 339 | "Version": "2012-10-17", 340 | "Statement": [ 341 | { 342 | "Effect": "Allow", 343 | "Action": { 344 | "Fn::Split" : [ "," , { 345 | "Ref": "s3PermissionsAuthenticatedPrivate" 346 | } ] 347 | }, 348 | "Resource": [ 349 | { 350 | "Fn::Join": [ 351 | "", 352 | [ 353 | "arn:aws:s3:::", 354 | { 355 | "Ref": "S3Bucket" 356 | }, 357 | "/private/${cognito-identity.amazonaws.com:sub}/*" 358 | ] 359 | ] 360 | } 361 | ] 362 | } 363 | ] 364 | } 365 | } 366 | }, 367 | "S3AuthUploadPolicy": { 368 | "DependsOn": [ 369 | "S3Bucket" 370 | ], 371 | "Condition": "CreateAuthUploads", 372 | "Type": "AWS::IAM::Policy", 373 | "Properties": { 374 | "PolicyName": { 375 | "Ref": "s3UploadsPolicy" 376 | }, 377 | "Roles": [ 378 | { 379 | "Ref": "authRoleName" 380 | } 381 | ], 382 | "PolicyDocument": { 383 | "Version": "2012-10-17", 384 | "Statement": [ 385 | { 386 | "Effect": "Allow", 387 | "Action": { 388 | "Fn::Split" : [ "," , { 389 | "Ref": "s3PermissionsAuthenticatedUploads" 390 | } ] 391 | }, 392 | "Resource": [ 393 | { 394 | "Fn::Join": [ 395 | "", 396 | [ 397 | "arn:aws:s3:::", 398 | { 399 | "Ref": "S3Bucket" 400 | }, 401 | "/uploads/*" 402 | ] 403 | ] 404 | } 405 | ] 406 | } 407 | ] 408 | } 409 | } 410 | }, 411 | "S3AuthReadPolicy": { 412 | "DependsOn": [ 413 | "S3Bucket" 414 | ], 415 | "Condition": "AuthReadAndList", 416 | "Type": "AWS::IAM::Policy", 417 | "Properties": { 418 | "PolicyName": { 419 | "Ref": "s3ReadPolicy" 420 | }, 421 | "Roles": [ 422 | { 423 | "Ref": "authRoleName" 424 | } 425 | ], 426 | "PolicyDocument": { 427 | "Version": "2012-10-17", 428 | "Statement": [ 429 | { 430 | "Effect": "Allow", 431 | "Action": [ 432 | "s3:GetObject" 433 | ], 434 | "Resource": [ 435 | { 436 | "Fn::Join": [ 437 | "", 438 | [ 439 | "arn:aws:s3:::", 440 | { 441 | "Ref": "S3Bucket" 442 | }, 443 | "/protected/*" 444 | ] 445 | ] 446 | } 447 | ] 448 | }, 449 | { 450 | "Effect": "Allow", 451 | "Action": [ 452 | "s3:ListBucket" 453 | ], 454 | "Resource": [ 455 | { 456 | "Fn::Join": [ 457 | "", 458 | [ 459 | "arn:aws:s3:::", 460 | { 461 | "Ref": "S3Bucket" 462 | } 463 | ] 464 | ] 465 | } 466 | ], 467 | "Condition": { 468 | "StringLike": { 469 | "s3:prefix": [ 470 | "public/", 471 | "public/*", 472 | "protected/", 473 | "protected/*", 474 | "private/${cognito-identity.amazonaws.com:sub}/", 475 | "private/${cognito-identity.amazonaws.com:sub}/*" 476 | ] 477 | } 478 | } 479 | } 480 | ] 481 | } 482 | } 483 | }, 484 | "S3GuestPublicPolicy": { 485 | "DependsOn": [ 486 | "S3Bucket" 487 | ], 488 | "Condition": "CreateGuestPublic", 489 | "Type": "AWS::IAM::Policy", 490 | "Properties": { 491 | "PolicyName": { 492 | "Ref": "s3PublicPolicy" 493 | }, 494 | "Roles": [ 495 | { 496 | "Ref": "unauthRoleName" 497 | } 498 | ], 499 | "PolicyDocument": { 500 | "Version": "2012-10-17", 501 | "Statement": [ 502 | { 503 | "Effect": "Allow", 504 | "Action": { 505 | "Fn::Split" : [ "," , { 506 | "Ref": "s3PermissionsGuestPublic" 507 | } ] 508 | }, 509 | "Resource": [ 510 | { 511 | "Fn::Join": [ 512 | "", 513 | [ 514 | "arn:aws:s3:::", 515 | { 516 | "Ref": "S3Bucket" 517 | }, 518 | "/public/*" 519 | ] 520 | ] 521 | } 522 | ] 523 | } 524 | ] 525 | } 526 | } 527 | }, 528 | "S3GuestUploadPolicy": { 529 | "DependsOn": [ 530 | "S3Bucket" 531 | ], 532 | "Condition": "CreateGuestUploads", 533 | "Type": "AWS::IAM::Policy", 534 | "Properties": { 535 | "PolicyName": { 536 | "Ref": "s3UploadsPolicy" 537 | }, 538 | "Roles": [ 539 | { 540 | "Ref": "unauthRoleName" 541 | } 542 | ], 543 | "PolicyDocument": { 544 | "Version": "2012-10-17", 545 | "Statement": [ 546 | { 547 | "Effect": "Allow", 548 | "Action": { 549 | "Fn::Split" : [ "," , { 550 | "Ref": "s3PermissionsGuestUploads" 551 | } ] 552 | }, 553 | "Resource": [ 554 | { 555 | "Fn::Join": [ 556 | "", 557 | [ 558 | "arn:aws:s3:::", 559 | { 560 | "Ref": "S3Bucket" 561 | }, 562 | "/uploads/*" 563 | ] 564 | ] 565 | } 566 | ] 567 | } 568 | ] 569 | } 570 | } 571 | }, 572 | "S3GuestReadPolicy": { 573 | "DependsOn": [ 574 | "S3Bucket" 575 | ], 576 | "Condition": "GuestReadAndList", 577 | "Type": "AWS::IAM::Policy", 578 | "Properties": { 579 | "PolicyName": { 580 | "Ref": "s3ReadPolicy" 581 | }, 582 | "Roles": [ 583 | { 584 | "Ref": "unauthRoleName" 585 | } 586 | ], 587 | "PolicyDocument": { 588 | "Version": "2012-10-17", 589 | "Statement": [ 590 | { 591 | "Effect": "Allow", 592 | "Action": [ 593 | "s3:GetObject" 594 | ], 595 | "Resource": [ 596 | { 597 | "Fn::Join": [ 598 | "", 599 | [ 600 | "arn:aws:s3:::", 601 | { 602 | "Ref": "S3Bucket" 603 | }, 604 | "/protected/*" 605 | ] 606 | ] 607 | } 608 | ] 609 | }, 610 | { 611 | "Effect": "Allow", 612 | "Action": [ 613 | "s3:ListBucket" 614 | ], 615 | "Resource": [ 616 | { 617 | "Fn::Join": [ 618 | "", 619 | [ 620 | "arn:aws:s3:::", 621 | { 622 | "Ref": "S3Bucket" 623 | } 624 | ] 625 | ] 626 | } 627 | ], 628 | "Condition": { 629 | "StringLike": { 630 | "s3:prefix": [ 631 | "public/", 632 | "public/*", 633 | "protected/", 634 | "protected/*" 635 | ] 636 | } 637 | } 638 | } 639 | ] 640 | } 641 | } 642 | } 643 | }, 644 | "Outputs": { 645 | "BucketName": { 646 | "Value": { 647 | "Ref": "S3Bucket" 648 | }, 649 | "Description": "Bucket name for the S3 bucket" 650 | }, 651 | "Region": { 652 | "Value": { 653 | "Ref": "AWS::Region" 654 | } 655 | } 656 | } 657 | } 658 | -------------------------------------------------------------------------------- /amplify/backend/storage/s324fe3c07/storage-params.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /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/cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "features": { 3 | "graphqltransformer": { 4 | "addmissingownerfields": true, 5 | "validatetypenamereservedwords": true, 6 | "useexperimentalpipelinedtransformer": false, 7 | "enableiterativegsiupdates": false, 8 | "secondarykeyasgsi": true, 9 | "skipoverridemutationinputtypes": true 10 | }, 11 | "frontend-ios": { 12 | "enablexcodeintegration": true 13 | }, 14 | "auth": { 15 | "enablecaseinsensitivity": true 16 | }, 17 | "codegen": { 18 | "useappsyncmodelgenplugin": true, 19 | "usedocsgeneratorplugin": true, 20 | "usetypesgeneratorplugin": true, 21 | "cleangeneratedmodelsdirectory": true, 22 | "retaincasestyle": true 23 | }, 24 | "appsync": { 25 | "generategraphqlpermissions": true 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /amplify/team-provider-info.json: -------------------------------------------------------------------------------- 1 | { 2 | "dev": { 3 | "awscloudformation": { 4 | "AuthRoleName": "amplify-flutteramplify-dev-111734-authRole", 5 | "UnauthRoleArn": "arn:aws:iam::718679627680:role/amplify-flutteramplify-dev-111734-unauthRole", 6 | "AuthRoleArn": "arn:aws:iam::718679627680:role/amplify-flutteramplify-dev-111734-authRole", 7 | "Region": "ap-south-1", 8 | "DeploymentBucketName": "amplify-flutteramplify-dev-111734-deployment", 9 | "UnauthRoleName": "amplify-flutteramplify-dev-111734-unauthRole", 10 | "StackName": "amplify-flutteramplify-dev-111734", 11 | "StackId": "arn:aws:cloudformation:ap-south-1:718679627680:stack/amplify-flutteramplify-dev-111734/72b544a0-82f6-11eb-b462-066d6b8defd6", 12 | "AmplifyAppId": "d2dc3sxod8r2np" 13 | }, 14 | "categories": { 15 | "auth": { 16 | "flutteramplify586ac13a": {} 17 | } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | def keystoreProperties = new Properties() 29 | def keystorePropertiesFile = rootProject.file('key.properties') 30 | if (keystorePropertiesFile.exists()) { 31 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 32 | } 33 | 34 | android { 35 | compileSdkVersion 30 36 | 37 | sourceSets { 38 | main.java.srcDirs += 'src/main/kotlin' 39 | } 40 | 41 | defaultConfig { 42 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 43 | applicationId "com.example.flutter_amplify_demo" 44 | minSdkVersion 21 45 | targetSdkVersion 30 46 | versionCode flutterVersionCode.toInteger() 47 | versionName flutterVersionName 48 | } 49 | 50 | signingConfigs { 51 | release { 52 | keyAlias keystoreProperties['keyAlias'] 53 | keyPassword keystoreProperties['keyPassword'] 54 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 55 | storePassword keystoreProperties['storePassword'] 56 | } 57 | } 58 | 59 | buildTypes { 60 | release { 61 | // TODO: Add your own signing config for the release build. 62 | // Signing with the debug keys for now, so `flutter run --release` works. 63 | signingConfig signingConfigs.debug 64 | } 65 | } 66 | } 67 | 68 | flutter { 69 | source '../..' 70 | } 71 | 72 | dependencies { 73 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 74 | } 75 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 13 | 17 | 21 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 49 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/flutter_amplify_demo/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.flutter_amplify_demo 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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 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 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip 7 | -------------------------------------------------------------------------------- /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/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/assets/demo.gif -------------------------------------------------------------------------------- /assets/fonts/Poppins/OFL.txt: -------------------------------------------------------------------------------- 1 | Copyright 2020 The Poppins Project Authors (https://github.com/itfoundry/Poppins) 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /assets/fonts/Poppins/Poppins-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/assets/fonts/Poppins/Poppins-Black.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins/Poppins-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/assets/fonts/Poppins/Poppins-Bold.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins/Poppins-ExtraBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/assets/fonts/Poppins/Poppins-ExtraBold.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins/Poppins-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/assets/fonts/Poppins/Poppins-ExtraLight.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins/Poppins-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/assets/fonts/Poppins/Poppins-Italic.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins/Poppins-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/assets/fonts/Poppins/Poppins-Light.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins/Poppins-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/assets/fonts/Poppins/Poppins-Medium.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins/Poppins-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/assets/fonts/Poppins/Poppins-Regular.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins/Poppins-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/assets/fonts/Poppins/Poppins-SemiBold.ttf -------------------------------------------------------------------------------- /assets/fonts/Poppins/Poppins-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/assets/fonts/Poppins/Poppins-Thin.ttf -------------------------------------------------------------------------------- /assets/images/bg_chat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/assets/images/bg_chat.jpg -------------------------------------------------------------------------------- /assets/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/assets/images/splash.png -------------------------------------------------------------------------------- /assets/title_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/assets/title_image.png -------------------------------------------------------------------------------- /clean.sh: -------------------------------------------------------------------------------- 1 | flutter clean 2 | pod repo update 3 | rm -rf ios/Podfile.lock ios/Pods/ 4 | rm -rf pubspec.lock .packages .flutter-plugins 5 | flutter pub pub cache repair 6 | flutter pub get 7 | cd ios; pod install -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /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 | 8.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, '13.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 | target.build_configurations.each do |config| 41 | config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET' 42 | end 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Amplify (1.6.1): 3 | - Amplify/Default (= 1.6.1) 4 | - Amplify/Default (1.6.1) 5 | - amplify_api (0.0.1): 6 | - Amplify 7 | - amplify_core 8 | - AmplifyPlugins/AWSAPIPlugin 9 | - Flutter 10 | - amplify_auth_cognito (0.0.1): 11 | - Amplify 12 | - amplify_core 13 | - AmplifyPlugins/AWSCognitoAuthPlugin 14 | - Flutter 15 | - ObjectMapper 16 | - amplify_core (0.0.1): 17 | - Flutter 18 | - amplify_datastore (0.0.1): 19 | - Amplify 20 | - amplify_core 21 | - AmplifyPlugins/AWSDataStorePlugin 22 | - Flutter 23 | - amplify_flutter (0.0.1): 24 | - Amplify 25 | - amplify_core 26 | - AmplifyPlugins/AWSCognitoAuthPlugin 27 | - AWSPluginsCore 28 | - Flutter 29 | - AmplifyPlugins/AWSAPIPlugin (1.6.1): 30 | - AppSyncRealTimeClient (~> 1.4.0) 31 | - AWSCore (~> 2.23.0) 32 | - AWSPluginsCore (= 1.6.1) 33 | - AmplifyPlugins/AWSCognitoAuthPlugin (1.6.1): 34 | - AWSAuthCore (~> 2.23.0) 35 | - AWSCognitoIdentityProvider (~> 2.23.0) 36 | - AWSCognitoIdentityProviderASF (~> 2.23.0) 37 | - AWSCore (~> 2.23.0) 38 | - AWSMobileClient (~> 2.23.0) 39 | - AWSPluginsCore (= 1.6.1) 40 | - AmplifyPlugins/AWSDataStorePlugin (1.6.1): 41 | - AWSCore (~> 2.23.0) 42 | - AWSPluginsCore (= 1.6.1) 43 | - SQLite.swift (~> 0.12.0) 44 | - AppSyncRealTimeClient (1.4.3): 45 | - Starscream (~> 3.1.0) 46 | - AWSAuthCore (2.23.1): 47 | - AWSCore (= 2.23.1) 48 | - AWSCognitoIdentityProvider (2.23.1): 49 | - AWSCognitoIdentityProviderASF (= 2.23.1) 50 | - AWSCore (= 2.23.1) 51 | - AWSCognitoIdentityProviderASF (2.23.1) 52 | - AWSCore (2.23.1) 53 | - AWSMobileClient (2.23.1): 54 | - AWSAuthCore (= 2.23.1) 55 | - AWSCognitoIdentityProvider (= 2.23.1) 56 | - AWSCognitoIdentityProviderASF (= 2.23.1) 57 | - AWSCore (= 2.23.1) 58 | - AWSPluginsCore (1.6.1): 59 | - Amplify (= 1.6.1) 60 | - AWSCore (~> 2.23.0) 61 | - Flutter (1.0.0) 62 | - FMDB (2.7.5): 63 | - FMDB/standard (= 2.7.5) 64 | - FMDB/standard (2.7.5) 65 | - ObjectMapper (4.2.0) 66 | - path_provider (0.0.1): 67 | - Flutter 68 | - sqflite (0.0.2): 69 | - Flutter 70 | - FMDB (>= 2.7.5) 71 | - SQLite.swift (0.12.2): 72 | - SQLite.swift/standard (= 0.12.2) 73 | - SQLite.swift/standard (0.12.2) 74 | - Starscream (3.1.1) 75 | 76 | DEPENDENCIES: 77 | - amplify_api (from `.symlinks/plugins/amplify_api/ios`) 78 | - amplify_auth_cognito (from `.symlinks/plugins/amplify_auth_cognito/ios`) 79 | - amplify_core (from `.symlinks/plugins/amplify_core/ios`) 80 | - amplify_datastore (from `.symlinks/plugins/amplify_datastore/ios`) 81 | - amplify_flutter (from `.symlinks/plugins/amplify_flutter/ios`) 82 | - Flutter (from `Flutter`) 83 | - path_provider (from `.symlinks/plugins/path_provider/ios`) 84 | - sqflite (from `.symlinks/plugins/sqflite/ios`) 85 | 86 | SPEC REPOS: 87 | trunk: 88 | - Amplify 89 | - AmplifyPlugins 90 | - AppSyncRealTimeClient 91 | - AWSAuthCore 92 | - AWSCognitoIdentityProvider 93 | - AWSCognitoIdentityProviderASF 94 | - AWSCore 95 | - AWSMobileClient 96 | - AWSPluginsCore 97 | - FMDB 98 | - ObjectMapper 99 | - SQLite.swift 100 | - Starscream 101 | 102 | EXTERNAL SOURCES: 103 | amplify_api: 104 | :path: ".symlinks/plugins/amplify_api/ios" 105 | amplify_auth_cognito: 106 | :path: ".symlinks/plugins/amplify_auth_cognito/ios" 107 | amplify_core: 108 | :path: ".symlinks/plugins/amplify_core/ios" 109 | amplify_datastore: 110 | :path: ".symlinks/plugins/amplify_datastore/ios" 111 | amplify_flutter: 112 | :path: ".symlinks/plugins/amplify_flutter/ios" 113 | Flutter: 114 | :path: Flutter 115 | path_provider: 116 | :path: ".symlinks/plugins/path_provider/ios" 117 | sqflite: 118 | :path: ".symlinks/plugins/sqflite/ios" 119 | 120 | SPEC CHECKSUMS: 121 | Amplify: 4d0c3b0871e1f268c81ce5e9b65532df0a013900 122 | amplify_api: 9a94aa18c1576ee0f3bae038b34c227e07b5c6ab 123 | amplify_auth_cognito: 2e090a788d0ba4e08f6438170b1e7aac872782b4 124 | amplify_core: 8b38d20089fe4f225c14db6892f586bd03824a42 125 | amplify_datastore: e73979905bbee5c192e59f6afcd4e0692c3132bf 126 | amplify_flutter: d64b45e3a016e491bbb2e110fee57e150daf2164 127 | AmplifyPlugins: 51dfaeafe662cd571c4d4dfd3bdc5987fef2d5a7 128 | AppSyncRealTimeClient: 04df4dffe57cfbd06da336d0bfcd5641ba9adcb5 129 | AWSAuthCore: d9f9c418df00f45f399e407dc6c2bce33870b0af 130 | AWSCognitoIdentityProvider: 8bf316cc96554d4c88ca4b1dcae0589923fb95f5 131 | AWSCognitoIdentityProviderASF: f9b0d373dfce6d5395f0b69f2301f5db927d9fc8 132 | AWSCore: 48bf561f5fb3616ef42c1f7f3eeb4b502d11f69d 133 | AWSMobileClient: ffe1179b9e6d5db7cea3f807df7d47f5d00a5c65 134 | AWSPluginsCore: f41f8a478fe3fd8839ec16278b86dededefb0628 135 | Flutter: 434fef37c0980e73bb6479ef766c45957d4b510c 136 | FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a 137 | ObjectMapper: 1eb41f610210777375fa806bf161dc39fb832b81 138 | path_provider: abfe2b5c733d04e238b0d8691db0cfd63a27a93c 139 | sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 140 | SQLite.swift: d2b4642190917051ce6bd1d49aab565fe794eea3 141 | Starscream: 4bb2f9942274833f7b4d296a55504dcfc7edb7b0 142 | 143 | PODFILE CHECKSUM: 2d003d2a4f4b67e0a26ff44e9ea1b5df6c5e26d8 144 | 145 | COCOAPODS: 1.10.1 146 | -------------------------------------------------------------------------------- /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 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/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 | CFBundleURLTypes 6 | 7 | 8 | CFBundleURLSchemes 9 | 10 | myapp 11 | 12 | 13 | 14 | NSCameraUsageDescription 15 | This app requires access to the camera. 16 | NSMicrophoneUsageDescription 17 | This app does not require access to the microphone. 18 | NSPhotoLibraryUsageDescription 19 | This app requires access to the photo library. 20 | 21 | CFBundleDevelopmentRegion 22 | $(DEVELOPMENT_LANGUAGE) 23 | CFBundleExecutable 24 | $(EXECUTABLE_NAME) 25 | CFBundleIdentifier 26 | $(PRODUCT_BUNDLE_IDENTIFIER) 27 | CFBundleInfoDictionaryVersion 28 | 6.0 29 | CFBundleName 30 | flutter_amplify_demo 31 | CFBundlePackageType 32 | APPL 33 | CFBundleShortVersionString 34 | $(FLUTTER_BUILD_NAME) 35 | CFBundleSignature 36 | ???? 37 | CFBundleVersion 38 | $(FLUTTER_BUILD_NUMBER) 39 | LSRequiresIPhoneOS 40 | 41 | UILaunchStoryboardName 42 | LaunchScreen 43 | UIMainStoryboardFile 44 | Main 45 | UISupportedInterfaceOrientations 46 | 47 | UIInterfaceOrientationPortrait 48 | UIInterfaceOrientationLandscapeLeft 49 | UIInterfaceOrientationLandscapeRight 50 | 51 | UISupportedInterfaceOrientations~ipad 52 | 53 | UIInterfaceOrientationPortrait 54 | UIInterfaceOrientationPortraitUpsideDown 55 | UIInterfaceOrientationLandscapeLeft 56 | UIInterfaceOrientationLandscapeRight 57 | 58 | UIViewControllerBasedStatusBarAppearance 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/components/get_name_bottom_sheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_amplify_demo/repositories/user_repository.dart'; 3 | import 'package:flutter_amplify_demo/components/login_text_field.dart'; 4 | import 'package:flutter_amplify_demo/components/primary_button.dart'; 5 | import 'package:flutter_amplify_demo/routes/routes_path.dart'; 6 | import 'package:flutter_amplify_demo/services/get_it_service.dart'; 7 | import 'package:flutter_amplify_demo/services/navigation_service.dart'; 8 | import 'package:flutter_amplify_demo/services/size_config.dart'; 9 | import 'package:flutter_amplify_demo/services/text_style.dart'; 10 | import 'package:flutter_amplify_demo/services/validations.dart'; 11 | import 'package:flutter_amplify_demo/stores/user.dart'; 12 | import 'package:flutter_amplify_demo/theme/colors.dart'; 13 | 14 | getNameBottomSheet(context) { 15 | TextEditingController nameCtrl = TextEditingController(); 16 | final GlobalKey formKey = GlobalKey(); 17 | NavigationService _navigationService = 18 | get_it_instance_const(); 19 | return showModalBottomSheet( 20 | context: context, 21 | isScrollControlled: true, 22 | backgroundColor: bgColor, 23 | builder: (context) { 24 | return Container( 25 | height: 400, 26 | width: SizeConfig.screenWidth, 27 | decoration: BoxDecoration( 28 | color: bgColor, 29 | borderRadius: BorderRadius.only( 30 | topLeft: Radius.circular(10), 31 | topRight: Radius.circular(10), 32 | ), 33 | border: Border.all(color: Colors.white.withOpacity(0.1), width: 3), 34 | ), 35 | child: Form( 36 | key: formKey, 37 | child: Padding( 38 | padding: EdgeInsets.symmetric(vertical: 20, horizontal: 30), 39 | child: Column( 40 | children: [ 41 | Text( 42 | "What should we call you?", 43 | style: CustomTextStyle.loginTitleStyle, 44 | ), 45 | SizedBox(height: 24.toHeight), 46 | CustomLoginTextField( 47 | hintTextL: "Full Name", 48 | ctrl: nameCtrl, 49 | validation: Validations.validateFullName, 50 | type: TextInputType.text, 51 | ), 52 | SizedBox(height: 24.toHeight), 53 | PrimaryButton( 54 | text: "Continue", 55 | onPress: () async { 56 | if (!formKey.currentState.validate()) return; 57 | await UserRepository().updateFullname(nameCtrl.text); 58 | await UserStore().fetchCurrentUser(); 59 | await UserStore().fetchAllOtherUsers(); 60 | _navigationService.popAllAndReplace(RoutePath.Home); 61 | }, 62 | ), 63 | ], 64 | ), 65 | ), 66 | ), 67 | ); 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /lib/components/google_login_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_amplify_demo/components/get_name_bottom_sheet.dart'; 3 | import 'package:flutter_amplify_demo/components/primary_button.dart'; 4 | import 'package:flutter_amplify_demo/components/secondary_button.dart'; 5 | import 'package:flutter_amplify_demo/components/show_snackbar.dart'; 6 | import 'package:flutter_amplify_demo/routes/routes_path.dart'; 7 | import 'package:flutter_amplify_demo/services/get_it_service.dart'; 8 | import 'package:flutter_amplify_demo/services/navigation_service.dart'; 9 | import 'package:flutter_amplify_demo/stores/auth.dart'; 10 | import 'package:flutter_amplify_demo/stores/state_keeper.dart'; 11 | import 'package:provider/provider.dart'; 12 | 13 | class GoogleButton extends StatefulWidget { 14 | final bool isPrimaryButton; 15 | GoogleButton({Key key, this.isPrimaryButton}) : super(key: key); 16 | 17 | @override 18 | _GoogleButtonState createState() => _GoogleButtonState(); 19 | } 20 | 21 | class _GoogleButtonState extends State { 22 | NavigationService _navigationService = 23 | get_it_instance_const(); 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return Consumer(builder: (_, authStore, __) { 28 | if (authStore.status[AuthStore.LOGIN_GOOGLE_USER] == Status.Error) { 29 | WidgetsBinding.instance.addPostFrameCallback((_) { 30 | showSnackBar( 31 | context, 32 | authStore.error[AuthStore.LOGIN_GOOGLE_USER], 33 | ); 34 | authStore.reset(AuthStore.LOGIN_GOOGLE_USER); 35 | }); 36 | } 37 | if (authStore.status[AuthStore.LOGIN_GOOGLE_USER] == Status.Loading) { 38 | return CircularProgressIndicator(); 39 | } 40 | return widget.isPrimaryButton 41 | ? PrimaryButton( 42 | text: "Continue with Google", 43 | onPress: () async { 44 | bool isSignedIn = await authStore.loginWithGoogle(); 45 | if (isSignedIn) { 46 | bool hasUsername = await authStore.checkUsername(); 47 | if (!hasUsername) 48 | getNameBottomSheet(context); 49 | else 50 | _navigationService.popAllAndReplace(RoutePath.Home); 51 | } 52 | }, 53 | ) 54 | : SecondaryButton( 55 | text: "Continue with Google", 56 | onPress: () async { 57 | bool isSignedIn = await authStore.loginWithGoogle(); 58 | if (isSignedIn) { 59 | bool hasUsername = await authStore.checkUsername(); 60 | if (!hasUsername) 61 | getNameBottomSheet(context); 62 | else 63 | _navigationService.popAllAndReplace(RoutePath.Home); 64 | } 65 | }, 66 | ); 67 | }); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/components/login_text_field.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_amplify_demo/theme/colors.dart'; 3 | import 'package:flutter_amplify_demo/services/size_config.dart'; 4 | 5 | class CustomLoginTextField extends StatelessWidget { 6 | final String hintTextL; 7 | final TextEditingController ctrl; 8 | final Function validation; 9 | final bool obscureText; 10 | final TextInputType type; 11 | final int maxLength; 12 | const CustomLoginTextField({ 13 | Key key, 14 | @required this.hintTextL, 15 | @required this.ctrl, 16 | @required this.validation, 17 | this.obscureText = false, 18 | this.type, 19 | this.maxLength, 20 | }) : super(key: key); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return TextFormField( 25 | validator: (value) => validation(value), 26 | controller: ctrl, 27 | obscureText: obscureText, 28 | keyboardType: type, 29 | maxLength: maxLength, 30 | autovalidateMode: AutovalidateMode.onUserInteraction, 31 | decoration: InputDecoration( 32 | border: OutlineInputBorder( 33 | borderSide: BorderSide(color: Colors.grey[700], width: 2.toWidth), 34 | borderRadius: BorderRadius.all( 35 | Radius.circular(18.toWidth), 36 | ), 37 | ), 38 | enabledBorder: OutlineInputBorder( 39 | borderSide: BorderSide(color: Colors.grey[700], width: 2.toWidth), 40 | borderRadius: BorderRadius.all( 41 | Radius.circular(18.toWidth), 42 | ), 43 | ), 44 | focusedBorder: OutlineInputBorder( 45 | borderSide: BorderSide(color: ThemeColor.primary, width: 2.toWidth), 46 | borderRadius: BorderRadius.all( 47 | Radius.circular(18.toWidth), 48 | ), 49 | ), 50 | disabledBorder: OutlineInputBorder( 51 | borderSide: BorderSide(color: Colors.grey[700], width: 2.toWidth), 52 | borderRadius: BorderRadius.all( 53 | Radius.circular(18.toWidth), 54 | ), 55 | ), 56 | contentPadding: EdgeInsets.symmetric( 57 | horizontal: 18.toWidth, 58 | vertical: 16.toHeight, 59 | ), 60 | hintText: hintTextL, 61 | hintStyle: TextStyle( 62 | color: Colors.grey[600], 63 | fontSize: 18.toFont, 64 | fontWeight: FontWeight.w500, 65 | ), 66 | ), 67 | style: TextStyle( 68 | color: Colors.grey[400], 69 | fontSize: 16.toFont, 70 | fontWeight: FontWeight.w500, 71 | ), 72 | ); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /lib/components/primary_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_amplify_demo/services/size_config.dart'; 3 | 4 | class PrimaryButton extends StatelessWidget { 5 | final String text; 6 | final Function onPress; 7 | const PrimaryButton({ 8 | Key key, 9 | this.text, 10 | this.onPress, 11 | }) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Container( 16 | width: SizeConfig.screenWidth, 17 | height: 50.toHeight, 18 | child: TextButton( 19 | onPressed: onPress, 20 | child: Text( 21 | text, 22 | style: TextStyle( 23 | fontSize: 20.toFont, 24 | fontWeight: FontWeight.w600, 25 | ), 26 | ), 27 | style: ButtonStyle( 28 | shape: MaterialStateProperty.all( 29 | RoundedRectangleBorder( 30 | borderRadius: BorderRadius.circular(18.0.toWidth), 31 | ), 32 | ), 33 | ), 34 | ), 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/components/secondary_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_amplify_demo/services/size_config.dart'; 3 | 4 | class SecondaryButton extends StatefulWidget { 5 | final String text; 6 | final Function onPress; 7 | SecondaryButton({ 8 | Key key, 9 | this.text, 10 | this.onPress, 11 | }) : super(key: key); 12 | 13 | @override 14 | _SecondaryButtonState createState() => _SecondaryButtonState(); 15 | } 16 | 17 | class _SecondaryButtonState extends State { 18 | MaterialStateProperty backgroundColor = 19 | MaterialStateProperty.all(Colors.black); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | return Container( 24 | width: SizeConfig.screenWidth, 25 | height: 50.toHeight, 26 | decoration: BoxDecoration( 27 | border: Border.all( 28 | color: Colors.white, 29 | width: 2.5, 30 | ), 31 | borderRadius: BorderRadius.circular(20), 32 | ), 33 | child: TextButton( 34 | onPressed: widget.onPress, 35 | child: Text( 36 | widget.text, 37 | style: TextStyle( 38 | fontSize: 20.toFont, 39 | fontWeight: FontWeight.w600, 40 | color: Colors.white, 41 | ), 42 | ), 43 | style: ButtonStyle( 44 | shape: MaterialStateProperty.all( 45 | RoundedRectangleBorder( 46 | borderRadius: BorderRadius.circular(18.0), 47 | ), 48 | ), 49 | backgroundColor: MaterialStateProperty.all(Colors.black), 50 | ), 51 | ), 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/components/show_snackbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | showSnackBar(BuildContext context, String message) { 4 | final snackBar = SnackBar(content: Text(message)); 5 | ScaffoldMessenger.of(context).showSnackBar(snackBar); 6 | } 7 | -------------------------------------------------------------------------------- /lib/components/textarea.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_amplify_demo/theme/colors.dart'; 3 | import 'package:flutter_amplify_demo/services/size_config.dart'; 4 | 5 | class TextArea extends StatelessWidget { 6 | final TextEditingController ctrl; 7 | const TextArea({ 8 | Key key, 9 | @required this.ctrl, 10 | }) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return TextFormField( 15 | controller: ctrl, 16 | maxLines: 3, 17 | autovalidateMode: AutovalidateMode.onUserInteraction, 18 | decoration: InputDecoration( 19 | border: OutlineInputBorder( 20 | borderSide: BorderSide(color: Colors.grey[700], width: 2.toWidth), 21 | borderRadius: BorderRadius.all( 22 | Radius.circular(18.toWidth), 23 | ), 24 | ), 25 | enabledBorder: OutlineInputBorder( 26 | borderSide: BorderSide(color: Colors.grey[700], width: 2.toWidth), 27 | borderRadius: BorderRadius.all( 28 | Radius.circular(18.toWidth), 29 | ), 30 | ), 31 | focusedBorder: OutlineInputBorder( 32 | borderSide: BorderSide(color: ThemeColor.primary, width: 2.toWidth), 33 | borderRadius: BorderRadius.all( 34 | Radius.circular(18.toWidth), 35 | ), 36 | ), 37 | disabledBorder: OutlineInputBorder( 38 | borderSide: BorderSide(color: Colors.grey[700], width: 2.toWidth), 39 | borderRadius: BorderRadius.all( 40 | Radius.circular(18.toWidth), 41 | ), 42 | ), 43 | contentPadding: EdgeInsets.symmetric( 44 | horizontal: 18.toWidth, 45 | vertical: 16.toHeight, 46 | ), 47 | hintText: "This cannot be empty.", 48 | hintStyle: TextStyle( 49 | color: Colors.grey[600], 50 | fontSize: 18.toFont, 51 | fontWeight: FontWeight.w500, 52 | ), 53 | ), 54 | style: TextStyle( 55 | color: Colors.grey[400], 56 | fontSize: 16.toFont, 57 | fontWeight: FontWeight.w500, 58 | ), 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/components/update_message_bottom_sheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_amplify_demo/components/textarea.dart'; 3 | import 'package:flutter_amplify_demo/components/primary_button.dart'; 4 | import 'package:flutter_amplify_demo/models/ChatData.dart'; 5 | import 'package:flutter_amplify_demo/services/get_it_service.dart'; 6 | import 'package:flutter_amplify_demo/services/navigation_service.dart'; 7 | import 'package:flutter_amplify_demo/services/size_config.dart'; 8 | import 'package:flutter_amplify_demo/services/text_style.dart'; 9 | import 'package:flutter_amplify_demo/theme/colors.dart'; 10 | 11 | updateMessageBottomSheet( 12 | context, 13 | Chatdata chatData, 14 | Function updateMessage, 15 | Function closeSelectionModel, 16 | ) { 17 | TextEditingController messageCtrl = 18 | TextEditingController(text: chatData.message); 19 | 20 | NavigationService _navigationService = 21 | get_it_instance_const(); 22 | return showModalBottomSheet( 23 | context: context, 24 | isScrollControlled: true, 25 | backgroundColor: bgColor, 26 | builder: (context) { 27 | return Container( 28 | height: 300.toHeight, 29 | width: SizeConfig.screenWidth, 30 | decoration: BoxDecoration( 31 | color: bgColor, 32 | borderRadius: BorderRadius.only( 33 | topLeft: Radius.circular(10), 34 | topRight: Radius.circular(10), 35 | ), 36 | border: Border.all(color: Colors.white.withOpacity(0.1), width: 3), 37 | ), 38 | child: Padding( 39 | padding: EdgeInsets.symmetric(vertical: 20, horizontal: 30), 40 | child: Column( 41 | crossAxisAlignment: CrossAxisAlignment.start, 42 | children: [ 43 | Text( 44 | "Update message", 45 | style: CustomTextStyle.loginTitleStyle.copyWith( 46 | fontSize: 23.toFont, 47 | ), 48 | ), 49 | SizedBox(height: 24.toHeight), 50 | TextArea(ctrl: messageCtrl), 51 | SizedBox(height: 24.toHeight), 52 | PrimaryButton( 53 | text: "Continue", 54 | onPress: () async { 55 | if (messageCtrl.text == "") return; 56 | await updateMessage(chatData.id, messageCtrl.text); 57 | closeSelectionModel(); 58 | if (Navigator.canPop(context)) { 59 | Navigator.pop(context); 60 | } 61 | // await UserRepository().updateFullname(messageCtrl.text); 62 | // _navigationService.popAllAndReplace(RoutePath.Home); 63 | }, 64 | ), 65 | ], 66 | ), 67 | ), 68 | ); 69 | }); 70 | } 71 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:flutter_amplify_demo/routes/routes_generator.dart'; 4 | import 'package:flutter_amplify_demo/routes/routes_path.dart'; 5 | import 'package:flutter_amplify_demo/services/amplify_services.dart'; 6 | 7 | import 'package:flutter_amplify_demo/services/get_it_service.dart'; 8 | import 'package:flutter_amplify_demo/services/navigation_service.dart'; 9 | import 'package:flutter_amplify_demo/stores/auth.dart'; 10 | import 'package:flutter_amplify_demo/stores/chat.dart'; 11 | import 'package:flutter_amplify_demo/stores/user.dart'; 12 | import 'package:flutter_amplify_demo/wrapper.dart'; 13 | import 'package:provider/provider.dart'; 14 | 15 | void main() { 16 | GetItService.setupLocator(); 17 | 18 | SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( 19 | systemNavigationBarColor: Colors.black, 20 | statusBarColor: Colors.black, 21 | )); 22 | 23 | WidgetsFlutterBinding.ensureInitialized(); 24 | runApp(MyApp()); 25 | } 26 | 27 | class MyApp extends StatefulWidget { 28 | MyApp({Key key}) : super(key: key); 29 | 30 | @override 31 | _MyAppState createState() => _MyAppState(); 32 | } 33 | 34 | class _MyAppState extends State { 35 | @override 36 | void initState() { 37 | super.initState(); 38 | AmplifyService.configureAmplify(); 39 | } 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | return MultiProvider( 44 | providers: [ 45 | ChangeNotifierProvider( 46 | create: (_) => ChatStore(), 47 | lazy: false, 48 | ), 49 | ChangeNotifierProvider( 50 | create: (_) => UserStore(), 51 | lazy: false, 52 | ), 53 | ChangeNotifierProvider( 54 | create: (_) => AuthStore(), 55 | lazy: false, 56 | ) 57 | ], 58 | child: MaterialApp( 59 | debugShowCheckedModeBanner: false, 60 | home: Wrapper(), 61 | theme: ThemeData( 62 | fontFamily: 'Poppins', 63 | textButtonTheme: TextButtonThemeData( 64 | style: TextButton.styleFrom( 65 | primary: Colors.black, 66 | backgroundColor: Colors.white, 67 | ), 68 | ), 69 | ), 70 | navigatorKey: get_it_instance_const().navigatorKey, 71 | onGenerateRoute: generateRoute, 72 | initialRoute: RoutePath.Wrapper, 73 | ), 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/models/ChatData.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 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 | // ignore_for_file: public_member_api_docs 17 | 18 | import 'package:amplify_datastore_plugin_interface/amplify_datastore_plugin_interface.dart'; 19 | import 'package:flutter/foundation.dart'; 20 | 21 | @immutable 22 | class Chatdata extends Model { 23 | static const classType = const ChatdataType(); 24 | final String id; 25 | final String message; 26 | final TemporalTimestamp createdAt; 27 | final TemporalTimestamp updatedAt; 28 | final String chatId; 29 | final String senderId; 30 | 31 | @override 32 | getInstanceType() => classType; 33 | 34 | @override 35 | String getId() { 36 | return id; 37 | } 38 | 39 | const Chatdata._internal( 40 | {@required this.id, 41 | this.message, 42 | this.createdAt, 43 | this.updatedAt, 44 | this.chatId, 45 | this.senderId}); 46 | 47 | factory Chatdata( 48 | {String id, 49 | String message, 50 | TemporalTimestamp createdAt, 51 | TemporalTimestamp updatedAt, 52 | String chatId, 53 | String senderId}) { 54 | return Chatdata._internal( 55 | id: id == null ? UUID.getUUID() : id, 56 | message: message, 57 | createdAt: createdAt, 58 | updatedAt: updatedAt, 59 | chatId: chatId, 60 | senderId: senderId); 61 | } 62 | 63 | bool equals(Object other) { 64 | return this == other; 65 | } 66 | 67 | @override 68 | bool operator ==(Object other) { 69 | if (identical(other, this)) return true; 70 | return other is Chatdata && 71 | id == other.id && 72 | message == other.message && 73 | createdAt == other.createdAt && 74 | updatedAt == other.updatedAt && 75 | chatId == other.chatId && 76 | senderId == other.senderId; 77 | } 78 | 79 | @override 80 | int get hashCode => toString().hashCode; 81 | 82 | @override 83 | String toString() { 84 | var buffer = new StringBuffer(); 85 | 86 | buffer.write("Chatdata {"); 87 | buffer.write("id=" + "$id" + ", "); 88 | buffer.write("message=" + "$message" + ", "); 89 | buffer.write("createdAt=" + 90 | (createdAt != null ? createdAt.toString() : "null") + 91 | ", "); 92 | buffer.write("updatedAt=" + 93 | (updatedAt != null ? updatedAt.toString() : "null") + 94 | ", "); 95 | buffer.write("chatId=" + "$chatId" + ", "); 96 | buffer.write("senderId=" + "$senderId"); 97 | buffer.write("}"); 98 | 99 | return buffer.toString(); 100 | } 101 | 102 | Chatdata copyWith( 103 | {String id, 104 | String message, 105 | TemporalTimestamp createdAt, 106 | TemporalTimestamp updatedAt, 107 | String chatId, 108 | String senderId}) { 109 | return Chatdata( 110 | id: id ?? this.id, 111 | message: message ?? this.message, 112 | createdAt: createdAt ?? this.createdAt, 113 | updatedAt: updatedAt ?? this.updatedAt, 114 | chatId: chatId ?? this.chatId, 115 | senderId: senderId ?? this.senderId); 116 | } 117 | 118 | Chatdata.fromJson(Map json) 119 | : id = json['id'], 120 | message = json['message'], 121 | createdAt = json['createdAt'] != null 122 | ? TemporalTimestamp.fromSeconds(json['createdAt']) 123 | : null, 124 | updatedAt = json['updatedAt'] != null 125 | ? TemporalTimestamp.fromSeconds(json['updatedAt']) 126 | : null, 127 | chatId = json['chatId'], 128 | senderId = json['senderId']; 129 | 130 | Map toJson() => { 131 | 'id': id, 132 | 'message': message, 133 | 'createdAt': createdAt?.toSeconds(), 134 | 'updatedAt': updatedAt?.toSeconds(), 135 | 'chatId': chatId, 136 | 'senderId': senderId 137 | }; 138 | 139 | static final QueryField ID = QueryField(fieldName: "chatdata.id"); 140 | static final QueryField MESSAGE = QueryField(fieldName: "message"); 141 | static final QueryField CREATEDAT = QueryField(fieldName: "createdAt"); 142 | static final QueryField UPDATEDAT = QueryField(fieldName: "updatedAt"); 143 | static final QueryField CHATID = QueryField(fieldName: "chatId"); 144 | static final QueryField SENDERID = QueryField(fieldName: "senderId"); 145 | static var schema = 146 | Model.defineSchema(define: (ModelSchemaDefinition modelSchemaDefinition) { 147 | modelSchemaDefinition.name = "Chatdata"; 148 | modelSchemaDefinition.pluralName = "Chatdata"; 149 | 150 | modelSchemaDefinition.authRules = [ 151 | AuthRule(authStrategy: AuthStrategy.PUBLIC, operations: [ 152 | ModelOperation.CREATE, 153 | ModelOperation.UPDATE, 154 | ModelOperation.DELETE, 155 | ModelOperation.READ 156 | ]) 157 | ]; 158 | 159 | modelSchemaDefinition.addField(ModelFieldDefinition.id()); 160 | 161 | modelSchemaDefinition.addField(ModelFieldDefinition.field( 162 | key: Chatdata.MESSAGE, 163 | isRequired: false, 164 | ofType: ModelFieldType(ModelFieldTypeEnum.string))); 165 | 166 | modelSchemaDefinition.addField(ModelFieldDefinition.field( 167 | key: Chatdata.CREATEDAT, 168 | isRequired: false, 169 | ofType: ModelFieldType(ModelFieldTypeEnum.timestamp))); 170 | 171 | modelSchemaDefinition.addField(ModelFieldDefinition.field( 172 | key: Chatdata.UPDATEDAT, 173 | isRequired: false, 174 | ofType: ModelFieldType(ModelFieldTypeEnum.timestamp))); 175 | 176 | modelSchemaDefinition.addField(ModelFieldDefinition.field( 177 | key: Chatdata.CHATID, 178 | isRequired: false, 179 | ofType: ModelFieldType(ModelFieldTypeEnum.string))); 180 | 181 | modelSchemaDefinition.addField(ModelFieldDefinition.field( 182 | key: Chatdata.SENDERID, 183 | isRequired: false, 184 | ofType: ModelFieldType(ModelFieldTypeEnum.string))); 185 | }); 186 | } 187 | 188 | class ChatdataType extends ModelType { 189 | const ChatdataType(); 190 | 191 | @override 192 | Chatdata fromJson(Map jsonData) { 193 | return Chatdata.fromJson(jsonData); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /lib/models/ModelProvider.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 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 | // ignore_for_file: public_member_api_docs 17 | 18 | import 'package:amplify_datastore_plugin_interface/amplify_datastore_plugin_interface.dart'; 19 | import 'Chatdata.dart'; 20 | import 'Users.dart'; 21 | 22 | export 'Chatdata.dart'; 23 | export 'Users.dart'; 24 | 25 | class ModelProvider implements ModelProviderInterface { 26 | @override 27 | String version = "5dfd86f8531f44b70423917b3da919d5"; 28 | @override 29 | List modelSchemas = [Chatdata.schema, Users.schema]; 30 | static final ModelProvider _instance = ModelProvider(); 31 | 32 | static ModelProvider get instance => _instance; 33 | 34 | ModelType getModelTypeByModelName(String modelName) { 35 | switch (modelName) { 36 | case "Chatdata": 37 | { 38 | return Chatdata.classType; 39 | } 40 | break; 41 | case "Users": 42 | { 43 | return Users.classType; 44 | } 45 | break; 46 | default: 47 | { 48 | throw Exception( 49 | "Failed to find model in model provider for model name: " + 50 | modelName); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/models/Users.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 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 | // ignore_for_file: public_member_api_docs 17 | 18 | import 'package:amplify_datastore_plugin_interface/amplify_datastore_plugin_interface.dart'; 19 | import 'package:collection/collection.dart'; 20 | import 'package:flutter/foundation.dart'; 21 | 22 | /** This is an auto generated class representing the Users type in your schema. */ 23 | @immutable 24 | class Users extends Model { 25 | static const classType = const UsersType(); 26 | final String id; 27 | final String username; 28 | final String email; 29 | final String bio; 30 | final String profileImage; 31 | final bool isVerified; 32 | final TemporalTimestamp createdAt; 33 | final List chats; 34 | 35 | @override 36 | getInstanceType() => classType; 37 | 38 | @override 39 | String getId() { 40 | return id; 41 | } 42 | 43 | const Users._internal( 44 | {@required this.id, 45 | this.username, 46 | this.email, 47 | this.bio, 48 | this.profileImage, 49 | this.isVerified, 50 | this.createdAt, 51 | this.chats}); 52 | 53 | factory Users( 54 | {String id, 55 | String username, 56 | String email, 57 | String bio, 58 | String profileImage, 59 | bool isVerified, 60 | TemporalTimestamp createdAt, 61 | List chats}) { 62 | return Users._internal( 63 | id: id == null ? UUID.getUUID() : id, 64 | username: username, 65 | email: email, 66 | bio: bio, 67 | profileImage: profileImage, 68 | isVerified: isVerified, 69 | createdAt: createdAt, 70 | chats: chats != null ? List.unmodifiable(chats) : chats); 71 | } 72 | 73 | bool equals(Object other) { 74 | return this == other; 75 | } 76 | 77 | @override 78 | bool operator ==(Object other) { 79 | if (identical(other, this)) return true; 80 | return other is Users && 81 | id == other.id && 82 | username == other.username && 83 | email == other.email && 84 | bio == other.bio && 85 | profileImage == other.profileImage && 86 | isVerified == other.isVerified && 87 | createdAt == other.createdAt && 88 | DeepCollectionEquality().equals(chats, other.chats); 89 | } 90 | 91 | @override 92 | int get hashCode => toString().hashCode; 93 | 94 | @override 95 | String toString() { 96 | var buffer = new StringBuffer(); 97 | 98 | buffer.write("Users {"); 99 | buffer.write("id=" + "$id" + ", "); 100 | buffer.write("username=" + "$username" + ", "); 101 | buffer.write("email=" + "$email" + ", "); 102 | buffer.write("bio=" + "$bio" + ", "); 103 | buffer.write("profileImage=" + "$profileImage" + ", "); 104 | buffer.write("isVerified=" + 105 | (isVerified != null ? isVerified.toString() : "null") + 106 | ", "); 107 | buffer.write("createdAt=" + 108 | (createdAt != null ? createdAt.toString() : "null") + 109 | ", "); 110 | buffer.write("chats=" + (chats != null ? chats.toString() : "null")); 111 | buffer.write("}"); 112 | 113 | return buffer.toString(); 114 | } 115 | 116 | Users copyWith( 117 | {String id, 118 | String username, 119 | String email, 120 | String bio, 121 | String profileImage, 122 | bool isVerified, 123 | TemporalTimestamp createdAt, 124 | List chats}) { 125 | return Users( 126 | id: id ?? this.id, 127 | username: username ?? this.username, 128 | email: email ?? this.email, 129 | bio: bio ?? this.bio, 130 | profileImage: profileImage ?? this.profileImage, 131 | isVerified: isVerified ?? this.isVerified, 132 | createdAt: createdAt ?? this.createdAt, 133 | chats: chats ?? this.chats); 134 | } 135 | 136 | Users.fromJson(Map json) 137 | : id = json['id'], 138 | username = json['username'], 139 | email = json['email'], 140 | bio = json['bio'], 141 | profileImage = json['profileImage'], 142 | isVerified = json['isVerified'], 143 | createdAt = json['createdAt'] != null 144 | ? TemporalTimestamp.fromSeconds(json['createdAt']) 145 | : null, 146 | chats = json['chats']?.cast(); 147 | 148 | Map toJson() => { 149 | 'id': id, 150 | 'username': username, 151 | 'email': email, 152 | 'bio': bio, 153 | 'profileImage': profileImage, 154 | 'isVerified': isVerified, 155 | 'createdAt': createdAt?.toSeconds(), 156 | 'chats': chats 157 | }; 158 | 159 | static final QueryField ID = QueryField(fieldName: "users.id"); 160 | static final QueryField USERNAME = QueryField(fieldName: "username"); 161 | static final QueryField EMAIL = QueryField(fieldName: "email"); 162 | static final QueryField BIO = QueryField(fieldName: "bio"); 163 | static final QueryField PROFILEIMAGE = QueryField(fieldName: "profileImage"); 164 | static final QueryField ISVERIFIED = QueryField(fieldName: "isVerified"); 165 | static final QueryField CREATEDAT = QueryField(fieldName: "createdAt"); 166 | static final QueryField CHATS = QueryField(fieldName: "chats"); 167 | static var schema = 168 | Model.defineSchema(define: (ModelSchemaDefinition modelSchemaDefinition) { 169 | modelSchemaDefinition.name = "Users"; 170 | modelSchemaDefinition.pluralName = "Users"; 171 | 172 | modelSchemaDefinition.authRules = [ 173 | AuthRule(authStrategy: AuthStrategy.PUBLIC, operations: [ 174 | ModelOperation.CREATE, 175 | ModelOperation.UPDATE, 176 | ModelOperation.DELETE, 177 | ModelOperation.READ 178 | ]) 179 | ]; 180 | 181 | modelSchemaDefinition.addField(ModelFieldDefinition.id()); 182 | 183 | modelSchemaDefinition.addField(ModelFieldDefinition.field( 184 | key: Users.USERNAME, 185 | isRequired: false, 186 | ofType: ModelFieldType(ModelFieldTypeEnum.string))); 187 | 188 | modelSchemaDefinition.addField(ModelFieldDefinition.field( 189 | key: Users.EMAIL, 190 | isRequired: false, 191 | ofType: ModelFieldType(ModelFieldTypeEnum.string))); 192 | 193 | modelSchemaDefinition.addField(ModelFieldDefinition.field( 194 | key: Users.BIO, 195 | isRequired: false, 196 | ofType: ModelFieldType(ModelFieldTypeEnum.string))); 197 | 198 | modelSchemaDefinition.addField(ModelFieldDefinition.field( 199 | key: Users.PROFILEIMAGE, 200 | isRequired: false, 201 | ofType: ModelFieldType(ModelFieldTypeEnum.string))); 202 | 203 | modelSchemaDefinition.addField(ModelFieldDefinition.field( 204 | key: Users.ISVERIFIED, 205 | isRequired: false, 206 | ofType: ModelFieldType(ModelFieldTypeEnum.bool))); 207 | 208 | modelSchemaDefinition.addField(ModelFieldDefinition.field( 209 | key: Users.CREATEDAT, 210 | isRequired: false, 211 | ofType: ModelFieldType(ModelFieldTypeEnum.timestamp))); 212 | 213 | modelSchemaDefinition.addField(ModelFieldDefinition.field( 214 | key: Users.CHATS, 215 | isRequired: false, 216 | isArray: true, 217 | ofType: ModelFieldType(ModelFieldTypeEnum.collection, 218 | ofModelName: describeEnum(ModelFieldTypeEnum.string)))); 219 | }); 220 | } 221 | 222 | class UsersType extends ModelType { 223 | const UsersType(); 224 | 225 | @override 226 | Users fromJson(Map jsonData) { 227 | return Users.fromJson(jsonData); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /lib/repositories/chat_repository.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:amplify_auth_cognito/amplify_auth_cognito.dart'; 4 | import 'package:amplify_datastore/amplify_datastore.dart'; 5 | import 'package:amplify_flutter/amplify.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter_amplify_demo/models/ChatData.dart'; 8 | import 'package:flutter_amplify_demo/models/Users.dart'; 9 | import 'package:flutter_amplify_demo/stores/user.dart'; 10 | import 'package:uuid/uuid.dart'; 11 | 12 | class ChartRepository { 13 | Future addUserToChatList({Users otherUser}) async { 14 | AuthUser authUser = await Amplify.Auth.getCurrentUser(); 15 | Users user = (await Amplify.DataStore.query(Users.classType, 16 | where: Users.ID.eq(authUser.userId)))[0]; 17 | String chatId = Uuid().v4(); 18 | Map updateChatData = { 19 | 'chatId': chatId, 20 | "otherUserId": otherUser.id, 21 | 'otherUserProfileImage': otherUser.profileImage, 22 | 'otherUserName': otherUser.username, 23 | }; 24 | 25 | List tempJsonData = 26 | new List.from(user.chats == null ? [] : user.chats); 27 | tempJsonData.add(json.encode(updateChatData)); 28 | Users updatedUser = user.copyWith(chats: tempJsonData); 29 | await Amplify.DataStore.save(updatedUser); 30 | 31 | Map otherUserChatData = { 32 | 'chatId': chatId, 33 | "otherUserId": user.id, 34 | 'otherUserProfileImage': user.profileImage, 35 | 'otherUserName': user.username, 36 | }; 37 | 38 | List tempOtherUserJsonData = 39 | new List.from(otherUser.chats == null ? [] : otherUser.chats); 40 | tempOtherUserJsonData.add(json.encode(otherUserChatData)); 41 | Users updatedOtherUser = otherUser.copyWith(chats: tempOtherUserJsonData); 42 | await Amplify.DataStore.save(updatedOtherUser); 43 | } 44 | 45 | Future>> getUserChatList() async { 46 | try { 47 | List> data = []; 48 | Users users = UserStore().currUser; 49 | if (users == null) return data; 50 | if (users.chats == null) return data; 51 | users.chats.forEach((chat) { 52 | print("===> ${json.decode(chat)['chatId']}"); 53 | data.add(json.decode(chat)); 54 | }); 55 | return data; 56 | } catch (e) { 57 | rethrow; 58 | } 59 | } 60 | 61 | Future addChatData({ 62 | @required String message, 63 | @required String chatId, 64 | @required String senderId, 65 | }) async { 66 | Chatdata chat = Chatdata( 67 | createdAt: TemporalTimestamp.now(), 68 | updatedAt: TemporalTimestamp.now(), 69 | message: message, 70 | chatId: chatId, 71 | senderId: senderId, 72 | ); 73 | await Amplify.DataStore.save(chat); 74 | } 75 | 76 | Future> getChatData({@required String chatId}) async { 77 | List chatData = await Amplify.DataStore.query(Chatdata.classType, 78 | where: Chatdata.CHATID.eq(chatId), 79 | sortBy: [Chatdata.CREATEDAT.descending()]); 80 | 81 | return chatData; 82 | } 83 | 84 | Future deleteChats(List messageIdList) async { 85 | for (String messageId in messageIdList) { 86 | Chatdata chatdata = (await Amplify.DataStore.query(Chatdata.classType, 87 | where: Chatdata.ID.eq(messageId)))[0]; 88 | print("Deleting ==> ${chatdata.message}"); 89 | await Amplify.DataStore.delete(chatdata); 90 | } 91 | } 92 | 93 | Future updateChats(String messageId, String updatedMessage) async { 94 | Chatdata messagedata = (await Amplify.DataStore.query(Chatdata.classType, 95 | where: Chatdata.ID.eq(messageId)))[0]; 96 | await Amplify.DataStore.save(messagedata.copyWith(message: updatedMessage)); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /lib/repositories/user_repository.dart: -------------------------------------------------------------------------------- 1 | import 'package:amplify_auth_cognito/amplify_auth_cognito.dart'; 2 | import 'package:amplify_datastore/amplify_datastore.dart'; 3 | import 'package:amplify_flutter/amplify.dart'; 4 | import 'package:flutter_amplify_demo/models/Users.dart'; 5 | import 'package:meta/meta.dart'; 6 | 7 | class UserRepository { 8 | Users currUser; 9 | 10 | Future register({ 11 | @required String email, 12 | @required String password, 13 | @required String name, 14 | }) async { 15 | try { 16 | Map userAttributes = {'name': name}; 17 | SignUpResult res = await Amplify.Auth.signUp( 18 | username: email, 19 | password: password, 20 | options: CognitoSignUpOptions(userAttributes: userAttributes), 21 | ); 22 | return res.isSignUpComplete; 23 | } catch (e) { 24 | print(e.toString()); 25 | rethrow; 26 | } 27 | } 28 | 29 | Future optVerfication({ 30 | @required String email, 31 | @required String password, 32 | @required String otp, 33 | @required String name, 34 | }) async { 35 | SignUpResult res = await Amplify.Auth.confirmSignUp( 36 | username: email, 37 | confirmationCode: otp, 38 | ); 39 | if (res.isSignUpComplete) { 40 | SignInResult signInRes = await Amplify.Auth.signIn( 41 | username: email, 42 | password: password, 43 | ); 44 | AuthUser authUser = await Amplify.Auth.getCurrentUser(); 45 | Amplify.DataStore.save( 46 | Users( 47 | email: email, 48 | username: name, 49 | bio: "", 50 | profileImage: 51 | "https://www.arrowbenefitsgroup.com/wp-content/uploads/2018/04/unisex-avatar.png", 52 | id: authUser.userId, 53 | createdAt: TemporalTimestamp.now(), 54 | isVerified: true, 55 | ), 56 | ); 57 | return signInRes.isSignedIn; 58 | } 59 | return false; 60 | } 61 | 62 | Future logout() async { 63 | try { 64 | await Amplify.Auth.signOut(); 65 | } on AuthException catch (_) { 66 | rethrow; 67 | } 68 | } 69 | 70 | Future login({ 71 | @required String email, 72 | @required String password, 73 | }) async { 74 | SignInResult res = await Amplify.Auth.signIn( 75 | username: email, 76 | password: password, 77 | ); 78 | return res.isSignedIn; 79 | } 80 | 81 | Future loginWithGoogle() async { 82 | try { 83 | var res = 84 | await Amplify.Auth.signInWithWebUI(provider: AuthProvider.google); 85 | List authUserAttributes = 86 | await Amplify.Auth.fetchUserAttributes(); 87 | String email = authUserAttributes[3].value; 88 | String userId = authUserAttributes[0].value; 89 | List user = await Amplify.DataStore.query(Users.classType, 90 | where: Users.ID.eq(userId)); 91 | if (user.length > 0) { 92 | return res.isSignedIn; 93 | } else { 94 | Amplify.DataStore.save( 95 | Users( 96 | email: email, 97 | username: "", 98 | bio: "", 99 | profileImage: 100 | "https://www.arrowbenefitsgroup.com/wp-content/uploads/2018/04/unisex-avatar.png", 101 | id: userId, 102 | createdAt: TemporalTimestamp.now(), 103 | isVerified: true, 104 | ), 105 | ); 106 | return res.isSignedIn; 107 | } 108 | } on AmplifyException catch (e) { 109 | print(e); 110 | return false; 111 | } 112 | } 113 | 114 | Future checkUsername() async { 115 | try { 116 | Users user = await getCurrUser(); 117 | if (user == null) return false; 118 | if (user.username == "") 119 | return false; 120 | else 121 | return true; 122 | } on AmplifyException catch (e) { 123 | print(e); 124 | return false; 125 | } 126 | } 127 | 128 | Future getCurrUser() async { 129 | AuthUser authUser = await Amplify.Auth.getCurrentUser(); 130 | List user = await Amplify.DataStore.query(Users.classType, 131 | where: Users.ID.eq(authUser.userId)); 132 | if (user.length > 0) { 133 | print(user.first); 134 | return user.first; 135 | } else 136 | return null; 137 | } 138 | 139 | Future> getAllOtherUses() async { 140 | AuthUser authUser = await Amplify.Auth.getCurrentUser(); 141 | List users = await Amplify.DataStore.query(Users.classType, 142 | where: Users.ID.ne(authUser.userId)); 143 | return users; 144 | } 145 | 146 | Future updateFullname(String name) async { 147 | AuthUser authUser = await Amplify.Auth.getCurrentUser(); 148 | Users user = (await Amplify.DataStore.query( 149 | Users.classType, 150 | where: Users.ID.eq(authUser.userId), 151 | ))[0]; 152 | await Amplify.DataStore.save(user.copyWith(username: name)); 153 | } 154 | 155 | // Future updateProfileImage(File image, String key) async { 156 | // try { 157 | // await Amplify.Storage.downloadFile( 158 | // key: key, 159 | // local: image, 160 | // ); 161 | // GetUrlResult getUrlResult = await Amplify.Storage.getUrl(key: key); 162 | // await Amplify.DataStore.save( 163 | // UserStore().currUser.copyWith(profileImage: getUrlResult.url), 164 | // ); 165 | // } on StorageException catch (e) { 166 | // print(e.message); 167 | // } 168 | // } 169 | } 170 | -------------------------------------------------------------------------------- /lib/routes/routes_generator.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_amplify_demo/routes/routes_path.dart'; 3 | import 'package:flutter_amplify_demo/screens/LoginScreen/login_screen.dart'; 4 | import 'package:flutter_amplify_demo/screens/OtpScreen/otp_screen.dart'; 5 | import 'package:flutter_amplify_demo/screens/RegisterScreen/register_screen.dart'; 6 | import 'package:flutter_amplify_demo/screens/SplashScreen/splash_screen.dart'; 7 | import 'package:flutter_amplify_demo/screens/home_screen.dart'; 8 | import 'package:flutter_amplify_demo/wrapper.dart'; 9 | 10 | Route generateRoute(RouteSettings settings) { 11 | switch (settings.name) { 12 | case RoutePath.Wrapper: 13 | return MaterialPageRoute(builder: (_) => Wrapper()); 14 | 15 | case RoutePath.Login: 16 | return MaterialPageRoute(builder: (_) => LoginScreen()); 17 | 18 | case RoutePath.Register: 19 | return MaterialPageRoute(builder: (_) => RegisterScreen()); 20 | 21 | case RoutePath.Splash: 22 | return MaterialPageRoute(builder: (_) => SplashScreen()); 23 | 24 | case RoutePath.Otp: 25 | Map arguments = settings.arguments; 26 | return MaterialPageRoute( 27 | builder: (_) => OtpScreen( 28 | email: arguments['email'], 29 | password: arguments['password'], 30 | name: arguments['name'], 31 | ), 32 | ); 33 | 34 | case RoutePath.Home: 35 | return MaterialPageRoute(builder: (_) => HomeScreen()); 36 | 37 | default: 38 | return MaterialPageRoute(builder: (_) => Wrapper()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/routes/routes_path.dart: -------------------------------------------------------------------------------- 1 | class RoutePath { 2 | static const String Wrapper = '/wrapper'; 3 | static const String Login = '/login'; 4 | static const String Register = '/register'; 5 | static const String Splash = '/splash'; 6 | static const String Otp = '/otp'; 7 | static const String Home = '/home'; 8 | } 9 | -------------------------------------------------------------------------------- /lib/screens/ChatScreen/ChatDetails/appbar.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_amplify_demo/theme/colors.dart'; 4 | import 'package:line_icons/line_icons.dart'; 5 | 6 | Widget getChatDetailsAppBar({ 7 | BuildContext context, 8 | String img, 9 | String name, 10 | bool inSelectMode, 11 | int selectedChatCount, 12 | Function closeSelectionModel, 13 | Function deleteSelectedChats, 14 | Function openUpdateMessageSheet, 15 | }) { 16 | return AppBar( 17 | brightness: Brightness.dark, 18 | backgroundColor: greyColor, 19 | title: inSelectMode 20 | ? Container(child: Text(selectedChatCount.toString())) 21 | : Container( 22 | child: Row( 23 | children: [ 24 | Container( 25 | width: 40, 26 | height: 40, 27 | decoration: BoxDecoration( 28 | shape: BoxShape.circle, 29 | image: DecorationImage( 30 | image: CachedNetworkImageProvider(img), 31 | fit: BoxFit.cover), 32 | ), 33 | ), 34 | SizedBox( 35 | width: 10, 36 | ), 37 | Container( 38 | width: 150, 39 | child: Text( 40 | name, 41 | style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold), 42 | ), 43 | ) 44 | ], 45 | ), 46 | ), 47 | leading: inSelectMode 48 | ? GestureDetector( 49 | onTap: () { 50 | closeSelectionModel(); 51 | }, 52 | child: Icon( 53 | Icons.cancel_outlined, 54 | ), 55 | ) 56 | : GestureDetector( 57 | onTap: () { 58 | Navigator.pop(context); 59 | }, 60 | child: Icon( 61 | Icons.arrow_back_ios, 62 | ), 63 | ), 64 | actions: inSelectMode 65 | ? [ 66 | IconButton( 67 | icon: Icon(Icons.edit, size: 30), 68 | onPressed: () async { 69 | await openUpdateMessageSheet(); 70 | }), 71 | SizedBox(width: 20), 72 | IconButton( 73 | icon: Icon(Icons.delete, size: 30), 74 | onPressed: () async { 75 | await deleteSelectedChats(); 76 | }), 77 | SizedBox(width: 20), 78 | IconButton( 79 | icon: Icon(Icons.more_vert, size: 30), 80 | onPressed: () async {}, 81 | ), 82 | SizedBox(width: 20), 83 | ] 84 | : [ 85 | Icon(LineIcons.phone, size: 30), 86 | SizedBox(width: 20), 87 | Icon(Icons.more_vert, size: 30), 88 | SizedBox(width: 20), 89 | ], 90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /lib/screens/ChatScreen/chat_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_amplify_demo/stores/chat.dart'; 4 | import 'package:line_icons/line_icons.dart'; 5 | import 'package:flutter_amplify_demo/screens/ChatScreen/chat_detail_page.dart'; 6 | import 'package:flutter_amplify_demo/theme/colors.dart'; 7 | import 'package:provider/provider.dart'; 8 | 9 | class ChatPage extends StatefulWidget { 10 | @override 11 | _ChatPageState createState() => _ChatPageState(); 12 | } 13 | 14 | class _ChatPageState extends State { 15 | ChatStore chatStore; 16 | @override 17 | void initState() { 18 | chatStore = Provider.of(context, listen: false); 19 | fetchUserChats(); 20 | super.initState(); 21 | } 22 | 23 | fetchUserChats() async { 24 | await chatStore.fetchUserChats(); 25 | if (mounted) setState(() {}); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Scaffold( 31 | backgroundColor: bgColor, 32 | body: chatStore.chatData != [] ? getBody() : Container(), 33 | ); 34 | } 35 | 36 | Widget getBody() { 37 | var size = MediaQuery.of(context).size; 38 | return ListView( 39 | children: [ 40 | Padding( 41 | padding: EdgeInsets.only(top: 15, right: 15, left: 15), 42 | child: Column( 43 | crossAxisAlignment: CrossAxisAlignment.start, 44 | children: [ 45 | Text( 46 | "Chats", 47 | style: TextStyle( 48 | fontSize: 23, color: white, fontWeight: FontWeight.bold), 49 | ), 50 | SizedBox( 51 | height: 15, 52 | ), 53 | Container( 54 | height: 38, 55 | decoration: BoxDecoration( 56 | color: textfieldColor, 57 | borderRadius: BorderRadius.circular(10), 58 | ), 59 | child: TextField( 60 | style: TextStyle(color: white), 61 | cursorColor: primary, 62 | decoration: InputDecoration( 63 | prefixIcon: 64 | Icon(LineIcons.search, color: white.withOpacity(0.3)), 65 | border: InputBorder.none, 66 | hintText: "Search", 67 | hintStyle: 68 | TextStyle(color: white.withOpacity(0.3), fontSize: 17), 69 | ), 70 | ), 71 | ), 72 | ], 73 | ), 74 | ), 75 | Padding( 76 | padding: const EdgeInsets.only(left: 5, top: 5, right: 5), 77 | child: Divider(color: white.withOpacity(0.3)), 78 | ), 79 | SizedBox( 80 | height: 10, 81 | ), 82 | if (chatStore.userChats.length > 0) 83 | Column( 84 | children: chatStore.userChats 85 | .map((user) => UserChatCard( 86 | context: context, 87 | size: size, 88 | name: user['otherUserName'], 89 | profileImage: user['otherUserProfileImage'], 90 | chatId: user['chatId'], 91 | )) 92 | .toList(), 93 | ), 94 | ], 95 | ); 96 | } 97 | } 98 | 99 | class UserChatCard extends StatelessWidget { 100 | const UserChatCard({ 101 | Key key, 102 | @required this.context, 103 | @required this.size, 104 | @required this.name, 105 | @required this.profileImage, 106 | this.chatId, 107 | }) : super(key: key); 108 | 109 | final BuildContext context; 110 | final Size size; 111 | final String name; 112 | final String profileImage; 113 | final String chatId; 114 | 115 | @override 116 | Widget build(BuildContext context) { 117 | return GestureDetector( 118 | onTap: () { 119 | Navigator.push( 120 | context, 121 | MaterialPageRoute( 122 | builder: (_) => ChatDetailPage( 123 | name: name, 124 | img: profileImage, 125 | chatId: chatId, 126 | ), 127 | ), 128 | ); 129 | }, 130 | child: Padding( 131 | padding: const EdgeInsets.only(left: 15, right: 15, bottom: 5), 132 | child: Row( 133 | crossAxisAlignment: CrossAxisAlignment.start, 134 | children: [ 135 | Container( 136 | width: 60, 137 | height: 60, 138 | decoration: BoxDecoration( 139 | shape: BoxShape.circle, 140 | image: DecorationImage( 141 | image: CachedNetworkImageProvider(profileImage), 142 | fit: BoxFit.cover), 143 | ), 144 | ), 145 | SizedBox(width: 15), 146 | Expanded( 147 | child: Container( 148 | height: 85, 149 | child: Column( 150 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 151 | children: [ 152 | Column( 153 | children: [ 154 | Row( 155 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 156 | children: [ 157 | Container( 158 | width: (size.width - 40) * 0.6, 159 | child: Text(name, 160 | style: TextStyle( 161 | fontSize: 16, 162 | color: white, 163 | fontWeight: FontWeight.w600)), 164 | ), 165 | Expanded( 166 | child: Text( 167 | "10/08/20", 168 | style: TextStyle( 169 | fontSize: 14, 170 | color: white.withOpacity(0.4)), 171 | ), 172 | ) 173 | ], 174 | ), 175 | ], 176 | ), 177 | Divider( 178 | color: white.withOpacity(0.3), 179 | ) 180 | ], 181 | ), 182 | ), 183 | ) 184 | ], 185 | ), 186 | ), 187 | ); 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /lib/screens/LoginScreen/login_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_amplify_demo/components/google_login_button.dart'; 3 | import 'package:flutter_amplify_demo/components/login_text_field.dart'; 4 | import 'package:flutter_amplify_demo/components/primary_button.dart'; 5 | import 'package:flutter_amplify_demo/components/show_snackbar.dart'; 6 | import 'package:flutter_amplify_demo/routes/routes_path.dart'; 7 | import 'package:flutter_amplify_demo/services/get_it_service.dart'; 8 | import 'package:flutter_amplify_demo/services/navigation_service.dart'; 9 | import 'package:flutter_amplify_demo/services/text_style.dart'; 10 | import 'package:flutter_amplify_demo/services/validations.dart'; 11 | import 'package:flutter_amplify_demo/stores/auth.dart'; 12 | import 'package:flutter_amplify_demo/stores/state_keeper.dart'; 13 | import 'package:flutter_amplify_demo/theme/colors.dart'; 14 | import 'package:flutter_amplify_demo/services/size_config.dart'; 15 | import 'package:provider/provider.dart'; 16 | 17 | class LoginScreen extends StatefulWidget { 18 | @override 19 | _LoginScreenState createState() => _LoginScreenState(); 20 | } 21 | 22 | class _LoginScreenState extends State { 23 | final GlobalKey formKey = GlobalKey(); 24 | 25 | AuthStore _authStore; 26 | 27 | NavigationService _navigationService = 28 | get_it_instance_const(); 29 | 30 | TextEditingController emailCtrl = TextEditingController(); 31 | TextEditingController passwordCtrl = TextEditingController(); 32 | 33 | @override 34 | void initState() { 35 | super.initState(); 36 | _authStore = context.read(); 37 | } 38 | 39 | @override 40 | Widget build(BuildContext context) { 41 | return Scaffold( 42 | backgroundColor: bgColor, 43 | body: getBody(), 44 | ); 45 | } 46 | 47 | Widget getBody() { 48 | return SingleChildScrollView( 49 | child: Container( 50 | padding: EdgeInsets.symmetric(vertical: 80, horizontal: 30), 51 | child: Column( 52 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 53 | children: [ 54 | Column( 55 | crossAxisAlignment: CrossAxisAlignment.start, 56 | children: [ 57 | // SizedBox(height: 80.toHeight), 58 | Text( 59 | "Let's sign you in.", 60 | style: CustomTextStyle.loginTitleStyle, 61 | ), 62 | SizedBox(height: 10.toHeight), 63 | Text( 64 | "Welcome back.", 65 | style: CustomTextStyle.loginTitleSecondaryStyle, 66 | ), 67 | Text( 68 | "You've been missed!", 69 | style: CustomTextStyle.loginTitleSecondaryStyle, 70 | ), 71 | buildEmailLoginForm(), 72 | ], 73 | ), 74 | Column( 75 | children: [ 76 | SizedBox(height: 20.toHeight), 77 | Row( 78 | mainAxisAlignment: MainAxisAlignment.center, 79 | children: [ 80 | Text( 81 | "Don't have an account? ", 82 | style: CustomTextStyle.registerButtonStyle, 83 | ), 84 | GestureDetector( 85 | onTap: () { 86 | _navigationService.popAllAndReplace(RoutePath.Register); 87 | }, 88 | child: Text( 89 | "Register. 😁", 90 | style: CustomTextStyle.registerButtonStyle 91 | .copyWith(fontWeight: FontWeight.bold), 92 | ), 93 | ) 94 | ], 95 | ), 96 | SizedBox(height: 20.toHeight), 97 | GoogleButton(isPrimaryButton: false), 98 | ], 99 | ), 100 | ], 101 | ), 102 | ), 103 | ); 104 | } 105 | 106 | Form buildEmailLoginForm() { 107 | return Form( 108 | key: formKey, 109 | child: Column( 110 | children: [ 111 | SizedBox(height: 45.toHeight), 112 | CustomLoginTextField( 113 | hintTextL: "Email", 114 | ctrl: emailCtrl, 115 | validation: Validations.validateEmail, 116 | type: TextInputType.emailAddress, 117 | ), 118 | SizedBox(height: 24.toHeight), 119 | CustomLoginTextField( 120 | hintTextL: "Password", 121 | ctrl: passwordCtrl, 122 | validation: Validations.valdiatePassword, 123 | obscureText: true, 124 | type: TextInputType.text, 125 | ), 126 | SizedBox(height: 24.toHeight), 127 | Consumer(builder: (_, authStore, __) { 128 | if (authStore.status[AuthStore.LOGIN_USER] == Status.Error) { 129 | WidgetsBinding.instance.addPostFrameCallback((_) { 130 | showSnackBar( 131 | context, 132 | authStore.error[AuthStore.LOGIN_USER], 133 | ); 134 | authStore.reset(AuthStore.LOGIN_USER); 135 | }); 136 | } 137 | if (authStore.status[AuthStore.LOGIN_USER] == Status.Loading) { 138 | return CircularProgressIndicator(); 139 | } 140 | return PrimaryButton( 141 | text: "Log in", 142 | onPress: () async { 143 | if (!formKey.currentState.validate()) return; 144 | bool isSignedIn = await _authStore.loginWithEmailPassword( 145 | email: emailCtrl.text, 146 | password: passwordCtrl.text, 147 | ); 148 | if (isSignedIn) 149 | _navigationService.popAllAndReplace(RoutePath.Home); 150 | }, 151 | ); 152 | }), 153 | ], 154 | ), 155 | ); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /lib/screens/OtpScreen/otp_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_amplify_demo/components/login_text_field.dart'; 3 | import 'package:flutter_amplify_demo/components/primary_button.dart'; 4 | import 'package:flutter_amplify_demo/components/secondary_button.dart'; 5 | import 'package:flutter_amplify_demo/routes/routes_path.dart'; 6 | import 'package:flutter_amplify_demo/services/get_it_service.dart'; 7 | import 'package:flutter_amplify_demo/services/navigation_service.dart'; 8 | import 'package:flutter_amplify_demo/services/text_style.dart'; 9 | import 'package:flutter_amplify_demo/services/validations.dart'; 10 | import 'package:flutter_amplify_demo/stores/auth.dart'; 11 | import 'package:flutter_amplify_demo/theme/colors.dart'; 12 | import 'package:flutter_amplify_demo/services/size_config.dart'; 13 | import 'package:provider/provider.dart'; 14 | 15 | class OtpScreen extends StatefulWidget { 16 | final String email; 17 | final String password; 18 | final String name; 19 | 20 | const OtpScreen({ 21 | Key key, 22 | this.email, 23 | this.password, 24 | @required this.name, 25 | }) : super(key: key); 26 | @override 27 | _OtpScreenState createState() => _OtpScreenState(); 28 | } 29 | 30 | class _OtpScreenState extends State { 31 | TextEditingController otpCtrl = TextEditingController(); 32 | 33 | bool showOtpScreen = false; 34 | 35 | @override 36 | void initState() { 37 | super.initState(); 38 | } 39 | 40 | NavigationService _navigationService = 41 | get_it_instance_const(); 42 | 43 | @override 44 | Widget build(BuildContext context) { 45 | return Scaffold( 46 | backgroundColor: bgColor, 47 | body: getBody(), 48 | ); 49 | } 50 | 51 | Widget getBody() { 52 | return SingleChildScrollView( 53 | child: Container( 54 | padding: EdgeInsets.symmetric(vertical: 80, horizontal: 30), 55 | child: Column( 56 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 57 | children: [ 58 | Column( 59 | crossAxisAlignment: CrossAxisAlignment.start, 60 | children: [ 61 | // SizedBox(height: 80.toHeight), 62 | Text( 63 | "A WhatsApp Clone.", 64 | style: CustomTextStyle.loginTitleStyle.copyWith(fontSize: 36), 65 | ), 66 | SizedBox(height: 10.toHeight), 67 | Text( 68 | "Welcome aboard!", 69 | style: CustomTextStyle.loginTitleSecondaryStyle, 70 | ), 71 | buildOtpForm(), 72 | SizedBox(height: 24.toHeight), 73 | ], 74 | ), 75 | Column( 76 | children: [ 77 | SizedBox(height: 16.toHeight), 78 | SecondaryButton( 79 | text: "Sign In", 80 | onPress: () { 81 | _navigationService.popAllAndReplace(RoutePath.Login); 82 | }, 83 | ), 84 | ], 85 | ), 86 | ], 87 | ), 88 | ), 89 | ); 90 | } 91 | 92 | Column buildOtpForm() { 93 | return Column( 94 | children: [ 95 | SizedBox(height: 45.toHeight), 96 | CustomLoginTextField( 97 | hintTextL: "Enter OTP Send to your Email", 98 | ctrl: otpCtrl, 99 | validation: Validations.validateOTP, 100 | maxLength: 6, 101 | type: TextInputType.number, 102 | ), 103 | SizedBox(height: 24.toHeight), 104 | Consumer(builder: (_, authStore, __) { 105 | return PrimaryButton( 106 | text: "Continue", 107 | onPress: () async { 108 | try { 109 | bool isVerified = await authStore.optVerification( 110 | email: widget.email, 111 | otp: otpCtrl.text, 112 | password: widget.password, 113 | name: widget.name, 114 | ); 115 | if (isVerified) 116 | _navigationService.popAllAndReplace(RoutePath.Home); 117 | } catch (e) { 118 | print(e); 119 | } 120 | }, 121 | ); 122 | }), 123 | ], 124 | ); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /lib/screens/RegisterScreen/register_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_amplify_demo/components/login_text_field.dart'; 3 | import 'package:flutter_amplify_demo/components/primary_button.dart'; 4 | import 'package:flutter_amplify_demo/components/secondary_button.dart'; 5 | import 'package:flutter_amplify_demo/components/show_snackbar.dart'; 6 | import 'package:flutter_amplify_demo/routes/routes_path.dart'; 7 | import 'package:flutter_amplify_demo/services/get_it_service.dart'; 8 | import 'package:flutter_amplify_demo/services/navigation_service.dart'; 9 | import 'package:flutter_amplify_demo/services/text_style.dart'; 10 | import 'package:flutter_amplify_demo/services/validations.dart'; 11 | import 'package:flutter_amplify_demo/stores/auth.dart'; 12 | import 'package:flutter_amplify_demo/stores/state_keeper.dart'; 13 | import 'package:flutter_amplify_demo/theme/colors.dart'; 14 | import 'package:flutter_amplify_demo/services/size_config.dart'; 15 | import 'package:provider/provider.dart'; 16 | 17 | class RegisterScreen extends StatefulWidget { 18 | @override 19 | _RegisterScreenState createState() => _RegisterScreenState(); 20 | } 21 | 22 | class _RegisterScreenState extends State { 23 | final GlobalKey formKey = GlobalKey(); 24 | 25 | TextEditingController nameCtrl = TextEditingController(); 26 | TextEditingController emailCtrl = TextEditingController(); 27 | TextEditingController passwordCtrl = TextEditingController(); 28 | 29 | @override 30 | void initState() { 31 | super.initState(); 32 | } 33 | 34 | NavigationService _navigationService = 35 | get_it_instance_const(); 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return Scaffold( 40 | backgroundColor: bgColor, 41 | body: getBody(), 42 | ); 43 | } 44 | 45 | Widget getBody() { 46 | return SingleChildScrollView( 47 | child: Container( 48 | padding: EdgeInsets.symmetric(vertical: 80, horizontal: 30), 49 | child: Column( 50 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 51 | children: [ 52 | Column( 53 | crossAxisAlignment: CrossAxisAlignment.start, 54 | children: [ 55 | // SizedBox(height: 80.toHeight), 56 | Text( 57 | "A WhatsApp Clone.", 58 | style: CustomTextStyle.loginTitleStyle 59 | .copyWith(fontSize: 36.toFont), 60 | ), 61 | SizedBox(height: 10.toHeight), 62 | Text( 63 | "Welcome aboard!", 64 | style: CustomTextStyle.loginTitleSecondaryStyle, 65 | ), 66 | buildEmailSignUpForm(), 67 | SizedBox(height: 24.toHeight), 68 | ], 69 | ), 70 | Column( 71 | children: [ 72 | SizedBox(height: 16.toHeight), 73 | SecondaryButton( 74 | text: "Sign In", 75 | onPress: () { 76 | _navigationService.popAllAndReplace(RoutePath.Login); 77 | }, 78 | ), 79 | ], 80 | ), 81 | ], 82 | ), 83 | ), 84 | ); 85 | } 86 | 87 | Form buildEmailSignUpForm() { 88 | return Form( 89 | key: formKey, 90 | child: Column( 91 | children: [ 92 | SizedBox(height: 45.toHeight), 93 | CustomLoginTextField( 94 | hintTextL: "Full Name", 95 | ctrl: nameCtrl, 96 | validation: Validations.validateFullName, 97 | type: TextInputType.text, 98 | ), 99 | SizedBox(height: 12.toHeight), 100 | CustomLoginTextField( 101 | hintTextL: "Email", 102 | ctrl: emailCtrl, 103 | validation: Validations.validateEmail, 104 | type: TextInputType.emailAddress, 105 | ), 106 | SizedBox(height: 12.toHeight), 107 | CustomLoginTextField( 108 | hintTextL: "Password", 109 | ctrl: passwordCtrl, 110 | validation: Validations.valdiatePassword, 111 | obscureText: true, 112 | ), 113 | SizedBox(height: 24.toHeight), 114 | Consumer(builder: (_, authStore, __) { 115 | if (authStore.status[AuthStore.LOGIN_USER] == Status.Error) { 116 | WidgetsBinding.instance.addPostFrameCallback((_) { 117 | showSnackBar( 118 | context, 119 | authStore.error[AuthStore.LOGIN_USER], 120 | ); 121 | authStore.reset(AuthStore.LOGIN_USER); 122 | }); 123 | } 124 | if (authStore.status[AuthStore.LOGIN_USER] == Status.Loading) { 125 | return CircularProgressIndicator(); 126 | } 127 | return PrimaryButton( 128 | text: "Register", 129 | onPress: () async { 130 | if (!formKey.currentState.validate()) return; 131 | bool isSignupComplete = await authStore.signUpWithEmailPassword( 132 | name: nameCtrl.text, 133 | email: emailCtrl.text, 134 | password: passwordCtrl.text, 135 | ); 136 | if (isSignupComplete) 137 | _navigationService.navigateTo( 138 | RoutePath.Otp, 139 | arguments: { 140 | "email": emailCtrl.text, 141 | "password": passwordCtrl.text, 142 | "name": nameCtrl.text, 143 | }, 144 | ); 145 | }, 146 | ); 147 | }) 148 | ], 149 | ), 150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/screens/SettingsScreen/components/image_picker.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_amplify_demo/stores/user.dart'; 4 | import 'package:flutter_amplify_demo/theme/colors.dart'; 5 | import 'package:image_picker/image_picker.dart'; 6 | 7 | class ImagePickerButton extends StatefulWidget { 8 | ImagePickerButton({Key key}) : super(key: key); 9 | 10 | @override 11 | _ImagePickerButtonState createState() => _ImagePickerButtonState(); 12 | } 13 | 14 | class _ImagePickerButtonState extends State { 15 | final picker = ImagePicker(); 16 | File _image; 17 | 18 | Future getImage() async { 19 | final pickedFile = await picker.getImage(source: ImageSource.gallery); 20 | _image = File(pickedFile.path); 21 | final key = new DateTime.now().toString(); 22 | // await UserStore().updateProfileImage(image: _image, key: key); 23 | } 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return Center( 28 | child: IconButton( 29 | onPressed: () { 30 | getImage(); 31 | }, 32 | icon: Icon( 33 | Icons.qr_code, 34 | color: primary, 35 | size: 20, 36 | ), 37 | ), 38 | ); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/screens/SettingsScreen/components/users_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_amplify_demo/models/Users.dart'; 4 | import 'package:flutter_amplify_demo/stores/chat.dart'; 5 | import 'package:flutter_amplify_demo/stores/user.dart'; 6 | import 'package:flutter_amplify_demo/theme/colors.dart'; 7 | 8 | class UsersList extends StatefulWidget { 9 | final Users user; 10 | final bool hasAdded; 11 | UsersList({Key key, this.user, this.hasAdded}) : super(key: key); 12 | 13 | @override 14 | _UsersListState createState() => _UsersListState(); 15 | } 16 | 17 | class _UsersListState extends State { 18 | bool addButtonloading = false; 19 | @override 20 | Widget build(BuildContext context) { 21 | return Container( 22 | width: double.infinity, 23 | height: 90, 24 | decoration: BoxDecoration(color: textfieldColor), 25 | child: Padding( 26 | padding: const EdgeInsets.all(10.0), 27 | child: Row( 28 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 29 | children: [ 30 | Row( 31 | children: [ 32 | Container( 33 | width: 70, 34 | height: 70, 35 | decoration: BoxDecoration( 36 | shape: BoxShape.circle, 37 | image: DecorationImage( 38 | image: CachedNetworkImageProvider( 39 | widget.user.profileImage, 40 | ), 41 | fit: BoxFit.cover)), 42 | ), 43 | SizedBox( 44 | width: 10, 45 | ), 46 | Text( 47 | widget.user.username, 48 | style: TextStyle( 49 | fontSize: 20, fontWeight: FontWeight.bold, color: white), 50 | ), 51 | ], 52 | ), 53 | Row( 54 | children: [ 55 | Container( 56 | width: 38, 57 | height: 38, 58 | decoration: BoxDecoration( 59 | color: white.withOpacity(0.1), shape: BoxShape.circle), 60 | child: Center( 61 | child: addButtonloading 62 | ? CircularProgressIndicator() 63 | : IconButton( 64 | icon: Icon( 65 | widget.hasAdded ? Icons.check : Icons.add, 66 | size: 20, 67 | ), 68 | color: primary, 69 | onPressed: () async { 70 | if (!widget.hasAdded) { 71 | setState(() { 72 | addButtonloading = true; 73 | }); 74 | await ChatStore() 75 | .addUserToChatList(widget.user); 76 | await UserStore().fetchCurrentUser(); 77 | await ChatStore().fetchUserChats(); 78 | setState(() { 79 | addButtonloading = false; 80 | }); 81 | } 82 | }, 83 | ), 84 | ), 85 | ) 86 | ], 87 | ) 88 | ], 89 | ), 90 | ), 91 | ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/screens/SettingsScreen/settings_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:cached_network_image/cached_network_image.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_amplify_demo/models/Users.dart'; 6 | import 'package:flutter_amplify_demo/routes/routes_path.dart'; 7 | import 'package:flutter_amplify_demo/screens/SettingsScreen/components/image_picker.dart'; 8 | import 'package:flutter_amplify_demo/screens/SettingsScreen/components/users_list.dart'; 9 | import 'package:flutter_amplify_demo/services/get_it_service.dart'; 10 | import 'package:flutter_amplify_demo/services/navigation_service.dart'; 11 | import 'package:flutter_amplify_demo/stores/auth.dart'; 12 | import 'package:flutter_amplify_demo/stores/user.dart'; 13 | import 'package:flutter_amplify_demo/theme/colors.dart'; 14 | import 'package:provider/provider.dart'; 15 | import 'package:pull_to_refresh/pull_to_refresh.dart'; 16 | 17 | class SettingsPage extends StatefulWidget { 18 | final Function updateHomeScreen; 19 | 20 | const SettingsPage({Key key, this.updateHomeScreen}) : super(key: key); 21 | @override 22 | _SettingsPageState createState() => _SettingsPageState(); 23 | } 24 | 25 | class _SettingsPageState extends State { 26 | NavigationService _navigationService = 27 | get_it_instance_const(); 28 | 29 | RefreshController _refreshController = 30 | RefreshController(initialRefresh: false); 31 | 32 | bool addButtonloading = false; 33 | 34 | Future _onRefresh() async { 35 | await UserStore().fetchCurrentUser(); 36 | await UserStore().fetchAllOtherUsers(); 37 | _refreshController.refreshCompleted(); 38 | } 39 | 40 | @override 41 | void initState() { 42 | super.initState(); 43 | } 44 | 45 | @override 46 | Widget build(BuildContext context) { 47 | return Scaffold( 48 | backgroundColor: bgColor, 49 | body: SmartRefresher( 50 | enablePullDown: true, 51 | enablePullUp: true, 52 | header: MaterialClassicHeader(color: Colors.white), 53 | controller: _refreshController, 54 | onRefresh: _onRefresh, 55 | child: getBody(), 56 | ), 57 | ); 58 | } 59 | 60 | Widget getBody() { 61 | return ListView( 62 | children: [ 63 | Padding( 64 | padding: const EdgeInsets.only(left: 15, right: 15, top: 15), 65 | child: Text( 66 | "Settings", 67 | style: TextStyle( 68 | fontSize: 23, color: white, fontWeight: FontWeight.bold), 69 | ), 70 | ), 71 | SizedBox( 72 | height: 10, 73 | ), 74 | Consumer(builder: (_, userStore, __) { 75 | return buildUserSection(userStore.currUser); 76 | }), 77 | SizedBox( 78 | height: 30, 79 | ), 80 | Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ 81 | Padding( 82 | padding: const EdgeInsets.all(8.0), 83 | child: Text( 84 | "Users List", 85 | style: TextStyle( 86 | fontSize: 20, 87 | fontWeight: FontWeight.bold, 88 | color: Colors.white, 89 | ), 90 | ), 91 | ), 92 | Consumer2( 93 | builder: (_, userStore, authStore, __) { 94 | if (userStore.allOtherUsers == null) 95 | return Container(); 96 | else if (userStore.allOtherUsers.length == 0) 97 | return Text("Sorry there is no other user."); 98 | else { 99 | return Column( 100 | children: [ 101 | ...userStore.allOtherUsers.map((user) { 102 | bool hasAdded = false; 103 | if (userStore.currUser != null && 104 | userStore.currUser.chats != null) 105 | userStore.currUser.chats.forEach((chat) { 106 | if (json.decode(chat)['otherUserId'] == user.id) 107 | hasAdded = true; 108 | }); 109 | return UsersList(hasAdded: hasAdded, user: user); 110 | }).toList() 111 | ], 112 | ); 113 | } 114 | }), 115 | SizedBox(height: 20), 116 | Container( 117 | width: double.infinity, 118 | decoration: BoxDecoration(color: textfieldColor), 119 | child: Padding( 120 | padding: 121 | const EdgeInsets.only(top: 8, bottom: 8, left: 10, right: 10), 122 | child: Column( 123 | children: [ 124 | buildListItem( 125 | title: "Logout", 126 | icon: Icons.logout, 127 | onTap: () async { 128 | await AuthStore().logout(); 129 | _navigationService.popAllAndReplace(RoutePath.Wrapper); 130 | }, 131 | ) 132 | ], 133 | ), 134 | ), 135 | ), 136 | ]), 137 | ], 138 | ); 139 | } 140 | 141 | Container buildUserSection(Users currUser) { 142 | return Container( 143 | width: double.infinity, 144 | height: 90, 145 | decoration: BoxDecoration(color: textfieldColor), 146 | child: Padding( 147 | padding: const EdgeInsets.all(10.0), 148 | child: Row( 149 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 150 | children: [ 151 | Row( 152 | children: [ 153 | Container( 154 | width: 70, 155 | height: 70, 156 | decoration: BoxDecoration( 157 | shape: BoxShape.circle, 158 | image: currUser == null 159 | ? null 160 | : DecorationImage( 161 | image: CachedNetworkImageProvider( 162 | currUser.profileImage), 163 | fit: BoxFit.cover, 164 | ), 165 | ), 166 | ), 167 | SizedBox( 168 | width: 10, 169 | ), 170 | Text( 171 | currUser == null ? "" : currUser.username, 172 | style: TextStyle( 173 | fontSize: 20, fontWeight: FontWeight.bold, color: white), 174 | ), 175 | ], 176 | ), 177 | Container( 178 | width: 38, 179 | height: 38, 180 | decoration: BoxDecoration( 181 | color: white.withOpacity(0.1), shape: BoxShape.circle), 182 | child: ImagePickerButton(), 183 | ) 184 | ], 185 | ), 186 | ), 187 | ); 188 | } 189 | 190 | GestureDetector buildListItem({String title, IconData icon, Function onTap}) { 191 | return GestureDetector( 192 | onTap: onTap, 193 | child: Row( 194 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 195 | children: [ 196 | Row( 197 | children: [ 198 | Container( 199 | width: 30, 200 | height: 30, 201 | decoration: BoxDecoration( 202 | borderRadius: BorderRadius.circular(8), 203 | color: Colors.red, 204 | ), 205 | child: Center( 206 | child: Icon( 207 | icon, 208 | color: white, 209 | size: 20, 210 | ), 211 | ), 212 | ), 213 | SizedBox( 214 | width: 12, 215 | ), 216 | Text( 217 | title, 218 | style: TextStyle( 219 | fontSize: 16, 220 | color: white, 221 | fontWeight: FontWeight.w500, 222 | ), 223 | ) 224 | ], 225 | ), 226 | Icon( 227 | Icons.arrow_forward_ios, 228 | color: white.withOpacity(0.2), 229 | size: 15, 230 | ) 231 | ], 232 | ), 233 | ); 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /lib/screens/SplashScreen/splash_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_amplify_demo/components/primary_button.dart'; 3 | import 'package:flutter_amplify_demo/components/secondary_button.dart'; 4 | import 'package:flutter_amplify_demo/routes/routes_path.dart'; 5 | import 'package:flutter_amplify_demo/services/get_it_service.dart'; 6 | import 'package:flutter_amplify_demo/services/navigation_service.dart'; 7 | import 'package:flutter_amplify_demo/services/text_style.dart'; 8 | import 'package:flutter_amplify_demo/theme/colors.dart'; 9 | import 'package:flutter_amplify_demo/services/size_config.dart'; 10 | 11 | class SplashScreen extends StatefulWidget { 12 | @override 13 | _SplashScreenState createState() => _SplashScreenState(); 14 | } 15 | 16 | class _SplashScreenState extends State { 17 | NavigationService _navigationService = 18 | get_it_instance_const(); 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | SizeConfig().init(context); 23 | return Scaffold( 24 | backgroundColor: bgColor, 25 | body: getBody(), 26 | ); 27 | } 28 | 29 | Widget getBody() { 30 | return SingleChildScrollView( 31 | child: Container( 32 | width: SizeConfig.screenWidth, 33 | padding: EdgeInsets.only( 34 | top: 80.toHeight, 35 | bottom: 40.toHeight, 36 | left: 30.toWidth, 37 | right: 30.toWidth, 38 | ), 39 | child: Column( 40 | crossAxisAlignment: CrossAxisAlignment.center, 41 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 42 | children: [ 43 | Column( 44 | children: [ 45 | Container( 46 | decoration: BoxDecoration( 47 | color: Colors.white, 48 | borderRadius: BorderRadius.circular(25), 49 | ), 50 | width: 300.toWidth, 51 | height: 300.toHeight, 52 | child: Image.asset( 53 | "assets/images/splash.png", 54 | width: 500.toWidth, 55 | height: 500.toHeight, 56 | ), 57 | ), 58 | SizedBox(height: 20.toHeight), 59 | Text( 60 | "A Whatsapp Clone.", 61 | style: CustomTextStyle.loginTitleStyle, 62 | textAlign: TextAlign.center, 63 | ), 64 | SizedBox(height: 20.toHeight), 65 | ], 66 | ), 67 | Column( 68 | children: [ 69 | PrimaryButton( 70 | text: "Sign In", 71 | onPress: () { 72 | _navigationService.popAllAndReplace(RoutePath.Login); 73 | }, 74 | ), 75 | SizedBox(height: 12.toHeight), 76 | SecondaryButton( 77 | text: "Register", 78 | onPress: () { 79 | _navigationService.popAllAndReplace(RoutePath.Register); 80 | }, 81 | ), 82 | ], 83 | ), 84 | ], 85 | ), 86 | ), 87 | ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /lib/screens/StatusScreens/status_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:line_icons/line_icons.dart'; 3 | import 'package:flutter_amplify_demo/theme/colors.dart'; 4 | 5 | class StatusPage extends StatefulWidget { 6 | @override 7 | _StatusPageState createState() => _StatusPageState(); 8 | } 9 | 10 | class _StatusPageState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return Scaffold( 14 | backgroundColor: bgColor, 15 | appBar: getAppBar(), 16 | body: getBody(), 17 | ); 18 | } 19 | 20 | Widget getAppBar() { 21 | return AppBar( 22 | brightness: Brightness.dark, 23 | backgroundColor: bgColor, 24 | title: Row( 25 | children: [ 26 | Text( 27 | "Privacy", 28 | style: TextStyle( 29 | fontSize: 16, color: primary, fontWeight: FontWeight.w500), 30 | ) 31 | ], 32 | ), 33 | ); 34 | } 35 | 36 | Widget getBody() { 37 | return ListView( 38 | children: [ 39 | Padding( 40 | padding: const EdgeInsets.only(left: 15, right: 15, top: 15), 41 | child: Column( 42 | crossAxisAlignment: CrossAxisAlignment.start, 43 | children: [ 44 | Text( 45 | "Status", 46 | style: TextStyle( 47 | fontSize: 23, color: white, fontWeight: FontWeight.bold), 48 | ), 49 | SizedBox( 50 | height: 15, 51 | ), 52 | Container( 53 | height: 38, 54 | decoration: BoxDecoration( 55 | color: textfieldColor, 56 | borderRadius: BorderRadius.circular(10)), 57 | child: TextField( 58 | style: TextStyle(color: white), 59 | cursorColor: primary, 60 | decoration: InputDecoration( 61 | prefixIcon: 62 | Icon(LineIcons.search, color: white.withOpacity(0.3)), 63 | border: InputBorder.none, 64 | hintText: "Search", 65 | hintStyle: TextStyle( 66 | color: white.withOpacity(0.3), fontSize: 17)), 67 | ), 68 | ) 69 | ], 70 | ), 71 | ), 72 | SizedBox( 73 | height: 50, 74 | ), 75 | Container( 76 | height: 80, 77 | width: double.infinity, 78 | decoration: BoxDecoration(color: textfieldColor), 79 | child: Padding( 80 | padding: const EdgeInsets.all(10.0), 81 | child: Row( 82 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 83 | children: [ 84 | Row( 85 | children: [ 86 | Container( 87 | child: Stack( 88 | children: [ 89 | Container( 90 | width: 70, 91 | height: 70, 92 | decoration: BoxDecoration( 93 | shape: BoxShape.circle, 94 | image: DecorationImage( 95 | image: NetworkImage(""), 96 | fit: BoxFit.cover)), 97 | ), 98 | Positioned( 99 | right: 5, 100 | bottom: 0, 101 | child: Container( 102 | width: 20, 103 | height: 20, 104 | decoration: BoxDecoration( 105 | shape: BoxShape.circle, color: primary), 106 | child: Center( 107 | child: Icon( 108 | Icons.add, 109 | color: white, 110 | size: 18, 111 | ), 112 | ), 113 | ), 114 | ) 115 | ], 116 | ), 117 | ), 118 | SizedBox(width: 5), 119 | Column( 120 | crossAxisAlignment: CrossAxisAlignment.start, 121 | mainAxisAlignment: MainAxisAlignment.center, 122 | children: [ 123 | Text( 124 | "My Status", 125 | style: TextStyle( 126 | fontSize: 17, 127 | fontWeight: FontWeight.w600, 128 | color: white), 129 | ), 130 | SizedBox( 131 | height: 3, 132 | ), 133 | Text( 134 | "Add to my status", 135 | style: TextStyle( 136 | fontSize: 14, 137 | fontWeight: FontWeight.w400, 138 | color: white.withOpacity(0.5)), 139 | ) 140 | ], 141 | ) 142 | ], 143 | ), 144 | Row( 145 | children: [ 146 | Container( 147 | width: 38, 148 | height: 38, 149 | decoration: BoxDecoration( 150 | color: white.withOpacity(0.1), 151 | shape: BoxShape.circle), 152 | child: Center( 153 | child: Icon( 154 | Icons.camera_alt, 155 | color: primary, 156 | size: 20, 157 | ), 158 | ), 159 | ), 160 | SizedBox( 161 | width: 10, 162 | ), 163 | Container( 164 | width: 38, 165 | height: 38, 166 | decoration: BoxDecoration( 167 | color: white.withOpacity(0.1), 168 | shape: BoxShape.circle), 169 | child: Center( 170 | child: Icon( 171 | Icons.edit, 172 | color: primary, 173 | size: 20, 174 | ), 175 | ), 176 | ), 177 | ], 178 | ) 179 | ], 180 | ), 181 | ), 182 | ), 183 | SizedBox( 184 | height: 30, 185 | ), 186 | Container( 187 | height: 40, 188 | width: double.infinity, 189 | decoration: BoxDecoration(color: textfieldColor), 190 | child: Center( 191 | child: Text( 192 | "No recent updates to show right now.", 193 | style: TextStyle( 194 | fontSize: 14, 195 | color: white.withOpacity(0.5), 196 | fontWeight: FontWeight.w400), 197 | ), 198 | ), 199 | ) 200 | ], 201 | ); 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /lib/screens/home_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:amplify_flutter/amplify.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_amplify_demo/screens/SettingsScreen/settings_page.dart'; 4 | import 'package:flutter_amplify_demo/stores/user.dart'; 5 | import 'package:line_icons/line_icons.dart'; 6 | import 'package:flutter_amplify_demo/screens/ChatScreen/chat_page.dart'; 7 | import 'package:flutter_amplify_demo/screens/StatusScreens/status_page.dart'; 8 | import 'package:flutter_amplify_demo/theme/colors.dart'; 9 | import 'package:provider/provider.dart'; 10 | 11 | class HomeScreen extends StatefulWidget { 12 | @override 13 | _HomeScreenState createState() => _HomeScreenState(); 14 | } 15 | 16 | class _HomeScreenState extends State { 17 | int pageIndex = 0; 18 | 19 | UserStore userStore; 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | userStore = context.read(); 25 | } 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return Scaffold( 30 | backgroundColor: bgColor, 31 | body: getBody(), 32 | bottomNavigationBar: getFooter(), 33 | ); 34 | } 35 | 36 | Widget getBody() { 37 | return IndexedStack( 38 | index: pageIndex, 39 | children: [ 40 | ChatPage(), 41 | StatusPage(), 42 | SettingsPage(), 43 | ], 44 | ); 45 | } 46 | 47 | Widget getFooter() { 48 | List iconItems = [ 49 | LineIcons.circle, 50 | LineIcons.comment, 51 | Icons.settings, 52 | ]; 53 | List textItems = ["Chats", "Status", "Settings"]; 54 | return Container( 55 | height: 90, 56 | width: double.infinity, 57 | decoration: BoxDecoration(color: greyColor), 58 | child: Padding( 59 | padding: const EdgeInsets.only(left: 20, right: 20, top: 5), 60 | child: Row( 61 | mainAxisAlignment: MainAxisAlignment.spaceAround, 62 | children: List.generate( 63 | textItems.length, 64 | (index) { 65 | return GestureDetector( 66 | onTap: () { 67 | setState(() { 68 | pageIndex = index; 69 | }); 70 | }, 71 | child: Column( 72 | children: [ 73 | Icon( 74 | iconItems[index], 75 | color: 76 | pageIndex == index ? primary : white.withOpacity(0.5), 77 | size: 29, 78 | ), 79 | SizedBox( 80 | height: 5, 81 | ), 82 | Text( 83 | textItems[index], 84 | style: TextStyle( 85 | fontSize: 12, 86 | color: pageIndex == index 87 | ? primary 88 | : white.withOpacity(0.5).withOpacity(0.5), 89 | ), 90 | ), 91 | ], 92 | ), 93 | ); 94 | }, 95 | ), 96 | ), 97 | ), 98 | ); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lib/services/amplify_services.dart: -------------------------------------------------------------------------------- 1 | import 'package:amplify_api/amplify_api.dart'; 2 | import 'package:amplify_auth_cognito/amplify_auth_cognito.dart'; 3 | import 'package:amplify_datastore/amplify_datastore.dart'; 4 | import 'package:amplify_flutter/amplify.dart'; 5 | import 'package:flutter_amplify_demo/amplifyconfiguration.dart'; 6 | import 'package:flutter_amplify_demo/models/ModelProvider.dart'; 7 | 8 | class AmplifyService { 9 | static configureAmplify() async { 10 | // Add Pinpoint and Cognito Plugins, or any other plugins you want to use 11 | AmplifyAPI apiPlugin = AmplifyAPI(); 12 | AmplifyAuthCognito authPlugin = AmplifyAuthCognito(); 13 | AmplifyDataStore datastorePlugin = AmplifyDataStore( 14 | modelProvider: ModelProvider.instance, 15 | ); 16 | // AmplifyStorageS3 amplifyStorageS3 = AmplifyStorageS3(); 17 | Amplify.addPlugins([ 18 | datastorePlugin, 19 | // amplifyStorageS3, 20 | authPlugin, 21 | apiPlugin, 22 | ]); 23 | // Once Plugins are added, configure Amplify 24 | // Note: Amplify can only be configured once. 25 | try { 26 | await Amplify.configure(amplifyconfig); 27 | } catch (e) { 28 | print( 29 | "Tried to reconfigure Amplify; this can occur when your app restarts on Android."); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/services/get_it_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_amplify_demo/services/navigation_service.dart'; 2 | import 'package:get_it/get_it.dart'; 3 | 4 | // ignore: non_constant_identifier_names 5 | GetIt get_it_instance_const = GetIt.instance; 6 | 7 | /// Using get_it as service locator for navigation 8 | class GetItService { 9 | static void setupLocator() { 10 | get_it_instance_const.registerLazySingleton(() => NavigationService()); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/services/navigation_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// Navigation service using Global Key. 4 | /// 5 | /// Send data to route generator as arguments. 6 | class NavigationService { 7 | final GlobalKey navigatorKey = 8 | new GlobalKey(); 9 | 10 | /// Navigate to particular screen and add last screen to stack. 11 | Future navigateTo(String routeName, {dynamic arguments}) { 12 | return navigatorKey.currentState.pushNamed(routeName, arguments: arguments); 13 | } 14 | 15 | /// Remove last screen and add current screen on top of stack 16 | Future replaceAndNavigateTo(String routeName, {dynamic arguments}) { 17 | return navigatorKey.currentState 18 | .pushReplacementNamed(routeName, arguments: arguments); 19 | } 20 | 21 | /// Pop all the screen/routes from stack and add the current screen. 22 | Future popAllAndReplace(String routeName, {dynamic arguments}) { 23 | return navigatorKey.currentState.pushNamedAndRemoveUntil( 24 | routeName, (Route route) => false, 25 | arguments: arguments); 26 | } 27 | 28 | /// If called with data, then passes that data to last screen in stack or else pop the last screen without and data. 29 | void goBack({Map data}) { 30 | if (data != null) 31 | return navigatorKey.currentState.pop(data); 32 | else 33 | navigatorKey.currentState.pop(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/services/size_config.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// SizeConfig to make project scalable 4 | class SizeConfig { 5 | static MediaQueryData _mediaQueryData; 6 | static double screenWidth; 7 | static double screenHeight; 8 | static double blockSizeHorizontal; 9 | static double blockSizeVertical; 10 | 11 | static double _safeAreaHorizontal; 12 | static double _safeAreaVertical; 13 | static double safeBlockHorizontal; 14 | static double safeBlockVertical; 15 | 16 | static double profileDrawerWidth; 17 | static double refHeight; 18 | static double refWidth; 19 | 20 | void init(BuildContext context) { 21 | _mediaQueryData = MediaQuery.of(context); 22 | screenWidth = _mediaQueryData.size.width; 23 | screenHeight = _mediaQueryData.size.height; 24 | refHeight = 667; 25 | refWidth = 375; 26 | 27 | if (screenHeight < 1200) { 28 | blockSizeHorizontal = screenWidth / 100; 29 | blockSizeVertical = screenHeight / 100; 30 | 31 | _safeAreaHorizontal = 32 | _mediaQueryData.padding.left + _mediaQueryData.padding.right; 33 | _safeAreaVertical = 34 | _mediaQueryData.padding.top + _mediaQueryData.padding.bottom; 35 | safeBlockHorizontal = (screenWidth - _safeAreaHorizontal) / 100; 36 | safeBlockVertical = (screenHeight - _safeAreaVertical) / 100; 37 | } else { 38 | blockSizeHorizontal = screenWidth / 120; 39 | blockSizeVertical = screenHeight / 120; 40 | 41 | _safeAreaHorizontal = 42 | _mediaQueryData.padding.left + _mediaQueryData.padding.right; 43 | _safeAreaVertical = 44 | _mediaQueryData.padding.top + _mediaQueryData.padding.bottom; 45 | safeBlockHorizontal = (screenWidth - _safeAreaHorizontal) / 120; 46 | safeBlockVertical = (screenHeight - _safeAreaVertical) / 120; 47 | } 48 | } 49 | 50 | static double getWidthRatio(double val) { 51 | double res = (val / refWidth) * 100; 52 | double temp = res * blockSizeHorizontal; 53 | 54 | return temp; 55 | } 56 | 57 | static double getHeightRatio(double val) { 58 | double res = (val / refHeight) * 100; 59 | double temp = res * blockSizeVertical; 60 | return temp; 61 | } 62 | 63 | static double getFontRatio(double val) { 64 | double res = (val / refWidth) * 100; 65 | double temp = 0.0; 66 | if (screenWidth < screenHeight) { 67 | temp = res * safeBlockHorizontal; 68 | } else { 69 | temp = res * safeBlockVertical; 70 | } 71 | 72 | return temp; 73 | } 74 | } 75 | 76 | extension SizeUtils on num { 77 | double get toWidth => SizeConfig.getWidthRatio(this.toDouble()); 78 | 79 | double get toHeight => SizeConfig.getHeightRatio(this.toDouble()); 80 | 81 | double get toFont => SizeConfig.getFontRatio(this.toDouble()); 82 | } 83 | -------------------------------------------------------------------------------- /lib/services/text_style.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_amplify_demo/services/size_config.dart'; 3 | 4 | class CustomTextStyle { 5 | static TextStyle loginTitleStyle = TextStyle( 6 | fontSize: 36.toFont, 7 | color: Colors.white, 8 | fontWeight: FontWeight.w600, 9 | ); 10 | static TextStyle loginTitleSecondaryStyle = TextStyle( 11 | fontSize: 33.toFont, 12 | color: Colors.white, 13 | fontWeight: FontWeight.w400, 14 | ); 15 | 16 | static TextStyle registerButtonStyle = TextStyle( 17 | fontSize: 16.toFont, 18 | color: Colors.white, 19 | fontWeight: FontWeight.w400, 20 | ); 21 | } 22 | -------------------------------------------------------------------------------- /lib/services/validations.dart: -------------------------------------------------------------------------------- 1 | class Validations { 2 | static String validateEmail(String value) { 3 | if (value.isEmpty) return 'Email is required.'; 4 | final RegExp nameExp = new RegExp( 5 | r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$'); 6 | if (!nameExp.hasMatch(value)) return 'Invalid email address'; 7 | return null; 8 | } 9 | 10 | static String valdiatePassword(String value) { 11 | if (value.trim().isEmpty) return "Enter password"; 12 | if (value.trim().length < 6) 13 | return "Password should be greater then 6 character."; 14 | return null; 15 | } 16 | 17 | static String validateFullName(String value) { 18 | if (value.isEmpty) return 'Name is required.'; 19 | final RegExp nameExp = new RegExp(r'^[A-za-z ]+$'); 20 | if (!nameExp.hasMatch(value)) 21 | return 'Please enter only alphabetical characters.'; 22 | return null; 23 | } 24 | 25 | static String validateOTP(String value) { 26 | if (value.isEmpty) return 'OTP is required.'; 27 | if (value.length != 6) return 'OTP has to be 6 digits'; 28 | return null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/stores/auth.dart: -------------------------------------------------------------------------------- 1 | import 'package:amplify_auth_cognito/amplify_auth_cognito.dart'; 2 | import 'package:amplify_flutter/amplify.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_amplify_demo/repositories/user_repository.dart'; 5 | import 'package:flutter_amplify_demo/stores/state_keeper.dart'; 6 | import 'package:flutter_amplify_demo/stores/user.dart'; 7 | 8 | class AuthStore extends StateKeeper { 9 | AuthStore._(); 10 | static AuthStore _instance = AuthStore._(); 11 | factory AuthStore() { 12 | return _instance; 13 | } 14 | 15 | static const String LOGIN_USER = 'login_user'; 16 | static const String LOGIN_GOOGLE_USER = 'login_gooogle_user'; 17 | static const String SIGNUP_USER = 'signup_user'; 18 | 19 | bool isAuthenticated; 20 | 21 | UserRepository _userRepository = UserRepository(); 22 | 23 | Future isSignedIn() async { 24 | AuthSession authSessions = await Amplify.Auth.fetchAuthSession(); 25 | isAuthenticated = authSessions.isSignedIn; 26 | notifyListeners(); 27 | } 28 | 29 | Future loginWithGoogle() async { 30 | try { 31 | setStatus(LOGIN_GOOGLE_USER, Status.Loading); 32 | bool isSignedIn = await _userRepository.loginWithGoogle(); 33 | if (isSignedIn == false) setStatus(LOGIN_GOOGLE_USER, Status.Idle); 34 | if (isSignedIn) { 35 | await UserStore().fetchCurrentUser(); 36 | await UserStore().fetchAllOtherUsers(); 37 | setStatus(LOGIN_GOOGLE_USER, Status.Done); 38 | return true; 39 | } 40 | setStatus(LOGIN_GOOGLE_USER, Status.Done); 41 | return false; 42 | } on UserCancelledException catch (_) { 43 | setError(LOGIN_GOOGLE_USER, "Google authentication cancelled by user."); 44 | return false; 45 | } catch (e) { 46 | print("ERROR"); 47 | setError(LOGIN_GOOGLE_USER, e.toString()); 48 | return false; 49 | } 50 | } 51 | 52 | Future loginWithEmailPassword({ 53 | @required String email, 54 | @required String password, 55 | }) async { 56 | try { 57 | setStatus(LOGIN_USER, Status.Loading); 58 | bool isSignInComplete = await UserRepository().login( 59 | email: email, 60 | password: password, 61 | ); 62 | if (isSignInComplete) { 63 | await UserStore().fetchCurrentUser(); 64 | await UserStore().fetchAllOtherUsers(); 65 | return true; 66 | } 67 | return false; 68 | } on UserNotFoundException catch (_) { 69 | setError(LOGIN_USER, "Email or Password incorrect"); 70 | return false; 71 | } catch (e) { 72 | setError(LOGIN_USER, e.toString()); 73 | return false; 74 | } 75 | } 76 | 77 | Future signUpWithEmailPassword({ 78 | @required name, 79 | @required email, 80 | @required password, 81 | }) async { 82 | try { 83 | setStatus(SIGNUP_USER, Status.Loading); 84 | bool isSignupComplete = await _userRepository.register( 85 | email: email, 86 | password: password, 87 | name: name, 88 | ); 89 | 90 | if (!isSignupComplete) 91 | setError(SIGNUP_USER, "Error Occured please try gain."); 92 | return isSignupComplete; 93 | } catch (e) { 94 | print(e); 95 | setError(SIGNUP_USER, "Error Occured please try gain."); 96 | return false; 97 | } 98 | } 99 | 100 | Future checkUsername() async { 101 | bool hasUsername = await _userRepository.checkUsername(); 102 | return hasUsername; 103 | } 104 | 105 | Future optVerification({ 106 | String email, 107 | String name, 108 | String otp, 109 | String password, 110 | }) async { 111 | bool isVerified = await _userRepository.optVerfication( 112 | email: email, 113 | name: name, 114 | otp: otp, 115 | password: password, 116 | ); 117 | if (isVerified) { 118 | await UserStore().fetchCurrentUser(); 119 | await UserStore().fetchAllOtherUsers(); 120 | } 121 | return isVerified; 122 | } 123 | 124 | Future logout() async { 125 | await _userRepository.logout(); 126 | notifyListeners(); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /lib/stores/chat.dart: -------------------------------------------------------------------------------- 1 | import 'package:amplify_flutter/amplify.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_amplify_demo/models/Users.dart'; 4 | import 'package:flutter_amplify_demo/repositories/chat_repository.dart'; 5 | import 'package:flutter_amplify_demo/models/ChatData.dart'; 6 | import 'package:flutter_amplify_demo/stores/state_keeper.dart'; 7 | 8 | class ChatStore extends StateKeeper { 9 | ChatStore._(); 10 | static ChatStore _instance = ChatStore._(); 11 | factory ChatStore() => _instance; 12 | 13 | List> userChats = []; 14 | List chatData = []; 15 | ChartRepository _chartRepository = ChartRepository(); 16 | 17 | Future fetchUserChats() async { 18 | userChats = await _chartRepository.getUserChatList(); 19 | notifyListeners(); 20 | } 21 | 22 | Future fetchChatData(String chatId) async { 23 | chatData = await _chartRepository.getChatData(chatId: chatId); 24 | notifyListeners(); 25 | } 26 | 27 | Future addChatData({ 28 | @required String message, 29 | @required String chatId, 30 | @required String senderId, 31 | }) async { 32 | await _chartRepository.addChatData( 33 | message: message, chatId: chatId, senderId: senderId); 34 | notifyListeners(); 35 | } 36 | 37 | Future deleteChats(List messageIdList) async { 38 | await _chartRepository.deleteChats(messageIdList); 39 | notifyListeners(); 40 | } 41 | 42 | Future updateChat(String messageId, String updatedMessage) async { 43 | await _chartRepository.updateChats(messageId, updatedMessage); 44 | notifyListeners(); 45 | } 46 | 47 | Future addUserToChatList(Users otherUser) async { 48 | await _chartRepository.addUserToChatList(otherUser: otherUser); 49 | } 50 | 51 | Future getMessage(String id) async { 52 | Chatdata messageData = (await Amplify.DataStore.query(Chatdata.classType, 53 | where: Chatdata.ID.eq(id)))[0]; 54 | return messageData; 55 | } 56 | 57 | addUpdatedChats(Chatdata updatedChatData) { 58 | chatData.insert(0, updatedChatData); 59 | notifyListeners(); 60 | } 61 | 62 | resetChatData() { 63 | chatData = []; 64 | notifyListeners(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/stores/state_keeper.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class StateKeeper extends ChangeNotifier { 4 | Map data = {}; 5 | Map status = {"main": Status.Idle}; 6 | Map error = {}; 7 | 8 | setStatus(String function, Status _status) { 9 | this.status[function] = _status; 10 | notifyListeners(); 11 | } 12 | 13 | setData(String function, dynamic _data) { 14 | this.data[function] = _data; 15 | notifyListeners(); 16 | } 17 | 18 | setError(String function, String _error, [Status _status]) { 19 | if (_error != null) { 20 | error[function] = _error; 21 | status[function] = Status.Error; 22 | } else { 23 | this.error[function] = null; 24 | this.status[function] = _status ?? Status.Idle; 25 | } 26 | notifyListeners(); 27 | } 28 | 29 | reset(String function) { 30 | this.data[function] = Status.Idle; 31 | this.error?.remove(function); 32 | this.status?.remove(function); 33 | } 34 | 35 | resetHard() { 36 | this.data = {}; 37 | this.status = {"main": Status.Idle}; 38 | this.error = {}; 39 | } 40 | 41 | notify() { 42 | notifyListeners(); 43 | } 44 | } 45 | 46 | enum Status { 47 | Idle, 48 | Loading, 49 | Done, 50 | Error, 51 | } 52 | -------------------------------------------------------------------------------- /lib/stores/user.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_amplify_demo/repositories/user_repository.dart'; 2 | import 'package:flutter_amplify_demo/models/Users.dart'; 3 | import 'package:flutter_amplify_demo/stores/state_keeper.dart'; 4 | import 'dart:io'; 5 | 6 | class UserStore extends StateKeeper { 7 | UserStore._(); 8 | static UserStore _instance = UserStore._(); 9 | factory UserStore() => _instance; 10 | 11 | static const String CURR_USER = 'curr_user'; 12 | 13 | UserRepository _userRepository = UserRepository(); 14 | 15 | Users currUser; 16 | List allOtherUsers; 17 | 18 | Future fetchCurrentUser() async { 19 | currUser = await _userRepository.getCurrUser(); 20 | notifyListeners(); 21 | } 22 | 23 | Future fetchAllOtherUsers() async { 24 | allOtherUsers = await _userRepository.getAllOtherUses(); 25 | notifyListeners(); 26 | } 27 | 28 | // Future updateProfileImage({File image, String key}) async { 29 | // await _userRepository.updateProfileImage(image, key); 30 | // } 31 | } 32 | -------------------------------------------------------------------------------- /lib/theme/colors.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const Color primary = Color(0xFF399df8); 4 | const Color bgColor = Color(0xFF010101); 5 | const Color white = Color(0xFFFFFFFF); 6 | const Color black = Color(0xFF000000); 7 | const Color textfieldColor = Color(0xFF1c1d1f); 8 | const Color greyColor = Color(0xFF161616); 9 | const Color chatBoxOther = Color(0xFF3d3d3f); 10 | const Color chatBoxMe = Color(0xFF066162); 11 | 12 | class ThemeColor { 13 | static const Color primary = Color(0xFF399df8); 14 | static const Color bgColor = Color(0xFF010101); 15 | static const Color white = Color(0xFFFFFFFF); 16 | static const Color black = Color(0xFF000000); 17 | static const Color textfieldColor = Color(0xFF1c1d1f); 18 | static const Color greyColor = Color(0xFF161616); 19 | static const Color chatBoxOther = Color(0xFF3d3d3f); 20 | static const Color chatBoxMe = Color(0xFF066162); 21 | } 22 | -------------------------------------------------------------------------------- /lib/wrapper.dart: -------------------------------------------------------------------------------- 1 | import 'package:amplify_auth_cognito/amplify_auth_cognito.dart'; 2 | import 'package:amplify_flutter/amplify.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_amplify_demo/routes/routes_path.dart'; 5 | import 'package:flutter_amplify_demo/services/get_it_service.dart'; 6 | import 'package:flutter_amplify_demo/services/navigation_service.dart'; 7 | import 'package:flutter_amplify_demo/services/size_config.dart'; 8 | import 'package:flutter_amplify_demo/stores/user.dart'; 9 | 10 | class Wrapper extends StatefulWidget { 11 | Wrapper({Key key}) : super(key: key); 12 | 13 | @override 14 | _WrapperState createState() => _WrapperState(); 15 | } 16 | 17 | class _WrapperState extends State { 18 | NavigationService _navigationService = 19 | get_it_instance_const(); 20 | @override 21 | void initState() { 22 | super.initState(); 23 | WidgetsBinding.instance.addPostFrameCallback((_) { 24 | handleNavigation(); 25 | }); 26 | } 27 | 28 | handleNavigation() async { 29 | AuthSession authSessions = await Amplify.Auth.fetchAuthSession(); 30 | if (authSessions.isSignedIn) { 31 | await UserStore().fetchCurrentUser(); 32 | await UserStore().fetchAllOtherUsers(); 33 | _navigationService.popAllAndReplace(RoutePath.Home); 34 | } else 35 | _navigationService.popAllAndReplace(RoutePath.Splash); 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | SizeConfig().init(context); 41 | return Container(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_amplify_demo 2 | description: A Demo project to test Flutter web with AWS Amplify. 3 | 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.7.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | 13 | # Dependencies 14 | amplify_api: "<1.0.0" 15 | amplify_flutter: "<1.0.0" 16 | amplify_datastore: "<1.0.0" 17 | amplify_auth_cognito: "<1.0.0" 18 | 19 | cached_network_image: 20 | pull_to_refresh: 21 | image_picker: 22 | flutter_svg: 23 | line_icons: 24 | animate_do: 25 | equatable: 26 | provider: 27 | bubble: 28 | get_it: 29 | uuid: 30 | 31 | cupertino_icons: ^1.0.2 32 | 33 | dev_dependencies: 34 | flutter_test: 35 | sdk: flutter 36 | 37 | flutter: 38 | uses-material-design: true 39 | 40 | assets: 41 | - assets/images/bg_chat.jpg 42 | - assets/images/splash.png 43 | # - images/a_dot_burr.jpeg 44 | # - images/a_dot_ham.jpeg 45 | 46 | fonts: 47 | - family: Poppins 48 | fonts: 49 | - asset: assets/fonts/Poppins/Poppins-Thin.ttf 50 | weight: 100 51 | - asset: assets/fonts/Poppins/Poppins-ExtraLight.ttf 52 | weight: 200 53 | - asset: assets/fonts/Poppins/Poppins-Light.ttf 54 | weight: 300 55 | - asset: assets/fonts/Poppins/Poppins-Regular.ttf 56 | weight: 400 57 | - asset: assets/fonts/Poppins/Poppins-Medium.ttf 58 | weight: 500 59 | - asset: assets/fonts/Poppins/Poppins-SemiBold.ttf 60 | weight: 600 61 | - asset: assets/fonts/Poppins/Poppins-Bold.ttf 62 | weight: 700 63 | - asset: assets/fonts/Poppins/Poppins-ExtraBold.ttf 64 | weight: 800 65 | - asset: assets/fonts/Poppins/Poppins-Black.ttf 66 | weight: 900 67 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | void main() { 12 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 13 | // Build our app and trigger a frame. 14 | // await tester.pumpWidget(MyApp()); 15 | 16 | // Verify that our counter starts at 0. 17 | expect(find.text('0'), findsOneWidget); 18 | expect(find.text('1'), findsNothing); 19 | 20 | // Tap the '+' icon and trigger a frame. 21 | await tester.tap(find.byIcon(Icons.add)); 22 | await tester.pump(); 23 | 24 | // Verify that our counter has incremented. 25 | expect(find.text('0'), findsNothing); 26 | expect(find.text('1'), findsOneWidget); 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GeekyAnts/flutter_amplify_datastore_demo/3c4b6a2732d5b5c8dfa23d0236ade575afc822be/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | flutter_amplify_demo 30 | 31 | 32 | 33 | 36 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flutter_amplify_demo", 3 | "short_name": "flutter_amplify_demo", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ] 23 | } 24 | --------------------------------------------------------------------------------