├── .gitignore ├── .gradle ├── 5.2.1 │ ├── fileChanges │ │ └── last-build.bin │ ├── fileHashes │ │ └── fileHashes.lock │ └── gc.properties ├── buildOutputCleanup │ ├── buildOutputCleanup.lock │ └── cache.properties └── vcs-1 │ └── gc.properties ├── .metadata ├── README.md ├── Screenshots ├── about.png ├── auth.png ├── chat_window.png ├── chat_window2.png ├── data_dialog.png ├── edit_Profile.png ├── full_Screen_image.png ├── main_page.png ├── received_message_detail.png ├── search_Result.png ├── search_user.png ├── sent_message_detail.png ├── sign_out_dialog.png ├── theme_settings.png ├── uid_dialog.png ├── user_info.png └── walkthrough.mp4 ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── messaging_app_new │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable │ │ │ ├── app_icon.png │ │ │ └── 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 │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── settings_aar.gradle ├── assets ├── giphy.gif ├── loading.png ├── msg.png ├── search.jpg └── search.svg ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ └── contents.xcworkspacedata └── 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-50x50@1x.png │ │ ├── Icon-App-50x50@2x.png │ │ ├── Icon-App-57x57@1x.png │ │ ├── Icon-App-57x57@2x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-72x72@1x.png │ │ ├── Icon-App-72x72@2x.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 ├── Layout │ ├── DrawerBuilder.dart │ ├── TextFormBuilder.dart │ ├── infoDialog.dart │ ├── signOutConfirmationDialog.dart │ ├── themeSettingsPage.dart │ ├── useOfDataDialog.dart │ └── verificationDialog.dart ├── appData.dart ├── auth │ ├── auth.dart │ └── signUp.dart ├── consts │ └── theme.dart ├── data │ ├── sharedPrefs.dart │ └── strings.dart ├── groupModel.dart ├── main.dart ├── mainPage.dart ├── mainRepo.dart ├── message │ ├── buildErrorPage.dart │ ├── buildMessageWidget.dart │ ├── demo.dart │ ├── demoDetail.dart │ ├── imageFullScreen.dart │ ├── message.dart │ ├── messageDetail.dart │ ├── messagePage.dart │ ├── messageRepo.dart │ ├── receivedMessageDetail.dart │ ├── searchPage.dart │ ├── searchRepo.dart │ └── searchResultTileBuilder.dart └── user │ ├── UserRepo.dart │ ├── editProfile.dart │ ├── editProfileBuilder.dart │ ├── storage.dart │ ├── user.dart │ ├── userInfoHelper.dart │ └── userInfoPage.dart ├── local.properties ├── pubspec.lock ├── pubspec.yaml └── test └── widget_test.dart /.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 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Exceptions to above rules. 37 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 38 | -------------------------------------------------------------------------------- /.gradle/5.2.1/fileChanges/last-build.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gradle/5.2.1/fileHashes/fileHashes.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/.gradle/5.2.1/fileHashes/fileHashes.lock -------------------------------------------------------------------------------- /.gradle/5.2.1/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/.gradle/5.2.1/gc.properties -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/buildOutputCleanup.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/.gradle/buildOutputCleanup/buildOutputCleanup.lock -------------------------------------------------------------------------------- /.gradle/buildOutputCleanup/cache.properties: -------------------------------------------------------------------------------- 1 | #Thu May 28 08:09:40 IST 2020 2 | gradle.version=5.2.1 3 | -------------------------------------------------------------------------------- /.gradle/vcs-1/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/.gradle/vcs-1/gc.properties -------------------------------------------------------------------------------- /.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: f139b11009aeb8ed2a3a3aa8b0066e482709dde3 8 | channel: unknown 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Messaging App - Flutter & Firebase 2 | ![Made with Love in India](https://madewithlove.org.in/badge.svg) 3 | > version 1.0.0 4 | 5 | Messaging app is a nicely designed and developed mobile application developed using Flutter. Flutter is an open-source mobile application development SDK created by Google and used to develop applications for Android and iOS. 6 | 7 | Messaging app gives an modern look and feel in the mobile application it has been build using flutter and firebase. It saves lots of hustle and time to develop a nicely designed messaging app Android & iOS. The app is ready to use and can be easily integrated in any flutter project. The code organization is easy to understand any part can be taken out and added into flutter application. 8 | 9 | Messaging App comes with 8 screen application pages. It comes with both light and dark theme and works great with both android and ios. 10 | 11 | ### App Screens 12 | 13 | - Signup 14 | - Login 15 | - User Info 16 | - Edit Details 17 | - Home Page 18 | - Messaging page 19 | - Message Detail 20 | - FullScreen Image Page 21 | 22 | ## Screenshots of the application 23 | 24 |

25 | 26 | 27 |

28 |

29 | 30 | 31 | 32 | 33 | 34 |

35 |

36 | 37 | 38 | 39 | 40 | 41 |

42 | Working Video https://github.com/AmanNegi/Messaging-App-Flutter-/blob/master/Screenshots/walkthrough.mp4 43 | 44 | ## Topics Covered 45 | 46 | * **Firebase Cloud Firestore**
47 | Users data, profile image etc is received and stored in the firestore. The messages are also saved there. 48 | 49 | * **Messaging through firebase**
50 | The app implements messaging feature as a side feature to help the users communicate. 51 | 52 | * **Shared Prefrences**
53 | Shared prefrences is used to save local user data. eg: User firebase id. 54 | 55 | * **Firebase Auth**
56 | For users to login and signUp we have used firebase Auth. 57 | 58 | * **Theme settings**
59 | The app provides a number of theme changing options from mainColor to primary font and dark and light Mode. 60 | 61 | 62 | ### Project Structure 63 | 64 | ``` 65 | ... 66 | ├── auth/ #This folder contains auth related files. 67 | ├── consts/ # This folder contains theme related files. 68 | ├── data/ # This folder contains data i.e sharedPrefs file and constant strings. 69 | ├── layout/ # This folder contains all element widgets used in other pages. 70 | ├── message/ # This folder contains all files regarding the message page and message detail. 71 | ├── user/ #This folder contains all files regarding the user and userPages. 72 | ├──app_data.dart # Contains all the theme related settings of the app 73 | └── main.dart # Root file of the project 74 | ``` 75 | 76 | ## Project Setup 77 | 78 | In order to set up the project, please follow below steps: 79 | 80 | ### Flutter setup 81 | 82 | 1. Install package dependencies: 83 | 84 | ``` 85 | flutter pub get 86 | ``` 87 | 88 | 2. Go to google firebase and create your own project and extract the googleservices.json. 89 | 90 | 91 | 3. Run the project by running command: 92 | 93 | ``` 94 | flutter run 95 | ``` 96 | 97 | 4. Use one of these commands to build the project: 98 | 99 | ``` 100 | flutter build ios 101 | flutter build apk 102 | flutter build appbundle 103 | ``` 104 | 105 | 5. If any issue (run the below command to troubleshoot): 106 | 107 | ``` 108 | flutter doctor 109 | ``` 110 | 111 | For help getting started with Flutter, check [online documentation](https://flutter.dev/docs), which offers great tutorials, samples, guidance on mobile development, and a full API reference. If you run into any issue or question, feel free to reach out to us via email akuro787898@gmail.com. 112 | 113 | ### Flutter packages used in Blogit: 114 | 115 | - firebase_analytics 116 | - firebase_auth 117 | - firebase_core 118 | - google_fonts 119 | - rxdart 120 | - fluttertoast 121 | - firebase_storage 122 | - image_picker 123 | - cloud_firestore 124 | - shared_preferences 125 | - flutter_clipboard_manager 126 | - flutter_svg 127 | - intl 128 | - material_design_icons_flutter 129 | - photo_view 130 | - dynamic_theme 131 | - shimmer 132 | - mdi 133 | - url_launcher 134 | - cached_network_image 135 | 136 | ## Authors 137 | 138 | > [**Aman Negi**](https://github.com/AmanNegi) - *Initial work* 139 | 140 | 141 | ## Feel Free to Contract 142 | 143 | * Gmail : akuro787898@gmail.com 144 | * Facebook : https://www.facebook.com/flyWithFlutter 145 | 146 | -------------------------------------------------------------------------------- /Screenshots/about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/about.png -------------------------------------------------------------------------------- /Screenshots/auth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/auth.png -------------------------------------------------------------------------------- /Screenshots/chat_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/chat_window.png -------------------------------------------------------------------------------- /Screenshots/chat_window2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/chat_window2.png -------------------------------------------------------------------------------- /Screenshots/data_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/data_dialog.png -------------------------------------------------------------------------------- /Screenshots/edit_Profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/edit_Profile.png -------------------------------------------------------------------------------- /Screenshots/full_Screen_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/full_Screen_image.png -------------------------------------------------------------------------------- /Screenshots/main_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/main_page.png -------------------------------------------------------------------------------- /Screenshots/received_message_detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/received_message_detail.png -------------------------------------------------------------------------------- /Screenshots/search_Result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/search_Result.png -------------------------------------------------------------------------------- /Screenshots/search_user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/search_user.png -------------------------------------------------------------------------------- /Screenshots/sent_message_detail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/sent_message_detail.png -------------------------------------------------------------------------------- /Screenshots/sign_out_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/sign_out_dialog.png -------------------------------------------------------------------------------- /Screenshots/theme_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/theme_settings.png -------------------------------------------------------------------------------- /Screenshots/uid_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/uid_dialog.png -------------------------------------------------------------------------------- /Screenshots/user_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/user_info.png -------------------------------------------------------------------------------- /Screenshots/walkthrough.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/Screenshots/walkthrough.mp4 -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /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 | apply plugin: 'com.google.gms.google-services' 28 | 29 | android { 30 | compileSdkVersion 28 31 | 32 | sourceSets { 33 | main.java.srcDirs += 'src/main/kotlin' 34 | } 35 | 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | 40 | defaultConfig { 41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 42 | applicationId "com.example.messaging_app_new" 43 | multiDexEnabled true 44 | minSdkVersion 16 45 | targetSdkVersion 28 46 | versionCode flutterVersionCode.toInteger() 47 | versionName flutterVersionName 48 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 49 | } 50 | 51 | buildTypes { 52 | release { 53 | // TODO: Add your own signing config for the release build. 54 | // Signing with the debug keys for now, so `flutter run --release` works. 55 | signingConfig signingConfigs.debug 56 | } 57 | } 58 | } 59 | 60 | flutter { 61 | source '../..' 62 | } 63 | 64 | dependencies { 65 | implementation 'com.google.firebase:firebase-auth:19.3.1' 66 | implementation 'com.google.firebase:firebase-analytics:17.2.2' 67 | implementation 'com.google.android.gms:play-services-auth:16.0.1' 68 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 69 | testImplementation 'junit:junit:4.12' 70 | androidTestImplementation 'androidx.test:runner:1.1.1' 71 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 72 | } 73 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | 30 | 31 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/messaging_app_new/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.messaging_app_new 2 | 3 | import androidx.annotation.NonNull; 4 | import io.flutter.embedding.android.FlutterActivity 5 | import io.flutter.embedding.engine.FlutterEngine 6 | import io.flutter.plugins.GeneratedPluginRegistrant 7 | 8 | class MainActivity: FlutterActivity() { 9 | override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { 10 | GeneratedPluginRegistrant.registerWith(flutterEngine); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/android/app/src/main/res/drawable/app_icon.png -------------------------------------------------------------------------------- /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/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /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.google.gms:google-services:4.3.3' 10 | classpath 'com.android.tools.build:gradle:3.6.1' 11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | jcenter() 19 | } 20 | } 21 | 22 | rootProject.buildDir = '../build' 23 | subprojects { 24 | project.buildDir = "${rootProject.buildDir}/${project.name}" 25 | } 26 | subprojects { 27 | project.evaluationDependsOn(':app') 28 | } 29 | 30 | task clean(type: Delete) { 31 | delete rootProject.buildDir 32 | } 33 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu May 28 08:10:52 IST 2020 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-5.6.4-all.zip 7 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /android/settings_aar.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /assets/giphy.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/assets/giphy.gif -------------------------------------------------------------------------------- /assets/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/assets/loading.png -------------------------------------------------------------------------------- /assets/msg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/assets/msg.png -------------------------------------------------------------------------------- /assets/search.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/assets/search.jpg -------------------------------------------------------------------------------- /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 | $(DEVELOPMENT_LANGUAGE) 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 "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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 | 8 | -------------------------------------------------------------------------------- /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/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/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/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/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/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/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/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/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/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/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/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/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/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/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/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/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/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/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/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/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/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/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/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/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/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/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/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AmanNegi/Messaging-App-Flutter-/baebd6c1e54af30bb49833271f9cd6511f490190/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 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | messaging_app_new 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /lib/Layout/DrawerBuilder.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; 4 | import 'package:mdi/mdi.dart'; 5 | import 'package:messaging_app_new/Layout/themeSettingsPage.dart'; 6 | import 'package:messaging_app_new/consts/theme.dart'; 7 | import 'package:messaging_app_new/message/imageFullScreen.dart'; 8 | import 'package:shimmer/shimmer.dart'; 9 | import 'package:url_launcher/url_launcher.dart'; 10 | import '../user/editProfile.dart'; 11 | import '../mainRepo.dart'; 12 | import '../user/user.dart'; 13 | import '../data/sharedPrefs.dart'; 14 | import '../Layout/signOutConfirmationDialog.dart'; 15 | import '../appData.dart'; 16 | 17 | class DrawerBuilder extends StatefulWidget { 18 | @override 19 | _DrawerBuilderState createState() => _DrawerBuilderState(); 20 | } 21 | 22 | class _DrawerBuilderState extends State { 23 | var height, width; 24 | User user; 25 | bool isLoading = true; 26 | _getCurrentUser() async { 27 | setState(() { 28 | isLoading = true; 29 | }); 30 | print("getting user "); 31 | user = await mainRepo 32 | .getUserFromUid(sharedPrefs.getValueFromSharedPrefs('uid')); 33 | setUser(user); 34 | setState(() { 35 | isLoading = false; 36 | }); 37 | } 38 | 39 | @override 40 | void initState() { 41 | _getCurrentUser(); 42 | super.initState(); 43 | } 44 | 45 | @override 46 | Drawer build(BuildContext context) { 47 | height = MediaQuery.of(context).size.height; 48 | width = MediaQuery.of(context).size.width; 49 | return Drawer( 50 | child: ListView( 51 | children: [ 52 | _buildTopImage(context), 53 | Divider(), 54 | _buildEditProfile(context), 55 | Divider(), 56 | _buildThemeSettingsTile(context), 57 | Divider(), 58 | ListTile( 59 | leading: Icon( 60 | Mdi.logout, 61 | ), 62 | title: Text("Sign out"), 63 | onTap: () { 64 | showDialog( 65 | context: context, 66 | barrierDismissible: true, 67 | builder: (context) => SignOutConfirmationDialog(), 68 | ); 69 | }, 70 | ), 71 | Divider(), 72 | ListTile( 73 | leading: Icon(Mdi.messageAlertOutline), 74 | title: Text('Want an feature? Suggest the developer'), 75 | onTap: () { 76 | _launchURL("asterJoules@gmail.com", "Suggestion of an feature", 77 | "Enter here"); 78 | }, 79 | ), 80 | Divider(), 81 | ListTile( 82 | leading: Icon(Mdi.bugCheckOutline), 83 | title: Text('Found an issue? Ping the developer'), 84 | subtitle: 85 | Text('Include screenshot with a well detailed description'), 86 | onTap: () { 87 | _launchURL("asterJoules@gmail.com", "Suggestion of an feature", 88 | "Enter here"); 89 | }, 90 | ), 91 | Divider(), 92 | _buildAboutTile(context), 93 | ], 94 | ), 95 | ); 96 | } 97 | 98 | _buildAboutTile(BuildContext context) { 99 | return ListTile( 100 | leading: Icon(Mdi.informationOutline), 101 | title: Text('About'), 102 | onTap: () { 103 | showAboutDialog( 104 | context: context, 105 | applicationIcon: Image.asset( 106 | 'assets/msg.png', 107 | height: 50.0, 108 | width: 50.0, 109 | ), 110 | children: [ 111 | SizedBox( 112 | height: 30.0, 113 | ), 114 | Text( 115 | "Copyright 2020 AsterJoules. All rights reserved.* Redistributions of source code must retain the above copyrightnotice, this list of conditions and the following disclaimer.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS") 116 | ], 117 | applicationName: 'Messaging app', 118 | applicationVersion: '1.0.0 (Debug version)', 119 | applicationLegalese: '@2020 AsterJoules'); 120 | }, 121 | ); 122 | } 123 | 124 | ListTile _buildThemeSettingsTile(BuildContext context) { 125 | return ListTile( 126 | title: Text( 127 | "Theme settings", 128 | style: TextStyle(color: AppTheme.textColor), 129 | ), 130 | leading: Icon( 131 | MdiIcons.themeLightDark, 132 | ), 133 | onTap: () { 134 | Navigator.of(context).push(MaterialPageRoute( 135 | builder: (context) => ThemeSettingsPage(), 136 | )); 137 | }, 138 | ); 139 | } 140 | 141 | ListTile _buildEditProfile(BuildContext context) { 142 | return ListTile( 143 | onTap: () { 144 | Navigator.of(context).push( 145 | MaterialPageRoute( 146 | builder: (BuildContext context) => EditProfile(), 147 | ), 148 | ); 149 | }, 150 | leading: Icon( 151 | Mdi.circleEditOutline, 152 | ), 153 | title: Text( 154 | "Edit profile", 155 | style: TextStyle(color: AppTheme.textColor), 156 | ), 157 | ); 158 | } 159 | 160 | Container _buildTopImage(BuildContext context) { 161 | return Container( 162 | color: Theme.of(context).cardColor, 163 | height: 0.2 * height, 164 | width: double.infinity, 165 | child: Stack( 166 | children: [ 167 | Column( 168 | children: [ 169 | Container( 170 | height: 0.12 * height, 171 | width: double.infinity, 172 | color: Theme.of(context).cardColor), 173 | Container( 174 | height: 0.08 * height, 175 | width: double.infinity, 176 | color: Theme.of(context).canvasColor, 177 | ), 178 | ], 179 | ), 180 | Positioned( 181 | left: 0.325 * width, 182 | top: 0.0925 * height, 183 | child: Text( 184 | user != null ? user.userName : '', 185 | style: TextStyle(fontSize: 16), 186 | ), 187 | ), 188 | Positioned( 189 | top: 0.05 * height, 190 | left: 0.05 * width, 191 | child: GestureDetector( 192 | onTap: () { 193 | if (user != null) { 194 | Navigator.of(context).push(MaterialPageRoute( 195 | builder: (context) => ImageFullScreen(user.imageUrl))); 196 | } 197 | }, 198 | child: _buildUserImage(context), 199 | ), 200 | ), 201 | ], 202 | ), 203 | ); 204 | } 205 | 206 | _launchURL(String toMailId, String subject, String body) async { 207 | var url = 'mailto:$toMailId?subject=$subject&body=$body'; 208 | if (await canLaunch(url)) { 209 | await launch(url); 210 | } else { 211 | throw 'Could not launch $url'; 212 | } 213 | } 214 | 215 | _buildUserImage(BuildContext context) { 216 | return user != null 217 | ? ValueListenableBuilder( 218 | valueListenable: userData, 219 | builder: (context, value, child) { 220 | return ClipRRect( 221 | borderRadius: BorderRadius.circular(120.0), 222 | child: Container( 223 | height: 0.125 * height, 224 | width: 0.125 * height, 225 | color: AppTheme.mainColor, 226 | child: CachedNetworkImage( 227 | fadeInDuration: Duration(microseconds: 100), 228 | imageUrl: value != null ? value.imageUrl : " ", 229 | fit: BoxFit.cover, 230 | errorWidget: (context, url, error) => 231 | Icon(Mdi.alert, color: AppTheme.iconColor), 232 | placeholder: (context, url) => Shimmer.fromColors( 233 | child: Container( 234 | color: Colors.red, 235 | ), 236 | baseColor: AppTheme.shimmerBaseColor, 237 | highlightColor: AppTheme.shimmerEndingColor)), 238 | ), 239 | ); 240 | }, 241 | ) 242 | : ClipRRect( 243 | borderRadius: BorderRadius.circular(120.0), 244 | child: Shimmer.fromColors( 245 | baseColor: AppTheme.shimmerBaseColor, 246 | highlightColor: AppTheme.shimmerEndingColor, 247 | child: Container( 248 | height: 0.125 * height, 249 | width: 0.125 * height, 250 | color: Colors.red, 251 | ), 252 | ), 253 | ); 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /lib/Layout/TextFormBuilder.dart: -------------------------------------------------------------------------------- 1 | import "package:flutter/material.dart"; 2 | import 'package:google_fonts/google_fonts.dart'; 3 | import 'package:messaging_app_new/consts/theme.dart'; 4 | 5 | class TextFormBuilder extends StatelessWidget { 6 | String hintText; 7 | Function validator; 8 | Function onSaved; 9 | TextEditingController controller; 10 | Widget suffixWidget; 11 | TextStyle textStyle; 12 | TextInputType keybordType; 13 | bool obscureText; 14 | TextFormBuilder( 15 | {this.hintText, 16 | this.validator, 17 | this.onSaved, 18 | this.controller, 19 | this.suffixWidget, 20 | this.textStyle, 21 | this.keybordType, 22 | this.obscureText}); 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return ClipRRect( 27 | borderRadius: BorderRadius.circular(30.0), 28 | child: Container( 29 | color: Colors.white, 30 | child: TextFormField( 31 | obscureText: obscureText != null ? obscureText : false, 32 | controller: controller, 33 | maxLines: 1, 34 | keyboardType: keybordType, 35 | // validator: validator, 36 | autocorrect: false, 37 | onSaved: onSaved, 38 | onChanged: (v) {}, 39 | style: TextStyle( 40 | fontFamily: AppTheme.fontFamily, 41 | color: Colors.black, 42 | ), 43 | decoration: InputDecoration( 44 | contentPadding: EdgeInsets.all(20.0), 45 | fillColor: Colors.white, 46 | hintText: hintText, 47 | hintStyle: textStyle != null ? textStyle : TextStyle(), 48 | suffixIcon: suffixWidget != null ? suffixWidget : null, 49 | border: InputBorder.none, 50 | ), 51 | ), 52 | ), 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/Layout/infoDialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:messaging_app_new/consts/theme.dart'; 3 | 4 | class InfoDialog extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | return SimpleDialog( 8 | backgroundColor: Colors.transparent, 9 | elevation: 0, 10 | // shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)), 11 | children: [ 12 | ClipRRect( 13 | borderRadius: BorderRadius.circular(15.0), 14 | child: Container( 15 | color: Theme.of(context).cardColor, 16 | child: Column( 17 | children: [ 18 | AppBar( 19 | elevation: 0, 20 | centerTitle: true, 21 | automaticallyImplyLeading: false, 22 | title: Text( 23 | "U-id", 24 | style: TextStyle(fontWeight: FontWeight.w800), 25 | ), 26 | ), 27 | Padding( 28 | padding: const EdgeInsets.all(30.0), 29 | child: Text( 30 | "This a unique id to uniquely identify users. Share the U-id to your friends so that they can find you. UserName can be duplicated however the U-id is always unique."), 31 | ) 32 | ], 33 | ), 34 | ), 35 | ), 36 | ], 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/Layout/signOutConfirmationDialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:messaging_app_new/auth/signUp.dart'; 3 | import 'package:messaging_app_new/consts/theme.dart'; 4 | import 'package:messaging_app_new/data/sharedPrefs.dart'; 5 | 6 | class SignOutConfirmationDialog extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return SimpleDialog( 10 | backgroundColor: Colors.transparent, 11 | elevation: 0, 12 | children: [ 13 | ClipRRect( 14 | borderRadius: BorderRadius.circular(15.0), 15 | child: Container( 16 | color: Theme.of(context).cardColor, 17 | child: Column( 18 | children: [ 19 | AppBar( 20 | elevation: 0, 21 | centerTitle: true, 22 | automaticallyImplyLeading: false, 23 | title: Text( 24 | "Sign Out?", 25 | style: TextStyle(fontWeight: FontWeight.w800), 26 | ), 27 | ), 28 | Padding( 29 | padding: const EdgeInsets.only( 30 | top: 20.0, left: 10.0, right: 10.0, bottom: 20.0), 31 | child: Text( 32 | "All your data regarding this account will be deleted from the device. However you can come back to this account.", 33 | textAlign: TextAlign.center, 34 | ), 35 | ), 36 | Row( 37 | mainAxisAlignment: MainAxisAlignment.center, 38 | children: [ 39 | RaisedButton( 40 | color: AppTheme.mainColor, 41 | elevation: 0, 42 | shape: RoundedRectangleBorder( 43 | borderRadius: BorderRadius.circular(30.0)), 44 | onPressed: () { 45 | sharedPrefs.clearSharedPrefsData(); 46 | 47 | Navigator.of(context).pushReplacement(MaterialPageRoute( 48 | builder: (context) => SignUpPage())); 49 | }, 50 | child: Text( 51 | "Continue", 52 | style: TextStyle(color: Colors.white), 53 | ), 54 | ), 55 | SizedBox( 56 | width: 10, 57 | ), 58 | FlatButton( 59 | shape: RoundedRectangleBorder( 60 | borderRadius: BorderRadius.circular(30.0)), 61 | color: Theme.of(context).canvasColor, 62 | child: Text("Cancel"), 63 | onPressed: () { 64 | Navigator.pop(context); 65 | }, 66 | ), 67 | ], 68 | ), 69 | SizedBox( 70 | height: 20, 71 | ), 72 | ], 73 | ), 74 | ), 75 | ), 76 | ], 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /lib/Layout/themeSettingsPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:dynamic_theme/dynamic_theme.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:google_fonts/google_fonts.dart'; 4 | import 'package:messaging_app_new/consts/theme.dart'; 5 | import 'package:messaging_app_new/data/sharedPrefs.dart'; 6 | import 'package:mdi/mdi.dart'; 7 | 8 | class ThemeSettingsPage extends StatefulWidget { 9 | @override 10 | _ThemeSettingsPageState createState() => _ThemeSettingsPageState(); 11 | } 12 | 13 | class _ThemeSettingsPageState extends State { 14 | int currentColorIndex = 2; 15 | bool isDarkModeOpened = false; 16 | 17 | List> fontList = [ 18 | {'Default': GoogleFonts.ubuntu().fontFamily}, 19 | {'Raleway': GoogleFonts.raleway().fontFamily}, 20 | {'Exo': GoogleFonts.exo().fontFamily}, 21 | {'QuickSand': GoogleFonts.quicksand().fontFamily}, 22 | { 23 | 'Comformtaa': GoogleFonts.comfortaa().fontFamily, 24 | } 25 | ]; 26 | var dropDownValue = GoogleFonts.ubuntu().fontFamily; 27 | 28 | @override 29 | void initState() { 30 | if (sharedPrefs.checkIfExistsInSharedPrefs("darkMode")) { 31 | isDarkModeOpened = sharedPrefs.getBoolFromSharedPrefs('darkMode'); 32 | print(" value of dark mode opened from sharedPrefs : $isDarkModeOpened"); 33 | } else { 34 | _changeValueOfDarkModeInSharedPrefs(false); 35 | } 36 | if (sharedPrefs.checkIfExistsInSharedPrefs("fontFamily")) { 37 | dropDownValue = sharedPrefs.getValueFromSharedPrefs('fontFamily'); 38 | print(" value of dropdown sharedPrefs : $dropDownValue"); 39 | } else { 40 | dropDownValue = null; 41 | _changeValueOfFontFamilyInSharedPrefs(GoogleFonts.ubuntu().fontFamily); 42 | } 43 | if (sharedPrefs.checkIfExistsInSharedPrefs('mainColor')) { 44 | currentColorIndex = sharedPrefs.getIntFromSharedPrefs('mainColor'); 45 | print(" value of currentColorIndex sharedPrefs : $currentColorIndex"); 46 | } else { 47 | print(" else color statement"); 48 | _changeValueOfMainColorInSharedPrefs(2); 49 | } 50 | super.initState(); 51 | } 52 | 53 | _changeValueOfDarkModeInSharedPrefs(bool value) { 54 | sharedPrefs.addBoolToSharedPrefs('darkMode', value); 55 | } 56 | 57 | _changeValueOfFontFamilyInSharedPrefs(String value) { 58 | sharedPrefs.addItemToSharedPrefs('fontFamily', value); 59 | } 60 | 61 | _changeValueOfMainColorInSharedPrefs(int value) { 62 | sharedPrefs.addIntToSharedPrefs('mainColor', value); 63 | } 64 | 65 | @override 66 | Widget build(BuildContext context) { 67 | return Scaffold( 68 | appBar: AppBar( 69 | automaticallyImplyLeading: false, 70 | leading: IconButton( 71 | icon: Icon( 72 | Mdi.chevronLeft, 73 | color: AppTheme.iconColor, 74 | ), 75 | onPressed: () => Navigator.of(context).pop(), 76 | ), 77 | elevation: 0, 78 | centerTitle: true, 79 | backgroundColor: Theme.of(context).cardColor, 80 | title: Text( 81 | 'Theme Settings', 82 | style: TextStyle(color: AppTheme.iconColor), 83 | ), 84 | ), 85 | body: Column( 86 | crossAxisAlignment: CrossAxisAlignment.start, 87 | children: [ 88 | _buildPrimaryColorSelector(), 89 | SwitchListTile( 90 | activeColor: AppTheme.mainColor, 91 | title: Text( 92 | "Dark Mode", 93 | style: TextStyle(color: AppTheme.textColor), 94 | ), 95 | value: isDarkModeOpened, 96 | onChanged: (value) { 97 | setState(() { 98 | isDarkModeOpened = value; 99 | _changeValueOfDarkModeInSharedPrefs(value); 100 | changeBrightness(value); 101 | }); 102 | }, 103 | ), 104 | Padding( 105 | padding: const EdgeInsets.all(8.0), 106 | child: DropdownButtonFormField( 107 | hint: Text('Select a font'), 108 | value: dropDownValue != null ? dropDownValue : null, 109 | onChanged: (value) { 110 | print(" value changed to $value"); 111 | setState(() { 112 | dropDownValue = value; 113 | changeFont(value); 114 | _changeValueOfFontFamilyInSharedPrefs(value); 115 | }); 116 | }, 117 | items: fontList.map((e) { 118 | return DropdownMenuItem( 119 | value: e.values.toList()[0], 120 | child: Padding( 121 | padding: const EdgeInsets.only(left: 8.0), 122 | child: Text( 123 | e.keys.toList()[0], 124 | style: TextStyle( 125 | fontFamily: e.values.toList()[0], 126 | color: AppTheme.textColor), 127 | )), 128 | ); 129 | }).toList(), 130 | ), 131 | ), 132 | ], 133 | ), 134 | ); 135 | } 136 | 137 | void changeColor(Color color) { 138 | AppTheme.changeColor(color); 139 | DynamicTheme.of(context).setThemeData(AppTheme.getThemeData()); 140 | } 141 | 142 | void changeFont(String fontStyle) { 143 | AppTheme.changeFont(fontStyle); 144 | DynamicTheme.of(context).setThemeData(AppTheme.getThemeData()); 145 | } 146 | 147 | void changeBrightness(value) { 148 | DynamicTheme.of(context) 149 | .setBrightness(value ? Brightness.dark : Brightness.light); 150 | } 151 | 152 | _buildPrimaryColorSelector() { 153 | return Container( 154 | color: Theme.of(context).cardColor, 155 | child: Column( 156 | crossAxisAlignment: CrossAxisAlignment.start, 157 | children: [ 158 | Padding( 159 | padding: const EdgeInsets.only(left: 20.0, top: 30.0, bottom: 10.0), 160 | child: Text( 161 | "Select primary color :-", 162 | style: TextStyle(fontSize: 18, color: AppTheme.textColor), 163 | ), 164 | ), 165 | SizedBox( 166 | height: 10, 167 | ), 168 | Padding( 169 | padding: const EdgeInsets.all(8.0), 170 | child: Row( 171 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 172 | children: [ 173 | _buildColorTile(Colors.blue, 0, () { 174 | setState(() { 175 | currentColorIndex = 0; 176 | changeColor(Colors.blue); 177 | _changeValueOfMainColorInSharedPrefs(0); 178 | }); 179 | }), 180 | _buildColorTile(Colors.teal, 1, () { 181 | setState(() { 182 | currentColorIndex = 1; 183 | changeColor(Colors.teal); 184 | _changeValueOfMainColorInSharedPrefs(1); 185 | }); 186 | }), 187 | _buildColorTile(Colors.deepOrange, 2, () { 188 | setState(() { 189 | currentColorIndex = 2; 190 | changeColor(Colors.deepOrange); 191 | _changeValueOfMainColorInSharedPrefs(2); 192 | }); 193 | }), 194 | _buildColorTile(Colors.deepPurple, 3, () { 195 | setState(() { 196 | currentColorIndex = 3; 197 | changeColor(Colors.deepPurple); 198 | _changeValueOfMainColorInSharedPrefs(3); 199 | }); 200 | }), 201 | ], 202 | ), 203 | ), 204 | SizedBox( 205 | height: 30, 206 | ), 207 | ], 208 | ), 209 | ); 210 | } 211 | 212 | _buildColorTile(Color color, int index, Function onPressed) { 213 | return Stack( 214 | children: [ 215 | Container( 216 | decoration: BoxDecoration( 217 | // color: index == currentColorIndex ? Colors.transparent : color, 218 | color: color, 219 | shape: BoxShape.circle), 220 | child: Padding( 221 | padding: EdgeInsets.all(3), 222 | child: Container( 223 | decoration: BoxDecoration( 224 | shape: BoxShape.circle, 225 | color: 226 | index == currentColorIndex ? color : AppTheme.canvasColor, 227 | ), 228 | child: Padding( 229 | padding: const EdgeInsets.all(5), 230 | child: Container( 231 | height: 45, 232 | width: 45, 233 | decoration: BoxDecoration( 234 | color: index == currentColorIndex 235 | ? color 236 | : Colors.transparent, 237 | shape: BoxShape.circle), 238 | ), 239 | ), 240 | ), 241 | ), 242 | ), 243 | Positioned.fill( 244 | child: Material( 245 | color: Colors.transparent, 246 | child: InkWell( 247 | borderRadius: BorderRadius.circular(30.0), 248 | onTap: onPressed, 249 | ), 250 | ), 251 | ), 252 | ], 253 | ); 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /lib/Layout/useOfDataDialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../consts/theme.dart'; 3 | 4 | class UseOfDataDialog extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | return SimpleDialog( 8 | backgroundColor: Colors.transparent, 9 | elevation: 0, 10 | children: [ 11 | ClipRRect( 12 | borderRadius: BorderRadius.circular(15.0), 13 | child: Container( 14 | color: Theme.of(context).cardColor, 15 | child: Column( 16 | children: [ 17 | AppBar( 18 | elevation: 0, 19 | centerTitle: true, 20 | automaticallyImplyLeading: false, 21 | title: Text( 22 | "How do we use your data?", 23 | style: TextStyle(fontWeight: FontWeight.w800), 24 | ), 25 | ), 26 | Padding( 27 | padding: const EdgeInsets.all(30.0), 28 | child: _buildText(), 29 | ) 30 | ], 31 | ), 32 | ), 33 | ), 34 | ], 35 | ); 36 | } 37 | 38 | _buildText() { 39 | return RichText( 40 | text: TextSpan( 41 | style: TextStyle( 42 | fontFamily: AppTheme.fontFamily, color: AppTheme.textColor), 43 | children: [ 44 | TextSpan(text: 'The data we take from a user are : \n\n'), 45 | TextSpan( 46 | text: 'UserName\nE-mail\nProfile Photo\n\n', 47 | style: TextStyle( 48 | fontWeight: FontWeight.w900, 49 | ), 50 | ), 51 | TextSpan( 52 | text: 53 | 'These data fields are general and no private information is gathered. The permissions this app uses are :\n\n'), 54 | TextSpan( 55 | text: '1) Internet\n2) Storage - to send images', 56 | style: TextStyle(fontWeight: FontWeight.w900), 57 | ), 58 | ]), 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/Layout/verificationDialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:messaging_app_new/consts/theme.dart'; 3 | import 'package:messaging_app_new/data/strings.dart'; 4 | 5 | class VerificationDialog extends StatelessWidget { 6 | final Function onTapResend; 7 | VerificationDialog(this.onTapResend); 8 | @override 9 | Widget build(BuildContext context) { 10 | return SimpleDialog( 11 | backgroundColor: Colors.transparent, 12 | elevation: 0, 13 | children: [ 14 | ClipRRect( 15 | borderRadius: BorderRadius.circular(15.0), 16 | child: Container( 17 | color: Theme.of(context).cardColor, 18 | child: Column( 19 | children: [ 20 | AppBar( 21 | elevation: 0, 22 | centerTitle: true, 23 | automaticallyImplyLeading: false, 24 | title: Text( 25 | "Verify your email to proceed", 26 | style: TextStyle(fontWeight: FontWeight.w800), 27 | ), 28 | ), 29 | Padding( 30 | padding: const EdgeInsets.only( 31 | top: 20.0, left: 10.0, right: 10.0, bottom: 20.0), 32 | child: Text( 33 | dialogVerificationBodyText, 34 | textAlign: TextAlign.center, 35 | ), 36 | ), 37 | Row( 38 | mainAxisAlignment: MainAxisAlignment.center, 39 | children: [ 40 | RaisedButton( 41 | color: AppTheme.mainColor, 42 | elevation: 0, 43 | shape: RoundedRectangleBorder( 44 | borderRadius: BorderRadius.circular(30.0)), 45 | onPressed: onTapResend, 46 | child: Text( 47 | "Resend", 48 | style: TextStyle(color: Colors.white), 49 | ), 50 | ), 51 | SizedBox( 52 | width: 10, 53 | ), 54 | FlatButton( 55 | shape: RoundedRectangleBorder( 56 | borderRadius: BorderRadius.circular(30.0)), 57 | color: Theme.of(context).canvasColor, 58 | child: Text("Cancel"), 59 | onPressed: () { 60 | Navigator.of(context).pop(); 61 | }, 62 | ), 63 | ], 64 | ), 65 | SizedBox( 66 | height: 20, 67 | ), 68 | ], 69 | ), 70 | ), 71 | ), 72 | ], 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/appData.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:messaging_app_new/data/strings.dart'; 3 | import 'package:messaging_app_new/user/user.dart'; 4 | 5 | ValueNotifier userData = ValueNotifier(User(imageUrl: demoImage)); 6 | 7 | setUser(User user) { 8 | userData.value = user; 9 | } 10 | -------------------------------------------------------------------------------- /lib/auth/auth.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:firebase_auth/firebase_auth.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/services.dart'; 6 | import 'package:rxdart/rxdart.dart'; 7 | 8 | import 'package:messaging_app_new/user/UserRepo.dart'; 9 | import 'package:messaging_app_new/user/user.dart'; 10 | import '../data/strings.dart'; 11 | import '../data/sharedPrefs.dart'; 12 | import '../appData.dart'; 13 | 14 | class AuthService { 15 | GlobalKey scaffoldKey; 16 | AuthService(this.scaffoldKey); 17 | 18 | FirebaseAuth _firebaseAuth = FirebaseAuth.instance; 19 | 20 | PublishSubject errorHandler = PublishSubject(); 21 | PublishSubject loading = PublishSubject(); 22 | PublishSubject userEmailVerified = PublishSubject(); 23 | 24 | Stream getMainStream() { 25 | return _firebaseAuth.onAuthStateChanged; 26 | } 27 | 28 | login(String email, String password) async { 29 | print(" in login "); 30 | loading.add(true); 31 | try { 32 | var result = await _firebaseAuth.signInWithEmailAndPassword( 33 | email: email, password: password); 34 | 35 | loading.add(false); 36 | errorHandler.add(false); 37 | 38 | await sendVerification(); 39 | _keepCheckOfEmailVerification(); 40 | return result.user; 41 | } catch (error) { 42 | if (error is PlatformException) { 43 | errorHandler.add(true); 44 | scaffoldKey.currentState.showSnackBar(SnackBar( 45 | content: Text(error.code.toString()), 46 | )); 47 | } 48 | loading.add(false); 49 | return null; 50 | } 51 | } 52 | 53 | _keepCheckOfEmailVerification() { 54 | Timer.periodic(Duration(seconds: 3), (timer) async { 55 | await _firebaseAuth.currentUser() 56 | ..reload(); 57 | var user = await _firebaseAuth.currentUser(); 58 | print("in timer " + user.isEmailVerified.toString()); 59 | if (user.isEmailVerified) { 60 | timer.cancel(); 61 | userEmailVerified.add(true); 62 | _addToFirebaseDatabase(); 63 | sharedPrefs.addItemToSharedPrefs("uid", user.uid.toString()); 64 | sharedPrefs.addBoolToSharedPrefs('isLoggedIn', true); 65 | } 66 | }); 67 | } 68 | 69 | _addToFirebaseDatabase() async { 70 | var firebaseUser = await _firebaseAuth.currentUser(); 71 | bool exists = await UserRepo().checkIfUserExists(firebaseUser.uid); 72 | if (!exists) { 73 | User user = User( 74 | imageUrl: demoImage, 75 | email: firebaseUser.email, 76 | uid: firebaseUser.uid, 77 | userName: firebaseUser.email); 78 | await UserRepo().addUser(user); 79 | } 80 | } 81 | 82 | sendVerification() async { 83 | await _firebaseAuth.currentUser() 84 | ..reload(); 85 | var user = await _firebaseAuth.currentUser(); 86 | 87 | if (user.isEmailVerified) { 88 | userEmailVerified.add(true); 89 | } else { 90 | await user.sendEmailVerification(); 91 | userEmailVerified.add(user.isEmailVerified); 92 | } 93 | } 94 | 95 | signUp(String email, String password) async { 96 | print(" in sign Up "); 97 | loading.add(true); 98 | try { 99 | AuthResult result = await _firebaseAuth.createUserWithEmailAndPassword( 100 | email: email, password: password); 101 | 102 | errorHandler.add(false); 103 | loading.add(false); 104 | 105 | await sendVerification(); 106 | _keepCheckOfEmailVerification(); 107 | return result.user; 108 | } catch (error) { 109 | if (error is PlatformException) { 110 | errorHandler.add(true); 111 | scaffoldKey.currentState.showSnackBar(SnackBar( 112 | content: Text(error.code.toString()), 113 | )); 114 | } 115 | loading.add(false); 116 | return null; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /lib/auth/signUp.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_auth/firebase_auth.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:fluttertoast/fluttertoast.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:messaging_app_new/Layout/verificationDialog.dart'; 6 | import 'package:messaging_app_new/consts/theme.dart'; 7 | 8 | import '../Layout/TextFormBuilder.dart'; 9 | import 'auth.dart'; 10 | import '../mainPage.dart'; 11 | import '../data/strings.dart'; 12 | 13 | class SignUpPage extends StatefulWidget { 14 | @override 15 | _SignUpPageState createState() => _SignUpPageState(); 16 | } 17 | 18 | class _SignUpPageState extends State 19 | with SingleTickerProviderStateMixin { 20 | GlobalKey scaffoldKey = GlobalKey(); 21 | AuthService authService; 22 | AnimationController _animationController; 23 | Animation _animation; 24 | String buttonText = login; 25 | String helperText = loginHelperText; 26 | String email; 27 | String password; 28 | 29 | double width; 30 | bool error = false; 31 | bool isLoading = false; 32 | bool isVerified = false; 33 | var height; 34 | RegExp r = new RegExp( 35 | 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,}))$', 36 | caseSensitive: false); 37 | 38 | var obscureText = true; 39 | IconData currentIcon = Icons.visibility_off; 40 | TextEditingController emailController, passwordController; 41 | 42 | @override 43 | void initState() { 44 | emailController = TextEditingController(); 45 | passwordController = TextEditingController(); 46 | authService = AuthService(scaffoldKey); 47 | super.initState(); 48 | 49 | _animationController = 50 | AnimationController(vsync: this, duration: Duration(seconds: 1)); 51 | 52 | _animation = Tween(begin: 0.0, end: -1.0).animate( 53 | CurvedAnimation(curve: Curves.easeIn, parent: _animationController)); 54 | 55 | _animationController.addStatusListener((status) { 56 | if (status == AnimationStatus.completed) { 57 | print("in status completed "); 58 | _animationController.reverse(); 59 | setState(() { 60 | if (_getSignInOrLogin(buttonText) == 1) { 61 | buttonText = "Login"; 62 | helperText = loginHelperText; 63 | } else { 64 | buttonText = signUp; 65 | helperText = signUpHelperText; 66 | } 67 | }); 68 | } 69 | }); 70 | _initializeStreams(); 71 | } 72 | 73 | _initializeStreams() { 74 | authService.userEmailVerified.listen((data) { 75 | this.isVerified = data; 76 | if (isVerified) { 77 | Navigator.of(context).pushReplacement( 78 | MaterialPageRoute( 79 | builder: (BuildContext context) => MainPage(), 80 | ), 81 | ); 82 | } 83 | }); 84 | 85 | authService.loading.listen((data) => setState(() { 86 | this.isLoading = data; 87 | })); 88 | 89 | authService.errorHandler.listen((data) => setState(() { 90 | print("eror value : - " + data.toString()); 91 | this.error = data; 92 | })); 93 | } 94 | 95 | @override 96 | void dispose() { 97 | _animationController.dispose(); 98 | super.dispose(); 99 | } 100 | 101 | @override 102 | Widget build(BuildContext context) { 103 | width = MediaQuery.of(context).size.width; 104 | height = MediaQuery.of(context).size.height; 105 | return Scaffold( 106 | key: scaffoldKey, 107 | resizeToAvoidBottomInset: false, 108 | body: _buildForm(), 109 | ); 110 | } 111 | 112 | _buildForm() { 113 | return Stack( 114 | children: [ 115 | Positioned.fill( 116 | child: Opacity( 117 | opacity: isLoading ? 0.6 : 1.0, 118 | child: Image.asset( 119 | "assets/giphy.gif", 120 | fit: BoxFit.cover, 121 | height: height, 122 | ), 123 | ), 124 | ), 125 | Positioned( 126 | top: 0.4 * height, 127 | left: 0, 128 | right: 0, 129 | child: Opacity( 130 | opacity: isLoading ? 0.6 : 1.0, 131 | child: Padding( 132 | padding: const EdgeInsets.all(10.0), 133 | child: Column( 134 | children: [ 135 | Padding( 136 | padding: const EdgeInsets.only(left: 10.0, right: 10.0), 137 | child: TextFormBuilder( 138 | textStyle: TextStyle(color: Colors.grey), 139 | hintText: "Email", 140 | controller: emailController, 141 | keybordType: TextInputType.emailAddress, 142 | onSaved: (val) { 143 | email = val; 144 | }, 145 | validator: (val) { 146 | if (!r.hasMatch(val)) { 147 | return "Enter a valid Email"; 148 | } 149 | return null; 150 | }, 151 | ), 152 | ), 153 | SizedBox( 154 | height: 20, 155 | ), 156 | Padding( 157 | padding: const EdgeInsets.only(left: 10.0, right: 10.0), 158 | child: TextFormBuilder( 159 | textStyle: TextStyle(color: Colors.grey), 160 | obscureText: obscureText, 161 | suffixWidget: IconButton( 162 | icon: Icon(currentIcon), 163 | onPressed: () { 164 | setState(() { 165 | obscureText = !obscureText; 166 | if (obscureText) 167 | currentIcon = Icons.visibility_off; 168 | else 169 | currentIcon = Icons.visibility; 170 | }); 171 | }, 172 | ), 173 | controller: passwordController, 174 | hintText: "Password", 175 | keybordType: TextInputType.visiblePassword, 176 | onSaved: (val) { 177 | setState(() { 178 | password = val; 179 | }); 180 | }, 181 | validator: (val) { 182 | if (val.length < 6) { 183 | return "Enter more than 6 characters"; 184 | } 185 | return null; 186 | }, 187 | ), 188 | ), 189 | SizedBox(height: 60.0), 190 | AnimatedBuilder( 191 | animation: _animation, 192 | builder: (BuildContext context, Widget child) { 193 | return Transform( 194 | child: child, 195 | transform: Matrix4.translationValues( 196 | _animation.value * width, 0, 0), 197 | ); 198 | }, 199 | child: _buildRaisedButton(), 200 | ), 201 | SizedBox( 202 | height: 20, 203 | ), 204 | _buildHelperText(), 205 | ], 206 | ), 207 | ), 208 | ), 209 | ), 210 | Center( 211 | child: Visibility( 212 | visible: isLoading, 213 | child: CircularProgressIndicator( 214 | valueColor: AlwaysStoppedAnimation(Colors.deepOrange), 215 | ), 216 | ), 217 | ), 218 | ], 219 | ); 220 | } 221 | 222 | _buildRaisedButton() { 223 | return Material( 224 | color: Colors.transparent, 225 | child: InkWell( 226 | borderRadius: BorderRadius.circular(30.0), 227 | onTap: _getSignInOrLogin(buttonText) == 1 228 | ? _onPressedsignUpButton 229 | : _onPressedLoginButton, 230 | child: Ink( 231 | width: 0.9 * width, 232 | height: 0.07 * height, 233 | decoration: BoxDecoration( 234 | borderRadius: BorderRadius.circular(30.0), 235 | gradient: LinearGradient( 236 | colors: [ 237 | fromHex('#ff5f6d'), 238 | fromHex('#ffc371d'), 239 | ], 240 | ), 241 | ), 242 | child: Center( 243 | child: Text( 244 | buttonText, 245 | style: TextStyle(color: Colors.white), 246 | )), 247 | ), 248 | ), 249 | ); 250 | } 251 | 252 | int _getSignInOrLogin(String text) { 253 | if (text == signUp) { 254 | return 1; 255 | } else { 256 | return 0; 257 | } 258 | } 259 | 260 | _onPressedLoginButton() async { 261 | if (!(emailController.text.length <= 0) && 262 | !(passwordController.text.length <= 0)) { 263 | FirebaseUser user = await authService.login( 264 | emailController.text, passwordController.text); 265 | if (isVerified && !error) { 266 | Navigator.of(context).pushReplacement( 267 | MaterialPageRoute(builder: (BuildContext context) => MainPage())); 268 | } else { 269 | if (error == false && user != null) { 270 | _buildVerificationDialog(); 271 | } 272 | } 273 | } else { 274 | scaffoldKey.currentState.showSnackBar(SnackBar( 275 | content: Text("Enter valid Details"), 276 | )); 277 | } 278 | } 279 | 280 | _buildVerificationDialog() { 281 | showDialog( 282 | barrierDismissible: false, 283 | context: context, 284 | builder: (BuildContext context) { 285 | return VerificationDialog(() { 286 | if (!isVerified) { 287 | authService.sendVerification(); 288 | } 289 | }); 290 | }); 291 | } 292 | 293 | _onPressedsignUpButton() async { 294 | if (!(emailController.text.length <= 0) && 295 | !(passwordController.text.length <= 0)) { 296 | FirebaseUser user = await authService.signUp( 297 | emailController.text, passwordController.text); 298 | if (isVerified && !error) { 299 | Navigator.of(context).pushReplacement( 300 | MaterialPageRoute(builder: (BuildContext context) => MainPage())); 301 | } else { 302 | if (!error && user != null) { 303 | _buildVerificationDialog(); 304 | } 305 | } 306 | } else { 307 | scaffoldKey.currentState.showSnackBar(SnackBar( 308 | content: Text("Enter valid Details"), 309 | )); 310 | } 311 | } 312 | 313 | _buildHelperText() { 314 | return GestureDetector( 315 | child: Text(helperText, style: TextStyle(color: Colors.white)), 316 | onTap: () { 317 | _animationController.forward(); 318 | }); 319 | } 320 | } 321 | 322 | Color fromHex(String hexString) { 323 | final buffer = StringBuffer(); 324 | if (hexString.length == 6 || hexString.length == 7) buffer.write('ff'); 325 | buffer.write(hexString.replaceFirst('#', '')); 326 | return Color(int.parse(buffer.toString(), radix: 16)); 327 | } 328 | -------------------------------------------------------------------------------- /lib/consts/theme.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:google_fonts/google_fonts.dart'; 5 | import 'package:messaging_app_new/data/strings.dart'; 6 | 7 | class AppTheme { 8 | static Color textColor = Colors.black; 9 | static Color iconColor = Colors.white; 10 | static Brightness brightness = Brightness.light; 11 | 12 | static Color accentColor = Colors.blue; 13 | static Color mainColor = Colors.blue; 14 | static Color primarySwatch = Colors.blue; 15 | static Color linkColor = Colors.blue; 16 | 17 | static Color isSeen = Colors.blue; 18 | 19 | // Message related color: 20 | static Color shadowColor = Colors.black38.withOpacity(0.1); 21 | static Color secondaryColor = Colors.grey; 22 | static Color notSeen = Colors.grey[400]; 23 | static Color sendMessageTextColor = Colors.white; 24 | static Color receivedMessageTextColor = Colors.white; 25 | 26 | static Color seenTimeColor = Colors.white; 27 | static Color notSeenTimeColor = Colors.grey; 28 | 29 | static Color seenTextColor = Colors.white; 30 | static Color notSeenTextColor = Colors.black; 31 | 32 | static Color receivedTimeColor = Colors.grey; 33 | static Color secondaryTextColor = Colors.white; 34 | 35 | static Color buttonTextColor = Colors.white; 36 | static Color canvasColor = Colors.white; 37 | static String fontFamily = GoogleFonts.ubuntu().fontFamily; 38 | 39 | static Color shimmerBaseColor = Colors.grey[300]; 40 | static Color shimmerEndingColor = Colors.grey[100]; 41 | 42 | static changeColor(Color color) { 43 | accentColor = color; 44 | mainColor = color; 45 | primarySwatch = color; 46 | linkColor = color; 47 | isSeen = color; 48 | } 49 | 50 | static toDarkMode() { 51 | brightness = Brightness.dark; 52 | textColor = Colors.white; 53 | canvasColor = Colors.black; 54 | iconColor = Colors.white; 55 | sendMessageTextColor = Colors.white; 56 | receivedMessageTextColor = Colors.white; 57 | receivedTimeColor = Colors.white; 58 | notSeenTimeColor = Colors.white; 59 | 60 | shimmerBaseColor = Colors.grey[900]; 61 | shimmerEndingColor = Colors.grey[700]; 62 | notSeenTextColor = Colors.white; 63 | } 64 | 65 | static toLightMode() { 66 | brightness = Brightness.light; 67 | textColor = Colors.black; 68 | canvasColor = Colors.white; 69 | iconColor = Colors.black; 70 | sendMessageTextColor = Colors.black; 71 | receivedTimeColor = Colors.grey; 72 | shimmerBaseColor = Colors.grey[300]; 73 | shimmerEndingColor = Colors.grey[100]; 74 | receivedMessageTextColor = Colors.black; 75 | 76 | notSeenTimeColor = Colors.grey; 77 | notSeenTextColor = Colors.black; 78 | } 79 | 80 | static getThemeData() { 81 | return ThemeData( 82 | brightness: brightness, 83 | primaryColor: mainColor, 84 | accentColor: accentColor, 85 | primarySwatch: primarySwatch, 86 | fontFamily: fontFamily); 87 | } 88 | 89 | static changeFont(String font) { 90 | fontFamily = getFontFromString(font); 91 | } 92 | } 93 | 94 | getColorFromInt(int val) { 95 | if (val == 0) { 96 | return Colors.blue; 97 | } else if (val == 1) { 98 | return Colors.green; 99 | } else if (val == 2) { 100 | return Colors.deepOrange; 101 | } else { 102 | return Colors.deepPurple; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /lib/data/sharedPrefs.dart: -------------------------------------------------------------------------------- 1 | import 'package:shared_preferences/shared_preferences.dart'; 2 | 3 | class SharedPrefs { 4 | SharedPreferences sharedPreferences; 5 | 6 | //init in constructor 7 | initSharedPrefs() async { 8 | var value = await SharedPreferences.getInstance(); 9 | this.sharedPreferences = value; 10 | } 11 | 12 | Future addItemToSharedPrefs(String key, dynamic value) { 13 | return sharedPreferences.setString(key, value); 14 | } 15 | 16 | addBoolToSharedPrefs(String key, dynamic value) { 17 | return sharedPreferences.setBool(key, value); 18 | } 19 | 20 | addIntToSharedPrefs(String key, int value) { 21 | return sharedPreferences.setInt(key, value); 22 | } 23 | 24 | bool checkIfExistsInSharedPrefs(String key) { 25 | return sharedPreferences.containsKey(key); 26 | } 27 | 28 | getValueFromSharedPrefs(String key) { 29 | return sharedPreferences.getString(key); 30 | } 31 | 32 | getBoolFromSharedPrefs(String key) { 33 | return sharedPreferences.getBool(key); 34 | } 35 | 36 | getIntFromSharedPrefs(String key) { 37 | return sharedPreferences.getInt(key); 38 | } 39 | 40 | clearSharedPrefsData() { 41 | return sharedPreferences.clear(); 42 | } 43 | } 44 | 45 | SharedPrefs sharedPrefs = SharedPrefs(); 46 | -------------------------------------------------------------------------------- /lib/data/strings.dart: -------------------------------------------------------------------------------- 1 | //auth related strings-- 2 | 3 | import 'package:google_fonts/google_fonts.dart'; 4 | 5 | String dialogVerificationBodyText = 6 | "An verification was sent to your email. Kindly verify to continue. If you have verified wait for a while while it syncs with your device."; 7 | String dialogVerificationTitle = "Verify your email to proceed"; 8 | String login = "Login"; 9 | String signUp = "SignUp"; 10 | String loginHelperText = "New User? SignUp"; 11 | String signUpHelperText = "Already have an account? Log in"; 12 | 13 | // Demo Image Strings-- 14 | 15 | String demoImage = 16 | "https://image.freepik.com/free-vector/plexus-modern-design-connections-network-futuristic_1048-11932.jpg"; 17 | 18 | String getFontFromString(String text) { 19 | if (text == 'Ubuntu_regular') { 20 | return GoogleFonts.ubuntu().fontFamily; 21 | } else if (text == 'Quicksand_regular') { 22 | return GoogleFonts.quicksand().fontFamily; 23 | } else if (text == 'Exo_regular') { 24 | return GoogleFonts.exo().fontFamily; 25 | } else if (text == 'Comfortaa_regular') { 26 | return GoogleFonts.comfortaa().fontFamily; 27 | } else if (text == 'Raleway_regular') { 28 | return GoogleFonts.raleway().fontFamily; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/groupModel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class GroupModel { 4 | List participants; 5 | DateTime date; 6 | //last message date 7 | 8 | GroupModel({@required this.participants, @required this.date}); 9 | 10 | factory GroupModel.fromJson(Map json) { 11 | return GroupModel( 12 | participants: json['participants'] as List, 13 | date: json['date'] == null 14 | ? null 15 | : DateTime.fromMillisecondsSinceEpoch( 16 | json['date'].millisecondsSinceEpoch)); 17 | } 18 | Map toJson() { 19 | return {'participants': participants, 'date': date}; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:dynamic_theme/dynamic_theme.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:google_fonts/google_fonts.dart'; 4 | import 'package:messaging_app_new/mainPage.dart'; 5 | import './auth/signUp.dart'; 6 | import 'data/sharedPrefs.dart'; 7 | import './consts/theme.dart'; 8 | 9 | void main() async { 10 | WidgetsFlutterBinding.ensureInitialized(); 11 | GoogleFonts.config.allowRuntimeFetching = true; 12 | await sharedPrefs.initSharedPrefs(); 13 | runApp( 14 | DynamicTheme( 15 | defaultBrightness: _getDarkMode(), 16 | data: (brightness) { 17 | AppTheme.changeFont(getFontsFamily()); 18 | AppTheme.changeColor(getMainColor()); 19 | if (brightness == Brightness.dark) { 20 | AppTheme.toDarkMode(); 21 | return AppTheme.getThemeData(); 22 | } else { 23 | AppTheme.toLightMode(); 24 | return AppTheme.getThemeData(); 25 | } 26 | }, 27 | themedWidgetBuilder: (context, data) { 28 | return MaterialApp( 29 | debugShowCheckedModeBanner: false, 30 | theme: data, 31 | home: MyApp(), 32 | ); 33 | }, 34 | ), 35 | ); 36 | } 37 | 38 | String getFontsFamily() { 39 | if (sharedPrefs.checkIfExistsInSharedPrefs('fontFamily')) { 40 | print("got value from sharedPrefs : " + 41 | sharedPrefs.getValueFromSharedPrefs('fontFamily')); 42 | 43 | return sharedPrefs.getValueFromSharedPrefs('fontFamily'); 44 | } 45 | return GoogleFonts.ubuntu().fontFamily; 46 | } 47 | 48 | getMainColor() { 49 | if (sharedPrefs.checkIfExistsInSharedPrefs('mainColor')) { 50 | return getColorFromInt(sharedPrefs.getIntFromSharedPrefs('mainColor')); 51 | } 52 | return Colors.deepOrange; 53 | } 54 | 55 | _getDarkMode() { 56 | if (sharedPrefs.checkIfExistsInSharedPrefs('darkMode')) { 57 | if (sharedPrefs.getBoolFromSharedPrefs('darkMode')) { 58 | return Brightness.dark; 59 | } 60 | return Brightness.light; 61 | } 62 | return Brightness.light; 63 | } 64 | 65 | class MyApp extends StatelessWidget { 66 | Widget build(BuildContext context) { 67 | if (!sharedPrefs.checkIfExistsInSharedPrefs("isLoggedIn")) 68 | return SignUpPage(); 69 | else { 70 | if (sharedPrefs.getBoolFromSharedPrefs('isLoggedIn')) 71 | return MainPage(); 72 | else 73 | return SignUpPage(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/mainRepo.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:messaging_app_new/user/user.dart'; 3 | import './data/sharedPrefs.dart'; 4 | 5 | class MainRepo { 6 | var reference = Firestore.instance.collection("message"); 7 | DocumentReference documentReference; 8 | //* TODO: Get all users the current user is messaging with 9 | 10 | Stream getStream() { 11 | var uid = sharedPrefs.getValueFromSharedPrefs('uid'); 12 | 13 | return reference 14 | .where( 15 | "participants", 16 | arrayContains: uid, 17 | ) 18 | .orderBy('date', descending: true) 19 | .snapshots(); 20 | } 21 | 22 | Stream getUserStream(String uid) { 23 | return Firestore.instance.collection('user').document(uid).snapshots(); 24 | } 25 | 26 | Future getUserFromUid(String uid) async { 27 | print('getting user from uid : $uid'); 28 | QuerySnapshot a = await Firestore.instance 29 | .collection('user') 30 | .where('uid', isEqualTo: uid) 31 | .getDocuments(); 32 | User user = User.fromJson(a.documents[0].data); 33 | 34 | return user; 35 | } 36 | } 37 | 38 | MainRepo mainRepo = MainRepo(); 39 | -------------------------------------------------------------------------------- /lib/message/buildErrorPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | import 'package:messaging_app_new/consts/theme.dart'; 4 | 5 | class BuildErrorPage extends StatelessWidget { 6 | final String errorText; 7 | double height; 8 | BuildErrorPage(this.errorText); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | height = MediaQuery.of(context).size.height; 13 | return ListView( 14 | children: [ 15 | Container( 16 | height: height * 0.897, 17 | color: AppTheme.mainColor, 18 | child: Stack( 19 | children: [ 20 | Stack( 21 | children: [ 22 | SvgPicture.asset( 23 | "assets/search.svg", 24 | color: AppTheme.mainColor, 25 | allowDrawingOutsideViewBox: false, 26 | colorBlendMode: BlendMode.color, 27 | ), 28 | Positioned.fill( 29 | child: Container( 30 | color: Colors.black.withOpacity(0.02), 31 | )) 32 | ], 33 | ), 34 | Align( 35 | alignment: Alignment.bottomCenter, 36 | child: Padding( 37 | padding: const EdgeInsets.only(bottom: 8.0), 38 | child: Text( 39 | errorText, 40 | style: TextStyle( 41 | color: AppTheme.iconColor, fontWeight: FontWeight.w700), 42 | ), 43 | ), 44 | ), 45 | ], 46 | ), 47 | ), 48 | ], 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/message/demo.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:google_fonts/google_fonts.dart'; 3 | import 'package:messaging_app_new/consts/theme.dart'; 4 | import 'package:messaging_app_new/message/demoDetail.dart'; 5 | import 'package:shimmer/shimmer.dart'; 6 | 7 | class DemoPage extends StatefulWidget { 8 | final GlobalKey scaffoldKey; 9 | DemoPage(this.scaffoldKey); 10 | @override 11 | _DemoPageState createState() => _DemoPageState(); 12 | } 13 | 14 | class _DemoPageState extends State { 15 | var height, width; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | height = MediaQuery.of(context).size.height; 20 | width = MediaQuery.of(context).size.width; 21 | return Container( 22 | color: Theme.of(context).canvasColor, 23 | child: Stack( 24 | children: [ 25 | Positioned.fill( 26 | child: Shimmer.fromColors( 27 | direction: ShimmerDirection.ltr, 28 | period: Duration(seconds: 5), 29 | child: Container( 30 | color: Colors.red, 31 | ), 32 | baseColor: AppTheme.shimmerBaseColor, 33 | highlightColor: AppTheme.shimmerEndingColor), 34 | ), 35 | Positioned.fill( 36 | child: Column( 37 | children: [ 38 | Spacer(), 39 | /* Center( 40 | child: Text( 41 | "Demo", 42 | style: GoogleFonts.abrilFatface(fontSize: 25), 43 | ), 44 | ), */ 45 | _buildSentMessage(false, 'Unseen sent message', '4:11 PM'), 46 | _getReceivedMessage('Received message', '4:12 PM'), 47 | _buildSentMessage(true, 'Seen sent message', '4:13 PM'), 48 | _buildSentMessage(true, 49 | 'DoubleTap on a message\n to view details', '4:14 PM'), 50 | Spacer(), 51 | Center( 52 | child: Text( 53 | "Try sending a message...", 54 | style: TextStyle( 55 | fontSize: 25, 56 | fontFamily: GoogleFonts.poiretOne().fontFamily), 57 | ), 58 | ), 59 | Spacer(), 60 | ], 61 | ), 62 | ), 63 | ], 64 | ), 65 | ); 66 | } 67 | 68 | _buildSentMessage(var isSeen, String text, String time) { 69 | return Align( 70 | alignment: Alignment.centerRight, 71 | child: Padding( 72 | padding: const EdgeInsets.only(right: 5.0), 73 | child: ClipRRect( 74 | borderRadius: BorderRadius.only( 75 | bottomLeft: Radius.circular(20.0), 76 | topLeft: Radius.circular(20.0), 77 | topRight: Radius.circular(20.0), 78 | ), 79 | child: Padding( 80 | padding: const EdgeInsets.all(5.0), 81 | child: Stack( 82 | children: [ 83 | AnimatedContainer( 84 | duration: Duration(milliseconds: 500), 85 | constraints: BoxConstraints(maxWidth: width * 0.7), 86 | padding: EdgeInsets.only( 87 | top: 15.0, 88 | left: 15.0, 89 | right: 10.0, 90 | bottom: 5.0, 91 | ), 92 | decoration: BoxDecoration( 93 | borderRadius: BorderRadius.only( 94 | bottomLeft: Radius.circular(20.0), 95 | topLeft: Radius.circular(20.0), 96 | topRight: Radius.circular(20.0), 97 | ), 98 | color: isSeen 99 | ? AppTheme.isSeen.withOpacity(0.7) 100 | : Theme.of(context).cardColor, 101 | boxShadow: [ 102 | BoxShadow( 103 | blurRadius: 2.0, 104 | color: isSeen 105 | ? AppTheme.isSeen.withOpacity(0.1) 106 | : AppTheme.notSeen.withOpacity(0.1), 107 | offset: Offset(0.0, 3.0), 108 | spreadRadius: 1.0) 109 | ]), 110 | child: Column( 111 | crossAxisAlignment: CrossAxisAlignment.end, 112 | children: [ 113 | Text( 114 | text, 115 | style: TextStyle( 116 | color: isSeen 117 | ? AppTheme.seenTextColor 118 | : AppTheme.notSeenTextColor), 119 | ), 120 | SizedBox( 121 | height: 5.0, 122 | ), 123 | Text(time, 124 | style: TextStyle( 125 | color: isSeen 126 | ? AppTheme.seenTimeColor 127 | : AppTheme.notSeenTimeColor, 128 | fontSize: 10, 129 | fontWeight: FontWeight.w900)), 130 | ], 131 | ), 132 | ), 133 | Positioned.fill( 134 | child: Material( 135 | color: Colors.transparent, 136 | child: InkWell( 137 | borderRadius: BorderRadius.only( 138 | bottomLeft: Radius.circular(20.0), 139 | topLeft: Radius.circular(20.0), 140 | topRight: Radius.circular(20.0), 141 | ), 142 | onTap: () { 143 | widget.scaffoldKey.currentState.showSnackBar(SnackBar( 144 | backgroundColor: Theme.of(context).cardColor, 145 | content: Text("Double tap to see details", 146 | style: TextStyle(color: AppTheme.textColor)), 147 | )); 148 | }, 149 | onDoubleTap: () { 150 | Navigator.of(context).push( 151 | MaterialPageRoute( 152 | builder: (context) => DemoDetail( 153 | isSeen: isSeen, 154 | message: text, 155 | time: time, 156 | scaffoldKey: widget.scaffoldKey, 157 | ), 158 | ), 159 | ); 160 | }, 161 | ), 162 | ), 163 | ), 164 | ], 165 | ), 166 | ), 167 | ), 168 | ), 169 | ); 170 | } 171 | 172 | _getReceivedMessage(String text, String time) { 173 | return Container( 174 | child: Align( 175 | alignment: Alignment.centerLeft, 176 | child: Padding( 177 | padding: const EdgeInsets.only( 178 | left: 5.0, 179 | ), 180 | child: ClipRRect( 181 | borderRadius: BorderRadius.only( 182 | bottomRight: Radius.circular(20.0), 183 | topLeft: Radius.circular(20.0), 184 | topRight: Radius.circular(20.0), 185 | ), 186 | child: Padding( 187 | padding: const EdgeInsets.all(5.0), 188 | child: Container( 189 | constraints: BoxConstraints(maxWidth: width * 0.7), 190 | decoration: BoxDecoration( 191 | borderRadius: BorderRadius.only( 192 | bottomRight: Radius.circular(20.0), 193 | topLeft: Radius.circular(20.0), 194 | topRight: Radius.circular(20.0), 195 | ), 196 | color: AppTheme.notSeen.withOpacity(0.5), 197 | ), 198 | child: Padding( 199 | padding: const EdgeInsets.all(1.0), 200 | child: Stack( 201 | children: [ 202 | Container( 203 | decoration: BoxDecoration( 204 | color: Theme.of(context).canvasColor, 205 | borderRadius: BorderRadius.only( 206 | bottomRight: Radius.circular(20.0), 207 | topLeft: Radius.circular(20.0), 208 | topRight: Radius.circular(20.0), 209 | ), 210 | ), 211 | padding: EdgeInsets.only( 212 | top: 15.0, 213 | left: 10.0, 214 | right: 15.0, 215 | bottom: 5.0, 216 | ), 217 | child: Column( 218 | crossAxisAlignment: CrossAxisAlignment.start, 219 | children: [ 220 | Text( 221 | text, 222 | style: TextStyle( 223 | color: AppTheme.receivedMessageTextColor), 224 | ), 225 | SizedBox( 226 | height: 5.0, 227 | ), 228 | Text(time, 229 | style: TextStyle( 230 | color: AppTheme.receivedTimeColor, 231 | fontSize: 10, 232 | fontWeight: FontWeight.w900)) 233 | ], 234 | ), 235 | ), 236 | Positioned.fill( 237 | child: Material( 238 | color: Colors.transparent, 239 | child: InkWell( 240 | borderRadius: BorderRadius.only( 241 | bottomRight: Radius.circular(20.0), 242 | topLeft: Radius.circular(20.0), 243 | topRight: Radius.circular(20.0), 244 | ), 245 | onDoubleTap: () {}, 246 | ), 247 | ), 248 | ), 249 | ], 250 | ), 251 | ), 252 | ), 253 | ), 254 | ), 255 | ), 256 | ), 257 | ); 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /lib/message/demoDetail.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:intl/intl.dart'; 3 | import 'package:url_launcher/url_launcher.dart'; 4 | import '../consts/theme.dart'; 5 | 6 | class DemoDetail extends StatelessWidget { 7 | final GlobalKey scaffoldKey; 8 | final String message; 9 | final bool isSeen; 10 | final String time; 11 | DemoDetail({this.message, this.isSeen, this.time, this.scaffoldKey}); 12 | var width, height; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | width = MediaQuery.of(context).size.width; 17 | height = MediaQuery.of(context).size.height; 18 | return Scaffold( 19 | appBar: AppBar( 20 | backgroundColor: Theme.of(context).canvasColor, 21 | elevation: 0, 22 | leading: IconButton( 23 | icon: Icon( 24 | Icons.arrow_back, 25 | color: AppTheme.textColor, 26 | ), 27 | onPressed: () { 28 | Navigator.of(context).pop(); 29 | }, 30 | ), 31 | ), 32 | body: Column( 33 | children: [ 34 | _buildMainWidget(context), 35 | SizedBox( 36 | height: 10, 37 | ), 38 | _buildColorContainer(AppTheme.isSeen.withOpacity(0.8), "Seen color", 39 | AppTheme.buttonTextColor), 40 | _buildColorContainer( 41 | Theme.of(context).cardColor, "Unseen color", AppTheme.textColor), 42 | Spacer(), 43 | Text( 44 | "Delete a sent message using this button", 45 | style: TextStyle(fontWeight: FontWeight.w700), 46 | ), 47 | SizedBox(height: 10), 48 | _showOrNotShowDeleteMessage(context) 49 | ], 50 | ), 51 | ); 52 | } 53 | 54 | _showOrNotShowDeleteMessage(context) { 55 | return Padding( 56 | padding: const EdgeInsets.only(bottom: 4.0), 57 | child: ButtonTheme( 58 | minWidth: 0.75 * width, 59 | height: 0.075 * height, 60 | child: RaisedButton( 61 | elevation: 0, 62 | shape: 63 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(30.0)), 64 | color: Colors.red, 65 | onPressed: () { 66 | Navigator.of(context).pop(); 67 | scaffoldKey.currentState.showSnackBar(SnackBar( 68 | backgroundColor: Theme.of(context).cardColor, 69 | content: Text( 70 | "You deleted the message sucessfully", 71 | style: TextStyle(color: AppTheme.textColor), 72 | ), 73 | )); 74 | }, 75 | child: Row( 76 | mainAxisSize: MainAxisSize.min, 77 | children: [ 78 | Icon( 79 | Icons.delete_forever, 80 | color: AppTheme.buttonTextColor, 81 | ), 82 | Text( 83 | "Delete message?", 84 | style: 85 | TextStyle(color: Colors.white, fontWeight: FontWeight.w800), 86 | ), 87 | ], 88 | ), 89 | ), 90 | ), 91 | ); 92 | } 93 | 94 | _buildMainWidget(context) { 95 | return _buildMessageWidget(context); 96 | } 97 | 98 | _buildColorContainer(Color color, String text, Color textColor) { 99 | return Container( 100 | height: 50, 101 | width: width, 102 | decoration: BoxDecoration(color: color), 103 | child: Align( 104 | alignment: Alignment.centerLeft, 105 | child: Padding( 106 | padding: const EdgeInsets.only(left: 20.0), 107 | child: Text( 108 | text, 109 | style: TextStyle(color: textColor), 110 | ), 111 | ), 112 | ), 113 | ); 114 | } 115 | 116 | _buildMessageWidget(context) { 117 | return Container( 118 | child: Padding( 119 | padding: const EdgeInsets.only(left: 5.0, top: 5.0), 120 | child: ClipRRect( 121 | borderRadius: BorderRadius.only( 122 | bottomRight: Radius.circular(20.0), 123 | topLeft: Radius.circular(20.0), 124 | topRight: Radius.circular(20.0), 125 | ), 126 | child: Container( 127 | decoration: BoxDecoration(boxShadow: [ 128 | BoxShadow( 129 | blurRadius: 5.0, 130 | color: isSeen 131 | ? AppTheme.isSeen.withOpacity(0.05) 132 | : AppTheme.notSeen.withOpacity(0.05), 133 | offset: Offset(0.0, 2.0), 134 | spreadRadius: 1.0) 135 | ]), 136 | child: Padding( 137 | padding: const EdgeInsets.all(5.0), 138 | child: ClipRRect( 139 | borderRadius: BorderRadius.only( 140 | bottomRight: Radius.circular(20.0), 141 | topLeft: Radius.circular(20.0), 142 | topRight: Radius.circular(20.0), 143 | ), 144 | child: Stack( 145 | children: [ 146 | Container( 147 | constraints: BoxConstraints(maxWidth: width * 0.7), 148 | decoration: BoxDecoration( 149 | color: isSeen 150 | ? AppTheme.isSeen.withOpacity(0.7) 151 | : Theme.of(context).cardColor, 152 | ), 153 | padding: EdgeInsets.only( 154 | top: 15.0, 155 | left: 10.0, 156 | right: 15.0, 157 | bottom: 5.0, 158 | ), 159 | child: Column( 160 | crossAxisAlignment: CrossAxisAlignment.start, 161 | children: [ 162 | buildTextWithLinks( 163 | 'The message appears here', isSeen), 164 | SizedBox( 165 | height: 5.0, 166 | ), 167 | Text(time, 168 | style: TextStyle( 169 | color: Colors.white54, fontSize: 10)) 170 | ], 171 | ), 172 | ), 173 | ], 174 | ), 175 | ), 176 | ), 177 | ), 178 | ), 179 | ), 180 | ); 181 | } 182 | 183 | _getFormattedDate(DateTime date, String format) { 184 | var formatter = new DateFormat(format); 185 | String formatted = formatter.format(date); 186 | return formatted; 187 | } 188 | } 189 | 190 | Text buildTextWithLinks(String textToLink, bool isSeen) => Text.rich(TextSpan( 191 | children: linkify(textToLink), 192 | style: TextStyle( 193 | fontSize: 16.0, 194 | fontFamily: AppTheme.fontFamily, 195 | color: isSeen ? Colors.white : AppTheme.textColor))); 196 | 197 | Future openUrl(String url) async { 198 | if (await canLaunch(url)) { 199 | await launch(url); 200 | } else { 201 | throw 'Could not launch $url'; 202 | } 203 | } 204 | 205 | const String urlPattern = r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+'; 206 | const String emailPattern = r'\S+@\S+'; 207 | const String phonePattern = r'[\d-]{9,}'; 208 | const String emojiPattern = 209 | r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])'; 210 | 211 | final RegExp linkRegExp = RegExp( 212 | '($urlPattern)|($emailPattern)|($phonePattern)|($emojiPattern)', 213 | caseSensitive: false); 214 | 215 | WidgetSpan buildEmoteComponent(String text) { 216 | return WidgetSpan( 217 | child: Text( 218 | text, 219 | style: TextStyle(fontSize: 30), 220 | )); 221 | } 222 | 223 | WidgetSpan buildLinkComponent(String text, String linkToOpen) => WidgetSpan( 224 | child: InkWell( 225 | child: SelectableText(text, 226 | style: TextStyle( 227 | fontSize: 16.0, 228 | decoration: TextDecoration.underline, 229 | color: AppTheme.linkColor)), 230 | onTap: () => openUrl(linkToOpen), 231 | )); 232 | 233 | List linkify(String text) { 234 | final List list = []; 235 | final RegExpMatch match = linkRegExp.firstMatch(text); 236 | if (match == null) { 237 | list.add(TextSpan( 238 | text: text, style: TextStyle(fontFamily: AppTheme.fontFamily))); 239 | return list; 240 | } 241 | 242 | if (match.start > 0) { 243 | list.add(TextSpan(text: text.substring(0, match.start))); 244 | } 245 | 246 | final String linkText = match.group(0); 247 | if (linkText.contains(RegExp(urlPattern, caseSensitive: false))) { 248 | list.add(buildLinkComponent(linkText, linkText)); 249 | } else if (linkText.contains(RegExp(emailPattern, caseSensitive: false))) { 250 | list.add(buildLinkComponent(linkText, 'mailto:$linkText')); 251 | } else if (linkText.contains(RegExp(phonePattern, caseSensitive: false))) { 252 | list.add(buildLinkComponent(linkText, 'tel:$linkText')); 253 | } else if (linkText.contains(RegExp(emojiPattern, caseSensitive: false))) { 254 | list.add(buildEmoteComponent(linkText)); 255 | } else { 256 | throw 'Unexpected match: $linkText'; 257 | } 258 | 259 | list.addAll(linkify(text.substring(match.start + linkText.length))); 260 | 261 | return list; 262 | } 263 | -------------------------------------------------------------------------------- /lib/message/imageFullScreen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:messaging_app_new/consts/theme.dart'; 4 | import 'package:photo_view/photo_view.dart'; 5 | import 'package:cached_network_image/cached_network_image.dart'; 6 | 7 | class ImageFullScreen extends StatelessWidget { 8 | final String imageUrl; 9 | ImageFullScreen(this.imageUrl); 10 | @override 11 | Widget build(BuildContext context) { 12 | return Scaffold( 13 | backgroundColor: Theme.of(context).canvasColor, 14 | body: Stack( 15 | children: [ 16 | PhotoView( 17 | maxScale: 1.5, 18 | minScale: 0.2, 19 | enableRotation: false, 20 | heroAttributes: PhotoViewHeroAttributes(tag: imageUrl), 21 | backgroundDecoration: 22 | BoxDecoration(color: Theme.of(context).canvasColor), 23 | imageProvider: NetworkImage(imageUrl), 24 | ), 25 | Positioned( 26 | top: 30, 27 | left: 5, 28 | child: Container( 29 | decoration: BoxDecoration( 30 | shape: BoxShape.circle, color: Theme.of(context).cardColor), 31 | child: IconButton( 32 | icon: Icon( 33 | Icons.arrow_back, 34 | color: AppTheme.textColor, 35 | ), 36 | onPressed: () { 37 | Navigator.of(context).pop(); 38 | }), 39 | ), 40 | ), 41 | ], 42 | ), 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/message/message.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class Message { 5 | String message; 6 | String idTo; 7 | String idFrom; 8 | DateTime date; 9 | bool isSeen = false; 10 | String documentId; 11 | int type; 12 | String imageUrl; 13 | bool notificationShown = false; 14 | DocumentReference reference; 15 | 16 | //0 message : 1 image 17 | Message( 18 | {@required this.message, 19 | @required this.idTo, 20 | @required this.idFrom, 21 | @required this.date, 22 | @required this.documentId, 23 | @required this.type, 24 | this.notificationShown = false, 25 | this.imageUrl, 26 | this.isSeen}); 27 | 28 | factory Message.fromSnapshot(DocumentSnapshot snapshot) { 29 | Message message = Message.fromJson(snapshot.data); 30 | message.reference = snapshot.reference; 31 | return message; 32 | } 33 | 34 | factory Message.fromJson(Map json) { 35 | return Message( 36 | message: json['message'] as String, 37 | date: json['date'] == null 38 | ? null 39 | : DateTime.fromMillisecondsSinceEpoch( 40 | json['date'].millisecondsSinceEpoch), 41 | idFrom: json['idFrom'] as String, 42 | idTo: json['idTo'] as String, 43 | isSeen: json['isSeen'] as bool, 44 | documentId: json['documentId'] as String, 45 | imageUrl: json['imageUrl'] as String, 46 | notificationShown: json['notification'] as bool, 47 | type: json['type'] as int); 48 | } 49 | 50 | Map toJson() { 51 | return { 52 | 'message': message, 53 | 'date': date, 54 | 'idFrom': idFrom, 55 | 'idTo': idTo, 56 | 'isSeen': isSeen, 57 | 'documentId': documentId, 58 | 'type': type, 59 | 'imageUrl': imageUrl, 60 | 'notificationShown': notificationShown 61 | }; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/message/messageDetail.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:intl/intl.dart'; 3 | import 'package:messaging_app_new/message/messageRepo.dart'; 4 | import 'package:url_launcher/url_launcher.dart'; 5 | import '../message/message.dart'; 6 | import '../consts/theme.dart'; 7 | 8 | class MessageDetail extends StatelessWidget { 9 | final Message message; 10 | final String documentId; 11 | var width, height; 12 | 13 | MessageDetail({this.message, @required this.documentId}); 14 | @override 15 | Widget build(BuildContext context) { 16 | width = MediaQuery.of(context).size.width; 17 | height = MediaQuery.of(context).size.height; 18 | return Scaffold( 19 | appBar: AppBar( 20 | backgroundColor: Theme.of(context).canvasColor, 21 | elevation: 0, 22 | leading: IconButton( 23 | icon: Icon( 24 | Icons.arrow_back, 25 | color: AppTheme.textColor, 26 | ), 27 | onPressed: () { 28 | Navigator.of(context).pop(); 29 | }, 30 | ), 31 | ), 32 | body: Column( 33 | children: [ 34 | _buildMainWidget(context), 35 | SizedBox( 36 | height: 10, 37 | ), 38 | _buildColorContainer(AppTheme.isSeen.withOpacity(0.8), "Seen color", 39 | AppTheme.buttonTextColor), 40 | _buildColorContainer( 41 | Theme.of(context).cardColor, "Unseen color", AppTheme.textColor), 42 | Spacer(), 43 | _showOrNotShowDeleteMessage(context), 44 | ], 45 | ), 46 | ); 47 | } 48 | 49 | _buildMainWidget(context) { 50 | if (message.type == 0) { 51 | return _buildMessageWidget(context); 52 | } else { 53 | return _buildImageWidget(context); 54 | } 55 | } 56 | 57 | _showOrNotShowDeleteMessage(context) { 58 | if (message.message != "This message has been deleted") { 59 | return Padding( 60 | padding: const EdgeInsets.only(bottom: 4.0), 61 | child: ButtonTheme( 62 | minWidth: 0.75 * width, 63 | height: 0.075 * height, 64 | child: RaisedButton( 65 | elevation: 0, 66 | shape: RoundedRectangleBorder( 67 | borderRadius: BorderRadius.circular(30.0)), 68 | color: Colors.red, 69 | onPressed: () { 70 | if (message.type == 0) { 71 | messageRepo.deleteMessage(message); 72 | Navigator.of(context).pop(); 73 | } else { 74 | messageRepo.deleteImage(documentId, message); 75 | Navigator.of(context).pop(); 76 | } 77 | }, 78 | child: Row( 79 | mainAxisSize: MainAxisSize.min, 80 | children: [ 81 | Icon( 82 | Icons.delete_forever, 83 | color: AppTheme.buttonTextColor, 84 | ), 85 | Text( 86 | "Delete message?", 87 | style: TextStyle( 88 | color: AppTheme.buttonTextColor, 89 | fontWeight: FontWeight.w800), 90 | ), 91 | ], 92 | ), 93 | ), 94 | ), 95 | ); 96 | } 97 | return Container(); 98 | } 99 | 100 | _buildColorContainer(Color color, String text, Color textColor) { 101 | return Container( 102 | height: 50, 103 | width: width, 104 | decoration: BoxDecoration(color: color), 105 | child: Align( 106 | alignment: Alignment.centerLeft, 107 | child: Padding( 108 | padding: const EdgeInsets.only(left: 20.0), 109 | child: Text( 110 | text, 111 | style: TextStyle(color: textColor), 112 | ), 113 | ), 114 | ), 115 | ); 116 | } 117 | 118 | _buildImageWidget(context) { 119 | return Center( 120 | child: Padding( 121 | padding: const EdgeInsets.only(right: 10.0, top: 10.0), 122 | child: ClipRRect( 123 | borderRadius: BorderRadius.only( 124 | bottomLeft: Radius.circular(20.0), 125 | topLeft: Radius.circular(20.0), 126 | topRight: Radius.circular(20.0), 127 | ), 128 | child: Stack( 129 | children: [ 130 | AnimatedContainer( 131 | duration: Duration(milliseconds: 500), 132 | constraints: BoxConstraints(maxWidth: width * 0.7), 133 | padding: EdgeInsets.only( 134 | top: 8.0, 135 | left: 8.0, 136 | right: 8.0, 137 | bottom: 5.0, 138 | ), 139 | color: message.isSeen 140 | ? AppTheme.isSeen.withOpacity(0.7) 141 | : Theme.of(context).cardColor, 142 | child: Column( 143 | crossAxisAlignment: CrossAxisAlignment.end, 144 | children: [ 145 | ClipRRect( 146 | borderRadius: BorderRadius.only( 147 | bottomLeft: Radius.circular(20.0), 148 | topLeft: Radius.circular(20.0), 149 | topRight: Radius.circular(20.0), 150 | ), 151 | child: Image.network( 152 | message.imageUrl, 153 | height: 250, 154 | width: 300, 155 | fit: BoxFit.cover, 156 | ), 157 | ), 158 | SizedBox( 159 | height: 5.0, 160 | ), 161 | Text(_getFormattedDate(message.date, 'jms'), 162 | style: TextStyle( 163 | color: message.isSeen 164 | ? Colors.white 165 | : AppTheme.receivedTimeColor, 166 | fontSize: 10, 167 | fontWeight: FontWeight.w900)), 168 | ], 169 | ), 170 | ), 171 | Positioned.fill( 172 | child: Material( 173 | color: Colors.transparent, 174 | child: InkWell( 175 | onDoubleTap: () {}, 176 | ), 177 | )), 178 | ], 179 | ), 180 | ), 181 | ), 182 | ); 183 | } 184 | 185 | _buildMessageWidget(context) { 186 | return Container( 187 | child: Padding( 188 | padding: const EdgeInsets.only(left: 5.0, top: 5.0), 189 | child: ClipRRect( 190 | borderRadius: BorderRadius.only( 191 | bottomRight: Radius.circular(20.0), 192 | topLeft: Radius.circular(20.0), 193 | topRight: Radius.circular(20.0), 194 | ), 195 | child: Container( 196 | decoration: BoxDecoration(boxShadow: [ 197 | BoxShadow( 198 | blurRadius: 5.0, 199 | color: message.isSeen 200 | ? AppTheme.isSeen.withOpacity(0.05) 201 | : AppTheme.notSeen.withOpacity(0.05), 202 | offset: Offset(0.0, 2.0), 203 | spreadRadius: 1.0) 204 | ]), 205 | child: Padding( 206 | padding: const EdgeInsets.all(5.0), 207 | child: ClipRRect( 208 | borderRadius: BorderRadius.only( 209 | bottomRight: Radius.circular(20.0), 210 | topLeft: Radius.circular(20.0), 211 | topRight: Radius.circular(20.0), 212 | ), 213 | child: Stack( 214 | children: [ 215 | Container( 216 | constraints: BoxConstraints(maxWidth: width * 0.7), 217 | decoration: BoxDecoration( 218 | color: message.isSeen 219 | ? AppTheme.isSeen.withOpacity(0.7) 220 | : Theme.of(context).cardColor, 221 | ), 222 | padding: EdgeInsets.only( 223 | top: 15.0, 224 | left: 10.0, 225 | right: 15.0, 226 | bottom: 5.0, 227 | ), 228 | child: Column( 229 | crossAxisAlignment: CrossAxisAlignment.start, 230 | children: [ 231 | buildTextWithLinks(message.message, message.isSeen), 232 | SizedBox( 233 | height: 5.0, 234 | ), 235 | Text(_getFormattedDate(message.date, 'jms'), 236 | style: TextStyle( 237 | color: Colors.white54, fontSize: 10)) 238 | ], 239 | ), 240 | ), 241 | ], 242 | ), 243 | ), 244 | ), 245 | ), 246 | ), 247 | ), 248 | ); 249 | } 250 | 251 | _getFormattedDate(DateTime date, String format) { 252 | var formatter = new DateFormat(format); 253 | String formatted = formatter.format(date); 254 | return formatted; 255 | } 256 | } 257 | 258 | Text buildTextWithLinks(String textToLink, bool isSeen) => Text.rich(TextSpan( 259 | children: linkify(textToLink), 260 | style: TextStyle( 261 | fontSize: 16.0, 262 | fontFamily: AppTheme.fontFamily, 263 | color: isSeen ? Colors.white : AppTheme.textColor))); 264 | 265 | Future openUrl(String url) async { 266 | if (await canLaunch(url)) { 267 | await launch(url); 268 | } else { 269 | throw 'Could not launch $url'; 270 | } 271 | } 272 | 273 | const String urlPattern = r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+'; 274 | const String emailPattern = r'\S+@\S+'; 275 | const String phonePattern = r'[\d-]{9,}'; 276 | const String emojiPattern = 277 | r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])'; 278 | 279 | final RegExp linkRegExp = RegExp( 280 | '($urlPattern)|($emailPattern)|($phonePattern)|($emojiPattern)', 281 | caseSensitive: false); 282 | 283 | WidgetSpan buildEmoteComponent(String text) { 284 | return WidgetSpan( 285 | child: Text( 286 | text, 287 | style: TextStyle(fontSize: 30), 288 | )); 289 | } 290 | 291 | WidgetSpan buildLinkComponent(String text, String linkToOpen) => WidgetSpan( 292 | child: InkWell( 293 | child: SelectableText(text, 294 | style: TextStyle( 295 | fontSize: 16.0, 296 | decoration: TextDecoration.underline, 297 | color: AppTheme.linkColor)), 298 | onTap: () => openUrl(linkToOpen), 299 | )); 300 | 301 | List linkify(String text) { 302 | final List list = []; 303 | final RegExpMatch match = linkRegExp.firstMatch(text); 304 | if (match == null) { 305 | list.add(TextSpan( 306 | text: text, style: TextStyle(fontFamily: AppTheme.fontFamily))); 307 | return list; 308 | } 309 | 310 | if (match.start > 0) { 311 | list.add(TextSpan(text: text.substring(0, match.start))); 312 | } 313 | 314 | final String linkText = match.group(0); 315 | if (linkText.contains(RegExp(urlPattern, caseSensitive: false))) { 316 | list.add(buildLinkComponent(linkText, linkText)); 317 | } else if (linkText.contains(RegExp(emailPattern, caseSensitive: false))) { 318 | list.add(buildLinkComponent(linkText, 'mailto:$linkText')); 319 | } else if (linkText.contains(RegExp(phonePattern, caseSensitive: false))) { 320 | list.add(buildLinkComponent(linkText, 'tel:$linkText')); 321 | } else if (linkText.contains(RegExp(emojiPattern, caseSensitive: false))) { 322 | list.add(buildEmoteComponent(linkText)); 323 | } else { 324 | throw 'Unexpected match: $linkText'; 325 | } 326 | 327 | list.addAll(linkify(text.substring(match.start + linkText.length))); 328 | 329 | return list; 330 | } 331 | -------------------------------------------------------------------------------- /lib/message/messagePage.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:fluttertoast/fluttertoast.dart'; 4 | import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; 5 | import 'package:mdi/mdi.dart'; 6 | import 'package:messaging_app_new/consts/theme.dart'; 7 | import 'package:messaging_app_new/data/sharedPrefs.dart'; 8 | import 'package:messaging_app_new/groupModel.dart'; 9 | import 'package:messaging_app_new/message/buildMessageWidget.dart'; 10 | import 'package:messaging_app_new/message/demo.dart'; 11 | import 'package:messaging_app_new/message/message.dart'; 12 | import 'package:messaging_app_new/message/messageRepo.dart'; 13 | import 'package:messaging_app_new/user/storage.dart'; 14 | import 'package:messaging_app_new/user/user.dart'; 15 | import 'package:rxdart/rxdart.dart'; 16 | import 'package:cached_network_image/cached_network_image.dart'; 17 | import 'package:shimmer/shimmer.dart'; 18 | 19 | class MessagePage extends StatefulWidget { 20 | final GroupModel model; 21 | final User user; 22 | MessagePage(this.model, this.user); 23 | 24 | @override 25 | _MessagePageState createState() => _MessagePageState(); 26 | } 27 | 28 | class _MessagePageState extends State 29 | with SingleTickerProviderStateMixin { 30 | GroupModel model; 31 | 32 | var scaffoldKey = GlobalKey(); 33 | var messageEntered = ""; 34 | var idTo, idFrom; 35 | var documentId; 36 | TextEditingController controller; 37 | 38 | var isLoading = true; 39 | PublishSubject isLoadingSubject = PublishSubject(); 40 | 41 | ScrollController scrollController; 42 | bool showGoToTopFab = false; 43 | bool showDemo = true; 44 | AnimationController animationController; 45 | Animation rotationAnimation; 46 | var photoColor = AppTheme.iconColor; 47 | var height; 48 | var anotherUserName; 49 | _getDataFromApi() async { 50 | isLoadingSubject.add(true); 51 | print(model.toJson().toString()); 52 | await messageRepo.setReference(model); 53 | documentId = await messageRepo.getGroupDocumentId(model); 54 | isLoadingSubject.add(false); 55 | } 56 | 57 | @override 58 | void dispose() { 59 | super.dispose(); 60 | animationController.dispose(); 61 | } 62 | 63 | @override 64 | void initState() { 65 | scrollController = ScrollController(); 66 | model = widget.model; 67 | idFrom = sharedPrefs.getValueFromSharedPrefs('uid'); 68 | animationController = AnimationController( 69 | vsync: this, 70 | duration: Duration( 71 | milliseconds: 150, 72 | ), 73 | ); 74 | 75 | rotationAnimation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation( 76 | parent: animationController, 77 | curve: Curves.easeIn, 78 | )); 79 | _getAnotherUserId(); 80 | 81 | controller = TextEditingController(); 82 | _getDataFromApi(); 83 | super.initState(); 84 | isLoadingSubject.listen((value) { 85 | setState(() { 86 | isLoading = value; 87 | }); 88 | }); 89 | 90 | scrollController.addListener(() { 91 | if (scrollController.offset == 92 | scrollController.position.minScrollExtent) { 93 | setState(() { 94 | showGoToTopFab = false; 95 | }); 96 | } else { 97 | setState(() { 98 | showGoToTopFab = true; 99 | }); 100 | } 101 | }); 102 | } 103 | 104 | _getAnotherUserId() { 105 | for (var a in model.participants) { 106 | if (a != idFrom) { 107 | idTo = a; 108 | } 109 | } 110 | } 111 | 112 | @override 113 | Widget build(BuildContext context) { 114 | height = MediaQuery.of(context).size.height; 115 | return Scaffold( 116 | key: scaffoldKey, 117 | floatingActionButton: Visibility( 118 | visible: showGoToTopFab, 119 | child: Column( 120 | mainAxisAlignment: MainAxisAlignment.end, 121 | children: [ 122 | FloatingActionButton( 123 | onPressed: () { 124 | scrollController.animateTo( 125 | scrollController.position.minScrollExtent, 126 | duration: Duration(milliseconds: 500), 127 | curve: Curves.fastOutSlowIn); 128 | }, 129 | child: Icon(Mdi.menuDown), 130 | ), 131 | SizedBox( 132 | height: 0.075 * height, 133 | ) 134 | ], 135 | ), 136 | ), 137 | appBar: AppBar( 138 | title: Hero( 139 | tag: widget.user.userName, 140 | child: Material( 141 | child: Text( 142 | "${widget.user.userName}", 143 | overflow: TextOverflow.ellipsis, 144 | style: TextStyle( 145 | fontWeight: FontWeight.w600, 146 | fontSize: 16, 147 | color: AppTheme.textColor), 148 | ), 149 | ), 150 | ), 151 | backgroundColor: Theme.of(context).canvasColor, 152 | elevation: 0, 153 | automaticallyImplyLeading: false, 154 | actions: [ 155 | PopupMenuButton( 156 | offset: Offset(0, 50.0), 157 | onSelected: (value) { 158 | if (value == 0) { 159 | messageRepo.clearChat(model); 160 | Navigator.of(context).pop(); 161 | } 162 | }, 163 | icon: Icon(MdiIcons.dotsVertical, color: AppTheme.iconColor), 164 | itemBuilder: (context) { 165 | return [ 166 | PopupMenuItem( 167 | child: Text("Clear chat"), 168 | value: 0, 169 | ), 170 | ]; 171 | }, 172 | ), 173 | ], 174 | leading: IconButton( 175 | onPressed: () { 176 | Navigator.of(context).pop(); 177 | }, 178 | icon: Icon( 179 | MdiIcons.chevronLeft, 180 | color: AppTheme.iconColor, 181 | ), 182 | ), 183 | ), 184 | body: _getMainWidget()); 185 | } 186 | 187 | _getMainWidget() { 188 | if (isLoading) { 189 | return Center( 190 | child: CircularProgressIndicator(), 191 | ); 192 | } 193 | return StreamBuilder( 194 | stream: messageRepo.getStream(), 195 | builder: (context, AsyncSnapshot snapshot) { 196 | if (snapshot.hasData) { 197 | return _buildMainWidget(snapshot.data); 198 | } else if (snapshot.connectionState == ConnectionState.active) { 199 | return Center(child: CircularProgressIndicator()); 200 | } else if (snapshot.hasError) { 201 | return Center( 202 | child: Text(" An error occured "), 203 | ); 204 | } else { 205 | return Center( 206 | child: CircularProgressIndicator(), 207 | ); 208 | } 209 | }, 210 | ); 211 | } 212 | 213 | _buildMainWidget(QuerySnapshot data) { 214 | List list = data.documents; 215 | return _buildMessagingLayout(list); 216 | } 217 | 218 | _getList(list) { 219 | if (list.length > 0) { 220 | return Container( 221 | color: Colors.transparent, 222 | margin: EdgeInsets.only(bottom: 60.0), 223 | child: ListView.builder( 224 | physics: BouncingScrollPhysics(), 225 | controller: scrollController, 226 | reverse: true, 227 | itemBuilder: (context, index) { 228 | return BuildMessageWidget( 229 | list[index], 230 | sharedPrefs.getValueFromSharedPrefs('uid'), 231 | documentId, 232 | index == 0); 233 | }, 234 | itemCount: list.length, 235 | ), 236 | ); 237 | } else { 238 | if (showDemo) { 239 | return Center( 240 | child: DemoPage(scaffoldKey), 241 | ); 242 | } 243 | return Container(); 244 | } 245 | } 246 | 247 | _buildMessagingLayout(list) { 248 | return Container( 249 | color: Colors.transparent, 250 | child: Stack( 251 | children: [ 252 | _getList(list), 253 | Align( 254 | alignment: Alignment.bottomCenter, 255 | child: Container( 256 | height: 60, 257 | color: Colors.black12, 258 | child: Row( 259 | children: [ 260 | Expanded( 261 | child: Padding( 262 | padding: const EdgeInsets.all(8.0), 263 | child: TextField( 264 | onTap: () { 265 | setState(() { 266 | showDemo = false; 267 | }); 268 | }, 269 | controller: controller, 270 | cursorRadius: Radius.circular(20.0), 271 | decoration: InputDecoration( 272 | isDense: true, 273 | contentPadding: EdgeInsets.only( 274 | left: 10.0, right: 10.0, bottom: 5.0), 275 | hintText: "Type your message.", 276 | alignLabelWithHint: true, 277 | border: InputBorder.none), 278 | ), 279 | ), 280 | ), 281 | AnimatedBuilder( 282 | builder: (context, child) { 283 | return Transform.rotate( 284 | angle: 1.5 * rotationAnimation.value, 285 | child: child, 286 | ); 287 | }, 288 | animation: animationController, 289 | child: IconButton( 290 | tooltip: 'Send a photo', 291 | icon: Icon( 292 | MdiIcons.googlePhotos, 293 | color: photoColor, 294 | ), 295 | onPressed: _onPressedPhotoIcon, 296 | ), 297 | ), 298 | IconButton( 299 | tooltip: 'Send', 300 | icon: Icon( 301 | Icons.send, 302 | color: AppTheme.mainColor, 303 | ), 304 | onPressed: () { 305 | if (controller.text.length > 0) { 306 | messageRepo.addMessage( 307 | Message( 308 | date: DateTime.now(), 309 | message: controller.text, 310 | idFrom: sharedPrefs 311 | .getValueFromSharedPrefs('uid'), 312 | idTo: idTo, 313 | isSeen: false, 314 | documentId: documentId, 315 | type: 0), 316 | model, 317 | documentId); 318 | setState(() { 319 | controller.text = ""; 320 | }); 321 | } else { 322 | Fluttertoast.showToast( 323 | msg: "Enter a message to send"); 324 | } 325 | }, 326 | ) 327 | ], 328 | )), 329 | ), 330 | ], 331 | ), 332 | ); 333 | } 334 | 335 | _onPressedPhotoIcon() async { 336 | setState(() { 337 | photoColor = AppTheme.accentColor; 338 | }); 339 | animationController.repeat(); 340 | 341 | DateTime time = DateTime.now(); 342 | var value = await storageService.pickFile(); 343 | if (value != null) { 344 | // Fluttertoast.showToast(msg: "The image may take a while to display"); 345 | scaffoldKey.currentState.showSnackBar(SnackBar( 346 | content: Text( 347 | "The image may take a while to display", 348 | style: TextStyle(color: AppTheme.textColor), 349 | ), 350 | backgroundColor: Theme.of(context).cardColor, 351 | )); 352 | storageService.uploadChatImage(value, documentId, time).then((url) { 353 | print(url); 354 | if (url != null) { 355 | Message message = Message( 356 | documentId: documentId, 357 | type: 1, 358 | date: time, 359 | idFrom: idFrom, 360 | idTo: idTo, 361 | message: "Image", 362 | imageUrl: url, 363 | isSeen: false); 364 | 365 | messageRepo.addMessage(message, model, documentId); 366 | } 367 | 368 | animationController.stop(); 369 | setState(() { 370 | photoColor = AppTheme.iconColor; 371 | }); 372 | }); 373 | } else { 374 | animationController.stop(); 375 | setState(() { 376 | photoColor = AppTheme.iconColor; 377 | }); 378 | } 379 | } 380 | } 381 | -------------------------------------------------------------------------------- /lib/message/messageRepo.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:firebase_storage/firebase_storage.dart'; 3 | import 'message.dart'; 4 | import '../groupModel.dart'; 5 | 6 | class MessageRepo { 7 | CollectionReference reference = Firestore.instance.collection("message"); 8 | 9 | //? Check if room exists else create it 10 | setReference(GroupModel model) async { 11 | print(" in set reference "); 12 | QuerySnapshot a = await Firestore.instance 13 | .collection("message") 14 | .where('participants', isEqualTo: model.participants) 15 | .getDocuments(); 16 | 17 | print("First query results = : ${a.documents.length}"); 18 | 19 | if (a.documents.length <= 0) { 20 | print(" in if ----checking another way"); 21 | a = await Firestore.instance.collection("message").where('participants', 22 | isEqualTo: [ 23 | model.participants[1], 24 | model.participants[0] 25 | ]).getDocuments(); 26 | } 27 | 28 | if (a.documents.length > 0) { 29 | print(" in set reference -- if -- \n "); 30 | var e = a.documents[0].reference; 31 | 32 | reference = e.collection("messages").reference(); 33 | } else { 34 | print(" in set reference --else -- \n"); 35 | await Firestore.instance.collection('message').document().setData( 36 | {'participants': model.participants, 'date': DateTime.now()}, 37 | merge: true); 38 | 39 | QuerySnapshot a = await Firestore.instance 40 | .collection('message') 41 | .where('participants', isEqualTo: model.participants) 42 | .getDocuments(); 43 | 44 | var e = a.documents[0].reference; 45 | reference = e.collection("messages").reference(); 46 | } 47 | } 48 | 49 | Stream getStream() { 50 | return reference.orderBy("date", descending: true).snapshots(); 51 | } 52 | 53 | Future addMessage(Message message, GroupModel model, var docId) { 54 | updateTime(docId, message.date, model); 55 | return reference 56 | .document(message.date.millisecondsSinceEpoch.toString()) 57 | .setData(message.toJson()); 58 | } 59 | 60 | updateTime(String docId, DateTime date, GroupModel model) { 61 | Firestore.instance 62 | .collection('message') 63 | .document(docId) 64 | .setData({'participants': model.participants, 'date': date}); 65 | } 66 | 67 | Future getGroupDocumentId(GroupModel model) async { 68 | var snapshot = await Firestore.instance 69 | .collection("message") 70 | .where( 71 | 'participants', 72 | isEqualTo: model.participants, 73 | ) 74 | .getDocuments(); 75 | if (snapshot.documents.length <= 0) return null; 76 | return snapshot.documents[0].documentID; 77 | } 78 | 79 | Stream getLastMessage(GroupModel model, var documentId) { 80 | return Firestore.instance 81 | .collection('message') 82 | .document(documentId) 83 | .collection('messages') 84 | .snapshots(); 85 | } 86 | 87 | deleteMessage(Message message) async { 88 | if (message.message != "This message has been deleted") { 89 | await reference 90 | .document(message.date.millisecondsSinceEpoch.toString()) 91 | .updateData( 92 | Message( 93 | message: "This message has been deleted", 94 | date: message.date, 95 | idFrom: message.idFrom, 96 | idTo: message.idTo, 97 | documentId: message.documentId, 98 | isSeen: message.isSeen, 99 | type: 0) 100 | .toJson(), 101 | ); 102 | } 103 | } 104 | 105 | clearChat(GroupModel model) async { 106 | print('-------------in Clear chat--------- '); 107 | getGroupDocumentId(model).then((value) async { 108 | var querySnapshot = await Firestore.instance 109 | .collection('message') 110 | .document(value) 111 | .collection('messages') 112 | .getDocuments(); 113 | 114 | var inst = Firestore.instance.collection('message').document(value); 115 | 116 | if (querySnapshot.documents.length > 0) { 117 | for (int i = 0; i < querySnapshot.documents.length; i++) { 118 | Message message = Message.fromSnapshot(querySnapshot.documents[i]); 119 | if (message.type == 1) { 120 | deleteImageCompletely(value, message); 121 | inst 122 | .collection('messages') 123 | .document(message.date.millisecondsSinceEpoch.toString()) 124 | .delete(); 125 | } else { 126 | print("\n--- Deleting message(${message.documentId})---\n "); 127 | inst 128 | .collection('messages') 129 | .document(message.date.millisecondsSinceEpoch.toString()) 130 | .delete(); 131 | } 132 | } 133 | } 134 | }); 135 | } 136 | 137 | deleteGroup(GroupModel model) async { 138 | _printer(" in delete group "); 139 | getGroupDocumentId(model).then((value) async { 140 | var querySnapshot = await Firestore.instance 141 | .collection('message') 142 | .document(value) 143 | .collection('messages') 144 | .where('type', isEqualTo: 1) 145 | .getDocuments(); 146 | 147 | if (querySnapshot.documents.length > 0) { 148 | for (int i = 0; i < querySnapshot.documents.length; i++) { 149 | Message message = Message.fromSnapshot(querySnapshot.documents[i]); 150 | deleteImageCompletely(value, message); 151 | } 152 | } 153 | Firestore.instance.collection("message").document(value).delete(); 154 | }); 155 | } 156 | 157 | deleteImageCompletely(String documentId, Message message) { 158 | FirebaseStorage.instance 159 | .ref() 160 | .child('chats/$documentId/${message.date.millisecondsSinceEpoch}') 161 | .delete() 162 | .then((value) { 163 | _printer("-Deleted message completely-"); 164 | }); 165 | } 166 | 167 | deleteImage(String documentId, Message message) { 168 | deleteMessage(message); 169 | FirebaseStorage.instance 170 | .ref() 171 | .child('chats/$documentId/${message.date.millisecondsSinceEpoch}') 172 | .delete() 173 | .then((value) { 174 | _printer("-Deleted message completely-"); 175 | }); 176 | } 177 | 178 | updateIsSeen(Message message) async { 179 | await reference 180 | .document(message.date.millisecondsSinceEpoch.toString()) 181 | .updateData( 182 | Message( 183 | message: message.message, 184 | date: message.date, 185 | idFrom: message.idFrom, 186 | idTo: message.idTo, 187 | documentId: message.documentId, 188 | isSeen: true, 189 | notificationShown: message.notificationShown, 190 | imageUrl: message.imageUrl, 191 | type: message.type) 192 | .toJson(), 193 | ); 194 | } 195 | 196 | updateIsNotificationShown(Message message) async { 197 | await Firestore.instance 198 | .collection('message') 199 | .document(message.documentId) 200 | .collection("messages") 201 | .document(message.date.millisecondsSinceEpoch.toString()) 202 | .updateData(Message( 203 | message: message.message, 204 | date: message.date, 205 | idFrom: message.idFrom, 206 | idTo: message.idTo, 207 | documentId: message.documentId, 208 | isSeen: message.isSeen, 209 | notificationShown: true, 210 | imageUrl: message.imageUrl, 211 | type: message.type) 212 | .toJson()); 213 | } 214 | 215 | getChatRoom(String uid) async { 216 | _printer("getChatRoom [MessageRepo.dart]"); 217 | /* Stream a = Firestore.instance 218 | .collection("message") 219 | .where('participants', arrayContains: uid) 220 | .getDocuments() 221 | .asStream(); 222 | _printer("-"); 223 | 224 | return a; */ 225 | var a = await Firestore.instance 226 | .collection('message') 227 | .where( 228 | 'participants', 229 | arrayContains: uid, 230 | ) 231 | .getDocuments(); 232 | var b = a.documents[0].reference; 233 | b.collection("message").add({'kuchbhi': "aur nitpo lue"}); 234 | } 235 | } 236 | 237 | _printer(String text) { 238 | print( 239 | "\n\n--------------------------------$text---------------------------\n\n"); 240 | print("\n\n\n"); 241 | } 242 | 243 | MessageRepo messageRepo = MessageRepo(); 244 | -------------------------------------------------------------------------------- /lib/message/receivedMessageDetail.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_clipboard_manager/flutter_clipboard_manager.dart'; 3 | import 'package:fluttertoast/fluttertoast.dart'; 4 | import 'package:intl/intl.dart'; 5 | import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; 6 | import 'package:url_launcher/url_launcher.dart'; 7 | import '../message/message.dart'; 8 | import '../consts/theme.dart'; 9 | 10 | class ReceivedMessageDetail extends StatelessWidget { 11 | final Message message; 12 | final String documentId; 13 | 14 | ReceivedMessageDetail({this.message, @required this.documentId}); 15 | @override 16 | Widget build(BuildContext context) { 17 | var width = MediaQuery.of(context).size.width; 18 | var height = MediaQuery.of(context).size.height; 19 | return Scaffold( 20 | floatingActionButton: message.type == 0 21 | ? FloatingActionButton.extended( 22 | onPressed: () { 23 | FlutterClipboardManager.copyToClipBoard(message.message); 24 | Fluttertoast.showToast( 25 | msg: 'Copied "${message.message}" to clipboard', 26 | ); 27 | }, 28 | label: Row( 29 | children: [ 30 | Icon(MdiIcons.clipboardFileOutline), 31 | SizedBox( 32 | width: 5, 33 | ), 34 | Text("Copy Text"), 35 | ], 36 | ), 37 | ) 38 | : null, 39 | appBar: AppBar( 40 | backgroundColor: Theme.of(context).canvasColor, 41 | elevation: 0, 42 | leading: IconButton( 43 | icon: Icon( 44 | Icons.arrow_back, 45 | color: AppTheme.textColor, 46 | ), 47 | onPressed: () { 48 | Navigator.of(context).pop(); 49 | }, 50 | ), 51 | ), 52 | body: Column( 53 | children: [ 54 | _buildMainWidget(context, width), 55 | Container(), 56 | ], 57 | ), 58 | ); 59 | } 60 | 61 | _buildMainWidget(context, width) { 62 | if (message.type == 0) { 63 | return _buildMessageWidget(context, width); 64 | } else { 65 | return _buildImageWidget(context, width); 66 | } 67 | } 68 | 69 | _buildImageWidget(context, width) { 70 | return Center( 71 | child: Stack( 72 | children: [ 73 | Padding( 74 | padding: const EdgeInsets.all(8.0), 75 | child: AnimatedContainer( 76 | duration: Duration(milliseconds: 500), 77 | decoration: BoxDecoration( 78 | borderRadius: BorderRadius.only( 79 | bottomLeft: Radius.circular(20.0), 80 | topLeft: Radius.circular(20.0), 81 | topRight: Radius.circular(20.0), 82 | ), 83 | color: Theme.of(context).cardColor, 84 | boxShadow: [ 85 | BoxShadow( 86 | blurRadius: 5.0, 87 | color: Colors.black38.withOpacity(0.05), 88 | offset: Offset(0.0, 3.0), 89 | spreadRadius: 5.0), 90 | ], 91 | ), 92 | constraints: BoxConstraints(maxWidth: width * 0.7), 93 | padding: EdgeInsets.only( 94 | top: 8.0, 95 | left: 8.0, 96 | right: 8.0, 97 | bottom: 5.0, 98 | ), 99 | child: Column( 100 | crossAxisAlignment: CrossAxisAlignment.end, 101 | children: [ 102 | ClipRRect( 103 | borderRadius: BorderRadius.only( 104 | bottomLeft: Radius.circular(20.0), 105 | topLeft: Radius.circular(20.0), 106 | topRight: Radius.circular(20.0), 107 | ), 108 | child: Image.network( 109 | message.imageUrl, 110 | height: 250, 111 | width: 300, 112 | fit: BoxFit.cover, 113 | ), 114 | ), 115 | SizedBox( 116 | height: 5.0, 117 | ), 118 | Text(_getFormattedDate(message.date, 'jms'), 119 | style: TextStyle( 120 | color: AppTheme.receivedTimeColor, 121 | fontSize: 10, 122 | fontWeight: FontWeight.w900)), 123 | ], 124 | ), 125 | ), 126 | ), 127 | Positioned.fill( 128 | child: Material( 129 | color: Colors.transparent, 130 | child: InkWell( 131 | onDoubleTap: () {}, 132 | ), 133 | )), 134 | ], 135 | ), 136 | ); 137 | } 138 | 139 | _buildMessageWidget(context, width) { 140 | return Container( 141 | child: Padding( 142 | padding: const EdgeInsets.only(left: 5.0, top: 5.0), 143 | child: ClipRRect( 144 | borderRadius: BorderRadius.only( 145 | bottomRight: Radius.circular(20.0), 146 | topLeft: Radius.circular(20.0), 147 | topRight: Radius.circular(20.0), 148 | ), 149 | child: Container( 150 | decoration: BoxDecoration(boxShadow: [ 151 | BoxShadow( 152 | blurRadius: 2.0, 153 | color: AppTheme.notSeen.withOpacity(0.05), 154 | offset: Offset(0.0, 2.0), 155 | spreadRadius: 1.0) 156 | ]), 157 | child: Padding( 158 | padding: const EdgeInsets.all(5.0), 159 | child: ClipRRect( 160 | borderRadius: BorderRadius.only( 161 | bottomRight: Radius.circular(20.0), 162 | topLeft: Radius.circular(20.0), 163 | topRight: Radius.circular(20.0), 164 | ), 165 | child: Stack( 166 | children: [ 167 | Container( 168 | constraints: BoxConstraints(maxWidth: width * 0.7), 169 | decoration: BoxDecoration( 170 | color: Theme.of(context).cardColor, 171 | ), 172 | padding: EdgeInsets.only( 173 | top: 15.0, 174 | left: 10.0, 175 | right: 15.0, 176 | bottom: 5.0, 177 | ), 178 | child: Column( 179 | crossAxisAlignment: CrossAxisAlignment.start, 180 | children: [ 181 | buildTextWithLinks(message.message), 182 | SizedBox( 183 | height: 5.0, 184 | ), 185 | Text(_getFormattedDate(message.date, 'jms'), 186 | style: TextStyle( 187 | color: Colors.white54, fontSize: 10)) 188 | ], 189 | ), 190 | ), 191 | ], 192 | ), 193 | ), 194 | ), 195 | ), 196 | ), 197 | ), 198 | ); 199 | } 200 | 201 | _getFormattedDate(DateTime date, String format) { 202 | var formatter = new DateFormat(format); 203 | String formatted = formatter.format(date); 204 | return formatted; 205 | } 206 | } 207 | 208 | Text buildTextWithLinks(String textToLink) => Text.rich(TextSpan( 209 | children: linkify(textToLink), 210 | style: TextStyle(fontSize: 16.0, fontFamily: AppTheme.fontFamily))); 211 | 212 | Future openUrl(String url) async { 213 | if (await canLaunch(url)) { 214 | await launch(url); 215 | } else { 216 | throw 'Could not launch $url'; 217 | } 218 | } 219 | 220 | const String urlPattern = r'(?:(?:https?|ftp):\/\/)?[\w/\-?=%.]+\.[\w/\-?=%.]+'; 221 | const String emailPattern = r'\S+@\S+'; 222 | const String phonePattern = r'[\d-]{9,}'; 223 | const String emojiPattern = 224 | r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])'; 225 | 226 | final RegExp linkRegExp = RegExp( 227 | '($urlPattern)|($emailPattern)|($phonePattern)|($emojiPattern)', 228 | caseSensitive: false); 229 | 230 | WidgetSpan buildEmoteComponent(String text) { 231 | return WidgetSpan( 232 | child: Text( 233 | text, 234 | style: TextStyle(fontSize: 30), 235 | )); 236 | } 237 | 238 | WidgetSpan buildLinkComponent(String text, String linkToOpen) => WidgetSpan( 239 | child: InkWell( 240 | child: SelectableText(text, 241 | style: TextStyle( 242 | fontSize: 16.0, 243 | decoration: TextDecoration.underline, 244 | color: AppTheme.linkColor)), 245 | onTap: () => openUrl(linkToOpen), 246 | )); 247 | 248 | List linkify(String text) { 249 | final List list = []; 250 | final RegExpMatch match = linkRegExp.firstMatch(text); 251 | if (match == null) { 252 | list.add(TextSpan( 253 | text: text, style: TextStyle(fontFamily: AppTheme.fontFamily))); 254 | return list; 255 | } 256 | 257 | if (match.start > 0) { 258 | list.add(TextSpan(text: text.substring(0, match.start))); 259 | } 260 | 261 | final String linkText = match.group(0); 262 | if (linkText.contains(RegExp(urlPattern, caseSensitive: false))) { 263 | list.add(buildLinkComponent(linkText, linkText)); 264 | } else if (linkText.contains(RegExp(emailPattern, caseSensitive: false))) { 265 | list.add(buildLinkComponent(linkText, 'mailto:$linkText')); 266 | } else if (linkText.contains(RegExp(phonePattern, caseSensitive: false))) { 267 | list.add(buildLinkComponent(linkText, 'tel:$linkText')); 268 | } else if (linkText.contains(RegExp(emojiPattern, caseSensitive: false))) { 269 | list.add(buildEmoteComponent(linkText)); 270 | } else { 271 | throw 'Unexpected match: $linkText'; 272 | } 273 | 274 | list.addAll(linkify(text.substring(match.start + linkText.length))); 275 | 276 | return list; 277 | } 278 | -------------------------------------------------------------------------------- /lib/message/searchPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_svg/flutter_svg.dart'; 3 | import 'package:mdi/mdi.dart'; 4 | import 'package:messaging_app_new/consts/theme.dart'; 5 | import 'package:messaging_app_new/message/searchRepo.dart'; 6 | import 'package:messaging_app_new/message/searchResultTileBuilder.dart'; 7 | 8 | class SearchPage extends StatefulWidget { 9 | @override 10 | _SearchPageState createState() => _SearchPageState(); 11 | } 12 | 13 | class _SearchPageState extends State { 14 | var errorText = ""; 15 | var height, width; 16 | GlobalKey scaffoldKey = GlobalKey(); 17 | bool isLoading = false; 18 | TextEditingController controller; 19 | var fabIndexSelected = 0; 20 | 21 | @override 22 | initState() { 23 | controller = TextEditingController(); 24 | super.initState(); 25 | _initializeStreams(); 26 | } 27 | 28 | _initializeStreams() { 29 | searchRepo.loading.listen((value) => setState(() { 30 | this.isLoading = value; 31 | })); 32 | 33 | controller.addListener(_queryOnBasisOfFab); 34 | } 35 | 36 | _queryOnBasisOfFab() { 37 | if (fabIndexSelected == 0) { 38 | if (controller.text.length >= 4) { 39 | _queryData(); 40 | } 41 | } else { 42 | if (controller.text.length >= 6) { 43 | _queryData(); 44 | } 45 | } 46 | } 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | height = MediaQuery.of(context).size.height; 51 | width = MediaQuery.of(context).size.width; 52 | 53 | return Scaffold( 54 | key: scaffoldKey, 55 | floatingActionButton: Column( 56 | mainAxisAlignment: MainAxisAlignment.end, 57 | children: [ 58 | FloatingActionButton.extended( 59 | heroTag: 'name', 60 | onPressed: () { 61 | setState(() { 62 | fabIndexSelected = 0; 63 | _queryOnBasisOfFab(); 64 | }); 65 | }, 66 | label: Row( 67 | children: [ 68 | Visibility( 69 | visible: fabIndexSelected == 0, 70 | child: Icon( 71 | Mdi.progressCheck, 72 | ), 73 | ), 74 | SizedBox( 75 | width: 5.0, 76 | ), 77 | Text("Name"), 78 | ], 79 | ), 80 | backgroundColor: Colors.black, 81 | ), 82 | SizedBox( 83 | height: 10, 84 | ), 85 | FloatingActionButton.extended( 86 | heroTag: 'Uid', 87 | onPressed: () { 88 | setState(() { 89 | fabIndexSelected = 1; 90 | _queryOnBasisOfFab(); 91 | }); 92 | }, 93 | label: Row( 94 | children: [ 95 | Visibility( 96 | visible: fabIndexSelected == 1, 97 | child: Icon( 98 | Mdi.progressCheck, 99 | ), 100 | ), 101 | SizedBox( 102 | width: 5.0, 103 | ), 104 | Text("UiD"), 105 | ], 106 | ), 107 | backgroundColor: Colors.black, 108 | ), 109 | ], 110 | ), 111 | appBar: _buildAppBar(), 112 | body: _buildBody(), 113 | ); 114 | } 115 | 116 | _buildBody() { 117 | return StreamBuilder( 118 | stream: searchRepo.searchResult.stream, 119 | builder: (BuildContext context, AsyncSnapshot snapshot) { 120 | if (isLoading) { 121 | return Center( 122 | child: CircularProgressIndicator(), 123 | ); 124 | } else { 125 | if (!snapshot.hasData || snapshot.data.length <= 0) { 126 | return ListView( 127 | children: [ 128 | Container( 129 | height: 0.8 * height, 130 | child: SvgPicture.asset("assets/search.svg"), 131 | ), 132 | Center(child: Text(errorText)), 133 | SizedBox( 134 | height: 10, 135 | ), 136 | ], 137 | ); 138 | } else if (snapshot.hasData) { 139 | return ListView.builder( 140 | itemBuilder: (context, index) { 141 | return SearchResultTileBuilder(snapshot.data[index]); 142 | }, 143 | itemCount: snapshot.data.length, 144 | ); 145 | } else { 146 | return Text(" id k "); 147 | } 148 | } 149 | }, 150 | ); 151 | } 152 | 153 | _buildAppBar() { 154 | return AppBar( 155 | elevation: 0, 156 | backgroundColor: Theme.of(context).canvasColor, 157 | automaticallyImplyLeading: false, 158 | title: TextField( 159 | autofocus: true, 160 | autocorrect: false, 161 | controller: controller, 162 | decoration: InputDecoration( 163 | border: UnderlineInputBorder(), 164 | hintText: 165 | "Search your friends ${fabIndexSelected == 0 ? 'name' : 'Uid'} here...", 166 | hintStyle: TextStyle(color: AppTheme.iconColor), 167 | ), 168 | ), 169 | actions: [ 170 | IconButton( 171 | onPressed: _queryData, 172 | icon: Icon( 173 | Mdi.shieldSearch, 174 | color: AppTheme.iconColor, 175 | ), 176 | ), 177 | IconButton( 178 | onPressed: () { 179 | Navigator.of(context).pop(); 180 | }, 181 | icon: Icon( 182 | Icons.close, 183 | color: AppTheme.iconColor, 184 | ), 185 | ), 186 | ], 187 | ); 188 | } 189 | 190 | _queryData() async { 191 | if (!(controller.text.length <= 0)) { 192 | if (fabIndexSelected == 0) { 193 | bool exists = 194 | await searchRepo.checkUserExistsByUserName(controller.text); 195 | if (!exists) { 196 | setState(() { 197 | errorText = "Check the name again"; 198 | }); 199 | } 200 | } else { 201 | if (controller.text != null) { 202 | print(" in search the value of uid : " + controller.text.toString()); 203 | bool exists = await searchRepo.checkIfUserExists(controller.text); 204 | if (exists != true) { 205 | setState(() { 206 | errorText = "Check the U-id and try again"; 207 | }); 208 | } 209 | if (exists) { 210 | int result = await searchRepo.getConfirmedUser(controller.text); 211 | if (result == 0) { 212 | setState(() { 213 | errorText = "Don't find yourself"; 214 | }); 215 | } 216 | } 217 | } 218 | } 219 | } else { 220 | scaffoldKey.currentState.showSnackBar(SnackBar( 221 | backgroundColor: Theme.of(context).cardColor, 222 | content: Text( 223 | "Enter a valid text", 224 | style: TextStyle(color: AppTheme.textColor), 225 | ), 226 | )); 227 | } 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /lib/message/searchRepo.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:messaging_app_new/message/buildErrorPage.dart'; 3 | import 'package:messaging_app_new/user/user.dart'; 4 | import '../data/sharedPrefs.dart'; 5 | 6 | import 'package:rxdart/rxdart.dart'; 7 | 8 | class SearchRepo { 9 | CollectionReference reference = Firestore.instance.collection("user"); 10 | 11 | PublishSubject> searchResult = PublishSubject(); 12 | PublishSubject loading = PublishSubject(); 13 | 14 | Future checkIfUserExists(String uid) async { 15 | loading.add(true); 16 | var snapshot = await reference.document(uid).get(); 17 | loading.add(false); 18 | if (snapshot == null || !snapshot.exists) { 19 | searchResult.add([]); 20 | return false; 21 | } 22 | return true; 23 | } 24 | 25 | Future checkUserExistsByUserName(String userName) async { 26 | loading.add(true); 27 | var documents = await reference.getDocuments(); 28 | var a = documents.documents.where((element) { 29 | return element.data['userName'] == userName; 30 | }); 31 | 32 | loading.add(false); 33 | 34 | List value = a.map((e) => User.fromSnapshot(e)).skipWhile((value) { 35 | return value.uid == sharedPrefs.getValueFromSharedPrefs('uid'); 36 | }).toList(); 37 | 38 | if (value.length <= 0) { 39 | searchResult.add([]); 40 | return false; 41 | } 42 | searchResult.add(value); 43 | return true; 44 | } 45 | 46 | Future getConfirmedUser(String uid) async { 47 | print(" in confirmed user + " + uid); 48 | loading.add(true); 49 | 50 | DocumentSnapshot a = 51 | await reference.document(uid).get(source: Source.serverAndCache); 52 | 53 | loading.add(false); 54 | User searchUser = User.fromSnapshot(a); 55 | if (searchUser.uid != sharedPrefs.getValueFromSharedPrefs("uid")) { 56 | searchResult.add([searchUser]); 57 | return 1; 58 | } else { 59 | searchResult.add([]); 60 | return 0; 61 | } 62 | } 63 | } 64 | 65 | SearchRepo searchRepo = SearchRepo(); 66 | -------------------------------------------------------------------------------- /lib/message/searchResultTileBuilder.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:messaging_app_new/data/sharedPrefs.dart'; 3 | import 'package:messaging_app_new/groupModel.dart'; 4 | import 'package:messaging_app_new/message/messagePage.dart'; 5 | import 'package:messaging_app_new/user/user.dart'; 6 | import '../consts/theme.dart'; 7 | 8 | class SearchResultTileBuilder extends StatelessWidget { 9 | final User user; 10 | 11 | SearchResultTileBuilder(this.user); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | var height = MediaQuery.of(context).size.height; 16 | return Padding( 17 | padding: const EdgeInsets.all(8.0), 18 | child: InkWell( 19 | borderRadius: BorderRadius.circular(15.0), 20 | onTap: () async { 21 | var model = GroupModel(participants: [ 22 | user.uid, 23 | sharedPrefs.getValueFromSharedPrefs('uid'), 24 | ], date: DateTime.now()); 25 | 26 | Navigator.of(context).push(MaterialPageRoute( 27 | builder: (context) => MessagePage(model, user))); 28 | }, 29 | child: Ink( 30 | height: 0.15 * height, 31 | child: Card( 32 | elevation: 0, 33 | shape: RoundedRectangleBorder( 34 | borderRadius: BorderRadius.circular(15.0)), 35 | color: AppTheme.mainColor.withOpacity(0.3), 36 | child: Row( 37 | mainAxisAlignment: MainAxisAlignment.start, 38 | crossAxisAlignment: CrossAxisAlignment.center, 39 | children: [ 40 | Padding( 41 | padding: const EdgeInsets.all(8.0), 42 | child: CircleAvatar( 43 | radius: 30, 44 | backgroundImage: NetworkImage(user.imageUrl), 45 | ), 46 | ), 47 | SizedBox( 48 | width: 5, 49 | ), 50 | Column( 51 | mainAxisAlignment: MainAxisAlignment.center, 52 | crossAxisAlignment: CrossAxisAlignment.start, 53 | children: [ 54 | Text( 55 | user.userName, 56 | ), 57 | Text( 58 | user.uid, 59 | ), 60 | Text( 61 | user.email, 62 | ) 63 | ], 64 | ), 65 | ], 66 | ), 67 | ), 68 | ), 69 | ), 70 | ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /lib/user/UserRepo.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | 3 | import 'package:messaging_app_new/user/user.dart'; 4 | 5 | class UserRepo { 6 | CollectionReference reference = Firestore.instance.collection("user"); 7 | 8 | Stream getStream() { 9 | return reference.snapshots(); 10 | } 11 | 12 | Stream getReferenceSnapshots(String uid) { 13 | return reference.document(uid).snapshots(); 14 | } 15 | 16 | Future addUser(User user) { 17 | // return reference.add(user.toJson()); 18 | return reference.document(user.uid).setData(user.toJson(), merge: false); 19 | } 20 | 21 | checkIfUserExists(String uid) async { 22 | var snapshot = await reference.document(uid).get(); 23 | if (snapshot == null || !snapshot.exists) { 24 | return false; 25 | } 26 | return true; 27 | } 28 | 29 | Future getConfirmedUser(String uid) async { 30 | return reference.document(uid).get(source: Source.server); 31 | } 32 | 33 | updateUser(User newUser) async { 34 | print("-----------------------------------URL{" + 35 | newUser.imageUrl.toString() + 36 | "}-------------------\n uid:{$newUser.uid}"); 37 | await Firestore.instance 38 | .collection('user') 39 | .document(newUser.uid) 40 | .updateData(newUser.toJson()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /lib/user/editProfile.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'package:messaging_app_new/user/UserRepo.dart'; 4 | import 'package:messaging_app_new/user/editProfileBuilder.dart'; 5 | import '../data/sharedPrefs.dart'; 6 | import '../consts/theme.dart'; 7 | import 'package:mdi/mdi.dart'; 8 | import '../Layout/useOfDataDialog.dart'; 9 | 10 | class EditProfile extends StatefulWidget { 11 | @override 12 | _EditProfileState createState() => _EditProfileState(); 13 | } 14 | 15 | class _EditProfileState extends State { 16 | var height, width; 17 | String uid; 18 | var scaffoldKey = GlobalKey(); 19 | 20 | @override 21 | void initState() { 22 | _getUId(); 23 | super.initState(); 24 | } 25 | 26 | _getUId() { 27 | dynamic val = sharedPrefs.getValueFromSharedPrefs("uid"); 28 | setState(() { 29 | this.uid = val; 30 | }); 31 | } 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | height = MediaQuery.of(context).size.height; 36 | width = MediaQuery.of(context).size.width; 37 | return Scaffold( 38 | key: scaffoldKey, 39 | appBar: AppBar( 40 | elevation: 0, 41 | backgroundColor: Theme.of(context).cardColor, 42 | title: Text( 43 | "Edit profile", 44 | style: TextStyle(color: AppTheme.textColor), 45 | ), 46 | centerTitle: true, 47 | actions: [ 48 | IconButton( 49 | icon: Icon( 50 | Icons.info, 51 | color: AppTheme.iconColor, 52 | ), 53 | onPressed: () { 54 | showDialog( 55 | context: context, 56 | barrierDismissible: true, 57 | builder: (context) => UseOfDataDialog(), 58 | ); 59 | }, 60 | ) 61 | ], 62 | automaticallyImplyLeading: false, 63 | leading: IconButton( 64 | icon: Icon( 65 | Mdi.chevronLeft, 66 | color: AppTheme.iconColor, 67 | ), 68 | onPressed: () => Navigator.of(context).pop(), 69 | ), 70 | ), 71 | body: StreamBuilder( 72 | stream: UserRepo().getReferenceSnapshots(uid), 73 | builder: (BuildContext context, AsyncSnapshot snapshot) { 74 | if (snapshot.hasData) { 75 | return EditProfileBuilder( 76 | snapshot: snapshot.data, scaffoldKey: scaffoldKey); 77 | } else if (snapshot.hasError || 78 | snapshot == null || 79 | snapshot.connectionState == ConnectionState.none) { 80 | return _errorBuilder(); 81 | } else 82 | return Center(child: CircularProgressIndicator()); 83 | }, 84 | )); 85 | } 86 | 87 | _errorBuilder() { 88 | return Center( 89 | child: Text("An error Occured"), 90 | ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /lib/user/editProfileBuilder.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:cloud_firestore/cloud_firestore.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:messaging_app_new/user/UserRepo.dart'; 5 | import 'package:messaging_app_new/user/user.dart'; 6 | import 'storage.dart'; 7 | import '../consts/theme.dart'; 8 | import 'package:fluttertoast/fluttertoast.dart'; 9 | import '../appData.dart'; 10 | 11 | class EditProfileBuilder extends StatefulWidget { 12 | final DocumentSnapshot snapshot; 13 | final GlobalKey scaffoldKey; 14 | EditProfileBuilder({this.snapshot, this.scaffoldKey}); 15 | @override 16 | _EditProfileBuilderState createState() => _EditProfileBuilderState(); 17 | } 18 | 19 | class _EditProfileBuilderState extends State { 20 | User user; 21 | 22 | var height, width; 23 | var userName, userEmail; 24 | String userImageUrl; 25 | 26 | var _formKey = GlobalKey(); 27 | var isImageLoading = false; 28 | var isLoading = false; 29 | 30 | UserRepo userRepo = UserRepo(); 31 | 32 | @override 33 | void initState() { 34 | user = User.fromSnapshot(widget.snapshot); 35 | userName = user.userName; 36 | userEmail = user.email; 37 | userImageUrl = user.imageUrl; 38 | super.initState(); 39 | 40 | storageService.imageUrl.listen((data) { 41 | print(data); 42 | setState(() { 43 | userImageUrl = data; 44 | User userObj = User( 45 | email: user.email, 46 | imageUrl: userImageUrl, 47 | reference: user.reference, 48 | uid: user.uid, 49 | userName: user.userName); 50 | userRepo.updateUser(userObj); 51 | setUser(userObj); 52 | }); 53 | }); 54 | 55 | storageService.editProfileIsLoading.listen((value) { 56 | print( 57 | '------------------- value of image is loading [$value] ------------------ '); 58 | if (value) { 59 | _showSnackBar( 60 | "Updating the image may take a while according to the size of the image selected."); 61 | } else { 62 | _showSnackBar("Updated the image"); 63 | } 64 | setState(() { 65 | isImageLoading = value; 66 | }); 67 | }); 68 | } 69 | 70 | _showSnackBar(String text) { 71 | widget.scaffoldKey.currentState.showSnackBar( 72 | SnackBar( 73 | backgroundColor: Theme.of(context).cardColor, 74 | content: Text( 75 | text, 76 | style: TextStyle( 77 | color: AppTheme.textColor, fontFamily: AppTheme.fontFamily), 78 | ), 79 | ), 80 | ); 81 | } 82 | 83 | @override 84 | Widget build(BuildContext context) { 85 | height = MediaQuery.of(context).size.height; 86 | width = MediaQuery.of(context).size.width; 87 | return Stack( 88 | children: [ 89 | Opacity( 90 | opacity: isLoading || isImageLoading ? 0.6 : 1.0, 91 | child: GestureDetector( 92 | onTap: () { 93 | FocusScope.of(context).requestFocus(FocusNode()); 94 | }, 95 | child: ListView( 96 | children: [ 97 | _buildTopWidget(), 98 | _buildForm(), 99 | ], 100 | ), 101 | ), 102 | ), 103 | Visibility( 104 | visible: isLoading || isImageLoading, 105 | child: Center( 106 | child: CircularProgressIndicator(), 107 | ), 108 | ), 109 | ], 110 | ); 111 | } 112 | 113 | _buildForm() { 114 | return Container( 115 | height: 0.525 * height, 116 | child: Form( 117 | key: _formKey, 118 | child: Column( 119 | children: [ 120 | SizedBox( 121 | height: 10.0, 122 | ), 123 | Padding( 124 | padding: const EdgeInsets.all(10.0), 125 | child: Column( 126 | children: [ 127 | Padding( 128 | padding: const EdgeInsets.all(8.0), 129 | child: Align( 130 | alignment: Alignment.centerLeft, 131 | child: Text("UserName")), 132 | ), 133 | Padding( 134 | padding: const EdgeInsets.only(left: 8.0), 135 | child: TextFormField( 136 | validator: (a) { 137 | if (a.length < 4) { 138 | return "Enter name with atleast 4 characters"; 139 | } 140 | return null; 141 | }, 142 | onSaved: (value) { 143 | setState(() { 144 | this.userName = value; 145 | }); 146 | }, 147 | initialValue: userName, 148 | decoration: InputDecoration()), 149 | ), 150 | ], 151 | ), 152 | ), 153 | Spacer(), 154 | _buildButton(), 155 | SizedBox( 156 | height: 0.05 * height, 157 | ), 158 | ], 159 | ), 160 | ), 161 | ); 162 | } 163 | 164 | _buildButton() { 165 | return OrientationBuilder( 166 | builder: (context, orientation) { 167 | return ButtonTheme( 168 | minWidth: width * 0.9, 169 | height: orientation == Orientation.landscape 170 | ? height * 0.5 171 | : height * 0.07, 172 | child: RaisedButton( 173 | shape: RoundedRectangleBorder( 174 | borderRadius: BorderRadius.circular(30.0)), 175 | color: AppTheme.accentColor, 176 | child: Text( 177 | " Save Details ", 178 | style: TextStyle(color: AppTheme.buttonTextColor), 179 | ), 180 | onPressed: () async { 181 | if (_formKey.currentState.validate()) { 182 | _formKey.currentState.save(); 183 | User newUser = User( 184 | email: user.email, 185 | userName: userName, 186 | imageUrl: userImageUrl, 187 | uid: user.uid); 188 | print(" in onPressed val: " + newUser.userName); 189 | setState(() { 190 | isLoading = true; 191 | }); 192 | await userRepo.updateUser(newUser); 193 | 194 | _showSnackBar("Updated data sucessfully"); 195 | 196 | setState(() { 197 | isLoading = false; 198 | }); 199 | } 200 | }, 201 | ), 202 | ); 203 | }, 204 | ); 205 | } 206 | 207 | _buildTopWidget() { 208 | return Container( 209 | child: Stack( 210 | children: [ 211 | Align( 212 | alignment: Alignment.topCenter, 213 | child: Container( 214 | // color: AppTheme.mainColor.withOpacity(0.3), 215 | color: Theme.of(context).cardColor, 216 | height: 0.22 * height, 217 | ), 218 | ), 219 | Center( 220 | child: Padding( 221 | padding: const EdgeInsets.all(30.0), 222 | child: GestureDetector( 223 | onTap: () async { 224 | var a = await storageService.pickFile(); 225 | if (a != null) { 226 | storageService.uploadFile(a); 227 | } 228 | }, 229 | child: Container( 230 | constraints: BoxConstraints( 231 | maxHeight: 0.25 * height, maxWidth: 0.25 * height), 232 | child: Stack( 233 | children: [ 234 | Center( 235 | child: CachedNetworkImage( 236 | imageUrl: userImageUrl, 237 | imageBuilder: (context, imageProvider) { 238 | return Container( 239 | decoration: BoxDecoration( 240 | // color: Theme.of(context).cardColor, 241 | color: AppTheme.mainColor, 242 | shape: BoxShape.circle, 243 | image: DecorationImage( 244 | image: imageProvider, fit: BoxFit.cover), 245 | ), 246 | ); 247 | }, 248 | placeholder: (context, url) => 249 | CircularProgressIndicator(), 250 | errorWidget: (context, url, error) => 251 | Icon(Icons.error), 252 | ), 253 | ), 254 | Positioned( 255 | right: 5, 256 | bottom: 5, 257 | child: FloatingActionButton( 258 | child: Icon( 259 | Icons.edit, 260 | color: Colors.white, 261 | ), 262 | onPressed: () async { 263 | var a = await storageService.pickFile(); 264 | if (a != null) { 265 | storageService.uploadFile(a); 266 | } 267 | }, 268 | ), 269 | ), 270 | ], 271 | ), 272 | ), 273 | ), 274 | ), 275 | ), 276 | ], 277 | ), 278 | ); 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /lib/user/storage.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:firebase_auth/firebase_auth.dart'; 3 | import 'package:firebase_storage/firebase_storage.dart'; 4 | import 'package:fluttertoast/fluttertoast.dart'; 5 | import 'package:image_picker/image_picker.dart'; 6 | import 'package:rxdart/rxdart.dart'; 7 | 8 | class StorageService { 9 | PublishSubject imageUrl = PublishSubject(); 10 | PublishSubject chatImageUrl = PublishSubject(); 11 | 12 | PublishSubject editProfileIsLoading = PublishSubject(); 13 | 14 | Future pickFile() async { 15 | try { 16 | PickedFile file = 17 | await ImagePicker().getImage(source: ImageSource.gallery); 18 | return file; 19 | } catch (e) { 20 | Fluttertoast.showToast(msg: "Select an image"); 21 | return null; 22 | } 23 | } 24 | 25 | Future uploadChatImage( 26 | PickedFile file, String documentID, DateTime time) async { 27 | try { 28 | StorageReference storageReference = FirebaseStorage.instance 29 | .ref() 30 | .child('chats/$documentID/${time.millisecondsSinceEpoch.toString()}'); 31 | File properFile = File(file.path); 32 | 33 | StorageUploadTask uploadTask = storageReference.putFile(properFile); 34 | await uploadTask.onComplete; 35 | print('File Uploaded'); 36 | var url = await storageReference.getDownloadURL(); 37 | 38 | chatImageUrl.add(url); 39 | return url; 40 | } catch (e) { 41 | Fluttertoast.showToast(msg: "An error occured while uploading the image"); 42 | return null; 43 | } 44 | } 45 | 46 | uploadFile(PickedFile file) async { 47 | editProfileIsLoading.add(true); 48 | FirebaseUser user = await FirebaseAuth.instance.currentUser(); 49 | StorageReference storageReference = 50 | FirebaseStorage.instance.ref().child('profiles/${user.uid}'); 51 | File properFile = File(file.path); 52 | 53 | StorageUploadTask uploadTask = storageReference.putFile(properFile); 54 | await uploadTask.onComplete; 55 | print('File Uploaded'); 56 | var url = await storageReference.getDownloadURL(); 57 | imageUrl.add(url); 58 | editProfileIsLoading.add(false); 59 | } 60 | } 61 | 62 | StorageService storageService = StorageService(); 63 | -------------------------------------------------------------------------------- /lib/user/user.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | 3 | class User { 4 | String uid; 5 | String userName; 6 | String email; 7 | String imageUrl; 8 | DocumentReference reference; 9 | 10 | User({ 11 | this.uid, 12 | this.userName, 13 | this.email, 14 | this.reference, 15 | this.imageUrl, 16 | }); 17 | 18 | factory User.fromSnapshot(DocumentSnapshot snapshot) { 19 | User user = User.fromJson(snapshot.data); 20 | user.reference = snapshot.reference; 21 | return user; 22 | } 23 | 24 | factory User.fromJson(Map json) { 25 | return User( 26 | imageUrl: json['imageUrl'] as String, 27 | email: json['email'] as String, 28 | uid: json['uid'] as String, 29 | userName: json['userName'] as String, 30 | ); 31 | } 32 | 33 | Map toJson() { 34 | return { 35 | 'email': email, 36 | 'userName': userName, 37 | 'imageUrl': imageUrl, 38 | 'uid': uid, 39 | }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/user/userInfoHelper.dart: -------------------------------------------------------------------------------- 1 | import 'package:cached_network_image/cached_network_image.dart'; 2 | import 'package:cloud_firestore/cloud_firestore.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_clipboard_manager/flutter_clipboard_manager.dart'; 5 | import 'package:mdi/mdi.dart'; 6 | import 'package:messaging_app_new/Layout/infoDialog.dart'; 7 | import 'package:messaging_app_new/user/user.dart'; 8 | import 'package:shimmer/shimmer.dart'; 9 | import '../consts/theme.dart'; 10 | 11 | class UserInfoHelper extends StatefulWidget { 12 | final DocumentSnapshot snapshot; 13 | 14 | UserInfoHelper({this.snapshot}); 15 | @override 16 | _UserInfoHelperState createState() => _UserInfoHelperState(); 17 | } 18 | 19 | class _UserInfoHelperState extends State { 20 | User user; 21 | var height, width; 22 | 23 | @override 24 | void initState() { 25 | user = User.fromSnapshot(widget.snapshot); 26 | super.initState(); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | height = MediaQuery.of(context).size.height; 32 | width = MediaQuery.of(context).size.width; 33 | return ListView( 34 | children: [ 35 | _buildTopWidget(), 36 | _buildRestPage(), 37 | SizedBox( 38 | height: 0.15 * height, 39 | ), 40 | _buildBottomPage(), 41 | ], 42 | ); 43 | } 44 | 45 | _buildBottomPage() { 46 | return Padding( 47 | padding: EdgeInsets.all(20.0), 48 | child: Container( 49 | height: 0.2 * height, 50 | child: Card( 51 | elevation: 0, 52 | shape: 53 | RoundedRectangleBorder(borderRadius: BorderRadius.circular(15.0)), 54 | color: AppTheme.mainColor.withOpacity(0.3), 55 | child: Stack( 56 | children: [ 57 | Align( 58 | alignment: Alignment.topRight, 59 | child: IconButton( 60 | icon: Icon(Icons.info), 61 | onPressed: () { 62 | showDialog( 63 | context: context, 64 | barrierDismissible: true, 65 | builder: (context) => InfoDialog()); 66 | }, 67 | ), 68 | ), 69 | Positioned( 70 | bottom: 5, 71 | right: 5, 72 | child: FloatingActionButton( 73 | child: Icon(Icons.content_copy), 74 | onPressed: () { 75 | FlutterClipboardManager.copyToClipBoard(user.uid) 76 | .then((result) { 77 | final snackBar = SnackBar( 78 | content: Text('Copied to Clipboard'), 79 | action: SnackBarAction( 80 | label: 'Undo', 81 | onPressed: () {}, 82 | ), 83 | ); 84 | Scaffold.of(context).showSnackBar(snackBar); 85 | }); 86 | }, 87 | ), 88 | ), 89 | Center( 90 | child: Padding( 91 | padding: const EdgeInsets.all(8.0), 92 | child: SelectableText( 93 | user.uid, 94 | style: TextStyle( 95 | fontSize: 25, fontFamily: AppTheme.fontFamily), 96 | ), 97 | ), 98 | ), 99 | ], 100 | ), 101 | ), 102 | ), 103 | ); 104 | } 105 | 106 | _buildRestPage() { 107 | return Padding( 108 | padding: const EdgeInsets.all(8.0), 109 | child: Container( 110 | decoration: BoxDecoration( 111 | color: Theme.of(context).cardColor, 112 | borderRadius: BorderRadius.circular(15.0), 113 | boxShadow: [ 114 | BoxShadow( 115 | blurRadius: 4.0, 116 | color: Colors.black38.withOpacity(0.1), 117 | offset: Offset(0.0, 3.0), 118 | spreadRadius: 3.0), 119 | ]), 120 | child: Row( 121 | mainAxisAlignment: MainAxisAlignment.start, 122 | children: [ 123 | Padding( 124 | padding: const EdgeInsets.all(8.0), 125 | child: ClipRRect( 126 | borderRadius: BorderRadius.circular(120.0), 127 | child: Container( 128 | height: 60, 129 | width: 60, 130 | color: AppTheme.mainColor, 131 | child: CachedNetworkImage( 132 | imageUrl: user.imageUrl, 133 | fit: BoxFit.cover, 134 | errorWidget: (context, url, error) => 135 | Icon(Mdi.alert, color: AppTheme.iconColor), 136 | placeholder: (context, url) => Shimmer.fromColors( 137 | child: Container( 138 | color: Colors.red, 139 | ), 140 | baseColor: AppTheme.shimmerBaseColor, 141 | highlightColor: AppTheme.shimmerEndingColor)), 142 | ), 143 | ), 144 | ), 145 | Padding( 146 | padding: const EdgeInsets.only(left: 10.0), 147 | child: Center( 148 | child: Text(user.userName), 149 | ), 150 | ), 151 | ], 152 | ), 153 | ), 154 | ); 155 | } 156 | 157 | _buildTopWidget() { 158 | return Stack( 159 | children: [ 160 | Align( 161 | alignment: Alignment.topCenter, 162 | child: Container( 163 | color: Theme.of(context).cardColor, 164 | height: 0.20 * height, 165 | ), 166 | ), 167 | Align( 168 | alignment: Alignment.topCenter, 169 | child: Padding( 170 | padding: const EdgeInsets.all(30.0), 171 | child: ClipRRect( 172 | borderRadius: BorderRadius.circular(120.0), 173 | child: Hero( 174 | tag: 'userImage', 175 | child: Container( 176 | height: 175, 177 | width: 175, 178 | color: AppTheme.mainColor, 179 | child: CachedNetworkImage( 180 | imageUrl: user.imageUrl, 181 | fit: BoxFit.cover, 182 | errorWidget: (context, url, error) => 183 | Icon(Mdi.alert, color: AppTheme.iconColor), 184 | placeholder: (context, url) => Shimmer.fromColors( 185 | child: Container( 186 | color: Colors.red, 187 | ), 188 | baseColor: AppTheme.shimmerBaseColor, 189 | highlightColor: AppTheme.shimmerEndingColor)), 190 | ), 191 | ), 192 | ), 193 | ), 194 | ), 195 | ], 196 | ); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /lib/user/userInfoPage.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:messaging_app_new/consts/theme.dart'; 3 | import 'package:messaging_app_new/user/UserRepo.dart'; 4 | import 'package:messaging_app_new/user/userInfoHelper.dart'; 5 | import '../data/sharedPrefs.dart'; 6 | import 'package:mdi/mdi.dart'; 7 | import '../Layout/useOfDataDialog.dart'; 8 | 9 | class UserInfo extends StatefulWidget { 10 | @override 11 | _UserInfoState createState() => _UserInfoState(); 12 | } 13 | 14 | class _UserInfoState extends State { 15 | String uid; 16 | 17 | _getUId() { 18 | dynamic val = sharedPrefs.getValueFromSharedPrefs("uid"); 19 | this.uid = val; 20 | } 21 | 22 | @override 23 | void initState() { 24 | _getUId(); 25 | super.initState(); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Scaffold( 31 | appBar: AppBar( 32 | automaticallyImplyLeading: false, 33 | leading: IconButton( 34 | icon: Icon( 35 | Mdi.chevronLeft, 36 | color: AppTheme.iconColor, 37 | ), 38 | onPressed: () => Navigator.of(context).pop(), 39 | ), 40 | actions: [ 41 | IconButton( 42 | icon: Icon( 43 | Icons.info, 44 | color: AppTheme.iconColor, 45 | ), 46 | onPressed: () { 47 | showDialog( 48 | context: context, 49 | barrierDismissible: true, 50 | builder: (context) => UseOfDataDialog()); 51 | }, 52 | ), 53 | ], 54 | elevation: 0, 55 | backgroundColor: Theme.of(context).cardColor, 56 | title: Text( 57 | "User info", 58 | style: TextStyle(color: AppTheme.iconColor), 59 | ), 60 | centerTitle: true, 61 | ), 62 | body: StreamBuilder( 63 | stream: UserRepo().getReferenceSnapshots(uid), 64 | builder: (BuildContext context, AsyncSnapshot snapshot) { 65 | if (snapshot.hasData) { 66 | return UserInfoHelper(snapshot: snapshot.data); 67 | } else if (snapshot.hasError || 68 | snapshot == null || 69 | snapshot.connectionState == ConnectionState.none) { 70 | return _errorBuilder(); 71 | } else 72 | return Center(child: CircularProgressIndicator()); 73 | }, 74 | ), 75 | ); 76 | } 77 | 78 | _errorBuilder() { 79 | return Center( 80 | child: Text("An error Occured"), 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /local.properties: -------------------------------------------------------------------------------- 1 | ## This file must *NOT* be checked into Version Control Systems, 2 | # as it contains information specific to your local configuration. 3 | # 4 | # Location of the SDK. This is only used by Gradle. 5 | # For customization when using a Version Control System, please read the 6 | # header note. 7 | #Sun Jul 05 20:28:27 IST 2020 8 | sdk.dir=C\:\\Users\\Aster\\AppData\\Local\\Android\\Sdk 9 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: messaging_app_new 2 | description: A new Flutter project. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 1.0.0+1 15 | 16 | environment: 17 | sdk: ">=2.1.0 <3.0.0" 18 | 19 | dependencies: 20 | flutter: 21 | sdk: flutter 22 | 23 | # The following adds the Cupertino Icons font to your application. 24 | # Use with the CupertinoIcons class for iOS style icons. 25 | cupertino_icons: ^0.1.2 26 | 27 | firebase_analytics: ^5.0.14 28 | firebase_auth: ^0.16.1 29 | firebase_core: ^0.4.5 30 | google_fonts: ^1.1.0 31 | rxdart: ^0.24.1 32 | fluttertoast: ^4.0.1 33 | firebase_storage: ^3.1.6 34 | image_picker: ^0.6.7 35 | cloud_firestore: ^0.13.5 36 | shared_preferences: ^0.5.7+3 37 | flutter_clipboard_manager: ^0.0.2 38 | flutter_svg: ^0.17.4 39 | intl: ^0.16.1 40 | material_design_icons_flutter: ^4.0.5345 41 | photo_view: ^0.9.2 42 | dynamic_theme: ^1.0.1 43 | shimmer: ^1.1.1 44 | mdi: ^3.0.0 45 | mvc_pattern: ^6.1.3+2 46 | url_launcher: ^5.4.10 47 | cached_network_image: ^2.2.0+1 48 | 49 | dev_dependencies: 50 | flutter_launcher_icons: "^0.7.3" 51 | flutter_test: 52 | sdk: flutter 53 | 54 | flutter_icons: 55 | android: true 56 | ios: true 57 | image_path: "assets/msg.png" 58 | # For information on the generic Dart part of this file, see the 59 | # following page: https://dart.dev/tools/pub/pubspec 60 | 61 | # The following section is specific to Flutter. 62 | flutter: 63 | # The following line ensures that the Material Icons font is 64 | # included with your application, so that you can use the icons in 65 | # the material Icons class. 66 | uses-material-design: true 67 | # To add assets to your application, add an assets section, like this: 68 | 69 | assets: 70 | - assets/. 71 | # - images/a_dot_ham.jpeg 72 | # An image asset can refer to one or more resolution-specific "variants", see 73 | # https://flutter.dev/assets-and-images/#resolution-aware. 74 | # For details regarding adding assets from package dependencies, see 75 | # https://flutter.dev/assets-and-images/#from-packages 76 | # To add custom fonts to your application, add a fonts section here, 77 | # in this "flutter" section. Each entry in this list should have a 78 | # "family" key with the font family name, and a "fonts" key with a 79 | # list giving the asset and other descriptors for the font. For 80 | # example: 81 | # fonts: 82 | # - family: Schyler 83 | # fonts: 84 | # - asset: fonts/Schyler-Regular.ttf 85 | # - asset: fonts/Schyler-Italic.ttf 86 | # style: italic 87 | # - family: Trajan Pro 88 | # fonts: 89 | # - asset: fonts/TrajanPro.ttf 90 | # - asset: fonts/TrajanPro_Bold.ttf 91 | # weight: 700 92 | # 93 | # For details regarding fonts from package dependencies, 94 | # see https://flutter.dev/custom-fonts/#from-packages 95 | -------------------------------------------------------------------------------- /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 | import 'package:messaging_app_new/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | --------------------------------------------------------------------------------