Repository: realdiganta/hitup-messenger
Branch: master
Commit: 2011fe3378e6
Files: 108
Total size: 251.3 KB
Directory structure:
gitextract_qp4kc1fo/
├── .gitignore
├── .metadata
├── LICENSE
├── README.md
├── android/
│ ├── .gitignore
│ ├── app/
│ │ ├── build.gradle
│ │ ├── google-services.json
│ │ └── src/
│ │ ├── debug/
│ │ │ └── AndroidManifest.xml
│ │ ├── main/
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── kotlin/
│ │ │ │ └── com/
│ │ │ │ └── digantakalita/
│ │ │ │ └── coocoo/
│ │ │ │ └── MainActivity.kt
│ │ │ └── res/
│ │ │ ├── drawable/
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ └── launch_background.xml
│ │ │ ├── mipmap-anydpi-v26/
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ └── values/
│ │ │ └── styles.xml
│ │ └── profile/
│ │ └── AndroidManifest.xml
│ ├── build.gradle
│ ├── gradle/
│ │ └── wrapper/
│ │ └── gradle-wrapper.properties
│ ├── gradle.properties
│ └── settings.gradle
├── ios/
│ ├── .gitignore
│ ├── Flutter/
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Runner/
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets/
│ │ │ ├── AppIcon.appiconset/
│ │ │ │ └── Contents.json
│ │ │ └── LaunchImage.imageset/
│ │ │ ├── Contents.json
│ │ │ └── README.md
│ │ ├── Base.lproj/
│ │ │ ├── LaunchScreen.storyboard
│ │ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── Runner-Bridging-Header.h
│ ├── Runner.xcodeproj/
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace/
│ │ │ ├── contents.xcworkspacedata
│ │ │ └── xcshareddata/
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ │ └── xcshareddata/
│ │ └── xcschemes/
│ │ └── Runner.xcscheme
│ └── Runner.xcworkspace/
│ ├── contents.xcworkspacedata
│ └── xcshareddata/
│ ├── IDEWorkspaceChecks.plist
│ └── WorkspaceSettings.xcsettings
├── lib/
│ ├── blocs/
│ │ ├── AddFriends/
│ │ │ ├── add_friends_bloc.dart
│ │ │ ├── add_friends_event.dart
│ │ │ └── add_friends_state.dart
│ │ ├── chats/
│ │ │ ├── chat_bloc.dart
│ │ │ ├── chat_event.dart
│ │ │ └── chat_state.dart
│ │ ├── contacts/
│ │ │ ├── contacts_bloc.dart
│ │ │ ├── contacts_event.dart
│ │ │ └── contacts_state.dart
│ │ ├── home/
│ │ │ ├── home_bloc.dart
│ │ │ ├── home_event.dart
│ │ │ └── home_state.dart
│ │ └── timer/
│ │ ├── timer_bloc.dart
│ │ ├── timer_event.dart
│ │ └── timer_state.dart
│ ├── config/
│ │ ├── Constants.dart
│ │ └── Paths.dart
│ ├── constants.dart
│ ├── functions/
│ │ ├── AddFriendsFunction.dart
│ │ ├── BaseFunctions.dart
│ │ ├── ChatFunction.dart
│ │ ├── MQTTFunction.dart
│ │ └── UserDataFunction.dart
│ ├── main.dart
│ ├── managers/
│ │ ├── db_manager.dart
│ │ └── mqtt_manager.dart
│ ├── models/
│ │ ├── ChatMessage.dart
│ │ ├── Conversation.dart
│ │ ├── MyContact.dart
│ │ ├── NonContact.dart
│ │ └── User.dart
│ ├── screens/
│ │ ├── ContactsHelpPage.dart
│ │ ├── account_screen.dart
│ │ ├── addFriends_screen.dart
│ │ ├── chat_screen.dart
│ │ ├── contacts_screen.dart
│ │ ├── enter_name_screen.dart
│ │ ├── friend_profile_screen.dart
│ │ ├── help_screen.dart
│ │ ├── home_screen.dart
│ │ ├── login_screen.dart
│ │ ├── otp_screen.dart
│ │ ├── profile_screen.dart
│ │ ├── settings_screen.dart
│ │ └── update_profile.dart
│ ├── splashscreen.dart
│ ├── stateProviders/
│ │ ├── mqtt_state.dart
│ │ ├── number_state.dart
│ │ └── profilePicUrlState.dart
│ ├── utils/
│ │ ├── Exceptions.dart
│ │ └── SharedObjects.dart
│ └── widgets/
│ ├── AddFriendCard.dart
│ ├── ChatCard.dart
│ ├── ChatItemWidget.dart
│ ├── ContactCard.dart
│ ├── ContactRowWidget.dart
│ ├── DangerCard.dart
│ ├── FriendRequestCard.dart
│ ├── GradientSnackBar.dart
│ ├── ImageFullScreenWidget.dart
│ ├── ListTileProfile.dart
│ ├── NameTextField.dart
│ ├── NoRequestsCard.dart
│ ├── NonContactCard.dart
│ ├── SentRequestCard.dart
│ └── SettingsTile.dart
├── pubspec.yaml
└── test/
└── widget_test.dart
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Web related
lib/generated_plugin_registrant.dart
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Exceptions to above rules.
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
================================================
FILE: .metadata
================================================
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: bbfbf1770cca2da7c82e887e4e4af910034800b6
channel: stable
project_type: app
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2020 Diganta Kalita
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
HitUp Messenger
A Fully Functioning Chat Messenger (like Whatsapp) built using Flutter.
## About the project
- Flutter for building the Android & IOS App.
- Firestore database for storing user data.
- Firebase Storage for storing images.
- [MQTT](https://www.hivemq.com/mqtt-essentials/) as messaging protocol hosted in AWS EC2.
- SQLite for storing contacts & chats in Local Database.
- [OneSignal](https://onesignal.com/) for Push Notifications.
## Features
### Super Fast Messaging using MQTT Protocol (MQTT is used by Facebook Messenger)
Architecture:
> When you send a message from the app, the message first goes to the MQTT server. Then the MQTT server sends the message directly to the device of the client who is supposed to receive it. When received, the client stores the message in the local sqlite database first then shows it to the chat screen. (Firestore is in no way used to store or send text messages. Firestore is used only to store user & contacts data). To learn more about MQTT please [refer here](https://www.hivemq.com/mqtt-essentials/).
### Phone Number Authentication Sign-in
Authentication is done using Firebase.
### Sending Images
Send & Receive Images (Snapchat style UI).
The images are not stored in Gallery. Instead they are stored in the local sqlite database in bytes format.
### Super Fast Loading of Messages (Using SQLite as Local DB to store messages)
Even when the client is offline, he/she can view the messages on opening the chat screen, because the messages are loaded directly from the local sqlite db. And that is the only place where the messages are stored. The MQTT server doesn't store old messages. The MQTT Server only stores the last message sent and then replaces it when a new message is sent.
### Sending texts to Phone Contacts
On the Contacts Screen, you will get a list of all your phone contacts who are also using the messenger and can chat with them. (Just like sending messages to your contacts in Whatsapp)
### Adding contacts using username. Sending & Receiving Friend Requests
You can also send friend request to someone using their username. On sending friend request, the other user will get a push notification & and a friend request. If he/she accepts the request then you will get an notification & will be able to chat with the user. (Same as the feature in Snapchat)
### Block Contact
### Realtime Push Notifications
Using OneSignal. User will get realtime push notifications (even when the app is closed) when
- He/She receives a new message
- He/She receives a friend request
- Someone accepts their friend request
### Change Profile Photo
### Image Compression Before Sending
### Emoji Support
### Neumorphic UI
### Invite Friends Feature
## Other Important Information
## Installation & Setup (Optional)
1. Firebase
> To change the firestore database, just replace the google-services.json in android/app to your own google-services.json file from your firebase account.
2. MQTT Server
> To transfer messages I am using an MQTT server which I have setup in a EC2 instance on AWS. For details on how to setup your own MQTT server please refer here : [Setup MQTT Server on AWS EC2](http://blog.yatis.io/install-secure-robust-mosquitto-mqtt-broker-aws-ubuntu/). Then change the serverAddress parameter in lib/functions/MQTTFunction.dart file, (connect() function) to the public address of your EC2 instance.
3. OneSignal
> First create an account in [OneSignal](https://onesignal.com/). Then replace the app_id parameter in lib/functions/UserDataFunction.dart sendNotification() function with your own OneSignal app_id.
## Motivation & Contribution
I want to build this messenger into the biggest open source messenger on the web with all functionalities from the top Messengers like Whatsapp, Telegram, Signal, Snapchat, etc. There a lot of features still to be added like end-to-end encryption, audio messages, audio/video calling, stories, etc. So, we are open to pull requests. Please contribute in any way you can, adding new features, finding or fixing bugs, adding to the documenation, better code commenting, etc.
## Contributing
If you want to contribute to this project other than by coding, please contact me here digantakalita.ai@gmail.com
## License
[MIT License](https://github.com/realdiganta/hitup-messenger/blob/master/LICENSE)
## Supporters
[](https://github.com/realdiganta/hitup-messenger/stargazers)
[](https://github.com/realdiganta/hitup-messenger/network/members)
================================================
FILE: android/.gitignore
================================================
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
================================================
FILE: android/app/build.gradle
================================================
buildscript {
repositories {
// ...
maven { url 'https://plugins.gradle.org/m2/' } // Gradle Plugin Portal
}
dependencies {
// OneSignal-Gradle-Plugin
classpath 'gradle.plugin.com.onesignal:onesignal-gradle-plugin:[0.12.6, 0.99.99]'
}
}
apply plugin: 'com.onesignal.androidsdk.onesignal-gradle-plugin'
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
apply plugin: 'com.google.gms.google-services'
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
compileSdkVersion 28
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.digantakalita.coocoo"
minSdkVersion 21
targetSdkVersion 29
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
}
}
}
flutter {
source '../..'
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.google.firebase:firebase-analytics:17.2.2'
}
================================================
FILE: android/app/google-services.json
================================================
{
"project_info": {
"project_number": "754179280048",
"firebase_url": "https://coocoo-private-fc1e0.firebaseio.com",
"project_id": "coocoo-private-fc1e0",
"storage_bucket": "coocoo-private-fc1e0.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:754179280048:android:05aeea871ac3c2980aff75",
"android_client_info": {
"package_name": "com.digantakalita.coocoo"
}
},
"oauth_client": [
{
"client_id": "754179280048-it11k86jo0o6paa7j49h5822chkn3bfb.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyCBIAEZd1QRl6aclVIvJ1Hwp53HRGz_cL0"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "754179280048-it11k86jo0o6paa7j49h5822chkn3bfb.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:754179280048:android:9879c8ab91f94f440aff75",
"android_client_info": {
"package_name": "com.digantakalita.coocooprivate"
}
},
"oauth_client": [
{
"client_id": "754179280048-7b457bisjiacvm9q5jtfc6v7qveldthc.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.digantakalita.coocooprivate",
"certificate_hash": "95d0565459f5ab2a868748443dbeddb9f31ab585"
}
},
{
"client_id": "754179280048-it11k86jo0o6paa7j49h5822chkn3bfb.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyCBIAEZd1QRl6aclVIvJ1Hwp53HRGz_cL0"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": [
{
"client_id": "754179280048-it11k86jo0o6paa7j49h5822chkn3bfb.apps.googleusercontent.com",
"client_type": 3
}
]
}
}
}
],
"configuration_version": "1"
}
================================================
FILE: android/app/src/debug/AndroidManifest.xml
================================================
================================================
FILE: android/app/src/main/AndroidManifest.xml
================================================
================================================
FILE: android/app/src/main/kotlin/com/digantakalita/coocoo/MainActivity.kt
================================================
package com.digantakalita.coocoo
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
================================================
FILE: android/app/src/main/res/drawable/ic_launcher_background.xml
================================================
================================================
FILE: android/app/src/main/res/drawable/launch_background.xml
================================================
================================================
FILE: android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
================================================
================================================
FILE: android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
================================================
================================================
FILE: android/app/src/main/res/values/styles.xml
================================================
================================================
FILE: android/app/src/profile/AndroidManifest.xml
================================================
================================================
FILE: android/build.gradle
================================================
buildscript {
ext.kotlin_version = '1.3.50'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.3.3'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}
================================================
FILE: android/gradle/wrapper/gradle-wrapper.properties
================================================
#Fri Jun 23 08:50:38 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
================================================
FILE: android/gradle.properties
================================================
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
android.useAndroidX=true
android.enableJetifier=true
================================================
FILE: android/settings.gradle
================================================
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
================================================
FILE: ios/.gitignore
================================================
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
================================================
FILE: ios/Flutter/AppFrameworkInfo.plist
================================================
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleExecutable
App
CFBundleIdentifier
io.flutter.flutter.app
CFBundleInfoDictionaryVersion
6.0
CFBundleName
App
CFBundlePackageType
FMWK
CFBundleShortVersionString
1.0
CFBundleSignature
????
CFBundleVersion
1.0
MinimumOSVersion
8.0
================================================
FILE: ios/Flutter/Debug.xcconfig
================================================
#include "Generated.xcconfig"
================================================
FILE: ios/Flutter/Release.xcconfig
================================================
#include "Generated.xcconfig"
================================================
FILE: ios/Runner/AppDelegate.swift
================================================
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
================================================
FILE: ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
================================================
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
================================================
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
================================================
FILE: ios/Runner/Base.lproj/LaunchScreen.storyboard
================================================
================================================
FILE: ios/Runner/Base.lproj/Main.storyboard
================================================
================================================
FILE: ios/Runner/Info.plist
================================================
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleName
coocoo
CFBundlePackageType
APPL
CFBundleShortVersionString
$(FLUTTER_BUILD_NAME)
CFBundleSignature
????
CFBundleVersion
$(FLUTTER_BUILD_NUMBER)
LSRequiresIPhoneOS
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
Main
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
UISupportedInterfaceOrientations~ipad
UIInterfaceOrientationPortrait
UIInterfaceOrientationPortraitUpsideDown
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
UIViewControllerBasedStatusBarAppearance
================================================
FILE: ios/Runner/Runner-Bridging-Header.h
================================================
#import "GeneratedPluginRegistrant.h"
================================================
FILE: ios/Runner.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
);
sourceTree = "";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = "";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.digantakalita.coocoo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.digantakalita.coocoo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = com.digantakalita.coocoo;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
================================================
FILE: ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
================================================
FILE: ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
IDEDidComputeMac32BitWarning
================================================
FILE: ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
================================================
PreviewsEnabled
================================================
FILE: ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
================================================
================================================
FILE: ios/Runner.xcworkspace/contents.xcworkspacedata
================================================
================================================
FILE: ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
================================================
IDEDidComputeMac32BitWarning
================================================
FILE: ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
================================================
PreviewsEnabled
================================================
FILE: lib/blocs/AddFriends/add_friends_bloc.dart
================================================
import 'dart:async';
import 'dart:convert';
import 'package:bloc/bloc.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:coocoo/config/Constants.dart';
import 'package:coocoo/config/Paths.dart';
import 'package:coocoo/functions/AddFriendsFunction.dart';
import 'package:coocoo/functions/BaseFunctions.dart';
import 'package:coocoo/functions/ChatFunction.dart';
import 'package:coocoo/functions/UserDataFunction.dart';
import 'package:coocoo/models/MyContact.dart';
import 'package:coocoo/utils/SharedObjects.dart';
import 'package:coocoo/widgets/GradientSnackBar.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
part 'add_friends_event.dart';
part 'add_friends_state.dart';
class AddFriendsBloc extends Bloc {
AddFriendsBloc() : super(AddFriendsInitial());
Firestore _firestore = Firestore.instance;
AddFriendsFunction addFriendsFunction = AddFriendsFunction();
ChatFunction chatFunction = ChatFunction();
UserDataFunction userDataFunction = UserDataFunction();
String myName = SharedObjects.prefs.getString(Constants.fullName);
@override
Stream mapEventToState(
AddFriendsEvent event,
) async* {
if (event is SearchHitUpIdEvent) {
yield* mapSearchHitUpIdEventToState(event);
}
if (event is AddButtonClickEvent) {
yield* mapAddButtonClickEventToState(event);
}
if (event is AcceptFriendRequestEvent) {
yield* mapAcceptFriendRequestEventToState(event);
}
if (event is DeclineFriendRequestEvent) {
yield* mapDeclineFriendRequestEventToState(event);
}
}
Stream mapDeclineFriendRequestEventToState(
DeclineFriendRequestEvent event) async* {
yield DecliningFriendRequestState();
await addFriendsFunction
.removeFromFriendRequestsCollection(event.phoneNumber);
addFriendsFunction.removeFromSentRequestsCollection(event.phoneNumber);
yield FriendRequestDeclinedState();
}
Stream mapAcceptFriendRequestEventToState(
AcceptFriendRequestEvent event) async* {
yield AcceptingFriendRequestState();
String chatId = await addFriendsFunction.addToLocalDBAndSubscribe(
event.context, event.phoneNumber);
chatFunction.sendMessageToServer(
event.context, 'Hi, I have accepted your friend request', chatId);
await addFriendsFunction
.removeFromFriendRequestsCollection(event.phoneNumber);
yield FriendRequestAcceptedState();
userDataFunction.sendNotification(toUid:event.phoneNumber, title: "New Notification", content: "$myName has accepted your friend request");
GradientSnackBar.showMessage(event.context, "Friend Request Accepted", 1);
addFriendsFunction.removeFromSentRequestsCollection(event.phoneNumber);
}
Stream mapAddButtonClickEventToState(
AddButtonClickEvent event) async* {
yield SendingFriendRequestState();
await addFriendsFunction.addToLocalDBAndSubscribe(
event.context, event.friend.phoneNumber);
await addFriendsFunction
.addToSentRequestsCollection(event.friend.phoneNumber);
yield FriendRequestSentState(event.friend);
userDataFunction.sendNotification(toUid:event.friend.phoneNumber, title: "New Notification", content: "$myName has sent you a friend request");
addFriendsFunction.addToFriendRequestsCollection(event.friend.phoneNumber);
}
Stream mapSearchHitUpIdEventToState(
SearchHitUpIdEvent event) async* {
yield SearchingHitUpIdState();
final HitUpIdLocation hitUpIdLocation =
await addFriendsFunction.checkHitUpId(event.hitUpId);
// if hitUpId does not exists in local db, friend requests & sent requests
if (hitUpIdLocation == HitUpIdLocation.Nowhere) {
QuerySnapshot snapshot = await _firestore
.collection(Paths.usersPath)
.where("username", isEqualTo: event.hitUpId)
.limit(1)
.getDocuments();
if (snapshot.documents.length > 0) {
DocumentSnapshot doc = snapshot.documents[0];
yield HitUpIdExistsState(MyContact.fromFireStore(doc));
} else {
yield HitUpIdNotExistsState(event.hitUpId);
}
} else {
yield HitUpIdAlreadyThere(event.hitUpId, hitUpIdLocation);
}
}
}
================================================
FILE: lib/blocs/AddFriends/add_friends_event.dart
================================================
part of 'add_friends_bloc.dart';
abstract class AddFriendsEvent extends Equatable {
const AddFriendsEvent();
}
class SearchHitUpIdEvent extends AddFriendsEvent {
final String hitUpId;
SearchHitUpIdEvent(this.hitUpId);
@override
List get props => [hitUpId];
}
class AddButtonClickEvent extends AddFriendsEvent {
final MyContact friend;
final BuildContext context;
AddButtonClickEvent(this.context, this.friend);
@override
List get props => [context, friend];
}
class AcceptFriendRequestEvent extends AddFriendsEvent {
final String phoneNumber;
final BuildContext context;
AcceptFriendRequestEvent(this.phoneNumber, this.context);
@override
List get props => [phoneNumber, context];
}
class DeclineFriendRequestEvent extends AddFriendsEvent{
final String phoneNumber;
DeclineFriendRequestEvent(this.phoneNumber);
@override
List get props => [phoneNumber];
}
================================================
FILE: lib/blocs/AddFriends/add_friends_state.dart
================================================
part of 'add_friends_bloc.dart';
abstract class AddFriendsState extends Equatable {
const AddFriendsState();
}
class AddFriendsInitial extends AddFriendsState {
@override
List get props => [];
}
class SearchingHitUpIdState extends AddFriendsState {
@override
List get props => [];
}
class HitUpIdExistsState extends AddFriendsState {
final MyContact friend;
HitUpIdExistsState(this.friend);
@override
List get props => [friend];
}
class HitUpIdNotExistsState extends AddFriendsState {
final String hitupId;
HitUpIdNotExistsState(this.hitupId);
@override
List get props => [hitupId];
}
class HitUpIdAlreadyThere extends AddFriendsState {
final String hitUpId;
final HitUpIdLocation hitUpIdLocation;
HitUpIdAlreadyThere(this.hitUpId, this.hitUpIdLocation);
@override
List get props => [hitUpId, hitUpIdLocation];
}
class SendingFriendRequestState extends AddFriendsState {
@override
List get props => [];
}
class FriendRequestSentState extends AddFriendsState {
final MyContact friend;
FriendRequestSentState(this.friend);
@override
List get props => [friend];
}
class AcceptingFriendRequestState extends AddFriendsState {
@override
List get props => [];
}
class FriendRequestAcceptedState extends AddFriendsState {
@override
List get props => [];
}
class DecliningFriendRequestState extends AddFriendsState{
@override
List get props => [];
}
class FriendRequestDeclinedState extends AddFriendsState{
@override
List get props => [];
}
================================================
FILE: lib/blocs/chats/chat_bloc.dart
================================================
import 'dart:async';
import 'dart:io';
import 'package:bloc/bloc.dart';
import 'package:coocoo/functions/ChatFunction.dart';
import 'package:coocoo/managers/db_manager.dart';
import 'package:coocoo/models/ChatMessage.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
part 'chat_event.dart';
part 'chat_state.dart';
class ChatBloc extends Bloc {
ChatBloc() : super(ChatInitial());
ChatFunction chatFunction = ChatFunction();
@override
Stream mapEventToState(
ChatEvent event,
) async* {
if (event is SendMessageEvent) {
chatFunction.sendMessageToServer(event.context, event.msg, event.chatId);
}
if (event is SendImageEvent) {
chatFunction.sendImageToServer(
event.context, event.imageFile, event.chatId);
}
if (event is ReceivedMessageEvent) {
List allMessages =
await chatFunction.getAllMsgsFromMessagesTable(event.chatId);
yield (ReceivedMessageState(allMessages));
}
if (event is LoadInitialMessagesEvent) {
List lastChatMsgs =
await chatFunction.getAllMsgsFromMessagesTable(event.chatId);
yield (InitialMessagesLoadedState(lastChatMsgs));
}
if (event is BlockUserEvent) {
chatFunction.blockUser(event.context, event.chatId);
yield (BlockedUserState());
DBManager.db.isBlocked(event.chatId);
}
if (event is UnblockUserEvent) {
chatFunction.unBlockUser(event.context, event.chatId);
yield (UnblockedUserState());
}
}
}
================================================
FILE: lib/blocs/chats/chat_event.dart
================================================
part of 'chat_bloc.dart';
abstract class ChatEvent extends Equatable {
const ChatEvent();
}
class SendMessageEvent extends ChatEvent {
final BuildContext context;
final String msg;
final String chatId;
SendMessageEvent(this.context, this.msg, this.chatId);
@override
List get props => [msg, chatId];
}
class SendImageEvent extends ChatEvent {
final BuildContext context;
final File imageFile;
final String chatId;
SendImageEvent(this.context, this.imageFile, this.chatId);
@override
List get props => [imageFile, chatId];
}
class ReceivedMessageEvent extends ChatEvent {
final String chatId;
ReceivedMessageEvent(this.chatId);
@override
List get props => [chatId];
}
class LoadInitialMessagesEvent extends ChatEvent {
final String chatId;
LoadInitialMessagesEvent(this.chatId);
@override
List get props => [chatId];
}
class BlockUserEvent extends ChatEvent{
final BuildContext context;
final String chatId;
BlockUserEvent(this.context, this.chatId);
@override
List get props => [context, chatId];
}
class UnblockUserEvent extends ChatEvent{
final BuildContext context;
final String chatId;
UnblockUserEvent(this.context, this.chatId);
@override
List get props => [context, chatId];
}
================================================
FILE: lib/blocs/chats/chat_state.dart
================================================
part of 'chat_bloc.dart';
abstract class ChatState extends Equatable {
final List chatMessages;
const ChatState(this.chatMessages);
}
class ChatInitial extends ChatState {
ChatInitial() : super([]);
@override
List get props => [];
}
class ReceivedMessageState extends ChatState {
final List chatMessages;
ReceivedMessageState(this.chatMessages) : super(chatMessages);
@override
List get props => [chatMessages];
}
class InitialMessagesLoadedState extends ChatState {
final List chatMessages;
InitialMessagesLoadedState(this.chatMessages) : super(chatMessages);
@override
List get props => [chatMessages];
}
class BlockedUserState extends ChatState {
BlockedUserState() : super([]);
@override
List get props => [];
}
class UnblockedUserState extends ChatState {
UnblockedUserState() : super([]);
@override
List get props => [];
}
================================================
FILE: lib/blocs/contacts/contacts_bloc.dart
================================================
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:coocoo/functions/UserDataFunction.dart';
import 'package:coocoo/models/MyContact.dart';
import 'package:equatable/equatable.dart';
part 'contacts_event.dart';
part 'contacts_state.dart';
class ContactsBloc extends Bloc {
ContactsBloc() : super(InitialContactsState());
UserDataFunction userDataFunction = UserDataFunction();
@override
Stream mapEventToState(
ContactsEvent event,
) async* {
if (event is FetchContactsEvent) {
try {
yield FetchingContactsState();
List contacts = await userDataFunction.getContactsFromDB();
add(ReceivedContactsEvent(contacts));
} on Exception catch (e) {
print(e);
}
}
if (event is ReceivedContactsEvent) {
yield FetchedContactsState(event.contacts);
}
}
}
================================================
FILE: lib/blocs/contacts/contacts_event.dart
================================================
part of 'contacts_bloc.dart';
abstract class ContactsEvent extends Equatable {
const ContactsEvent();
}
class FetchContactsEvent extends ContactsEvent {
@override
String toString() => 'FetchContactsEvent';
@override
List get props => [];
}
class ReceivedContactsEvent extends ContactsEvent {
final List contacts;
ReceivedContactsEvent(this.contacts);
@override
String toString() => "ReceivedContactsEvent";
@override
List get props => [contacts];
}
================================================
FILE: lib/blocs/contacts/contacts_state.dart
================================================
part of 'contacts_bloc.dart';
abstract class ContactsState extends Equatable {
const ContactsState();
}
class InitialContactsState extends ContactsState {
@override
String toString() => "InitialContactsState";
@override
List get props => [];
}
class FetchingContactsState extends ContactsState {
@override
String toString() => "FetchingContactsState";
@override
List get props => [];
}
class FetchedContactsState extends ContactsState {
final List contacts;
FetchedContactsState(this.contacts);
@override
String toString() => "FetchedContactsState";
@override
List get props => [contacts];
}
class ErrorState extends ContactsState {
// TODO: Implement errors
@override
List get props => [];
}
================================================
FILE: lib/blocs/home/home_bloc.dart
================================================
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:coocoo/functions/MQTTFunction.dart';
import 'package:coocoo/managers/db_manager.dart';
import 'package:coocoo/models/Conversation.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/cupertino.dart';
part 'home_event.dart';
part 'home_state.dart';
class HomeBloc extends Bloc {
HomeBloc() : super(HomeInitial());
MQTTFunction mqttFunction = MQTTFunction();
@override
Stream mapEventToState(
HomeEvent event,
) async* {
if (event is ConnectToServerEvent) {
try {
await mqttFunction.connect(event.context);
} catch (e) {
print("Couln't Connect to Server. Most Probably Internet is Off");
}
add(FetchHomeChatsEvent());
}
if (event is DisconnectEvent) {
mqttFunction.disconnect();
}
if (event is FetchHomeChatsEvent) {
yield* mapFetchHomeChatsEventToState();
}
}
Stream mapFetchHomeChatsEventToState() async* {
List conversations = [];
//TODO: Implement
List> dbData =
await DBManager.db.getAlConversationsFromChatTable();
dbData.forEach((dbMap) {
conversations.add(Conversation.fromMap(dbMap));
});
conversations.sort((b, a) => a.time.compareTo(b.time));
yield (FetchedHomeChatsState(conversations));
}
}
================================================
FILE: lib/blocs/home/home_event.dart
================================================
part of 'home_bloc.dart';
abstract class HomeEvent extends Equatable {
const HomeEvent();
}
class FetchHomeChatsEvent extends HomeEvent {
@override
List get props => [];
}
class ConnectToServerEvent extends HomeEvent {
final BuildContext context;
ConnectToServerEvent(this.context);
@override
List get props => [context];
}
class DisconnectEvent extends HomeEvent {
@override
List get props => [];
}
================================================
FILE: lib/blocs/home/home_state.dart
================================================
part of 'home_bloc.dart';
abstract class HomeState extends Equatable {
final List conversations;
const HomeState(this.conversations);
}
class HomeInitial extends HomeState {
HomeInitial() : super([]);
@override
List get props => [];
}
class FetchedHomeChatsState extends HomeState {
final List conversations;
FetchedHomeChatsState(this.conversations) : super(conversations);
@override
List get props => [conversations];
}
================================================
FILE: lib/blocs/timer/timer_bloc.dart
================================================
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:coocoo/config/Constants.dart';
import 'package:equatable/equatable.dart';
part 'timer_event.dart';
part 'timer_state.dart';
class TimerBloc extends Bloc {
TimerBloc() : super(TimerStoppedState());
Stream timerStream;
StreamSubscription timerSubscription;
@override
Stream mapEventToState(
TimerEvent event,
) async* {
if (event is StartTimerEvent) {
timerStream = stopWatchStream();
timerSubscription?.cancel();
timerSubscription = timerStream.listen((int newTick) {
add(TimerTickedEvent(newTick));
});
}
if (event is TimerTickedEvent) {
yield (TimerRunInProgressState(event.newTick));
}
if (event is StopTimerEvent) {
timerSubscription?.cancel();
timerStream = null;
yield (TimerStoppedState());
}
}
Stream stopWatchStream() {
StreamController streamController;
Timer timer;
Duration timerInterval = Duration(seconds: 1);
int counter = Constants.resendOtpTime;
void stopTimer() {
if (timer != null) {
timer.cancel();
timer = null;
counter = Constants.resendOtpTime;
streamController.close();
}
}
void tick(_) {
counter--;
streamController.add(counter);
if (counter < 1) {
add(StopTimerEvent());
}
}
void startTimer() {
timer = Timer.periodic(timerInterval, tick);
}
streamController = StreamController(
onListen: startTimer,
onCancel: stopTimer,
onResume: startTimer,
onPause: stopTimer,
);
return streamController.stream;
}
}
================================================
FILE: lib/blocs/timer/timer_event.dart
================================================
part of 'timer_bloc.dart';
abstract class TimerEvent extends Equatable {
const TimerEvent();
}
class StartTimerEvent extends TimerEvent {
@override
List get props => [];
}
class TimerTickedEvent extends TimerEvent {
final int newTick;
TimerTickedEvent(this.newTick);
@override
List get props => [newTick];
}
class StopTimerEvent extends TimerEvent {
@override
List get props => [];
}
================================================
FILE: lib/blocs/timer/timer_state.dart
================================================
part of 'timer_bloc.dart';
abstract class TimerState extends Equatable {
const TimerState();
}
class TimerInitial extends TimerState {
@override
List get props => [];
}
class TimerRunInProgressState extends TimerState {
final int newTick;
TimerRunInProgressState(this.newTick);
@override
List get props => [newTick];
}
class TimerStoppedState extends TimerState {
@override
List get props => [];
}
================================================
FILE: lib/config/Constants.dart
================================================
import 'package:flutter/material.dart';
class Constants {
static final Color stuffColor = Colors.blueAccent[400];
static final Color textStuffColor = Colors.blueGrey[700];
static final int resendOtpTime = 35;
static const firstRun = "firstRun";
static const sessionUid = "phoneNumber";
static const sessionUsername = 'sessionUsername';
static const fullName = 'fullName';
static const sessionName = 'sessionName';
static const sessionProfilePictureUrl = 'sessionProfilePictureUrl';
static const sessionCountryCode = 'sessionCountryCode';
static const configDarkMode = 'configDarkMode';
static String downloadsDirPath;
static String cacheDirPath;
static const profilePicChangeMsg = "profile_pic_change";
}
================================================
FILE: lib/config/Paths.dart
================================================
class Paths {
/*
Firebase paths
*/
static const String profilePicturePath = 'profile_pictures';
static const String imageAttachmentsPath = 'images';
static const String videoAttachmentsPath = 'videos';
static const String fileAttachmentsPath = 'files';
static const String usersPath = '/users';
static const String contactsPath = 'contacts';
static const String usernameUidMapPath = '/username_uid_map';
static const String chatsPath = '/chats';
static const String chat_messages = '/chat_messages';
static const String messagesPath = 'messages';
static const String friendRequestsPath = 'friend requests';
static const String sentRequestsPath = 'sent requests';
static const String MESSAGES_COLLECTION = "messages";
static const String USERS_COLLECTION = "users";
static const String CALL_COLLECTION = "call";
static const String TIMESTAMP_FIELD = "timestamp";
static const String EMAIL_FIELD = "email";
static const String MESSAGE_TYPE_IMAGE = "image";
}
================================================
FILE: lib/constants.dart
================================================
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:flutter_neumorphic/flutter_neumorphic.dart';
const kMobileTextFieldDecoration = InputDecoration(
hintText: 'Enter Mobile Number',
contentPadding: EdgeInsets.symmetric(vertical: 1.5),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFF6A1B9A),
width: 2.0,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFF6A1B9A),
width: 2.0,
),
),
focusedErrorBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Color(0xFF6A1B9A),
width: 2.0,
),
),
);
const kChatsGroupsTextStyle = TextStyle(
fontSize: 17.0,
fontWeight: FontWeight.w600,
);
const kCircleNeumorphicStyle = NeumorphicStyle(
shadowDarkColor: Colors.black,
shadowLightColor: Colors.white,
boxShape: NeumorphicBoxShape.circle(),
depth: 7,
intensity: 0.7,
surfaceIntensity: 0.6,
shape: NeumorphicShape.convex,
);
const kChatCircleNeumorphicStyle = NeumorphicStyle(
shadowDarkColor: Colors.black,
shadowLightColor: Colors.white,
boxShape: NeumorphicBoxShape.circle(),
depth: 5,
intensity: 0.65,
surfaceIntensity: 0.6,
);
const kRequestTitleStyle = TextStyle(
fontWeight: FontWeight.w700,
fontSize: 23.0,
);
const double perfectWidth = 411.4; // 683.4;
const kHitUpIdTextFieldDecoration = InputDecoration(
contentPadding: EdgeInsets.symmetric(vertical: 0.0, horizontal: 15.0),
hintText: 'Search Username',
fillColor: Colors.white,
filled: true,
border: OutlineInputBorder(
borderRadius: const BorderRadius.all(
Radius.circular(40.0),
),
),
focusedBorder: OutlineInputBorder(
borderRadius: const BorderRadius.all(
Radius.circular(40.0),
),
),
);
================================================
FILE: lib/functions/AddFriendsFunction.dart
================================================
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:coocoo/config/Constants.dart';
import 'package:coocoo/config/Paths.dart';
import 'package:coocoo/functions/BaseFunctions.dart';
import 'package:coocoo/functions/ChatFunction.dart';
import 'package:coocoo/managers/db_manager.dart';
import 'package:coocoo/managers/mqtt_manager.dart';
import 'package:coocoo/stateProviders/mqtt_state.dart';
import 'package:coocoo/utils/SharedObjects.dart';
import 'package:flutter/cupertino.dart';
import 'package:provider/provider.dart';
class AddFriendsFunction extends BaseAddFriendsFunction {
Firestore _firestore = Firestore.instance;
ChatFunction chatFunction = ChatFunction();
String uid = SharedObjects.prefs.getString(Constants.sessionUid);
@override
void dispose() {}
@override
Future addToFriendRequestsCollection(String phoneNumber) async {
// add to friend requests collection of the other user
CollectionReference usersRef = _firestore.collection(Paths.usersPath);
// first get my data
DocumentSnapshot snapshot = await usersRef.document(uid).get();
usersRef
.document(phoneNumber)
.collection(Paths.friendRequestsPath)
.document(uid)
.setData({
'username': snapshot.data['username'],
'photoUrl': snapshot.data['photoUrl'],
'name': snapshot.data['name'],
'phoneNumber': snapshot.data['phoneNumber'],
});
}
@override
Future addToLocalDBAndSubscribe(
BuildContext context, String phoneNumber) async {
DocumentSnapshot docSnapshot = await _firestore
.collection(Paths.usersPath)
.document(phoneNumber)
.get();
String chatId = await chatFunction.createChatIdForContact(phoneNumber);
// create a row for this user, i.e add the contact to my local db
String photoUrl = docSnapshot.data["photoUrl"];
String username = docSnapshot.data["username"];
String name = docSnapshot.data["name"];
await DBManager.db.createRow(phoneNumber, chatId, name, username, photoUrl,
0); // 0 because here this is not a phone contact
// Subscribe to the chatId
MQTTManager manager = context.read().manager;
print("SUBSCRIBING TO TOPIC : $chatId");
manager.subscribeTopic(chatId);
return chatId;
}
@override
Future addToSentRequestsCollection(String phoneNumber) async {
// add to my sent requests collection
CollectionReference usersRef = _firestore.collection(Paths.usersPath);
// first get the data for the phoneNumber
DocumentSnapshot snapshot = await usersRef.document(phoneNumber).get();
usersRef
.document(uid)
.collection(Paths.sentRequestsPath)
.document(phoneNumber)
.setData({
'username': snapshot.data['username'],
'photoUrl': snapshot.data['photoUrl'],
'name': snapshot.data['name'],
'phoneNumber': snapshot.data['phoneNumber'],
});
}
@override
Future checkHitUpId(String hitUpId) async {
bool ans;
// check if hitUpId exists in local db
ans = await DBManager.db.checkIfUsernameExistsInDb(hitUpId);
if (ans) {
return HitUpIdLocation.InLocalDb;
} else {
// check if hitUpId exists in Firebase Friend Requests collection
String uid = SharedObjects.prefs.getString(Constants.sessionUid);
QuerySnapshot snapshot = await _firestore
.collection(Paths.usersPath)
.document(uid)
.collection(Paths.friendRequestsPath)
.where('username', isEqualTo: hitUpId)
.limit(1)
.getDocuments();
if (snapshot.documents.length > 0) {
return HitUpIdLocation.InFriendRequests;
} else {
// check if hitUpId exists in Firebase Sent Requests Collection
snapshot = await _firestore
.collection(Paths.usersPath)
.document(uid)
.collection(Paths.sentRequestsPath)
.where('username', isEqualTo: hitUpId)
.limit(1)
.getDocuments();
if (snapshot.documents.length > 0) {
return HitUpIdLocation.InSentRequests;
} else {
return HitUpIdLocation.Nowhere;
}
}
}
}
@override
Future removeFromFriendRequestsCollection(String phoneNumber) async {
// remove from my friend requests collection
CollectionReference usersRef = _firestore.collection(Paths.usersPath);
await usersRef
.document(uid)
.collection(Paths.friendRequestsPath)
.document(phoneNumber)
.delete();
}
@override
void removeFromSentRequestsCollection(String phoneNumber) {
// remove from the other user's sent requests collection
CollectionReference usersRef = _firestore.collection(Paths.usersPath);
usersRef
.document(phoneNumber)
.collection(Paths.sentRequestsPath)
.document(uid)
.delete();
}
}
================================================
FILE: lib/functions/BaseFunctions.dart
================================================
import 'dart:io';
import 'package:coocoo/models/ChatMessage.dart';
import 'package:coocoo/models/MyContact.dart';
import 'package:coocoo/models/NonContact.dart';
import 'package:flutter/cupertino.dart';
import 'package:permission_handler/permission_handler.dart';
enum HitUpIdLocation {
InLocalDb,
InFriendRequests,
InSentRequests,
Nowhere,
}
abstract class BaseFunction {
void dispose();
}
abstract class BaseUserDataFunction extends BaseFunction {
Future> getContactsFromDB();
Future loadPhoneContactsV2(BuildContext context);
Future> loadNonContactsV2();
Future askContactPermissions();
void onShare(BuildContext context);
void sendNotification({String toUid, String title, String content});
Future verifyPhoneNumber(
BuildContext context, String phoneNum, Function verificationFailed);
}
abstract class BaseChatFunction extends BaseFunction {
Future createChatIdForContact(String contactPhoneNumber);
void sendMessageToServer(BuildContext context, String msg, String chatId);
void sendImageToServer(BuildContext context, File imageFile, String chatId);
void sendServiceMsgToServer(BuildContext context, String msg, String chatId);
Future> getAllMsgsFromMessagesTable(String chatId);
void blockUser(BuildContext context, String chatId);
void unBlockUser(BuildContext context, String chatId);
}
abstract class BaseMQTTFunction extends BaseFunction {
Future connect(BuildContext context);
void disconnect();
}
abstract class BaseUIFunction extends BaseFunction {
double cleanValue(double screenWidth, double value);
}
abstract class BaseAddFriendsFunction extends BaseFunction {
Future addToLocalDBAndSubscribe(
BuildContext context, String phoneNumber);
Future addToFriendRequestsCollection(String phoneNumber);
Future addToSentRequestsCollection(String phoneNumber);
Future checkHitUpId(String hitUpId);
Future removeFromFriendRequestsCollection(String phoneNumber);
void removeFromSentRequestsCollection(String phoneNumber);
}
================================================
FILE: lib/functions/ChatFunction.dart
================================================
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:coocoo/config/Constants.dart';
import 'package:coocoo/config/Paths.dart';
import 'package:coocoo/functions/BaseFunctions.dart';
import 'package:coocoo/managers/db_manager.dart';
import 'package:coocoo/managers/mqtt_manager.dart';
import 'package:coocoo/models/ChatMessage.dart';
import 'package:coocoo/stateProviders/mqtt_state.dart';
import 'package:coocoo/utils/SharedObjects.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
class ChatFunction extends BaseChatFunction {
Firestore _firestore = Firestore.instance;
MQTTManager _manager;
String myUid = SharedObjects.prefs.getString(Constants.sessionUid);
@override
Future createChatIdForContact(String contactPhoneNumber) async {
String chatId;
String uId = SharedObjects.prefs.getString(Constants.sessionUid);
CollectionReference usersCollection =
_firestore.collection(Paths.usersPath);
DocumentReference userRef = usersCollection.document(uId);
DocumentReference contactRef = usersCollection.document(contactPhoneNumber);
DocumentSnapshot userSnapshot = await userRef.get();
// if chatId doesn't exists for that contact then create new ChatId and update it
// for both the user and the contact
// else use the chatId which already exists
if (userSnapshot.data['chats'] == null ||
userSnapshot.data['chats'][contactPhoneNumber] == null) {
chatId = createChatId();
await userRef.setData({
'chats': {contactPhoneNumber: chatId}
}, merge: true);
await contactRef.setData({
'chats': {uId: chatId}
}, merge: true);
} else {
chatId = userSnapshot.data['chats'][contactPhoneNumber];
}
return chatId;
}
String createChatId() {
final Random _random = Random.secure();
final int idLength = 16; // length of the chatId
var values = List.generate(idLength, (i) => _random.nextInt(256));
return base64Url.encode(values);
}
@override
void dispose() {}
@override
void sendMessageToServer(BuildContext context, String msg, String chatId) {
if (_manager == null) {
_manager = context.read().manager;
}
print("calling send message to server");
Map msgMap = {"msg": "$msg", "type": "text", "uid": "$myUid"};
String msgToSend = json.encode(msgMap);
_manager.publish(msgToSend, chatId);
}
@override
Future> getAllMsgsFromMessagesTable(String chatId) async {
return await DBManager.db.readAllMessagesfromMessagesTable(chatId);
}
// 2. compress file and get file.
Future testCompressAndGetFile(File file) async {
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
var result = await FlutterImageCompress.compressAndGetFile(
file.absolute.path,
tempPath + "/temp.jpg",
minHeight: 400,
minWidth: 400,
quality: 80,
);
return result;
}
@override
void sendImageToServer(
BuildContext context, File imageFile, String chatId) async {
if (_manager == null) {
_manager = context.read().manager;
}
// compress the image & make it smaller in size
File newFile = await testCompressAndGetFile(imageFile);
// change the image to bytes format
String base64Image = base64Encode(newFile.readAsBytesSync());
String msgToSend =
'{"msg" : "$base64Image", "type" : "image", "uid" : "$myUid"}';
while (!_manager.getConnectionStatus()) {
print("NOT CONNECTED BEFORE PUBLISHING");
await Future.delayed(Duration(seconds: 1));
}
print("CONNECTED BEFORE PUBLISHING");
_manager.publish(msgToSend, chatId);
}
@override
void sendServiceMsgToServer(BuildContext context, String msg, String chatId) {
if (_manager == null) {
_manager = context.read().manager;
}
_manager.publish(msg, chatId);
}
@override
void blockUser(BuildContext context, String chatId) {
if (_manager == null) {
_manager = context.read().manager;
}
DBManager.db.updateBlockStatus(0, chatId);
_manager.unSubscribeTopic(chatId);
}
@override
void unBlockUser(BuildContext context, String chatId) {
if (_manager == null) {
_manager = context.read().manager;
}
DBManager.db.updateBlockStatus(1, chatId);
_manager.subscribeTopic(chatId);
}
}
================================================
FILE: lib/functions/MQTTFunction.dart
================================================
import 'package:coocoo/config/Constants.dart';
import 'package:coocoo/functions/BaseFunctions.dart';
import 'package:coocoo/managers/mqtt_manager.dart';
import 'package:coocoo/stateProviders/mqtt_state.dart';
import 'package:coocoo/utils/SharedObjects.dart';
import 'package:flutter/src/widgets/framework.dart';
import 'package:provider/provider.dart';
class MQTTFunction extends BaseMQTTFunction {
MQTTManager _manager;
String loggedInUser;
@override
Future connect(BuildContext context) async {
loggedInUser = SharedObjects.prefs.getString(Constants.sessionUid);
_manager = context.read().manager;
final String password = "S8x${loggedInUser.substring(1, 6)}S,.@";
if (_manager == null) {
_manager = MQTTManager(
serverAddress: "13.127.199.45",
clientName: loggedInUser,
context: context,
);
_manager.initializeMQTTClient();
// also set the mqtt_manager to the provider
context.read().setNewManager(_manager);
print(_manager);
}
print("Connecting to server");
await _manager.connect(loggedInUser, password);
}
@override
void dispose() {}
@override
void disconnect() {
if (_manager != null) {
_manager.disconnect();
}
}
}
================================================
FILE: lib/functions/UserDataFunction.dart
================================================
import 'dart:convert';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:contacts_service/contacts_service.dart';
import 'package:coocoo/config/Constants.dart';
import 'package:coocoo/config/Paths.dart';
import 'package:coocoo/functions/BaseFunctions.dart';
import 'package:coocoo/functions/ChatFunction.dart';
import 'package:coocoo/managers/db_manager.dart';
import 'package:coocoo/managers/mqtt_manager.dart';
import 'package:coocoo/models/MyContact.dart';
import 'package:coocoo/models/NonContact.dart';
import 'package:coocoo/stateProviders/mqtt_state.dart';
import 'package:coocoo/stateProviders/number_state.dart';
import 'package:coocoo/utils/SharedObjects.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
import 'package:share/share.dart';
class UserDataFunction extends BaseUserDataFunction {
Firestore _firestore = Firestore.instance;
ChatFunction chatFunction = ChatFunction();
@override
void dispose() {}
@override
Future> loadNonContactsV2() async {
List _nonContacts = [];
Iterable _contacts =
await ContactsService.getContacts(withThumbnails: false);
String countryCode =
SharedObjects.prefs.getString(Constants.sessionCountryCode);
await Future.forEach(_contacts, (_contact) async {
String tempNum = cleanNumber(_contact, countryCode);
if (tempNum != null) {
bool contactExists =
await DBManager.db.checkIfContactExistsInDb(tempNum);
// if contact does not exist in local db then add it to the nonContacts list
if (!contactExists) {
print(tempNum);
_nonContacts.add(NonContact(tempNum, name: _contact.displayName));
}
}
});
return _nonContacts;
}
@override
Future loadPhoneContactsV2(BuildContext context) async {
Iterable _contacts =
await ContactsService.getContacts(withThumbnails: false);
String countryCode =
SharedObjects.prefs.getString(Constants.sessionCountryCode);
try {
await Future.forEach(_contacts, (_contact) async {
String tempNum = cleanNumber(_contact, countryCode);
String contactName = _contact.displayName;
if (tempNum != null) {
bool contactExists =
await DBManager.db.checkIfContactExistsInDb(tempNum);
if (!contactExists) {
await addInstalledUserToDBANDSubscribe(
context, tempNum, contactName);
} else if (contactExists) {
// however if contact exists in local db but does not exist in primary firestore
// users database because he deleted his account then remove him from local db
removeUninstalledUserFromDB(context, tempNum);
}
}
});
} catch (e, s) {
print(s);
}
}
void removeUninstalledUserFromDB(BuildContext context, String tempNum) async {
final contactRef = _firestore.collection(Paths.usersPath).document(tempNum);
contactRef.get().then((docSnapshot) async {
if (!docSnapshot.exists) {
// delete the contact from local Database
await DBManager.db.deleteContact(tempNum);
}
});
}
Future addInstalledUserToDBANDSubscribe(
BuildContext context, String tempNum, String contactName) async {
final contactRef = _firestore.collection(Paths.usersPath).document(tempNum);
contactRef.get().then((docSnapshot) async {
// if the user has installed the app then add him as a contact to my db
// else do nothing
if (docSnapshot.exists) {
print("USER $tempNum EXISTS IN DB");
String chatId = await chatFunction.createChatIdForContact(tempNum);
// create a row for this user, i.e add the contact to my local db
String photoUrl = docSnapshot.data["photoUrl"];
String username = docSnapshot.data["username"];
await DBManager.db.createRow(
docSnapshot.documentID,
chatId,
contactName,
username,
photoUrl,
1); // true because here this is a phone contact
// Subscribe to the chatId
MQTTManager manager = context.read().manager;
print("SUBSCRIBING TO TOPIC : $chatId");
manager.subscribeTopic(chatId);
}
}).catchError((e) {
print("e");
});
}
dynamic cleanNumber(Contact dirtyNumber, String countryCode) {
// when we clean a number, we first remove all the white spaces and hyphens and then
// if the number does not has a country code,i.e, if its length is less than 11
// then we add the user's country code.
try {
String num = dirtyNumber.phones.first.value;
String num2 = num.replaceAll(RegExp(r"\D+"), '');
if (num2.length < 11) {
return "$countryCode$num2";
} else {
return num2;
}
} catch (e) {
return null;
}
}
@override
Future askContactPermissions() async {
PermissionStatus permissionStatus = await _getContactPermission();
return permissionStatus;
}
Future _getContactPermission() async {
PermissionStatus permission = await Permission.contacts.status;
if (permission != PermissionStatus.granted &&
permission != PermissionStatus.restricted) {
Map permissionStatus = await [
Permission.contacts,
].request();
return permissionStatus[Permission.contacts] ??
PermissionStatus.undetermined;
} else {
return permission;
}
}
@override
Future> getContactsFromDB() async {
List contacts = [];
List> dbData = await DBManager.db.getAllContacts();
dbData.forEach((map) {
contacts.add(MyContact.fromMap(map));
});
return contacts;
}
@override
void onShare(BuildContext context) async {
// A builder is used to retrieve the context immediately
// surrounding the RaisedButton.
//
// The context's `findRenderObject` returns the first
// RenderObject in its descendent tree when it's not
// a RenderObjectWidget. The RaisedButton's RenderObject
// has its position and size after it's built.
final RenderBox box = context.findRenderObject();
await Share.share(
"https://play.google.com/store/apps/details?id=com.digantakalita.coocoo",
subject: "Lets have our private chats on"
"this New Cool Messenger HitUp from now on. Its way safer than the others.",
sharePositionOrigin: box.localToGlobal(Offset.zero) & box.size);
}
@override
void sendNotification({String toUid, String title, String content}) async {
final String app_id = "98194ba4-9b9a-416b-ab0c-74b851af4f1a";
var body = jsonEncode({
"include_external_user_ids": [toUid],
"app_id": app_id,
"contents": {"en": content},
"headings": {"en": title},
"collapse_id": "123",
"android_channel_id": "0940813c-e319-4ff3-8d52-a202bf767b3a"
});
http.Response response = await http.post(
'https://onesignal.com/api/v1/notifications',
body: body,
headers: {
"content-type": "application/json",
"Authorization":
"Basic ZGY5MWZjZjctYmQ0My00ZDhjLTliYmItNzE0ZjlmOWVkYjYz"
});
if (response.statusCode == 200) {
print("NOTIFICATION SENT SUCCESSFULLY");
} else {
print('Notification Error | Error Code: ${response.statusCode}');
}
}
@override
Future verifyPhoneNumber(BuildContext context, String phoneNum,
Function verificationFailed) async {
final FirebaseAuth _auth = FirebaseAuth.instance;
await _auth.verifyPhoneNumber(
phoneNumber: phoneNum,
timeout: Duration(seconds: 30),
verificationCompleted: null,
verificationFailed: verificationFailed,
codeSent: (String verificationId, [int forceResendingToken]) async {
context.read().setOTP(verificationId);
},
codeAutoRetrievalTimeout: null,
);
}
}
================================================
FILE: lib/main.dart
================================================
import 'package:coocoo/blocs/AddFriends/add_friends_bloc.dart';
import 'package:coocoo/blocs/chats/chat_bloc.dart';
import 'package:coocoo/blocs/contacts/contacts_bloc.dart';
import 'package:coocoo/blocs/timer/timer_bloc.dart';
import 'package:coocoo/screens/home_screen.dart';
import 'package:coocoo/splashscreen.dart';
import 'package:coocoo/stateProviders/number_state.dart';
import 'package:coocoo/stateProviders/profilePicUrlState.dart';
import 'package:coocoo/utils/SharedObjects.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:provider/provider.dart';
import 'blocs/home/home_bloc.dart';
import 'stateProviders/mqtt_state.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
SharedObjects.prefs = await CachedSharedPreferences.getInstance();
runApp(MultiBlocProvider(
providers: [
BlocProvider(
create: (context) => ContactsBloc(),
),
BlocProvider(
create: (context) => ChatBloc(),
),
BlocProvider(
create: (context) => HomeBloc(),
),
BlocProvider(
create: (context) => AddFriendsBloc(),
),
BlocProvider(
create: (context) => TimerBloc(),
)
],
child: MyApp(),
));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => MQTTState()),
ChangeNotifierProvider(
create: (context) => NumberState(),
),
ChangeNotifierProvider(
create: (context) => ProfilePicUrlState()),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
title: 'HitUp Messenger',
routes: {
'/homeScreen': (context) => HomeScreen(),
},
theme: ThemeData(
primaryColor: Colors.white,
primarySwatch: Colors.blueGrey,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: SplashScreen(),
),
);
}
}
//Feature Ideas
//TODO: Mood of a user
//TODO: Online Offline...but only to the people whom I wanna show if I am online
//TODO: whether the user is active or not, i.e if she is inside your chat window or is she chatting with someone else
================================================
FILE: lib/managers/db_manager.dart
================================================
import 'package:coocoo/models/ChatMessage.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DBManager {
DBManager._();
static final DBManager db = DBManager._();
Database _database;
// final String contactsTable = "Contacts";
final String messagesTable = "Messages";
final String chatsTable = "Chats";
final String phoneNumberColumn = "phoneNumber";
final String chatIdColumn = "chatId";
final String nameColumn = 'name';
final String usernameColumn = 'username';
final String msgColumn = "msg";
final String timeColumn = "time";
final String photoUrlColumn = "photoUrl";
final String msgTypeColumn = 'msgType';
final String isContactColumn = 'isContact';
final String blockStatusColumn = 'blockStat'; // 0 => Blocked, 1 => Unblocked
final String msgStatusColumn = "msgStatus"; // 0=> Received, 1=> Sent
Future get database async {
if (_database != null) {
return _database;
}
_database = await initDB();
return _database;
}
initDB() async {
String dbPath = await getDatabasesPath();
return await openDatabase(
join(dbPath, "chat_database.db"),
version: 1,
// onCreate is called only if there was no prior database in the specified path
onCreate: (Database db, int version) async {
// we need 3 tables
// 1. for storing users data
// await db.execute(
// "CREATE TABLE $contactsTable ($phoneNumberColumn TEXT PRIMARY KEY, $chatIdColumn TEXT, "
// "$nameColumn TEXT, $usernameColumn TEXT,"
// "$photoUrlColumn TEXT, $isContactColumn INTEGER, $blockStatusColumn INTEGER)");
// 2. for storing the messages for the chat screen
await db.execute("CREATE TABLE $messagesTable ("
"$chatIdColumn TEXT, $msgColumn TEXT, $msgTypeColumn TEXT, "
"$timeColumn INTEGER, $msgStatusColumn INTEGER)");
// 3. for storing the last message to show in the home screen in the chat cards
await db.execute(
"CREATE TABLE $chatsTable ($phoneNumberColumn TEXT PRIMARY KEY, $nameColumn TEXT,"
"$chatIdColumn TEXT, $usernameColumn TEXT, $photoUrlColumn TEXT, $isContactColumn INTEGER, $blockStatusColumn INTEGER,"
" $msgColumn TEXT, $msgTypeColumn TEXT, "
"$timeColumn INTEGER)");
},
);
}
Future updateMessageToChatTable(
String chatId, String newMsg, String msgType, int currTime) async {
final Database db = await database;
int numOfUpdates = await db.rawUpdate(
"UPDATE $chatsTable SET $msgColumn = ?, $timeColumn = ?, $msgTypeColumn = ? WHERE $chatIdColumn = ?",
[newMsg, currTime, msgType, chatId]);
print("$numOfUpdates rows were changed in Chats Table");
}
Future addNewMessageToMessagesTable(String chatId, String newMsg,
String msgType, int currTime, int msgStatus) async {
final Database db = await database;
await db.insert(
messagesTable,
{
chatIdColumn: chatId,
msgColumn: newMsg,
msgTypeColumn: msgType,
timeColumn: currTime,
msgStatusColumn: msgStatus,
},
conflictAlgorithm: ConflictAlgorithm.ignore,
);
}
Future readMessageFromChatTable(String chatId) async {
final Database db = await database;
List> res = await db.query(chatsTable,
columns: [
msgColumn,
msgTypeColumn,
timeColumn,
chatIdColumn,
msgStatusColumn
],
where: "$chatIdColumn = ?",
whereArgs: [chatId]);
ChatMessage chatMessage = ChatMessage.fromMap(res[0]);
print("Most recent message read From Db :::::::: $chatMessage");
return chatMessage;
}
Future> readAllMessagesfromMessagesTable(
String chatId) async {
final Database db = await database;
var res = await db
.query(messagesTable, where: "$chatIdColumn = ?", whereArgs: [chatId]);
return res.map((e) => ChatMessage.fromMap(e)).toList();
}
// get all conversations from chat table to show in the home page
Future>> getAlConversationsFromChatTable() async {
final Database db = await database;
var res = await db.query(chatsTable, where: "$msgColumn IS NOT NULL");
List output = List.from(res);
return output;
}
Future updateProfilePicInChatsTable(
String photoUrl, String chatId) async {
final Database db = await database;
await db.rawUpdate(
"UPDATE $chatsTable SET $photoUrlColumn = ? WHERE $chatIdColumn = ?",
[photoUrl, chatId],
);
}
Future>> getAllContacts() async {
final Database db = await database;
var res = await db.query(
chatsTable,
columns: [
phoneNumberColumn,
nameColumn,
usernameColumn,
photoUrlColumn,
chatIdColumn
],
where: "$isContactColumn = ?",
whereArgs: [1],
);
List output = List.from(res);
return output;
}
Future createRow(String phoneNum, String chatId, String contactName,
String username, String photoUrl, int isContact) async {
final Database db = await database;
await db.insert(
chatsTable,
{
phoneNumberColumn: phoneNum,
chatIdColumn: chatId,
nameColumn: contactName,
usernameColumn: username,
photoUrlColumn: photoUrl,
isContactColumn: isContact,
blockStatusColumn: 1, // At first it will be unblocked ofcourse
msgColumn: null,
msgTypeColumn: null,
timeColumn: null,
},
conflictAlgorithm: ConflictAlgorithm.ignore,
);
}
Future deleteContact(String phoneNum) async {
final Database db = await database;
await db.delete(chatsTable,
where: "$phoneNumberColumn = ?", whereArgs: [phoneNum]);
}
Future checkIfContactExistsInDb(String phoneNumber) async {
final Database db = await database;
var result = await db.rawQuery(
"SELECT COUNT(1) FROM $chatsTable WHERE $phoneNumberColumn = ? LIMIT 1",
[phoneNumber],
);
return result[0]["COUNT(1)"] == 1 ? true : false;
}
Future checkIfUsernameExistsInDb(String username) async {
final Database db = await database;
var result = await db.rawQuery(
"SELECT COUNT(1) FROM $chatsTable WHERE $usernameColumn = ? LIMIT 1",
[username],
);
return result[0]["COUNT(1)"] == 1 ? true : false;
}
Future deleteTable() async {
final Database db = await database;
await db.delete(chatsTable, where: '1');
}
// check if contact is blocked or unblocked
Future isBlocked(String chatId) async {
final Database db = await database;
var res = await db.query(chatsTable,
columns: [blockStatusColumn],
where: '$chatIdColumn = ?',
whereArgs: [chatId]);
return (res[0]["blockStat"] == 1 ? false : true);
}
Future updateBlockStatus(int newStatus, String chatId) async {
final Database db = await database;
await db.rawUpdate(
"UPDATE $chatsTable SET $blockStatusColumn = ? WHERE $chatIdColumn = ?",
[newStatus, chatId],
);
}
Future updateName(String newName, String chatId) async {
final Database db = await database;
await db.rawUpdate(
"UPDATE $chatsTable SET $nameColumn = ? WHERE $chatIdColumn = ?",
[newName, chatId],
);
}
}
================================================
FILE: lib/managers/mqtt_manager.dart
================================================
import 'dart:convert';
import 'package:coocoo/blocs/chats/chat_bloc.dart';
import 'package:coocoo/blocs/home/home_bloc.dart';
import 'package:coocoo/config/Constants.dart';
import 'package:coocoo/stateProviders/mqtt_state.dart';
import 'package:coocoo/utils/SharedObjects.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqtt_client/mqtt_server_client.dart';
import 'package:provider/provider.dart';
import 'db_manager.dart';
class MQTTManager {
MQTTManager({
@required this.serverAddress,
@required this.clientName,
@required this.context,
});
final String serverAddress;
final String clientName;
final BuildContext context;
MqttServerClient _client;
ChatBloc chatBloc;
HomeBloc homeBloc;
void initializeMQTTClient() {
_client = MqttServerClient(serverAddress, clientName);
_client.port = 1883;
_client.keepAlivePeriod = 43200;
_client.logging(on: false);
_client.onDisconnected = onDisconnected;
_client.onConnected = onConnect;
// auto reconnecting when client is disconnected to server
_client.autoReconnect = true;
final connMess = MqttConnectMessage()
.withClientIdentifier(clientName)
.keepAliveFor(
43200) // Must agree with the keep alive set above or not set
.withWillTopic(
'willtopic') // If you set this you must set a will message
.withWillMessage('My Will message')
.withWillRetain()
.withWillQos(MqttQos.atLeastOnce);
_client.connectionMessage = connMess;
}
void disconnect() {
_client.disconnect();
}
bool getConnectionStatus() {
if (_client.connectionStatus.state == MqttConnectionState.connected) {
return true;
} else {
return false;
}
}
Future connect(String username, String password) async {
try {
await _client.connect(username, password);
} on Exception catch (e) {
print('EXAMPLE::client exception - $e');
_client.disconnect();
}
}
void subscribeTopic(String topic) {
_client.subscribe(topic, MqttQos.atLeastOnce);
/// The client has a change notifier object(see the Observable class) which we then listen to to get
/// notifications of published updates to each subscribed topic.
}
void unSubscribeTopic(String topic) {
_client.unsubscribe(topic);
}
Stream>> get messageStream =>
_client.updates;
void publish(String myMessage, String topic) {
final builder = MqttClientPayloadBuilder();
builder.addString(myMessage);
_client.publishMessage(topic, MqttQos.atLeastOnce, builder.payload,
retain: true);
}
void onDisconnected() {
print('Client Disconnect');
}
void onConnect() {
chatBloc = BlocProvider.of(context);
homeBloc = BlocProvider.of(context);
_client.updates.listen((List> c) async {
final MqttPublishMessage recMess = c[0].payload;
final String chatId = c[0].topic;
final msg =
MqttPublishPayload.bytesToStringAsString(recMess.payload.message);
print("THIS IS THE MESSAGE BEFORE DECODING");
print(msg);
Map parsedMsg = json.decode(msg);
final int currTime = DateTime.now().millisecondsSinceEpoch;
String myUid = SharedObjects.prefs.getString(Constants.sessionUid);
if (parsedMsg["type"] == "service") {
// mqtt message if any of my contacts changed his/her profile picture
if (parsedMsg["msg"] == Constants.profilePicChangeMsg) {
if (parsedMsg["uid"] != myUid) {
print("Profile Pic Changed For $chatId");
await DBManager.db.updateProfilePicInChatsTable(
parsedMsg["profilePicUrl"], chatId);
homeBloc.add(FetchHomeChatsEvent());
}
}
} else {
// save message to local db
print(parsedMsg["uid"]);
print(myUid);
if (parsedMsg["uid"] == myUid) {
// if uid of message is same as mine then that means
// I sent the message, so save it to db as sent message
print("seetting messages to local db 1");
await DBManager.db.addNewMessageToMessagesTable(
chatId,
parsedMsg["msg"],
parsedMsg["type"],
currTime,
1); // 1 bcox message is sent
} else {
print("seetting messages to local db 0");
// save the message to local db as received message
await DBManager.db.addNewMessageToMessagesTable(
chatId,
parsedMsg["msg"],
parsedMsg["type"],
currTime,
0); // 0 bcox message is received
}
DBManager.db.updateMessageToChatTable(
chatId,
parsedMsg["msg"],
parsedMsg["type"],
currTime,
);
//also notify the bloc that a new message is received so that it
// may read the last message from the local db
chatBloc.add(ReceivedMessageEvent(chatId));
// fetching the chatcards for the homePage
homeBloc.add(FetchHomeChatsEvent());
}
});
}
}
================================================
FILE: lib/models/ChatMessage.dart
================================================
class ChatMessage {
final String msg;
final int time;
final String msgType;
// final int msgStatus;
final String chatId;
final bool isSelf;
ChatMessage({this.msg, this.time, this.msgType, this.chatId, this.isSelf});
factory ChatMessage.fromMap(Map map) {
return ChatMessage(
msg: map["msg"],
time: map["time"],
msgType: map["msgType"],
chatId: map["chatId"],
isSelf: map["msgStatus"] == 1 ? true : false,
);
}
@override
String toString() => "msg : $msg, time : $time, "
"msgType : $msgType, chatId : $chatId, isSelf: $isSelf";
}
================================================
FILE: lib/models/Conversation.dart
================================================
class Conversation {
final String phoneNumber;
final String lastMessage;
final String msgType;
final int time;
final String photoUrl;
final String chatId;
final String name;
final String username;
Conversation({
this.phoneNumber,
this.lastMessage,
this.msgType,
this.time,
this.photoUrl,
this.chatId,
this.name,
this.username,
});
factory Conversation.fromMap(Map map) {
// here map is the map from the sqflite database
return Conversation(
phoneNumber: map["phoneNumber"],
lastMessage: map["msg"],
msgType: map["msgType"],
time: map["time"],
photoUrl: map["photoUrl"],
chatId: map["chatId"],
name: map['name'],
username: map['username'],
);
}
@override
String toString() => "phoneNumber : $phoneNumber,"
"lastMessage : $lastMessage, msgType : $msgType, time : $time, photoUrl : $photoUrl, "
"chatId : $chatId";
}
================================================
FILE: lib/models/MyContact.dart
================================================
import 'package:cloud_firestore/cloud_firestore.dart';
class MyContact {
final String phoneNumber;
final String name;
final String username;
final String photoUrl;
final String chatId;
final int ind;
MyContact(
{this.phoneNumber,
this.name,
this.username,
this.photoUrl,
this.chatId,
this.ind});
factory MyContact.fromFireStore(DocumentSnapshot snapshot) {
var data = snapshot.data;
return MyContact(
phoneNumber: data['phoneNumber'] ?? snapshot.documentID,
name: data['name'],
photoUrl: data['photoUrl'],
username: data['username'],
chatId: data['chatId']);
}
factory MyContact.fromMap(Map data) {
return MyContact(
phoneNumber: data['phoneNumber'],
name: data['name'],
photoUrl: data['photoUrl'],
username: data['username'],
chatId: data['chatId']);
}
@override
String toString() {
return "index : ${this.ind}, phoneNumber : ${this.phoneNumber}, "
"name : ${this.name} photoUrl : ${this.photoUrl}, "
"chatId : ${this.chatId}, username : ${this.username}";
}
}
================================================
FILE: lib/models/NonContact.dart
================================================
import 'package:equatable/equatable.dart';
class NonContact extends Equatable {
final String name;
final String phoneNumber;
NonContact(this.phoneNumber, {this.name});
@override
List get props => [phoneNumber];
}
================================================
FILE: lib/models/User.dart
================================================
import 'package:cloud_firestore/cloud_firestore.dart';
class User {
String documentId;
String phoneNumber;
String username;
String photoUrl;
User({this.documentId, this.phoneNumber, this.username, this.photoUrl});
factory User.fromFirestore(DocumentSnapshot doc) {
Map data = doc.data;
return User(
documentId: doc.documentID,
phoneNumber: data['phoneNumber'],
username: data['username'],
photoUrl: data['photoUrl']);
}
factory User.fromMap(Map data) {
return User(
documentId: data['uid'],
phoneNumber: data['phoneNumber'],
username: data['username'],
photoUrl: data['photoUrl']);
}
@override
String toString() {
return '{ documentId: $documentId, phoneNumb: $phoneNumber, username: $username, photoUrl: $photoUrl }';
}
}
================================================
FILE: lib/screens/ContactsHelpPage.dart
================================================
import 'package:coocoo/constants.dart';
import 'package:flutter/material.dart';
class ContactsHelpPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final double screenWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
title: Text('Contacts Help'),
),
body: Container(
padding: EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
" If some of your friends don't appear in\n the contacts"
" list, we recommend\n the following steps:",
style: TextStyle(
fontSize: (screenWidth / perfectWidth) * 18.0,
fontWeight: FontWeight.w400,
),
),
SizedBox(height: 8.0),
Text(
"1. Click on the Refresh Button on the\n "
" Top right hand side of the page and "
"\n check for the contact again.",
style: TextStyle(
fontSize: (screenWidth / perfectWidth) * 18.0,
fontWeight: FontWeight.w400,
),
),
SizedBox(height: 8.0),
Text(
"2. Make sure that your friend is using\n HitUp Messenger",
style: TextStyle(
fontSize: (screenWidth / perfectWidth) * 18.0,
fontWeight: FontWeight.w400,
),
),
SizedBox(height: 8.0),
Text(
"3. Make sure that your friend's phone number\n is in your "
"address book",
style: TextStyle(
fontSize: (screenWidth / perfectWidth) * 18.0,
fontWeight: FontWeight.w400,
),
),
],
),
),
);
}
}
================================================
FILE: lib/screens/account_screen.dart
================================================
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:coocoo/screens/profile_screen.dart';
import 'package:coocoo/config/Constants.dart';
import 'package:coocoo/config/Paths.dart';
import 'package:coocoo/managers/db_manager.dart';
import 'package:coocoo/utils/SharedObjects.dart';
import 'package:coocoo/widgets/DangerCard.dart';
import 'package:coocoo/widgets/SettingsTile.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import '../splashscreen.dart';
class AccountScreen extends StatefulWidget {
@override
_AccountScreenState createState() => _AccountScreenState();
}
class _AccountScreenState extends State {
final Firestore _firestore = Firestore.instance;
FirebaseAuth _auth = FirebaseAuth.instance;
bool loading = false;
Future _showDialogBox() async {
return (await showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Are you sure?'),
content: Text('You want to delete your HitUp account?'),
actions: [
FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text('No'),
),
FlatButton(
onPressed: () async {
setState(() {
loading = true;
});
String userPhone =
SharedObjects.prefs.getString(Constants.sessionUid);
try {
_firestore
.collection(Paths.usersPath)
.document(userPhone)
.delete();
} catch (e) {
print(e);
}
DBManager.db.deleteTable();
await SharedObjects.prefs.clearAll();
Navigator.push(context,
MaterialPageRoute(builder: (context) => SplashScreen()));
setState(() {
loading = false;
});
},
child: Text('Yes Delete'),
),
],
),
)) ??
false;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Account'),
),
body: !loading
? Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SettingsTile(
icon: Icons.account_circle,
title: 'My Account',
onPress: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProfileScreen()));
},
),
DangerCard(Colors.red, 'Delete Account', () {
_showDialogBox();
}),
],
),
)
: Center(child: CircularProgressIndicator()),
);
}
}
================================================
FILE: lib/screens/addFriends_screen.dart
================================================
import 'dart:ui';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:coocoo/blocs/AddFriends/add_friends_bloc.dart';
import 'package:coocoo/config/Constants.dart';
import 'package:coocoo/config/Paths.dart';
import 'package:coocoo/constants.dart';
import 'package:coocoo/functions/BaseFunctions.dart';
import 'package:coocoo/models/MyContact.dart';
import 'package:coocoo/utils/SharedObjects.dart';
import 'package:coocoo/widgets/AddFriendCard.dart';
import 'package:coocoo/widgets/FriendRequestCard.dart';
import 'package:coocoo/widgets/NoRequestsCard.dart';
import 'package:coocoo/widgets/SentRequestCard.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_neumorphic/flutter_neumorphic.dart';
enum RequestsType { FriendRequests, SentRequests }
class AddFriendsScreen extends StatefulWidget {
@override
_AddFriendsScreenState createState() => _AddFriendsScreenState();
}
class _AddFriendsScreenState extends State {
final TextEditingController hitupIdController = TextEditingController();
Firestore _firestore = Firestore.instance;
String uid = SharedObjects.prefs.getString(Constants.sessionUid);
AddFriendsBloc addFriendsBloc;
final _formKey = GlobalKey();
Widget _buildRequestsStreamBuilder(
double algo, RequestsType requestsType, BuildContext contex) {
return StreamBuilder(
stream: _firestore
.collection(Paths.usersPath)
.document(uid)
.collection(requestsType == RequestsType.SentRequests
? Paths.sentRequestsPath
: Paths.friendRequestsPath)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
Widget toShow;
if (snapshot.hasError) {
toShow = HitUpIdTextWidget(
algo: algo, text: 'Sorry! Not able to retrieve Data');
} else {
switch (snapshot.connectionState) {
case ConnectionState.none:
toShow = HitUpIdTextWidget(
algo: algo, text: 'Sorry! Not able to retrieve Data');
break;
case ConnectionState.waiting:
toShow = Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Center(
child: SizedBox(
child: CircularProgressIndicator(),
height: 60,
width: 60,
),
),
);
break;
case ConnectionState.active:
case ConnectionState.done:
if (snapshot.data.documents.isEmpty) {
toShow = NoRequestsCard(
algo: algo,
text: requestsType == RequestsType.SentRequests
? 'You h'
'ave not sent any Friend Requests'
: 'You have No '
'Friend Requests');
} else {
toShow = Column(
children: snapshot.data.documents
.map((e) => requestsType == RequestsType.SentRequests
? SentRequestCard(MyContact.fromFireStore(e))
: FriendRequestCard(MyContact.fromFireStore(e),
addFriendsBloc, contex))
.toList(),
);
}
break;
}
}
return toShow;
},
);
}
@override
void initState() {
addFriendsBloc = BlocProvider.of(context);
super.initState();
}
@override
Widget build(BuildContext context) {
double width = MediaQuery.of(context).size.width;
double algo = width / perfectWidth;
return Form(
key: _formKey,
child: Padding(
padding: EdgeInsets.only(top: 12.0, right: 10.0, left: 10.0),
child: ListView(
children: [
Row(
children: [
GestureDetector(
child: Icon(
Icons.arrow_back,
color: Colors.black,
),
onTap: () {
Navigator.pop(context);
},
),
SizedBox(width: 5.0),
Expanded(
child: TextFormField(
inputFormatters: [
FilteringTextInputFormatter.deny(
RegExp(r'\s+')) // no spaces allowed
],
validator: (value) {
if (value.isEmpty) {
return "Please enter a valid username";
}
return null;
},
controller: hitupIdController,
style: TextStyle(
fontSize: algo * 18.0,
),
autocorrect: false,
cursorColor: Colors.blueGrey,
decoration: kHitUpIdTextFieldDecoration,
)),
SizedBox(width: algo * 10.0),
GestureDetector(
onTap: () {
if (_formKey.currentState.validate()) {
addFriendsBloc
.add(SearchHitUpIdEvent(hitupIdController.text));
}
hitupIdController.clear();
},
child: Text(
'Search',
style: TextStyle(
letterSpacing: 0.5,
fontSize: algo * 21.0,
fontWeight: FontWeight.bold,
),
),
),
],
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: BlocBuilder(
builder: (context, state) {
if (state is SearchingHitUpIdState) {
return SearchingCard(algo: algo);
}
if (state is HitUpIdAlreadyThere) {
if (state.hitUpIdLocation == HitUpIdLocation.InLocalDb) {
return HitUpIdTextWidget(
algo: algo,
text:
'@${state.hitUpId} already exists in your contacts!');
} else if (state.hitUpIdLocation ==
HitUpIdLocation.InFriendRequests) {
return HitUpIdTextWidget(
algo: algo,
text:
'You have already received a friend request from @${state.hitUpId}. Please'
' check your Friend Requests List');
} else if (state.hitUpIdLocation ==
HitUpIdLocation.InSentRequests) {
return HitUpIdTextWidget(
algo: algo,
text:
'You have already sent a friend request to @${state.hitUpId}');
}
}
if (state is SendingFriendRequestState) {
return SearchingCard(algo: algo);
}
if (state is FriendRequestSentState) {
return SentRequestCard(state.friend);
}
if (state is HitUpIdExistsState) {
// return Text(state.friend.name);
return AddFriendCard(state.friend, () async {
// print(state.friend.phoneNumber);
addFriendsBloc
.add(AddButtonClickEvent(context, state.friend));
});
}
if (state is HitUpIdNotExistsState) {
return HitUpIdTextWidget(
algo: algo,
text: 'HitUp Id @${state.hitupId} not found!',
);
}
return SizedBox(height: 10.0);
},
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Friend Requests',
style: kRequestTitleStyle,
),
SizedBox(height: 6.0),
BlocBuilder(
builder: (context, state) {
if (state is AcceptingFriendRequestState ||
state is DecliningFriendRequestState) {
return SearchingCard(algo: algo);
}
return _buildRequestsStreamBuilder(
algo, RequestsType.FriendRequests, context);
},
),
],
),
SizedBox(height: 30.0),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Your Sent Requests',
style: kRequestTitleStyle,
),
SizedBox(height: 6.0),
_buildRequestsStreamBuilder(
algo,
RequestsType.SentRequests,
context,
),
],
),
],
),
),
);
}
@override
void dispose() {
super.dispose();
hitupIdController.dispose();
// addFriendsBloc.close();
}
}
class SearchingCard extends StatelessWidget {
const SearchingCard({
Key key,
@required this.algo,
}) : super(key: key);
final double algo;
@override
Widget build(BuildContext context) {
return Card(
elevation: 5.0,
child: Container(
padding: EdgeInsets.symmetric(vertical: 3.0),
height: algo * 50.0,
child: Center(
child: CircularProgressIndicator(),
),
),
);
}
}
class HitUpIdTextWidget extends StatelessWidget {
final double algo;
final String text;
HitUpIdTextWidget({
@required this.algo,
@required this.text,
});
@override
Widget build(BuildContext context) {
return Card(
elevation: 5.0,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 6.0, horizontal: 8.0),
child: Center(
child: Text(text,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: algo * 20.0,
color: Constants.textStuffColor,
)),
),
),
);
}
}
================================================
FILE: lib/screens/chat_screen.dart
================================================
import 'dart:async';
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:coocoo/screens/friend_profile_screen.dart';
import 'package:coocoo/blocs/chats/chat_bloc.dart';
import 'package:coocoo/constants.dart';
import 'package:coocoo/functions/UserDataFunction.dart';
import 'package:coocoo/models/ChatMessage.dart';
import 'package:coocoo/models/MyContact.dart';
import 'package:coocoo/stateProviders/profilePicUrlState.dart';
import 'package:coocoo/widgets/ChatItemWidget.dart';
import 'package:coocoo/widgets/GradientSnackBar.dart';
import 'package:coocoo/widgets/ImageFullScreenWidget.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_emoji/flutter_emoji.dart' as emj;
import 'package:flutter_neumorphic/flutter_neumorphic.dart';
import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart';
class ChatScreen extends StatefulWidget {
final MyContact toContact;
ChatScreen(this.toContact);
@override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State {
var parser = emj.EmojiParser();
TextEditingController chatTextController = TextEditingController();
ChatBloc chatBloc;
UserDataFunction userDataFunction = UserDataFunction();
List allMessages = [];
ScrollController _scrollController;
final picker = ImagePicker();
Widget _buildSendButton(double algo) {
return Expanded(
child: InkWell(
onTap: () {
String tempChatId = widget.toContact.chatId;
String msgToSend = parser.unemojify(chatTextController.text);
chatBloc.add(SendMessageEvent(context, msgToSend, tempChatId));
userDataFunction.sendNotification(
toUid: widget.toContact.phoneNumber,
title: "${widget.toContact.name}",
content: msgToSend,
);
chatTextController.clear();
},
child: Neumorphic(
style: kCircleNeumorphicStyle,
child: CircleAvatar(
backgroundColor: Colors.white,
radius: algo * 33.0,
child: Neumorphic(
style: NeumorphicStyle(
shadowDarkColor: Colors.blueGrey,
boxShape: NeumorphicBoxShape.circle(),
depth: 4,
intensity: 0.7,
surfaceIntensity: 0.6,
),
child: CircleAvatar(
radius: algo * 23.0,
backgroundColor: Colors.white,
child:
Icon(Icons.send, color: Colors.blueGrey, size: algo * 26.0),
),
),
),
),
),
);
}
Widget _buildTypeMessageTextField() {
return Expanded(
flex: 5,
child: Row(
children: [
Expanded(
child: Neumorphic(
margin: EdgeInsets.only(left: 6, right: 5, top: 2, bottom: 4),
style: NeumorphicStyle(
depth: -15,
boxShape: NeumorphicBoxShape.stadium(),
shadowDarkColorEmboss: Colors.black,
shadowLightColor: Colors.white,
intensity: 0.6,
),
padding: EdgeInsets.symmetric(vertical: 14, horizontal: 18),
child: TextField(
onTap: () {
// scroll to the bottom of the list when keyboard appears
Timer(
Duration(milliseconds: 200),
() => _scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: Duration(milliseconds: 500),
curve: Curves.easeIn));
},
focusNode: FocusNode(),
cursorColor: Colors.blueGrey,
controller: chatTextController,
style: TextStyle(
fontSize: 21.0,
),
decoration:
InputDecoration.collapsed(hintText: "Type a message"),
),
),
),
],
),
);
}
Widget _buildContactProfilePicture(double algo) {
return GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ImageFullScreen(
url: widget.toContact.photoUrl,
tag: 'dash${widget.toContact.ind}')));
},
child: Hero(
tag: 'dash${widget.toContact.ind}',
child: Neumorphic(
style: kCircleNeumorphicStyle,
child: CircleAvatar(
radius: algo * 23.5,
backgroundImage:
CachedNetworkImageProvider(widget.toContact.photoUrl),
),
),
),
);
}
@override
void initState() {
super.initState();
chatBloc = BlocProvider.of(context);
chatBloc.add(LoadInitialMessagesEvent(widget.toContact.chatId));
_scrollController = ScrollController();
}
@override
void dispose() {
chatTextController.dispose();
_scrollController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final double screenWidth = MediaQuery.of(context).size.width;
final double algo = screenWidth / perfectWidth;
return Scaffold(
backgroundColor: Colors.grey[200],
appBar: AppBar(
titleSpacing: 0.0,
automaticallyImplyLeading: false,
leading: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: Icon(
Icons.arrow_back,
color: Colors.blueGrey[700],
),
),
backgroundColor: Colors.white,
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
_buildContactProfilePicture(algo),
SizedBox(width: algo * 13.0),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
FriendProfileScreen(widget.toContact)));
},
child: Text(
widget.toContact.name,
style: TextStyle(
color: Colors.blueGrey[700],
fontSize: algo * 20.0,
fontWeight: FontWeight.w600,
),
),
),
],
),
actions: [
GestureDetector(
onTap: () {
pickImage();
},
child: Icon(
Icons.attach_file,
color: Colors.blueGrey[700],
size: 30.0,
),
),
SizedBox(width: 5.0),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
FriendProfileScreen(widget.toContact)));
},
child: Icon(
Icons.more_vert_sharp,
size: 30.0,
),
),
SizedBox(width: algo * 15.0),
],
),
body: Container(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
child: BlocListener(
listener: (context, state) {
if (state is ReceivedMessageState) {
if (state.chatMessages[0].chatId ==
widget.toContact.chatId) {
setState(() {
allMessages = state.chatMessages;
});
}
}
if (state is InitialMessagesLoadedState) {
setState(() {
allMessages = state.chatMessages;
});
}
// jump to the bottom of the screen when a new message arrives
// also using a timer because we need to jump to the bottom
// only after the new message is updated in the listview
Timer(
Duration(milliseconds: 600),
() => _scrollController.animateTo(
_scrollController.position.maxScrollExtent,
duration: Duration(milliseconds: 200),
curve: Curves.easeIn));
},
child: Padding(
padding: EdgeInsets.symmetric(vertical: algo * 8.0),
child: ListView.builder(
controller: _scrollController,
itemCount: allMessages.length,
itemBuilder: (context, index) {
return ChatItemWidget(allMessages[index]);
}),
),
),
),
Row(
children: [
_buildTypeMessageTextField(),
_buildSendButton(algo),
],
),
],
),
),
);
}
Future pickImage() async {
final pickedFile = await picker.getImage(source: ImageSource.gallery);
if (pickedFile != null) {
File tempFile = File(pickedFile.path);
chatBloc.add(SendImageEvent(context, tempFile, widget.toContact.chatId));
GradientSnackBar.showMessage(
context, "Sending Your Beautiful Image...", 2);
userDataFunction.sendNotification(
toUid: widget.toContact.phoneNumber,
title: "You have New Messages",
content: "Click To View",
);
}
}
}
================================================
FILE: lib/screens/contacts_screen.dart
================================================
import 'package:animated_bottom_navigation_bar/animated_bottom_navigation_bar.dart';
import 'package:coocoo/screens/ContactsHelpPage.dart';
import 'package:coocoo/screens/addFriends_screen.dart';
import 'package:coocoo/blocs/contacts/contacts_bloc.dart';
import 'package:coocoo/functions/UserDataFunction.dart';
import 'package:coocoo/models/MyContact.dart';
import 'package:coocoo/widgets/ContactRowWidget.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import '../constants.dart';
class ContactListPage extends StatefulWidget {
@override
State createState() => _ContactListPageState();
const ContactListPage();
}
class _ContactListPageState extends State
with SingleTickerProviderStateMixin {
ContactsBloc contactsBloc;
final TextEditingController usernameController = TextEditingController();
List contacts = [];
UserDataFunction userDataFunction;
bool refreshing = false;
Color stuffColor = Colors.blueGrey[700];
bool showSearchBar = false;
int _selectedIndex = 0;
AnimationController _animationController;
Animation animation;
CurvedAnimation curve;
final iconList = [
FontAwesomeIcons.addressBook,
// Icons.contacts,
// FontAwesomeIcons.solidAddressBook,
FontAwesomeIcons.userFriends,
];
@override
void dispose() {
usernameController.dispose();
_animationController.dispose();
// contactsBloc.close();
super.dispose();
}
@override
void initState() {
userDataFunction = UserDataFunction();
contactsBloc = BlocProvider.of(context);
contactsBloc.add(FetchContactsEvent());
super.initState();
final systemTheme = SystemUiOverlayStyle.light.copyWith(
systemNavigationBarColor: Colors.black,
systemNavigationBarIconBrightness: Brightness.light,
);
SystemChrome.setSystemUIOverlayStyle(systemTheme);
_animationController = AnimationController(
duration: Duration(seconds: 1),
vsync: this,
);
curve = CurvedAnimation(
parent: _animationController,
curve: Interval(
0.5,
1.0,
curve: Curves.fastOutSlowIn,
),
);
animation = Tween(
begin: 0,
end: 1,
).animate(curve);
Future.delayed(
Duration(seconds: 1),
() => _animationController.forward(),
);
}
Widget _buildRefreshButton() {
return InkWell(
child: Icon(
Icons.refresh,
color: stuffColor,
),
onTap: () async {
setState(() {
refreshing = true;
});
await userDataFunction.loadPhoneContactsV2(context);
await Future.delayed(Duration(seconds: 8));
contactsBloc.add(FetchContactsEvent());
setState(() {
refreshing = false;
});
},
);
}
Widget _buildRefreshing() {
return Container(
width: 20.0,
padding: EdgeInsets.symmetric(vertical: 18.0),
child: CircularProgressIndicator(
strokeWidth: 2.5,
backgroundColor: Colors.white,
),
);
}
Widget _buildNormalAppBar(double screenWidth) {
return AppBar(
automaticallyImplyLeading: false,
leading: IconButton(
icon: Icon(
Icons.arrow_back,
color: stuffColor,
),
onPressed: () {
Navigator.pop(context);
},
),
backgroundColor: Colors.white,
title: Text(
'Select Contact',
style: TextStyle(
fontSize: (screenWidth / perfectWidth) * 22.0, color: stuffColor),
),
actions: [
refreshing ? _buildRefreshing() : _buildRefreshButton(),
SizedBox(width: 25.0),
],
);
}
void _onItemTapped(int ind) {
setState(() {
_selectedIndex = ind;
});
}
@override
Widget build(BuildContext context) {
final double screenWidth = MediaQuery.of(context).size.width;
return SafeArea(
child: Scaffold(
backgroundColor: Colors.grey[200],
appBar: _selectedIndex == 0 ? _buildNormalAppBar(screenWidth) : null,
floatingActionButton: FloatingActionButton(
elevation: 8,
backgroundColor: Colors.white,
child: Icon(
Icons.favorite,
size: 40.0,
color: Colors.black,
),
onPressed: () {
_animationController.reset();
_animationController.forward();
},
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
bottomNavigationBar: AnimatedBottomNavigationBar(
elevation: 30.0,
activeColor: Colors.black,
splashColor: Colors.blueAccent,
backgroundColor: Colors.white,
icons: iconList,
iconSize: 30.0,
activeIndex: _selectedIndex,
inactiveColor: Colors.grey,
notchAndCornersAnimation: animation,
splashSpeedInMilliseconds: 300,
notchSmoothness: NotchSmoothness.softEdge,
gapLocation: GapLocation.center,
leftCornerRadius: 20,
rightCornerRadius: 20,
onTap: _onItemTapped,
),
body: _selectedIndex == 0
? BlocBuilder(
builder: (context, state) {
if (state is FetchingContactsState) {
print("Fetching Contacts");
return SizedBox(
height: (MediaQuery.of(context).size.height),
child: Center(child: CircularProgressIndicator()),
);
}
if (state is FetchedContactsState) {
contacts = state.contacts;
}
return ListView(
children: [
Column(
children: List.generate(
contacts.length,
(index) => ContactRowWidget(
contact: contacts[index],
)),
),
Divider(thickness: 1.5),
SizedBox(height: (screenWidth / perfectWidth) * 20.0),
ListTile(
leading: Icon(
Icons.share,
color: Colors.black,
),
title: Text(
'Invite Friends',
style: TextStyle(
fontSize: (screenWidth / perfectWidth) * 20.0,
fontWeight: FontWeight.w400,
),
),
onTap: () => userDataFunction.onShare(context),
),
ListTile(
leading: Icon(
Icons.help,
color: Colors.black,
),
title: Text(
'Contacts Help',
style: TextStyle(
fontSize: (screenWidth / perfectWidth) * 20.0,
fontWeight: FontWeight.w400,
),
),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ContactsHelpPage()));
},
),
],
);
})
: AddFriendsScreen(),
),
);
}
}
================================================
FILE: lib/screens/enter_name_screen.dart
================================================
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:coocoo/screens/update_profile.dart';
import 'package:coocoo/config/Constants.dart';
import 'package:coocoo/config/Paths.dart';
import 'package:coocoo/utils/SharedObjects.dart';
import 'package:coocoo/widgets/NameTextField.dart';
import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
class EnterName extends StatefulWidget {
@override
_EnterNameState createState() => _EnterNameState();
}
class _EnterNameState extends State {
final TextEditingController firstNameController = TextEditingController();
final TextEditingController lastNameController = TextEditingController();
final _formKey = GlobalKey();
Firestore firestore = Firestore.instance;
bool isLoading = false;
Future saveFullName(String firstname, String lastname) async {
String uid = SharedObjects.prefs.getString(Constants.sessionUid);
String fullName = '$firstname $lastname';
DocumentReference ref = firestore.collection(Paths.usersPath).document(
uid); //reference of the user's document node in database/users. This node is created using uid
var data = {'name': fullName};
await ref.setData(data, merge: true); // set the photourl, age and username
await SharedObjects.prefs.setString(Constants.fullName, fullName);
}
Widget buildLoadingScreen() {
return Center(
child: CircularProgressIndicator(),
);
}
Widget buildEnterNameScreen() {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 35.0, vertical: 40.0),
child: Form(
key: _formKey,
child: ListView(
children: [
Center(
child: Text("What's your name?",
style: TextStyle(
fontSize: 25.0,
fontWeight: FontWeight.w600,
)),
),
SizedBox(height: 8.0),
Column(
children: [
NameTextField(
hintText: 'FIRST NAME',
controller: firstNameController,
),
NameTextField(
hintText: 'LAST NAME',
controller: lastNameController,
),
SizedBox(height: 10.0),
Text("This name will appear when someone searches for you "
"on HitUp")
],
),
SizedBox(height: 40.0),
RaisedButton(
padding: EdgeInsets.symmetric(vertical: 8.0),
onPressed: () async {
if (_formKey.currentState.validate()) {
setState(() {
isLoading = true;
});
await saveFullName(
firstNameController.text, lastNameController.text);
Navigator.push(context,
MaterialPageRoute(builder: (context) => UpdateProfile()));
isLoading = false;
}
},
elevation: 10.0,
color: Colors.blueAccent[400],
child: Text(
"NEXT",
style: TextStyle(
color: Colors.white, fontSize: 25.0, letterSpacing: 1.0),
),
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: isLoading ? buildLoadingScreen() : buildEnterNameScreen(),
);
}
@override
void dispose() {
firstNameController.dispose();
lastNameController.dispose();
super.dispose();
}
}
================================================
FILE: lib/screens/friend_profile_screen.dart
================================================
import 'package:cached_network_image/cached_network_image.dart';
import 'package:coocoo/blocs/chats/chat_bloc.dart';
import 'package:coocoo/blocs/home/home_bloc.dart';
import 'package:coocoo/config/Constants.dart';
import 'package:coocoo/constants.dart';
import 'package:coocoo/managers/db_manager.dart';
import 'package:coocoo/models/MyContact.dart';
import 'package:coocoo/widgets/DangerCard.dart';
import 'package:coocoo/widgets/ImageFullScreenWidget.dart';
import 'package:coocoo/widgets/ListTileProfile.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
class FriendProfileScreen extends StatefulWidget {
final MyContact friend;
FriendProfileScreen(this.friend);
@override
_FriendProfileScreenState createState() => _FriendProfileScreenState();
}
class _FriendProfileScreenState extends State {
ChatBloc chatBloc;
bool isBlocked = false;
TextEditingController changeNameController = TextEditingController();
final _formKey = GlobalKey();
HomeBloc homeBloc;
Future _showPrompt(BuildContext context) async {
return (await showDialog(
context: context,
builder: (context) => AlertDialog(
title:
Text('Are you sure you want to block ${widget.friend.name}?'),
content:
Text("You won't be able to send or receive any messages from"
" ${widget.friend.name}."),
actions: [
FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text(
'NO',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
),
FlatButton(
onPressed: () {
chatBloc.add(BlockUserEvent(context, widget.friend.chatId));
Navigator.of(context).pop(false);
},
child: Text(
'YES',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
),
],
),
)) ??
false;
}
void updateIsBlocked() async {
bool temp = await DBManager.db.isBlocked(widget.friend.chatId);
setState(() {
isBlocked = temp;
print(isBlocked);
});
}
@override
void initState() {
chatBloc = BlocProvider.of(context);
updateIsBlocked();
super.initState();
}
@override
Widget build(BuildContext context) {
double screenWidth = MediaQuery.of(context).size.width;
double algo = screenWidth / perfectWidth;
return Scaffold(
appBar: AppBar(
title: Text("Profile"),
),
body: Container(
width: double.infinity,
padding: EdgeInsets.symmetric(
vertical: algo * 10.0, horizontal: algo * 15.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ImageFullScreen(url: widget.friend.photoUrl)));
},
child: CircleAvatar(
radius: algo * 120.0,
backgroundColor: Colors.white,
backgroundImage:
CachedNetworkImageProvider(widget.friend.photoUrl),
),
),
Spacer(
flex: 2,
),
ListTileProfile(
iconData: FontAwesomeIcons.book,
title: 'Full Name',
subTitle: widget.friend.name,
trailingWidget: GestureDetector(
onTap: () {
showModalBottomSheet(
context: context,
isScrollControlled: true,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.vertical(top: Radius.circular(20.0))),
builder: (BuildContext context) {
return Form(
key: _formKey,
child: Container(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
top: 10.0,
left: 15.0,
right: 15.0),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Edit Name',
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w500,
),
),
SizedBox(height: algo * 12.0),
TextFormField(
validator: (value) {
if (value.trim().isEmpty) {
return "Please enter a valid Name";
}
return null;
},
controller: changeNameController,
style: TextStyle(
fontSize: algo * 18.0,
),
autocorrect: false,
cursorColor: Colors.blueGrey,
decoration:
kHitUpIdTextFieldDecoration.copyWith(
hintText: 'New Name',
),
),
SizedBox(height: algo * 8.0),
FlatButton(
onPressed: () async {
if (_formKey.currentState.validate()) {
Navigator.popUntil(
context, (route) => route.isFirst);
await DBManager.db.updateName(
changeNameController.text,
widget.friend.chatId);
homeBloc =
BlocProvider.of(context);
homeBloc.add(FetchHomeChatsEvent());
}
},
color: Constants.stuffColor,
child: Text(
'DONE',
style: TextStyle(
color: Colors.white,
fontSize: algo * 22.0,
letterSpacing: 0.5,
),
),
),
],
),
),
);
},
);
},
child: Icon(
Icons.edit,
color: Constants.stuffColor,
),
),
),
ListTileProfile(
iconData: FontAwesomeIcons.solidUser,
title: "Username",
subTitle: widget.friend.username,
),
Spacer(
flex: 5,
),
BlocListener(
listener: (context, state) {
if (state is BlockedUserState) {
setState(() {
isBlocked = true;
});
} else {
setState(() {
isBlocked = false;
});
}
},
child: isBlocked
? DangerCard(Colors.green, 'UnBlock', () async {
chatBloc
.add(UnblockUserEvent(context, widget.friend.chatId));
})
: DangerCard(Colors.red, 'Block', () async {
_showPrompt(context);
}),
),
],
),
),
);
}
}
================================================
FILE: lib/screens/help_screen.dart
================================================
import 'package:flutter/material.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
class HelpScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Help"),
),
body: Container(
padding: const EdgeInsets.symmetric(vertical: 15.0, horizontal: 8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
"For help with any issues, bugs or feature requests please write a review "
"on Google Play Store or directly contact us "
"at: ",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.0,
fontWeight: FontWeight.w400,
),
),
Column(
children: [
ContactMeCard(
iconData: FontAwesomeIcons.envelope,
title: 'digantakalita.ai@gmail.com',
),
ContactMeCard(
iconData: FontAwesomeIcons.instagram,
title: '@digantakalita_real',
),
ContactMeCard(
iconData: FontAwesomeIcons.twitter,
title: '@realdiganta',
),
ContactMeCard(
iconData: FontAwesomeIcons.whatsapp,
title: '+91 1111111111',
),
SizedBox(height: 10.0),
Text(
"If your messages are not being sent, just check your "
"internet connection or restart the app,"
" and it will start working fine af",
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 17.0,
),
)
],
),
SizedBox(height: 20.0),
Text(
"We typically reply within 4 hours",
style: TextStyle(
fontSize: 18.0,
color: Colors.teal,
fontWeight: FontWeight.bold,
),
),
],
),
),
);
}
}
class ContactMeCard extends StatelessWidget {
final IconData iconData;
final String title;
ContactMeCard({this.iconData, this.title});
@override
Widget build(BuildContext context) {
return Card(
elevation: 5.0,
child: ListTile(
leading: FaIcon(
iconData,
color: Colors.pinkAccent,
),
title: Text(
title,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 19.0,
letterSpacing: 0.8,
),
),
),
);
}
}
================================================
FILE: lib/screens/home_screen.dart
================================================
import 'dart:async';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:coocoo/screens/contacts_screen.dart';
import 'package:coocoo/screens/profile_screen.dart';
import 'package:coocoo/screens/settings_screen.dart';
import 'package:coocoo/blocs/home/home_bloc.dart';
import 'package:coocoo/config/Constants.dart';
import 'package:coocoo/config/Paths.dart';
import 'package:coocoo/constants.dart';
import 'package:coocoo/models/Conversation.dart';
import 'package:coocoo/stateProviders/profilePicUrlState.dart';
import 'package:coocoo/utils/SharedObjects.dart';
import 'package:coocoo/widgets/ChatCard.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_neumorphic/flutter_neumorphic.dart';
import 'package:onesignal_flutter/onesignal_flutter.dart';
import 'package:provider/provider.dart';
class HomeScreen extends StatefulWidget {
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State with WidgetsBindingObserver {
HomeBloc homeBloc;
List conversations;
void setUserNameAndNameIfNull() {
Firestore _firestore = Firestore.instance;
String uid = SharedObjects.prefs.getString(Constants.sessionUid);
String username = SharedObjects.prefs.getString(Constants.sessionUsername);
String fullName = SharedObjects.prefs.getString(Constants.fullName);
if (username == null || fullName == null) {
_firestore.collection(Paths.usersPath).document(uid).get().then((doc) {
SharedObjects.prefs
.setString(Constants.sessionUsername, doc.data["username"]);
SharedObjects.prefs.setString(Constants.fullName, doc.data["name"]);
SharedObjects.prefs.setString(
Constants.sessionProfilePictureUrl, doc.data["photoUrl"]);
});
}
}
Future _onWillPop() async {
return (await showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Are you sure?'),
content: Text('Do you want to exit the App'),
actions: [
FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text('No'),
),
FlatButton(
onPressed: () =>
SystemChannels.platform.invokeMethod('SystemNavigator.pop'),
child: Text('Yes'),
),
],
),
)) ??
false;
}
void initOneSignal() async {
await OneSignal.shared.init("98194ba4-9b9a-416b-ab0c-74b851af4f1a",
iOSSettings: {
OSiOSSettings.autoPrompt: false,
OSiOSSettings.inAppLaunchUrl: false
});
await OneSignal.shared
.setInFocusDisplayType(OSNotificationDisplayType.notification);
final String myUid = SharedObjects.prefs.getString(Constants.sessionUid);
await OneSignal.shared.setExternalUserId(myUid);
OneSignal.shared.addTrigger('update', '1');
}
@override
void initState() {
WidgetsBinding.instance.addObserver(this);
initOneSignal();
homeBloc = BlocProvider.of(context);
super.initState();
// connecting to the mqtt server in the home page
homeBloc.add(ConnectToServerEvent(context));
setUserNameAndNameIfNull();
print("Home Screen Opened");
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.paused) {
homeBloc.add(DisconnectEvent());
}
if (state == AppLifecycleState.resumed) {
homeBloc.add(ConnectToServerEvent(context));
}
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
Widget build(BuildContext context) {
final double screenWidth = MediaQuery.of(context).size.width;
return WillPopScope(
onWillPop: _onWillPop,
child: Scaffold(
backgroundColor: Colors.grey[200],
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => ContactListPage()));
},
backgroundColor: Colors.blueGrey[600],
elevation: 15.0,
child: Icon(
Icons.message,
),
),
body: SafeArea(
child: Container(
padding: EdgeInsets.only(top: 15.0, left: 15.0, right: 15.0),
child: Column(
children: [
Row(
children: [
Text(
'Chats',
style: TextStyle(
color: Colors.blueGrey[700],
fontSize: (screenWidth / perfectWidth) * 30.0,
fontWeight: FontWeight.w600,
),
),
Spacer(
flex: 10,
),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProfileScreen()));
},
child: Padding(
padding: EdgeInsets.symmetric(
vertical: 3.0, horizontal: 15.0),
child: Hero(
tag: 'myProfile',
child: Neumorphic(
style: kCircleNeumorphicStyle,
child: CircleAvatar(
backgroundColor: Colors.white,
radius: (screenWidth / perfectWidth) * 33.0,
backgroundImage: CachedNetworkImageProvider(
context
.watch()
.profilePicUrl),
),
),
),
),
),
GestureDetector(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SettingsScreen()));
},
child: Icon(
Icons.more_vert,
size: (screenWidth / perfectWidth) * 30.0,
),
),
],
),
SizedBox(height: 10.0),
Expanded(
flex: 3,
child: BlocBuilder(
builder: (context, state) {
conversations = state.conversations;
if (conversations.length > 0 &&
conversations.isNotEmpty) {
return ListView.builder(
itemCount: conversations.length,
itemBuilder: (context, index) {
return ChatCard(conversations[index], index);
});
} else {
return Center(child: Text("You have No Messages"));
}
},
))
],
),
),
),
),
);
}
}
================================================
FILE: lib/screens/login_screen.dart
================================================
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:coocoo/screens/otp_screen.dart';
import 'package:coocoo/config/Constants.dart';
import 'package:coocoo/functions/UserDataFunction.dart';
import 'package:coocoo/stateProviders/number_state.dart';
import 'package:coocoo/utils/SharedObjects.dart';
import 'package:country_pickers/country.dart';
import 'package:country_pickers/country_pickers.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
class Login extends StatefulWidget {
@override
_LoginState createState() => _LoginState();
}
class _LoginState extends State {
final _formKey = GlobalKey();
Firestore _firestore = Firestore.instance;
TextEditingController mobileNumberController = TextEditingController();
final FirebaseAuth _auth = FirebaseAuth.instance;
UserDataFunction userDataFunction = UserDataFunction();
String countryCode = '91';
// TODO: Handle the exception nicely here instead of just printing out the error
final PhoneVerificationFailed _verificationFailed =
(AuthException authException) {
print(authException.message);
};
Future _showContinueDialog() async {
return (await showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(
'We will be verifying the phone number: +$countryCode ${mobileNumberController.text}'),
content: Text('Is this OK, or would you like to edit the number?'),
actions: [
FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text('EDIT'),
),
FlatButton(
onPressed: () async {
context.read().setPhoneNumber(
"$countryCode${mobileNumberController.text}");
await SharedObjects.prefs
.setString(Constants.sessionCountryCode, countryCode);
await userDataFunction.verifyPhoneNumber(
context,
'+$countryCode' + mobileNumberController.text.toString(),
_verificationFailed);
Navigator.push(context,
MaterialPageRoute(builder: (context) => OTPScreen()));
},
child: Text('OK'),
),
],
),
)) ??
false;
}
Widget _buildDropdownItem(Country country) => Container(
child: Row(
children: [
CountryPickerUtils.getDefaultFlagImage(country),
SizedBox(
width: 8.0,
),
Text("+${country.phoneCode} ${country.isoCode}"),
],
),
);
@override
void dispose() {
mobileNumberController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: ChangeNotifierProvider(
create: (context) => NumberState(),
child: Form(
key: _formKey,
child: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 35.0, horizontal: 22.0),
child: ListView(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
children: [
Text(
'Verify your phone number',
style: TextStyle(
color: Constants.textStuffColor,
fontSize: 22.0,
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 30.0),
Text(
'HitUp Messenger will send and SMS message (carrier charges may apply) to verify'
' your phone number. Enter your phone number.',
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w400,
),
textAlign: TextAlign.center,
),
SizedBox(height: 20.0),
CountryPickerDropdown(
initialValue: 'IN',
itemBuilder: _buildDropdownItem,
// itemFilter: ['AR', 'DE', 'GB', 'CN'].contains(c.isoCode),
sortComparator: (Country a, Country b) =>
a.isoCode.compareTo(b.isoCode),
onValuePicked: (Country country) {
setState(() {
countryCode = country.phoneCode;
});
},
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 50.0),
child: TextFormField(
controller: mobileNumberController,
maxLengthEnforced: true,
maxLength: 10,
cursorColor: Constants.stuffColor,
style: TextStyle(fontSize: 20.0),
keyboardType: TextInputType.number,
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: 'Phone Number',
),
inputFormatters: [
FilteringTextInputFormatter.deny(
RegExp(r'\s+')) // no spaces allowed
],
validator: (value) {
if (value.length != 10) {
return 'Please enter 10 digits';
}
return null;
},
),
),
],
),
RaisedButton(
padding: EdgeInsets.symmetric(
vertical: 8.0, horizontal: 30.0),
color: Constants.stuffColor,
child: Text(
'NEXT',
style: TextStyle(
color: Colors.white,
fontSize: 18.0,
letterSpacing: 1.0,
),
),
onPressed: () async {
if (_formKey.currentState.validate()) {
await _showContinueDialog();
}
},
),
],
),
],
),
),
),
),
),
);
}
}
================================================
FILE: lib/screens/otp_screen.dart
================================================
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:coocoo/screens/enter_name_screen.dart';
import 'package:coocoo/blocs/timer/timer_bloc.dart';
import 'package:coocoo/config/Constants.dart';
import 'package:coocoo/functions/MQTTFunction.dart';
import 'package:coocoo/functions/UserDataFunction.dart';
import 'package:coocoo/screens/home_screen.dart';
import 'package:coocoo/stateProviders/number_state.dart';
import 'package:coocoo/utils/SharedObjects.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
class OTPScreen extends StatefulWidget {
@override
_OTPScreenState createState() => _OTPScreenState();
}
class _OTPScreenState extends State {
final _formKey = GlobalKey();
TextEditingController otpController = TextEditingController();
FirebaseAuth _auth = FirebaseAuth.instance;
Firestore _firestore = Firestore.instance;
SharedPreferences loginData;
bool isVerifying = false;
UserDataFunction userDataFunction;
MQTTFunction mqttFunction;
bool showSpinner = false;
TimerBloc timerBLoc;
String minutesStr = '00';
String secondsStr = Constants.resendOtpTime.toString();
bool resendOtpSwitch = false;
// TODO: Handle the exception nicely here instead of just printing out the error
final PhoneVerificationFailed _verificationFailed =
(AuthException authException) {
print(authException.message);
};
Future _showWrongOTPDialog() async {
return (await showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('The code you entered was not correct'),
content: Text('Please enter the correct code!'),
actions: [
FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text('TRY AGAIN'),
),
],
),
)) ??
false;
}
Future _signInWithPhoneNumber(String smsCode) async {
AuthCredential _authCredential = PhoneAuthProvider.getCredential(
verificationId: context.read().otp, smsCode: smsCode);
await _auth.signInWithCredential(_authCredential).catchError((error) {
_showWrongOTPDialog();
print(error);
}).then((AuthResult _authResult) async {
if (_authResult != null) {
setState(() {
isVerifying = true;
});
FirebaseUser currUser = _authResult.user;
print(currUser.phoneNumber);
String myPhoneNumber = currUser.phoneNumber.substring(1);
DocumentReference ref = _firestore.collection('users').document(
myPhoneNumber); //reference of the user's document node in database/users. This node is created using uid
await SharedObjects.prefs.setBool('login', false);
await SharedObjects.prefs
.setString(Constants.sessionUid, myPhoneNumber);
ref.get().then((doc) async {
if (doc.exists) {
String profilePhotoUrl = doc.data['photoUrl'];
String username = doc.data['username'];
SharedObjects.prefs
.setString(Constants.sessionProfilePictureUrl, profilePhotoUrl);
SharedObjects.prefs.setString(Constants.sessionUsername, username);
PermissionStatus currPermission =
await userDataFunction.askContactPermissions();
if (currPermission == PermissionStatus.granted) {
await userDataFunction.loadPhoneContactsV2(context);
isVerifying = false;
Navigator.push(context,
MaterialPageRoute(builder: (context) => HomeScreen()));
} else {
setState(() {
isVerifying = false;
});
}
} else {
var data = {
//add details
'phoneNumber': myPhoneNumber,
};
ref.setData(data, merge: true);
isVerifying = false;
Navigator.push(
context, MaterialPageRoute(builder: (context) => EnterName()));
}
});
}
});
}
Widget _buildOTPScreen() {
return ChangeNotifierProvider(
create: (context) => NumberState(),
child: Form(
key: _formKey,
child: SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 35.0, horizontal: 25.0),
child: ListView(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
children: [
Text(
'Verify +${context.watch().phoneNumber}',
style: TextStyle(
color: Constants.textStuffColor,
fontSize: 22.0,
fontWeight: FontWeight.w600,
),
),
SizedBox(height: 30.0),
Text(
"Please enter the 6-digit code sent to +${context.watch().phoneNumber}",
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w400,
),
textAlign: TextAlign.center,
),
SizedBox(height: 20.0),
Padding(
padding: EdgeInsets.symmetric(horizontal: 50.0),
child: TextFormField(
controller: otpController,
maxLengthEnforced: true,
maxLength: 6,
cursorColor: Constants.stuffColor,
style: TextStyle(fontSize: 18.0),
keyboardType: TextInputType.number,
textAlign: TextAlign.center,
decoration: InputDecoration(
hintText: 'Enter 6-digit code',
),
),
),
],
),
RaisedButton(
padding:
EdgeInsets.symmetric(vertical: 8.0, horizontal: 30.0),
color: Constants.stuffColor,
child: Text(
'VERIFY',
style: TextStyle(
color: Colors.white,
fontSize: 18.0,
letterSpacing: 1.0,
),
),
onPressed: () async {
setState(() {
showSpinner = true;
});
await _signInWithPhoneNumber(
otpController.text.toString());
setState(() {
showSpinner = false;
});
},
),
SizedBox(height: 15.0),
Card(
elevation: 5.0,
child: ListTile(
onTap: resendOtpSwitch
? () {
timerBLoc.add(StartTimerEvent());
setState(() {
resendOtpSwitch = false;
});
String phoneNum =
context.read().phoneNumber;
userDataFunction.verifyPhoneNumber(context,
'+$phoneNum', _verificationFailed);
}
: () {},
leading: Icon(Icons.message,
color:
resendOtpSwitch ? Colors.black : Colors.grey),
title: Text(
'Resend OTP',
style: TextStyle(
color:
resendOtpSwitch ? Colors.black : Colors.grey,
),
),
trailing: BlocListener(
listener: (context, state) {
if (state is TimerRunInProgressState) {
setState(() {
secondsStr = (state.newTick % 60)
.floor()
.toString()
.padLeft(2, '0');
});
}
if (state is TimerStoppedState) {
setState(() {
resendOtpSwitch = true;
});
}
},
child: Text(
'$minutesStr:$secondsStr',
),
)),
),
],
),
],
),
),
),
),
);
}
Widget _loadingScreen() {
return Center(
child: CircularProgressIndicator(),
);
}
@override
void initState() {
userDataFunction = UserDataFunction();
timerBLoc = BlocProvider.of(context);
timerBLoc.add(StartTimerEvent());
super.initState();
}
@override
void dispose() {
otpController.dispose();
timerBLoc.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: isVerifying || showSpinner ? _loadingScreen() : _buildOTPScreen(),
);
}
}
================================================
FILE: lib/screens/profile_screen.dart
================================================
import 'dart:io';
import 'package:cached_network_image/cached_network_image.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:coocoo/config/Constants.dart';
import 'package:coocoo/config/Paths.dart';
import 'package:coocoo/constants.dart';
import 'package:coocoo/functions/ChatFunction.dart';
import 'package:coocoo/stateProviders/profilePicUrlState.dart';
import 'package:coocoo/utils/SharedObjects.dart';
import 'package:coocoo/widgets/ListTileProfile.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:provider/provider.dart';
class ProfileScreen extends StatefulWidget {
@override
_ProfileScreenState createState() => _ProfileScreenState();
}
class _ProfileScreenState extends State {
final picker = ImagePicker();
File profileImageFile;
ImageProvider profileImage;
FirebaseStorage firebaseStorage = FirebaseStorage.instance;
Firestore fireStoreDb = Firestore.instance;
bool uploadingDp = false;
ChatFunction chatFunction;
@override
Widget build(BuildContext context) {
profileImage = CachedNetworkImageProvider(
context.watch().profilePicUrl);
double screenWidth = MediaQuery.of(context).size.width;
double algo = screenWidth / perfectWidth;
return Scaffold(
appBar: AppBar(
title: Text("Profile"),
),
body: Container(
width: double.infinity,
padding: EdgeInsets.symmetric(
vertical: algo * 10.0, horizontal: algo * 15.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Stack(
children: [
Opacity(
opacity: uploadingDp ? 0.5 : 1.0,
child: Hero(
tag: 'myProfile',
child: CircleAvatar(
radius: algo * 120.0,
backgroundColor: Colors.white,
backgroundImage: profileImage,
),
),
),
Positioned.fill(
child: Align(
alignment: Alignment.center,
child: uploadingDp
? CircularProgressIndicator(
backgroundColor: Colors.white,
)
: Container(),
),
),
Positioned(
bottom: algo * 5.0,
right: algo * 10.0,
child: GestureDetector(
onTap: () {
pickImage(context);
},
child: CircleAvatar(
backgroundColor: Colors.grey[200],
radius: algo * 30.0,
child: Icon(
Icons.camera_alt,
color: Colors.blueGrey,
size: algo * 33.0,
),
),
),
)
],
),
Spacer(
flex: 2,
),
ListTileProfile(
iconData: FontAwesomeIcons.solidUser,
title: 'Username',
subTitle:
SharedObjects.prefs.getString(Constants.sessionUsername) ??
"",
),
ListTileProfile(
iconData: FontAwesomeIcons.book,
title: 'Full Name',
subTitle: SharedObjects.prefs.getString(Constants.fullName),
),
ListTileProfile(
iconData: Icons.phone,
title: 'Phone Number',
subTitle: SharedObjects.prefs.getString(Constants.sessionUid),
),
Spacer(
flex: 9,
),
],
),
),
);
}
Future pickImage(BuildContext context) async {
final pickedFile = await picker.getImage(source: ImageSource.gallery);
if (pickedFile != null) {
profileImageFile = File(pickedFile.path);
setState(() {
uploadingDp = true;
});
String profileImageUrl =
await uploadFile(profileImageFile, Paths.profilePicturePath);
await saveProfilePicture(profileImageUrl, context);
setState(() {
uploadingDp = false;
profileImage = FileImage(profileImageFile);
});
}
}
Future saveProfilePicture(
String profileImageUrl, BuildContext context) async {
String uid = SharedObjects.prefs.getString(Constants.sessionUid);
DocumentReference ref = fireStoreDb.collection(Paths.usersPath).document(
uid); //reference of the user's document node in database/users. This node is created using uid
var data = {
'photoUrl': profileImageUrl,
};
await ref.setData(data, merge: true); // set the photourl, age and username
await SharedObjects.prefs
.setString(Constants.sessionProfilePictureUrl, profileImageUrl);
context.read().setProfilePicUrl(profileImageUrl);
// notify my contacts devices that I have changed my DP
chatFunction = ChatFunction();
ref.get().then((snapshot) {
var chatIds = snapshot.data["chats"];
String msgToSend =
'{"msg" : "${Constants.profilePicChangeMsg}", "profilePicUrl" : "$profileImageUrl",'
'"uid" : "$uid", "type" : "service"}';
chatIds.values.forEach((chatId) {
chatFunction.sendServiceMsgToServer(context, msgToSend, chatId);
});
});
}
// 2. compress file and get file.
Future testCompressAndGetFile(File file, String username) async {
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
var result = await FlutterImageCompress.compressAndGetFile(
file.absolute.path,
tempPath + "/$username.jpg",
minHeight: 500,
minWidth: 500,
quality: 80,
);
return result;
}
Future uploadFile(File file, String path) async {
String username = SharedObjects.prefs.getString(Constants.sessionUsername);
file = await testCompressAndGetFile(file, username);
String fileName = basename(file.path);
final milliSecs = DateTime.now().millisecondsSinceEpoch;
StorageReference reference = firebaseStorage.ref().child(
'$path/$milliSecs\_$fileName'); // get a reference to the path of the image directory
String uploadPath = await reference.getPath();
print('uploading to $uploadPath');
StorageUploadTask uploadTask =
reference.putFile(file); // put the file in the path
StorageTaskSnapshot result =
await uploadTask.onComplete; // wait for the upload to complete
String url = await result.ref
.getDownloadURL(); //retrieve the download link and return it
return url;
}
}
================================================
FILE: lib/screens/settings_screen.dart
================================================
import 'package:coocoo/screens/account_screen.dart';
import 'package:coocoo/screens/help_screen.dart';
import 'package:coocoo/functions/UserDataFunction.dart';
import 'package:coocoo/widgets/SettingsTile.dart';
import 'package:flutter/material.dart';
class SettingsScreen extends StatelessWidget {
final UserDataFunction userDataFunction = UserDataFunction();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Settings'),
),
body: Column(
children: [
SettingsTile(
icon: Icons.settings,
title: 'Account',
onPress: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => AccountScreen()));
},
),
SettingsTile(
icon: Icons.help_outline,
title: 'Help',
onPress: () {
Navigator.push(context,
MaterialPageRoute(builder: (context) => HelpScreen()));
},
),
SettingsTile(
icon: Icons.supervisor_account,
title: 'Invite a Friend',
onPress: () => userDataFunction.onShare(context),
),
],
),
);
}
}
================================================
FILE: lib/screens/update_profile.dart
================================================
import 'dart:io';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:coocoo/config/Constants.dart';
import 'package:coocoo/config/Paths.dart';
import 'package:coocoo/functions/MQTTFunction.dart';
import 'package:coocoo/functions/UserDataFunction.dart';
import 'package:coocoo/screens/home_screen.dart';
import 'package:coocoo/stateProviders/profilePicUrlState.dart';
import 'package:coocoo/utils/SharedObjects.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path/path.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:provider/provider.dart';
enum PageState { initial, updating }
class UpdateProfile extends StatefulWidget {
@override
_UpdateProfileState createState() => _UpdateProfileState();
}
class _UpdateProfileState extends State {
PageState profilePageState = PageState.initial;
UserDataFunction userDataFunction;
MQTTFunction mqttFunction;
final _formKey = GlobalKey();
File profileImageFile;
ImageProvider profileImage = AssetImage('images/user.png');
final TextEditingController usernameController = TextEditingController();
FirebaseStorage firebaseStorage = FirebaseStorage.instance;
Firestore fireStoreDb = Firestore.instance;
final picker = ImagePicker();
String NoDpUrl =
"https://firebasestorage.googleapis.com/v0/b/coocoo-private-fc1e0.appspot.com/o/user.png?alt=media&token=0572ebb8-630d-4468-b5e1-91fd4cc9e049";
@override
void dispose() {
usernameController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Form(
key: _formKey,
child: buildUpdateProfilePage(context),
),
);
}
Widget buildUpdateProfilePage(BuildContext context) {
if (profilePageState == PageState.initial) {
return SafeArea(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 35.0, horizontal: 15.0),
child: ListView(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Column(
children: