├── .firebaserc
├── .gitignore
├── .metadata
├── README.md
├── SECURITY.md
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ ├── google-services.json
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── project_message_demo
│ │ │ │ └── Application.java
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── project_message_demo
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable
│ │ │ └── launch_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ └── values
│ │ │ └── styles.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── settings_aar.gradle
├── assets
├── calling.mp3
└── message.mp3
├── firebase.json
├── functions
├── .eslintrc.js
├── .eslintrc.json
├── .gitignore
├── package-lock.json
├── package.json
├── src
│ └── index.ts
└── tsconfig.json
├── images
├── avt.jpg
└── background.jpg
├── ios
├── .gitignore
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── Podfile
├── Podfile.lock
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
├── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── GoogleService-Info.plist
│ ├── Info.plist
│ └── Runner-Bridging-Header.h
└── build
│ └── XCBuildData
│ ├── 10c79b38152e34f2a9c51d06cab5c2c0-desc.xcbuild
│ ├── 10c79b38152e34f2a9c51d06cab5c2c0-manifest.xcbuild
│ ├── 4ad262fa702ad4022d24711c2ea76076-desc.xcbuild
│ ├── 4ad262fa702ad4022d24711c2ea76076-manifest.xcbuild
│ ├── 4e993dc3077a0c038363e034c90eb811-desc.xcbuild
│ ├── 4e993dc3077a0c038363e034c90eb811-manifest.xcbuild
│ ├── 6072702be6084f103dc13ed17afffb5f-desc.xcbuild
│ ├── 6072702be6084f103dc13ed17afffb5f-manifest.xcbuild
│ ├── 8be53b2722081bfcbc2104eb272b8dc8-desc.xcbuild
│ ├── 8be53b2722081bfcbc2104eb272b8dc8-manifest.xcbuild
│ ├── BuildDescriptionCacheIndex-2f874282badcdaa4b07a7bb620484549
│ ├── BuildDescriptionCacheIndex-67522bec3edf0f0dc585944b11ec7e1c
│ ├── build.db
│ ├── ced3ee9f042c373fc5cb613a54f24c90-desc.xcbuild
│ └── ced3ee9f042c373fc5cb613a54f24c90-manifest.xcbuild
├── lib
├── main.dart
└── src
│ ├── animation
│ └── fade_animation.dart
│ ├── app.dart
│ ├── model
│ └── user.dart
│ ├── page
│ ├── auth
│ │ ├── auth_page.dart
│ │ ├── forgot_page.dart
│ │ └── login_page.dart
│ ├── call
│ │ ├── call_page.dart
│ │ └── receive_call_page.dart
│ ├── home_page
│ │ └── home_page.dart
│ ├── notification_page
│ │ └── notification_page.dart
│ ├── receive_page
│ │ ├── receive_page.dart
│ │ └── room_page.dart
│ ├── request_page.dart
│ │ └── request_page.dart
│ └── user
│ │ └── profile_page.dart
│ ├── service
│ └── auth.dart
│ └── widget
│ ├── general
│ ├── cached_image.dart
│ ├── loading.dart
│ └── photo_viewer.dart
│ ├── notification_widget
│ └── notification_item.dart
│ ├── receive_widget
│ ├── build_chat_line.dart
│ ├── inbox_card.dart
│ ├── inbox_list.dart
│ └── input_bottom.dart
│ └── search_widget
│ └── user_card.dart
├── pubspec.lock
├── pubspec.yaml
└── test
└── widget_test.dart
/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "project-message-65c25"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 |
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | **/ios/Flutter/.last_build_id
26 | .dart_tool/
27 | .flutter-plugins
28 | .flutter-plugins-dependencies
29 | .packages
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Web related
35 | lib/generated_plugin_registrant.dart
36 |
37 | # Symbolication related
38 | app.*.symbols
39 |
40 | # Obfuscation related
41 | app.*.map.json
42 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: d408d302e22179d598f467e11da5dd968dbdc9ec
8 | channel: beta
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### Smart Intercom for Apartment :office::office::office:
2 |
3 | #### You can video call, notification to members and check your history :watch::watch:
4 |
5 | #### How I can run it? :tada:
6 |
7 | - :rocket: Clone this repository
8 | - :rocket: Run 'flutter get packages' on terminal in project
9 | - :rocket: Edit your firebase config
10 | - :rocket: Run app & register user
11 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | Use this section to tell people about which versions of your project are
6 | currently being supported with security updates.
7 |
8 | | Version | Supported |
9 | | ------- | ------------------ |
10 | | 5.1.x | :white_check_mark: |
11 | | 5.0.x | :white_check_mark: |
12 | | 4.0.x | :white_check_mark: |
13 | | < 4.0 | :white_check_mark: |
14 |
15 | ## Reporting a Vulnerability
16 |
17 | Use this section to tell people how to report a vulnerability.
18 |
19 | Tell them where to go, how often they can expect to get an update on a
20 | reported vulnerability, what to expect if the vulnerability is accepted or
21 | declined, etc.
22 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply plugin: 'kotlin-android'
26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
27 |
28 | android {
29 | compileSdkVersion 29
30 |
31 | sourceSets {
32 | main.java.srcDirs += 'src/main/kotlin'
33 | }
34 |
35 | lintOptions {
36 | disable 'InvalidPackage'
37 | }
38 |
39 | defaultConfig {
40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
41 | applicationId "com.lambiengcode.intercom"
42 | minSdkVersion 21
43 | targetSdkVersion 29
44 | versionCode flutterVersionCode.toInteger()
45 | versionName flutterVersionName
46 | multiDexEnabled true
47 | }
48 |
49 | buildTypes {
50 | release {
51 | signingConfig signingConfigs.debug
52 | minifyEnabled false
53 | shrinkResources false
54 | }
55 | }
56 | }
57 |
58 | flutter {
59 | source '../..'
60 | }
61 |
62 | dependencies {
63 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
64 | implementation platform('com.google.firebase:firebase-bom:25.12.0')
65 | implementation 'com.google.firebase:firebase-analytics'
66 | implementation 'com.google.firebase:firebase-messaging:20.1.6'
67 | }
68 |
69 | apply plugin: 'com.google.gms.google-services'
70 |
--------------------------------------------------------------------------------
/android/app/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_info": {
3 | "project_number": "375608740631",
4 | "firebase_url": "https://video-call-intercom-default-rtdb.firebaseio.com",
5 | "project_id": "video-call-intercom",
6 | "storage_bucket": "video-call-intercom.appspot.com"
7 | },
8 | "client": [
9 | {
10 | "client_info": {
11 | "mobilesdk_app_id": "1:375608740631:android:cd6ccab3cb202f04540552",
12 | "android_client_info": {
13 | "package_name": "com.lambiengcode.intercom"
14 | }
15 | },
16 | "oauth_client": [
17 | {
18 | "client_id": "375608740631-bd415qqs63g5onsfa7qd35p62bvh28k1.apps.googleusercontent.com",
19 | "client_type": 3
20 | }
21 | ],
22 | "api_key": [
23 | {
24 | "current_key": "AIzaSyC41j6MlW90MKjxZZoY_8isJQqPk9HAldU"
25 | }
26 | ],
27 | "services": {
28 | "appinvite_service": {
29 | "other_platform_oauth_client": [
30 | {
31 | "client_id": "375608740631-bd415qqs63g5onsfa7qd35p62bvh28k1.apps.googleusercontent.com",
32 | "client_type": 3
33 | }
34 | ]
35 | }
36 | }
37 | }
38 | ],
39 | "configuration_version": "1"
40 | }
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | // Add the following permission if your scenario involves reading the external storage:
17 |
18 |
19 |
20 |
21 |
25 |
32 |
36 |
40 |
45 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
60 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/example/project_message_demo/Application.java:
--------------------------------------------------------------------------------
1 | package com.lambiengcode.intercom;
2 |
3 | import io.flutter.app.FlutterApplication;
4 | import io.flutter.plugin.common.PluginRegistry;
5 | import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
6 | import io.flutter.plugins.GeneratedPluginRegistrant;
7 | import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService;
8 | import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin;
9 |
10 | public class Application extends FlutterApplication implements PluginRegistrantCallback {
11 | @Override
12 | public void onCreate() {
13 | super.onCreate();
14 | FlutterFirebaseMessagingService.setPluginRegistrant(this);
15 | }
16 |
17 | @Override
18 | public void registerWith(PluginRegistry registry) {
19 | FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
20 | }
21 | }
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/project_message_demo/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.lambiengcode.intercom
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity() {
6 | }
7 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext.kotlin_version = '1.3.50'
3 | repositories {
4 | google()
5 | jcenter()
6 | }
7 |
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.5.0'
10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
11 | classpath 'com.google.gms:google-services:4.3.4'
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | google()
18 | jcenter()
19 | }
20 | }
21 |
22 | rootProject.buildDir = '../build'
23 | subprojects {
24 | project.buildDir = "${rootProject.buildDir}/${project.name}"
25 | }
26 | subprojects {
27 | project.evaluationDependsOn(':app')
28 | }
29 |
30 | task clean(type: Delete) {
31 | delete rootProject.buildDir
32 | }
33 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 | android.enableR8=true
5 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jun 23 08:50:38 CEST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
7 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
4 | def properties = new Properties()
5 |
6 | assert localPropertiesFile.exists()
7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
8 |
9 | def flutterSdkPath = properties.getProperty("flutter.sdk")
10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
12 |
--------------------------------------------------------------------------------
/android/settings_aar.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/assets/calling.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/assets/calling.mp3
--------------------------------------------------------------------------------
/assets/message.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/assets/message.mp3
--------------------------------------------------------------------------------
/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "functions": {
3 | "predeploy": [
4 | "npm --prefix \"$RESOURCE_DIR\" run lint",
5 | "npm --prefix \"$RESOURCE_DIR\" run build"
6 | ],
7 | "source": "functions"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/functions/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | browser: true,
4 | es6: true,
5 | node: true,
6 | },
7 | extends: [
8 | "plugin:import/errors",
9 | "plugin:import/warnings",
10 | "plugin:import/typescript",
11 | ],
12 | parser: "@typescript-eslint/parser",
13 | parserOptions: {
14 | project: "tsconfig.json",
15 | sourceType: "module",
16 | },
17 | plugins: [
18 | "@typescript-eslint",
19 | "import",
20 | ],
21 | rules: {
22 | "@typescript-eslint/adjacent-overload-signatures": "error",
23 | "@typescript-eslint/no-empty-function": "error",
24 | "@typescript-eslint/no-empty-interface": "warn",
25 | "@typescript-eslint/no-floating-promises": "error",
26 | "@typescript-eslint/no-namespace": "error",
27 | "@typescript-eslint/no-unnecessary-type-assertion": "error",
28 | "@typescript-eslint/prefer-for-of": "warn",
29 | "@typescript-eslint/triple-slash-reference": "error",
30 | "@typescript-eslint/unified-signatures": "warn",
31 | "comma-dangle": ["error", "always-multiline"],
32 | "constructor-super": "error",
33 | eqeqeq: ["warn", "always"],
34 | "import/no-deprecated": "warn",
35 | "import/no-extraneous-dependencies": "error",
36 | "import/no-unassigned-import": "warn",
37 | "no-cond-assign": "error",
38 | "no-duplicate-case": "error",
39 | "no-duplicate-imports": "error",
40 | "no-empty": [
41 | "error",
42 | {
43 | allowEmptyCatch: true,
44 | },
45 | ],
46 | "no-invalid-this": "error",
47 | "no-new-wrappers": "error",
48 | "no-param-reassign": "error",
49 | "no-redeclare": "error",
50 | "no-sequences": "error",
51 | "no-shadow": [
52 | "error",
53 | {
54 | hoist: "all",
55 | },
56 | ],
57 | "no-throw-literal": "error",
58 | "no-unsafe-finally": "error",
59 | "no-unused-labels": "error",
60 | "no-var": "warn",
61 | "no-void": "error",
62 | "prefer-const": "warn",
63 | },
64 | settings: {
65 | jsdoc: {
66 | tagNamePreference: {
67 | returns: "return",
68 | },
69 | },
70 | },
71 | };
72 |
--------------------------------------------------------------------------------
/functions/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "parserOptions": {
3 | // Required for certain syntax usages
4 | "ecmaVersion": 2017
5 | },
6 | "plugins": [
7 | "promise"
8 | ],
9 | "extends": "eslint:recommended",
10 | "rules": {
11 | // Removed rule "disallow the use of console" from recommended eslint rules
12 | "no-console": "off",
13 |
14 | // Removed rule "disallow multiple spaces in regular expressions" from recommended eslint rules
15 | "no-regex-spaces": "off",
16 |
17 | // Removed rule "disallow the use of debugger" from recommended eslint rules
18 | "no-debugger": "off",
19 |
20 | // Removed rule "disallow unused variables" from recommended eslint rules
21 | "no-unused-vars": "off",
22 |
23 | // Removed rule "disallow mixed spaces and tabs for indentation" from recommended eslint rules
24 | "no-mixed-spaces-and-tabs": "off",
25 |
26 | // Removed rule "disallow the use of undeclared variables unless mentioned in /*global */ comments" from recommended eslint rules
27 | "no-undef": "off",
28 |
29 | // Warn against template literal placeholder syntax in regular strings
30 | "no-template-curly-in-string": 1,
31 |
32 | // Warn if return statements do not either always or never specify values
33 | "consistent-return": 1,
34 |
35 | // Warn if no return statements in callbacks of array methods
36 | "array-callback-return": 1,
37 |
38 | // Require the use of === and !==
39 | "eqeqeq": 2,
40 |
41 | // Disallow the use of alert, confirm, and prompt
42 | "no-alert": 2,
43 |
44 | // Disallow the use of arguments.caller or arguments.callee
45 | "no-caller": 2,
46 |
47 | // Disallow null comparisons without type-checking operators
48 | "no-eq-null": 2,
49 |
50 | // Disallow the use of eval()
51 | "no-eval": 2,
52 |
53 | // Warn against extending native types
54 | "no-extend-native": 1,
55 |
56 | // Warn against unnecessary calls to .bind()
57 | "no-extra-bind": 1,
58 |
59 | // Warn against unnecessary labels
60 | "no-extra-label": 1,
61 |
62 | // Disallow leading or trailing decimal points in numeric literals
63 | "no-floating-decimal": 2,
64 |
65 | // Warn against shorthand type conversions
66 | "no-implicit-coercion": 1,
67 |
68 | // Warn against function declarations and expressions inside loop statements
69 | "no-loop-func": 1,
70 |
71 | // Disallow new operators with the Function object
72 | "no-new-func": 2,
73 |
74 | // Warn against new operators with the String, Number, and Boolean objects
75 | "no-new-wrappers": 1,
76 |
77 | // Disallow throwing literals as exceptions
78 | "no-throw-literal": 2,
79 |
80 | // Require using Error objects as Promise rejection reasons
81 | "prefer-promise-reject-errors": 2,
82 |
83 | // Enforce “for” loop update clause moving the counter in the right direction
84 | "for-direction": 2,
85 |
86 | // Enforce return statements in getters
87 | "getter-return": 2,
88 |
89 | // Disallow await inside of loops
90 | "no-await-in-loop": 2,
91 |
92 | // Disallow comparing against -0
93 | "no-compare-neg-zero": 2,
94 |
95 | // Warn against catch clause parameters from shadowing variables in the outer scope
96 | "no-catch-shadow": 1,
97 |
98 | // Disallow identifiers from shadowing restricted names
99 | "no-shadow-restricted-names": 2,
100 |
101 | // Enforce return statements in callbacks of array methods
102 | "callback-return": 2,
103 |
104 | // Require error handling in callbacks
105 | "handle-callback-err": 2,
106 |
107 | // Warn against string concatenation with __dirname and __filename
108 | "no-path-concat": 1,
109 |
110 | // Prefer using arrow functions for callbacks
111 | "prefer-arrow-callback": 1,
112 |
113 | // Return inside each then() to create readable and reusable Promise chains.
114 | // Forces developers to return console logs and http calls in promises.
115 | "promise/always-return": 2,
116 |
117 | //Enforces the use of catch() on un-returned promises
118 | "promise/catch-or-return": 2,
119 |
120 | // Warn against nested then() or catch() statements
121 | "promise/no-nesting": 1
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/functions/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled JavaScript files
2 | **/*.js
3 | **/*.js.map
4 |
5 | # Except the ESLint config file
6 | !.eslintrc.js
7 |
8 | # TypeScript v1 declaration files
9 | typings/
10 |
11 | # Node.js dependency directory
12 | node_modules/
13 |
--------------------------------------------------------------------------------
/functions/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "functions",
3 | "scripts": {
4 | "lint": "eslint \"src/**/*\"",
5 | "build": "tsc",
6 | "serve": "npm run build && firebase emulators:start --only functions",
7 | "shell": "npm run build && firebase functions:shell",
8 | "start": "npm run shell",
9 | "deploy": "firebase deploy --only functions",
10 | "logs": "firebase functions:log"
11 | },
12 | "engines": {
13 | "node": "12"
14 | },
15 | "main": "lib/index.js",
16 | "dependencies": {
17 | "firebase-admin": "^9.2.0",
18 | "firebase-functions": "^3.11.0"
19 | },
20 | "devDependencies": {
21 | "@typescript-eslint/eslint-plugin": "^3.9.1",
22 | "@typescript-eslint/parser": "^3.8.0",
23 | "eslint": "^7.6.0",
24 | "eslint-plugin-import": "^2.22.0",
25 | "typescript": "^3.8.0",
26 | "firebase-functions-test": "^0.2.0"
27 | },
28 | "private": true
29 | }
30 |
--------------------------------------------------------------------------------
/functions/src/index.ts:
--------------------------------------------------------------------------------
1 | import * as functions from "firebase-functions";
2 | import * as admin from "firebase-admin";
3 | admin.initializeApp();
4 |
5 | const db = admin.firestore();
6 | const fcm = admin.messaging();
7 |
8 | export const sendToDevice = functions.firestore
9 | .document("requests/{requestsID}")
10 | .onCreate(async (snapshot) => {
11 | const message = snapshot.data();
12 |
13 | const querySnapshot = await db
14 | .collection("users")
15 | .where("id", "==", message.receiveID)
16 | .get();
17 |
18 | const tokens = querySnapshot.docs.map((snap) => snap.data().token);
19 |
20 | const payload: admin.messaging.MessagingPayload = {
21 | notification: {
22 | title: "Admin",
23 | body: `Calling...`,
24 | icon: "your-icon-url",
25 | click_action: "FLUTTER_NOTIFICATION_CLICK",
26 | priority: "high",
27 | },
28 | };
29 |
30 | return fcm.sendToDevice(tokens, payload);
31 | });
32 |
33 | export const sendToTopic = functions.firestore
34 | .document("notifications/{notificationsId}")
35 | .onCreate(async (snapshot) => {
36 | const notification = snapshot.data();
37 |
38 | if (notification.all) {
39 | const querySnapshot = await db
40 | .collection("users")
41 | .where("key", "==", notification.key)
42 | .where("notifications", '==', true)
43 | .get();
44 |
45 | const tokens = querySnapshot.docs.map((snap) => snap.data().token);
46 |
47 | const payload: admin.messaging.MessagingPayload = {
48 | notification: {
49 | title: `${notification.title}`,
50 | body: `${notification.body}`,
51 | image: `${notification.urlToImage}`,
52 | icon: "your-icon-url",
53 | click_action: "FLUTTER_NOTIFICATION_CLICK", // required only for onResume or onLaunch callbacks
54 | priority: "high",
55 | },
56 | };
57 |
58 | return fcm.sendToDevice(tokens, payload);
59 | } else {
60 | const querySnapshot = await db
61 | .collection("users")
62 | .where("key", "==", notification.key)
63 | .where("id", "in", notification.members)
64 | .where("notifications", '==', true)
65 | .get();
66 |
67 | const tokens = querySnapshot.docs.map((snap) => snap.data().token);
68 |
69 | const payload: admin.messaging.MessagingPayload = {
70 | notification: {
71 | title: `${notification.title}`,
72 | body: `${notification.body}`,
73 | image: `${notification.urlToImage}`,
74 | icon: "your-icon-url",
75 | click_action: "FLUTTER_NOTIFICATION_CLICK", // required only for onResume or onLaunch callbacks
76 | priority: "high",
77 | },
78 | };
79 |
80 | return fcm.sendToDevice(tokens, payload);
81 | }
82 | });
83 |
--------------------------------------------------------------------------------
/functions/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "noImplicitReturns": true,
5 | "noUnusedLocals": true,
6 | "outDir": "lib",
7 | "sourceMap": true,
8 | "strict": true,
9 | "target": "es2017"
10 | },
11 | "compileOnSave": true,
12 | "include": [
13 | "src"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/images/avt.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/images/avt.jpg
--------------------------------------------------------------------------------
/images/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/images/background.jpg
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 9.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
2 | #include "Generated.xcconfig"
3 |
--------------------------------------------------------------------------------
/ios/Podfile:
--------------------------------------------------------------------------------
1 | # Uncomment this line to define a global platform for your project
2 | # platform :ios, '9.0'
3 |
4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency.
5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true'
6 |
7 | project 'Runner', {
8 | 'Debug' => :debug,
9 | 'Profile' => :release,
10 | 'Release' => :release,
11 | }
12 |
13 | def flutter_root
14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
15 | unless File.exist?(generated_xcode_build_settings_path)
16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
17 | end
18 |
19 | File.foreach(generated_xcode_build_settings_path) do |line|
20 | matches = line.match(/FLUTTER_ROOT\=(.*)/)
21 | return matches[1].strip if matches
22 | end
23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
24 | end
25 |
26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
27 |
28 | flutter_ios_podfile_setup
29 |
30 | target 'Runner' do
31 | use_frameworks!
32 | use_modular_headers!
33 |
34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
35 | end
36 |
37 | post_install do |installer|
38 | installer.pods_project.targets.each do |target|
39 | target.build_configurations.each do |config|
40 | config.build_settings['ENABLE_BITCODE'] = 'NO'
41 | config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '9.0'
42 | end
43 | end
44 | end
45 |
46 | # add the Firebase pod for Google Analytics
47 | pod 'Firebase/Analytics'
48 | # add pods for any other desired Firebase products
49 | # https://firebase.google.com/docs/ios/setup#available-pods
50 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 | import Firebase
4 |
5 | @UIApplicationMain
6 | @objc class AppDelegate: FlutterAppDelegate {
7 | override func application(
8 | _ application: UIApplication,
9 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
10 | ) -> Bool {
11 | if #available(iOS 10.0, *) {
12 | UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
13 | }
14 | FirebaseApp.configure()
15 | GeneratedPluginRegistrant.register(with: self)
16 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/GoogleService-Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CLIENT_ID
6 | 1037020081116-873uu6crie5druud34u9ef7caje18t0r.apps.googleusercontent.com
7 | REVERSED_CLIENT_ID
8 | com.googleusercontent.apps.1037020081116-873uu6crie5druud34u9ef7caje18t0r
9 | API_KEY
10 | AIzaSyClAZPb14oPd86pKnkRTFU9L6wdwXN03jQ
11 | GCM_SENDER_ID
12 | 1037020081116
13 | PLIST_VERSION
14 | 1
15 | BUNDLE_ID
16 | com.example.projectMessageDemo
17 | PROJECT_ID
18 | project-message-65c25
19 | STORAGE_BUCKET
20 | project-message-65c25.appspot.com
21 | IS_ADS_ENABLED
22 |
23 | IS_ANALYTICS_ENABLED
24 |
25 | IS_APPINVITE_ENABLED
26 |
27 | IS_GCM_ENABLED
28 |
29 | IS_SIGNIN_ENABLED
30 |
31 | GOOGLE_APP_ID
32 | 1:1037020081116:ios:813d5d711c73a56293fd19
33 | DATABASE_URL
34 | https://project-message-65c25.firebaseio.com
35 |
36 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | project_message_demo
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | FirebaseAppDelegateProxyEnabled
24 |
25 | LSRequiresIPhoneOS
26 |
27 | NSCameraUsageDescription
28 | $(PRODUCT_NAME) Camera Usage!
29 | NSMicrophoneUsageDescription
30 | $(PRODUCT_NAME) Microphone Usage!
31 | NSPhotoLibraryUsageDescription
32 | photos description.
33 | UILaunchStoryboardName
34 | LaunchScreen
35 | UIMainStoryboardFile
36 | Main
37 | UIStatusBarHidden
38 |
39 | UISupportedInterfaceOrientations
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationLandscapeLeft
43 | UIInterfaceOrientationLandscapeRight
44 |
45 | UISupportedInterfaceOrientations~ipad
46 |
47 | UIInterfaceOrientationPortrait
48 | UIInterfaceOrientationPortraitUpsideDown
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UIViewControllerBasedStatusBarAppearance
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/ios/build/XCBuildData/10c79b38152e34f2a9c51d06cab5c2c0-desc.xcbuild:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/build/XCBuildData/10c79b38152e34f2a9c51d06cab5c2c0-desc.xcbuild
--------------------------------------------------------------------------------
/ios/build/XCBuildData/4ad262fa702ad4022d24711c2ea76076-desc.xcbuild:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/build/XCBuildData/4ad262fa702ad4022d24711c2ea76076-desc.xcbuild
--------------------------------------------------------------------------------
/ios/build/XCBuildData/4e993dc3077a0c038363e034c90eb811-desc.xcbuild:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/build/XCBuildData/4e993dc3077a0c038363e034c90eb811-desc.xcbuild
--------------------------------------------------------------------------------
/ios/build/XCBuildData/6072702be6084f103dc13ed17afffb5f-desc.xcbuild:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/build/XCBuildData/6072702be6084f103dc13ed17afffb5f-desc.xcbuild
--------------------------------------------------------------------------------
/ios/build/XCBuildData/8be53b2722081bfcbc2104eb272b8dc8-desc.xcbuild:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/build/XCBuildData/8be53b2722081bfcbc2104eb272b8dc8-desc.xcbuild
--------------------------------------------------------------------------------
/ios/build/XCBuildData/BuildDescriptionCacheIndex-2f874282badcdaa4b07a7bb620484549:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/build/XCBuildData/BuildDescriptionCacheIndex-2f874282badcdaa4b07a7bb620484549
--------------------------------------------------------------------------------
/ios/build/XCBuildData/BuildDescriptionCacheIndex-67522bec3edf0f0dc585944b11ec7e1c:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/build/XCBuildData/BuildDescriptionCacheIndex-67522bec3edf0f0dc585944b11ec7e1c
--------------------------------------------------------------------------------
/ios/build/XCBuildData/build.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/build/XCBuildData/build.db
--------------------------------------------------------------------------------
/ios/build/XCBuildData/ced3ee9f042c373fc5cb613a54f24c90-desc.xcbuild:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lambiengcode/flutter-intercom-mobile/6f4d41e438700dae65973f52cfdbb254cfb3e48a/ios/build/XCBuildData/ced3ee9f042c373fc5cb613a54f24c90-desc.xcbuild
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:project_message_demo/src/app.dart';
3 | import 'package:project_message_demo/src/model/user.dart';
4 | import 'package:project_message_demo/src/service/auth.dart';
5 | import 'package:provider/provider.dart';
6 |
7 | void main() {
8 | runApp(MyApp());
9 | }
10 |
11 | class MyApp extends StatelessWidget {
12 | @override
13 | Widget build(BuildContext context) {
14 | return StreamProvider.value(
15 | value: AuthService().user,
16 | child: MaterialApp(
17 | debugShowCheckedModeBanner: false,
18 | title: 'Intercom',
19 | theme: ThemeData(
20 | brightness: Brightness.light,
21 | primarySwatch: Colors.blue,
22 | ),
23 | home: App(),
24 | ),
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/lib/src/animation/fade_animation.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:simple_animations/simple_animations/controlled_animation.dart';
3 | import 'package:simple_animations/simple_animations/multi_track_tween.dart';
4 |
5 | class FadeAnimation extends StatelessWidget {
6 | final double delay;
7 | final Widget child;
8 |
9 | FadeAnimation(this.delay, this.child);
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | final tween = MultiTrackTween([
14 | Track("opacity")
15 | .add(Duration(milliseconds: 500), Tween(begin: 0.0, end: 1.0)),
16 | Track("translateY").add(
17 | Duration(milliseconds: 500), Tween(begin: -30.0, end: 0.0),
18 | curve: Curves.easeOut)
19 | ]);
20 |
21 | return ControlledAnimation(
22 | delay: Duration(milliseconds: (500 * delay).round()),
23 | duration: tween.duration,
24 | tween: tween,
25 | child: child,
26 | builderWithChild: (context, child, animation) => Opacity(
27 | opacity: animation["opacity"],
28 | child: Transform.translate(
29 | offset: Offset(0, animation["translateY"]), child: child),
30 | ),
31 | );
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/lib/src/app.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'package:cloud_firestore/cloud_firestore.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter/services.dart';
5 | import 'package:project_message_demo/src/model/user.dart';
6 | import 'package:project_message_demo/src/page/auth/auth_page.dart';
7 | import 'package:project_message_demo/src/page/call/call_page.dart';
8 | import 'package:project_message_demo/src/page/call/receive_call_page.dart';
9 | import 'package:project_message_demo/src/page/home_page/home_page.dart';
10 | import 'package:provider/provider.dart';
11 |
12 | class App extends StatefulWidget {
13 | static bool dark;
14 | static String systemLocales = Platform.localeName.substring(0, 2);
15 |
16 | @override
17 | State createState() => _AppState();
18 | }
19 |
20 | class _AppState extends State {
21 | @override
22 | void initState() {
23 | SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle(
24 | statusBarColor: Colors.transparent,
25 | ));
26 | super.initState();
27 | }
28 |
29 | @override
30 | Widget build(BuildContext context) {
31 | final user = Provider.of(context);
32 | final size = MediaQuery.of(context).size;
33 |
34 | return user == null
35 | ? AuthenticatePage()
36 | : StreamBuilder(
37 | stream: Firestore.instance
38 | .collection('requests')
39 | .where('receiveID', isEqualTo: user.uid)
40 | .where('completed', isEqualTo: false)
41 | .snapshots(),
42 | builder:
43 | (BuildContext context, AsyncSnapshot snapshot) {
44 | if (!snapshot.hasData) {
45 | return Container();
46 | }
47 |
48 | return snapshot.data.documents.length != 0
49 | ? snapshot.data.documents[0]['request']
50 | ? ReceiveCallPage(
51 | idSend: snapshot.data.documents[0]['idSend'],
52 | index: snapshot.data.documents[0].reference,
53 | )
54 | : CallPage(
55 | idSend: snapshot.data.documents[0]['idSend'],
56 | index: snapshot.data.documents[0].reference,
57 | info: snapshot.data.documents[0],
58 | )
59 | : HomePage(
60 | uid: user.uid,
61 | size: size,
62 | );
63 | });
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/lib/src/model/user.dart:
--------------------------------------------------------------------------------
1 | class User {
2 | final String uid;
3 |
4 | User({this.uid});
5 | }
6 |
--------------------------------------------------------------------------------
/lib/src/page/auth/auth_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:flutter/services.dart';
3 | import 'package:project_message_demo/src/page/auth/login_page.dart';
4 |
5 | class AuthenticatePage extends StatefulWidget {
6 | @override
7 | State createState() => _AuthenticatePageState();
8 | }
9 |
10 | class _AuthenticatePageState extends State {
11 | bool showSignIn = true;
12 |
13 | @override
14 | void initState() {
15 | SystemChrome.setPreferredOrientations([
16 | DeviceOrientation.portraitDown,
17 | DeviceOrientation.portraitUp,
18 | ]);
19 | super.initState();
20 | }
21 |
22 | @override
23 | Widget build(BuildContext context) {
24 | return LoginPage();
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lib/src/page/auth/forgot_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:project_message_demo/src/animation/fade_animation.dart';
3 | import 'package:project_message_demo/src/service/auth.dart';
4 | import 'package:project_message_demo/src/widget/general/loading.dart';
5 |
6 | class ForgotPage extends StatefulWidget {
7 | @override
8 | _ForgotPageState createState() => _ForgotPageState();
9 | }
10 |
11 | class _ForgotPageState extends State {
12 | final _formKey = GlobalKey();
13 | String email;
14 | bool loading = false;
15 |
16 | @override
17 | Widget build(BuildContext context) {
18 | final sizeHeight = MediaQuery.of(context).size.height;
19 | final sizeWidth = MediaQuery.of(context).size.width;
20 | final AuthService _auth = AuthService();
21 |
22 | return loading
23 | ? Loading()
24 | : Scaffold(
25 | backgroundColor: Colors.white,
26 | body: Stack(
27 | children: [
28 | Form(
29 | key: _formKey,
30 | child: SingleChildScrollView(
31 | child: Column(
32 | crossAxisAlignment: CrossAxisAlignment.start,
33 | mainAxisAlignment: MainAxisAlignment.center,
34 | children: [
35 | SizedBox(
36 | height: sizeHeight / 3,
37 | ),
38 | Padding(
39 | padding:
40 | EdgeInsets.symmetric(horizontal: sizeHeight / 20),
41 | child: Column(
42 | crossAxisAlignment: CrossAxisAlignment.start,
43 | children: [
44 | Row(
45 | mainAxisAlignment:
46 | MainAxisAlignment.spaceBetween,
47 | children: [
48 | FadeAnimation(
49 | 1.5,
50 | Text(
51 | "Reset PW",
52 | style: TextStyle(
53 | color: Colors.white70,
54 | fontWeight: FontWeight.bold,
55 | fontSize: 30),
56 | )),
57 | FadeAnimation(
58 | 1.5,
59 | IconButton(
60 | icon: Icon(
61 | Icons.exit_to_app,
62 | color: Colors.white,
63 | size: sizeWidth / 12,
64 | ),
65 | onPressed: () {
66 | Navigator.of(context).pop(context);
67 | },
68 | ),
69 | ),
70 | ],
71 | ),
72 | SizedBox(
73 | height: 20,
74 | ),
75 | FadeAnimation(
76 | 1.7,
77 | Container(
78 | decoration: BoxDecoration(
79 | borderRadius: BorderRadius.circular(10),
80 | color: Colors.white,
81 | boxShadow: [
82 | BoxShadow(
83 | color: Color.fromRGBO(
84 | 196, 135, 198, .3),
85 | blurRadius: 20,
86 | offset: Offset(0, 10),
87 | )
88 | ]),
89 | child: Column(
90 | children: [
91 | Container(
92 | padding: EdgeInsets.all(10),
93 | child: TextFormField(
94 | validator: (val) => val.length == 0
95 | ? 'Enter email'
96 | : null,
97 | onChanged: (val) =>
98 | email = val.trim(),
99 | decoration: InputDecoration(
100 | border: InputBorder.none,
101 | hintText: "Email",
102 | hintStyle: TextStyle(
103 | color: Colors.grey)),
104 | ),
105 | ),
106 | ],
107 | ),
108 | )),
109 | SizedBox(
110 | height: 20.0,
111 | ),
112 | FadeAnimation(
113 | 1.9,
114 | Row(
115 | mainAxisAlignment: MainAxisAlignment.center,
116 | children: [
117 | GestureDetector(
118 | onTap: () async {
119 | if (_formKey.currentState
120 | .validate()) {
121 | setState(() {
122 | loading = true;
123 | });
124 | dynamic result = await _auth
125 | .sendPasswordResetEmail(email);
126 | if (result == null) {
127 | setState(() {
128 | loading = false;
129 | });
130 | } else {
131 | Navigator.of(context)
132 | .pop(context);
133 | }
134 | }
135 | },
136 | child: Container(
137 | height: 50,
138 | width: 130.0,
139 | margin: EdgeInsets.symmetric(
140 | horizontal: 60),
141 | decoration: BoxDecoration(
142 | borderRadius:
143 | BorderRadius.circular(50),
144 | color: Colors.deepPurple[600],
145 | ),
146 | child: Center(
147 | child: Text(
148 | "Request",
149 | style: TextStyle(
150 | color: Colors.white),
151 | ),
152 | ),
153 | ),
154 | )
155 | ],
156 | )),
157 | SizedBox(
158 | height: 20,
159 | ),
160 | ],
161 | ),
162 | )
163 | ],
164 | ),
165 | ),
166 | ),
167 | ],
168 | ),
169 | );
170 | }
171 | }
172 |
--------------------------------------------------------------------------------
/lib/src/page/auth/login_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:project_message_demo/src/animation/fade_animation.dart';
3 | import 'package:project_message_demo/src/service/auth.dart';
4 | import 'package:project_message_demo/src/widget/general/loading.dart';
5 |
6 | class LoginPage extends StatefulWidget {
7 | @override
8 | _LoginPageState createState() => _LoginPageState();
9 | }
10 |
11 | class _LoginPageState extends State {
12 | final AuthService _auth = AuthService();
13 |
14 | final _formKey = GlobalKey();
15 |
16 | FocusNode textFieldFocus = FocusNode();
17 | String email = '';
18 | String password = '';
19 |
20 | bool hidePassword = true;
21 | bool loading = false;
22 |
23 | hideKeyboard() => textFieldFocus.unfocus();
24 |
25 | @override
26 | Widget build(BuildContext context) {
27 | final sizeHeight = MediaQuery.of(context).size.height;
28 | final sizeWidth = MediaQuery.of(context).size.width;
29 |
30 | return loading
31 | ? Loading()
32 | : Scaffold(
33 | backgroundColor: Colors.white,
34 | body: Stack(
35 | children: [
36 | Form(
37 | key: _formKey,
38 | child: SingleChildScrollView(
39 | child: Column(
40 | crossAxisAlignment: CrossAxisAlignment.end,
41 | mainAxisAlignment: MainAxisAlignment.center,
42 | children: [
43 | SizedBox(
44 | height: sizeHeight * .18,
45 | ),
46 | Container(
47 | height: sizeHeight * .26,
48 | decoration: BoxDecoration(
49 | image: DecorationImage(
50 | image: AssetImage('images/background.jpg'),
51 | fit: BoxFit.fitHeight,
52 | ),
53 | ),
54 | ),
55 | SizedBox(
56 | height: sizeHeight * .1,
57 | ),
58 | Padding(
59 | padding:
60 | EdgeInsets.symmetric(horizontal: sizeWidth * 0.1),
61 | child: Column(
62 | crossAxisAlignment: CrossAxisAlignment.start,
63 | children: [
64 | FadeAnimation(
65 | 1.7,
66 | Container(
67 | decoration: BoxDecoration(
68 | borderRadius: BorderRadius.circular(6.0),
69 | color: Colors.white,
70 | border: Border.all(
71 | color: Colors.grey.shade200,
72 | width: 1.2,
73 | )),
74 | child: Column(
75 | children: [
76 | Container(
77 | padding: EdgeInsets.all(8),
78 | decoration: BoxDecoration(
79 | border: Border(
80 | bottom: BorderSide(
81 | color: Colors.grey[200]))),
82 | child: TextFormField(
83 | style: TextStyle(
84 | color: Colors.black87,
85 | fontSize: sizeWidth / 24,
86 | fontWeight: FontWeight.w400,
87 | ),
88 | validator: (val) => val.length == 0
89 | ? 'Enter your Email'
90 | : null,
91 | onChanged: (val) =>
92 | email = val.trim(),
93 | decoration: InputDecoration(
94 | contentPadding: EdgeInsets.only(
95 | left: 12.0,
96 | ),
97 | border: InputBorder.none,
98 | hintText: "Email",
99 | hintStyle: TextStyle(
100 | color: Colors.grey,
101 | fontSize: sizeWidth / 24,
102 | fontWeight: FontWeight.w400,
103 | ),
104 | ),
105 | ),
106 | ),
107 | Container(
108 | padding: EdgeInsets.all(8),
109 | child: TextFormField(
110 | style: TextStyle(
111 | color: Colors.black87,
112 | fontSize: sizeWidth / 24,
113 | fontWeight: FontWeight.w400,
114 | ),
115 | focusNode: textFieldFocus,
116 | validator: (val) => val.length == 0
117 | ? 'Enter your password'
118 | : null,
119 | onChanged: (val) =>
120 | password = val.trim(),
121 | obscureText: hidePassword,
122 | decoration: InputDecoration(
123 | contentPadding: EdgeInsets.only(
124 | left: 12.0,
125 | ),
126 | border: InputBorder.none,
127 | hintText: "Password",
128 | hintStyle: TextStyle(
129 | color: Colors.grey,
130 | fontSize: sizeWidth / 24,
131 | fontWeight: FontWeight.w400,
132 | ),
133 | ),
134 | ),
135 | )
136 | ],
137 | ),
138 | ),
139 | ),
140 | SizedBox(
141 | height: 10,
142 | ),
143 | SizedBox(
144 | height: 32,
145 | ),
146 | FadeAnimation(
147 | 1.9,
148 | GestureDetector(
149 | onTap: () async {
150 | if (_formKey.currentState.validate()) {
151 | setState(() {
152 | loading = true;
153 | });
154 | dynamic result = await _auth
155 | .signInWithEmailAndPassword(
156 | email, password);
157 | if (result == null) {
158 | setState(() {
159 | loading = false;
160 | });
161 | } else {}
162 | }
163 | },
164 | child: Container(
165 | height: sizeHeight * 0.065,
166 | decoration: BoxDecoration(
167 | borderRadius: BorderRadius.circular(6.0),
168 | color: Colors.blue,
169 | ),
170 | child: Center(
171 | child: Text(
172 | "Login",
173 | style: TextStyle(
174 | color: Colors.white.withOpacity(.88),
175 | fontSize: 14.0,
176 | fontWeight: FontWeight.w800,
177 | ),
178 | ),
179 | ),
180 | ),
181 | ),
182 | ),
183 | SizedBox(
184 | height: 10,
185 | ),
186 | SizedBox(
187 | height: 30,
188 | ),
189 | ],
190 | ),
191 | )
192 | ],
193 | ),
194 | ),
195 | ),
196 | ],
197 | ),
198 | );
199 | }
200 | }
201 |
--------------------------------------------------------------------------------
/lib/src/page/call/call_page.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:convert';
3 | import 'dart:math';
4 | import 'package:cloud_firestore/cloud_firestore.dart';
5 | import 'package:firebase_database/firebase_database.dart';
6 | import 'package:flutter/material.dart';
7 | import 'package:flutter_webrtc/webrtc.dart';
8 | import 'package:project_message_demo/src/widget/general/photo_viewer.dart';
9 | import 'package:sdp_transform/sdp_transform.dart';
10 |
11 | class CallPage extends StatefulWidget {
12 | final String idSend;
13 | final DocumentSnapshot info;
14 | final index;
15 |
16 | CallPage({this.idSend, this.index, this.info});
17 |
18 | @override
19 | State createState() => _CallPageState();
20 | }
21 |
22 | class _CallPageState extends State {
23 | Timer _timmerInstance;
24 | int _start = 0;
25 | String _timmer = '';
26 |
27 | //VideoCallVariables
28 | RTCPeerConnection _peerConnection;
29 | MediaStream _localStream;
30 | RTCVideoRenderer _localRenderer = new RTCVideoRenderer();
31 | RTCVideoRenderer _remoteRenderer = new RTCVideoRenderer();
32 | bool isFrontCamera = true;
33 | int id = 12022000;
34 |
35 | void startTimmer() {
36 | var oneSec = Duration(seconds: 1);
37 | _timmerInstance = Timer.periodic(
38 | oneSec,
39 | (Timer timer) => setState(() {
40 | if (_start < 0) {
41 | _timmerInstance.cancel();
42 | } else {
43 | _start = _start + 1;
44 | _timmer = getTimerTime(_start);
45 | }
46 | }));
47 | }
48 |
49 | String getTimerTime(int start) {
50 | int minutes = (start ~/ 60);
51 | String sMinute = '';
52 | if (minutes.toString().length == 1) {
53 | sMinute = '0' + minutes.toString();
54 | } else
55 | sMinute = minutes.toString();
56 |
57 | int seconds = (start % 60);
58 | String sSeconds = '';
59 | if (seconds.toString().length == 1) {
60 | sSeconds = '0' + seconds.toString();
61 | } else
62 | sSeconds = seconds.toString();
63 |
64 | return sMinute + ':' + sSeconds;
65 | }
66 |
67 | Future _responce(responce) async {
68 | _peerConnection.close();
69 | _localStream.dispose();
70 | _localRenderer.dispose();
71 | _timmerInstance.cancel();
72 | Firestore.instance.runTransaction((Transaction transaction) async {
73 | DocumentSnapshot snapshot = await transaction.get(widget.index);
74 | await transaction.update(widget.index, {
75 | 'completed': true,
76 | 'responce': responce,
77 | 'responcedTime': DateTime.now(),
78 | });
79 | });
80 | }
81 |
82 | @override
83 | void initState() {
84 | super.initState();
85 |
86 | initRenderers();
87 | _createPeerConnection().then((pc) {
88 | _peerConnection = pc;
89 | _setRemoteDescription(
90 | jsonDecode(widget.info['sdp'])['sdp']['sdp'].toString());
91 | });
92 | startTimmer();
93 | }
94 |
95 | @override
96 | void dispose() {
97 | _peerConnection.close();
98 | _localStream.dispose();
99 | _localRenderer.dispose();
100 | _timmerInstance.cancel();
101 | super.dispose();
102 | }
103 |
104 | initRenderers() async {
105 | await _localRenderer.initialize();
106 | await _remoteRenderer.initialize();
107 | }
108 |
109 | void switchCamera() async {
110 | if (_localStream != null) {
111 | bool value = await _localStream.getVideoTracks()[0].switchCamera();
112 | while (value == this.isFrontCamera)
113 | value = await _localStream.getVideoTracks()[0].switchCamera();
114 | this.isFrontCamera = value;
115 | }
116 | }
117 |
118 | void _createAnswer() async {
119 | RTCSessionDescription description = await _peerConnection.createAnswer({
120 | 'offerToReceiveVideo': 1,
121 | 'offerToReceiveAudio': 1,
122 | });
123 |
124 | var session = parse(description.sdp);
125 | String sdp = write(session, null);
126 | await sendSdp(sdp);
127 |
128 | _peerConnection.setLocalDescription(description);
129 |
130 | _peerConnection.onIceCandidate = (event) => {
131 | sendCandidates(event.candidate.toString(), event.sdpMid.toString(),
132 | event.sdpMlineIndex),
133 | };
134 | }
135 |
136 | void _setRemoteDescription(sdp) async {
137 | RTCSessionDescription description = new RTCSessionDescription(sdp, 'offer');
138 | await _peerConnection.setRemoteDescription(description);
139 | _createAnswer();
140 | }
141 |
142 | _createPeerConnection() async {
143 | Map configuration = {
144 | "iceServers": [
145 | {"url": "stun:stun.l.google.com:19302"},
146 | ]
147 | };
148 |
149 | final Map offerSdpConstraints = {
150 | "mandatory": {
151 | "OfferToReceiveAudio": true,
152 | "OfferToReceiveVideo": true,
153 | },
154 | "optional": [],
155 | };
156 |
157 | _localStream = await _getUserMedia();
158 |
159 | RTCPeerConnection pc =
160 | await createPeerConnection(configuration, offerSdpConstraints);
161 | // if (pc != null) print(pc);
162 | pc.addStream(_localStream);
163 |
164 | pc.onIceCandidate = (e) {
165 | if (e.candidate != null) {
166 | print(json.encode({
167 | 'candidate': e.candidate.toString(),
168 | 'sdpMid': e.sdpMid.toString(),
169 | 'sdpMlineIndex': e.sdpMlineIndex,
170 | }));
171 | }
172 | };
173 |
174 | pc.onIceConnectionState = (e) {
175 | print(e);
176 | };
177 |
178 | pc.onAddStream = (stream) {
179 | print('addStream: ' + stream.id);
180 | _remoteRenderer.srcObject = stream;
181 | };
182 |
183 | return pc;
184 | }
185 |
186 | String _chars =
187 | 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890';
188 | Random _rnd = Random();
189 |
190 | String getRandomString(int length) => String.fromCharCodes(Iterable.generate(
191 | length, (_) => _chars.codeUnitAt(_rnd.nextInt(_chars.length))));
192 |
193 | Future sendCandidates(
194 | String candidate,
195 | String sdpMid,
196 | int sdpMLineIndex,
197 | ) async {
198 | DatabaseReference _candidateRef = FirebaseDatabase().reference();
199 | String jsonString =
200 | '{"ice":{"candidate":${jsonEncode(candidate)},"sdpMid":$sdpMid,"sdpMlineIndex":$sdpMLineIndex}}';
201 | _candidateRef.child(getRandomString(20)).set({
202 | 'sender': id,
203 | 'message': jsonString,
204 | });
205 | setState(() {
206 | id++;
207 | });
208 | _candidateRef.remove();
209 | }
210 |
211 | Future sendSdp(
212 | String sdp,
213 | ) async {
214 | DatabaseReference _sdpRef = FirebaseDatabase().reference();
215 | String jsonString = '{"sdp":{"type":"answer","sdp":${jsonEncode(sdp)}}}';
216 | _sdpRef.child(getRandomString(20)).set({
217 | 'sender': id,
218 | 'message': jsonString,
219 | });
220 | setState(() {
221 | id++;
222 | });
223 | _sdpRef.remove();
224 | }
225 |
226 | _getUserMedia() async {
227 | final Map mediaConstraints = {
228 | 'audio': true,
229 | 'video': {
230 | 'facingMode': 'user',
231 | },
232 | };
233 |
234 | MediaStream stream = await navigator.getUserMedia(mediaConstraints);
235 |
236 | // _localStream = stream;
237 | _localRenderer.srcObject = stream;
238 | _localRenderer.mirror = false;
239 |
240 | // _peerConnection.addStream(stream);
241 |
242 | return stream;
243 | }
244 |
245 | @override
246 | Widget build(BuildContext context) {
247 | Size size = MediaQuery.of(context).size;
248 | return Scaffold(
249 | body: Container(
250 | height: size.height,
251 | width: size.width,
252 | child: Column(
253 | crossAxisAlignment: CrossAxisAlignment.center,
254 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
255 | children: [
256 | Stack(
257 | children: [
258 | Container(
259 | width: size.width,
260 | height: size.height - size.width * .15,
261 | child: _remoteRenderer.textureId == null
262 | ? Container()
263 | : FittedBox(
264 | fit: BoxFit.cover,
265 | child: new Center(
266 | child: new SizedBox(
267 | width: size.height * 1.34,
268 | height: size.height,
269 | child: new Transform(
270 | transform: Matrix4.identity()..rotateY(0.0),
271 | alignment: FractionalOffset.center,
272 | child: new Texture(
273 | textureId: _remoteRenderer.textureId),
274 | ),
275 | ),
276 | ),
277 | ),
278 | ),
279 | Positioned(
280 | top: 40.0,
281 | left: 15.0,
282 | child: Column(
283 | children: [
284 | Text(
285 | _timmer,
286 | style: TextStyle(
287 | color: _remoteRenderer.textureId == null
288 | ? Colors.white
289 | : Colors.black,
290 | fontSize: size.width / 26.5,
291 | ),
292 | ),
293 | SizedBox(
294 | height: 8.0,
295 | ),
296 | Container(
297 | height: size.width * .54,
298 | width: size.width * .32,
299 | decoration: BoxDecoration(
300 | borderRadius: BorderRadius.all(Radius.circular(6.0)),
301 | border:
302 | Border.all(color: Colors.blueAccent, width: 2.0),
303 | ),
304 | child: _localRenderer.textureId == null
305 | ? Container()
306 | : SizedBox(
307 | width: size.height,
308 | height: size.height,
309 | child: new Transform(
310 | transform: Matrix4.identity()
311 | ..rotateY(
312 | isFrontCamera ? -pi : 0.0,
313 | ),
314 | alignment: FractionalOffset.center,
315 | child: new Texture(
316 | textureId: _localRenderer.textureId),
317 | ),
318 | ),
319 | ),
320 | SizedBox(
321 | height: 12.0,
322 | ),
323 | GestureDetector(
324 | onTap: () {
325 | Navigator.of(context).push(
326 | MaterialPageRoute(
327 | builder: (context) => PhotoViewer(
328 | image: widget.info['urlToImage'],
329 | ),
330 | ),
331 | );
332 | },
333 | child: Container(
334 | height: size.width * .3,
335 | width: size.width * .3,
336 | decoration: BoxDecoration(
337 | borderRadius:
338 | BorderRadius.all(Radius.circular(6.0)),
339 | border: Border.all(
340 | color: Colors.blueAccent, width: 2.0),
341 | image: DecorationImage(
342 | image: widget.info['urlToImage'] == ''
343 | ? AssetImage('images/avt.jpg')
344 | : NetworkImage(widget.info['urlToImage']),
345 | fit: BoxFit.cover,
346 | ),
347 | ),
348 | ),
349 | ),
350 | SizedBox(
351 | height: 8.0,
352 | ),
353 | GestureDetector(
354 | onTap: () {
355 | switchCamera();
356 | },
357 | child: Container(
358 | height: size.width * .125,
359 | width: size.width * .125,
360 | decoration: BoxDecoration(
361 | borderRadius:
362 | BorderRadius.all(Radius.circular(4.0)),
363 | border: Border.all(
364 | color: Colors.blueAccent, width: 2.0),
365 | color: Colors.blueAccent,
366 | ),
367 | alignment: Alignment.center,
368 | child: Icon(
369 | Icons.switch_camera,
370 | color: Colors.white,
371 | size: size.width / 14.0,
372 | ),
373 | ),
374 | ),
375 | ],
376 | ),
377 | ),
378 | ],
379 | ),
380 | Row(
381 | children: [
382 | Expanded(
383 | flex: 1,
384 | child: GestureDetector(
385 | onTap: () async {
386 | await _responce('Reject');
387 | },
388 | child: Container(
389 | height: size.width * .15,
390 | decoration: BoxDecoration(
391 | color: Colors.redAccent,
392 | ),
393 | child: Icon(
394 | Icons.close,
395 | color: Colors.white,
396 | size: size.width / 14.0,
397 | ),
398 | ),
399 | ),
400 | ),
401 | Expanded(
402 | flex: 1,
403 | child: GestureDetector(
404 | onTap: () async {
405 | await _responce('Accept');
406 | },
407 | child: Container(
408 | height: size.width * .15,
409 | decoration: BoxDecoration(
410 | color: Colors.green,
411 | ),
412 | child: Icon(
413 | Icons.check,
414 | color: Colors.white,
415 | size: size.width / 14.0,
416 | ),
417 | ),
418 | ),
419 | ),
420 | ],
421 | ),
422 | ],
423 | ),
424 | ),
425 | );
426 | }
427 | }
428 |
--------------------------------------------------------------------------------
/lib/src/page/call/receive_call_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:assets_audio_player/assets_audio_player.dart';
2 | import 'package:cloud_firestore/cloud_firestore.dart';
3 | import 'package:flutter/material.dart';
4 |
5 | class ReceiveCallPage extends StatefulWidget {
6 | final String idSend;
7 | final index;
8 |
9 | ReceiveCallPage({this.idSend, this.index});
10 |
11 | @override
12 | State createState() => _ReceiveCallPageState();
13 | }
14 |
15 | class _ReceiveCallPageState extends State {
16 | final assetsAudioPlayer = AssetsAudioPlayer();
17 | Future _responce(bool accept) async {
18 | Firestore.instance.runTransaction((Transaction transaction) async {
19 | await transaction.update(widget.index, {
20 | 'request': false,
21 | 'completed': !accept,
22 | 'responce': accept == false ? 'Rejected Call' : '',
23 | 'responcedTime': DateTime.now(),
24 | });
25 | });
26 | assetsAudioPlayer.stop();
27 | }
28 |
29 | playLocal() async {
30 | try {
31 | await assetsAudioPlayer.open(
32 | Audio('assets/calling.mp3'),
33 | loopMode: LoopMode.single,
34 | );
35 | } catch (t) {
36 | //stream unreachable
37 | }
38 | }
39 |
40 | @override
41 | void initState() {
42 | super.initState();
43 | playLocal();
44 | }
45 |
46 | @override
47 | void dispose() {
48 | super.dispose();
49 | assetsAudioPlayer.stop();
50 | }
51 |
52 | @override
53 | Widget build(BuildContext context) {
54 | Size size = MediaQuery.of(context).size;
55 | return Scaffold(
56 | body: Container(
57 | height: size.height,
58 | width: size.width,
59 | child: Column(
60 | crossAxisAlignment: CrossAxisAlignment.center,
61 | children: [
62 | SizedBox(
63 | height: size.height * 0.15,
64 | ),
65 | StreamBuilder(
66 | stream: Firestore.instance
67 | .collection('users')
68 | .where('id', isEqualTo: widget.idSend)
69 | .snapshots(),
70 | builder: (BuildContext context,
71 | AsyncSnapshot snapshot) {
72 | if (!snapshot.hasData) {
73 | return Container();
74 | }
75 |
76 | return Column(
77 | children: [
78 | Text(
79 | 'Incoming Call',
80 | style: TextStyle(
81 | color: Colors.green.shade700,
82 | fontSize: size.width / 18.4,
83 | fontWeight: FontWeight.w400,
84 | ),
85 | ),
86 | SizedBox(
87 | height: size.height * .08,
88 | ),
89 | Container(
90 | height: size.height * .18,
91 | width: size.height * .18,
92 | decoration: BoxDecoration(
93 | border: Border.all(
94 | color: Colors.grey.shade400,
95 | width: .8,
96 | ),
97 | borderRadius: BorderRadius.all(Radius.circular(30.0)),
98 | image: DecorationImage(
99 | image: snapshot.data.documents[0]['urlToImage'] == ''
100 | ? AssetImage('images/avt.jpg')
101 | : NetworkImage(
102 | snapshot.data.documents[0]['urlToImage'],
103 | ),
104 | fit: BoxFit.cover,
105 | ),
106 | ),
107 | ),
108 | SizedBox(
109 | height: 24.0,
110 | ),
111 | Text(
112 | snapshot.data.documents[0]['username'],
113 | style: TextStyle(
114 | color: Colors.blueAccent,
115 | fontSize: size.width / 16.8,
116 | fontWeight: FontWeight.w600,
117 | ),
118 | ),
119 | SizedBox(
120 | height: size.height * .24,
121 | ),
122 | Row(
123 | mainAxisAlignment: MainAxisAlignment.spaceEvenly,
124 | children: [
125 | GestureDetector(
126 | onTap: () async {
127 | await _responce(false);
128 | },
129 | child: Container(
130 | height: size.width * .18,
131 | width: size.width * .18,
132 | decoration: BoxDecoration(
133 | shape: BoxShape.circle,
134 | color: Colors.redAccent,
135 | ),
136 | child: Icon(
137 | Icons.call_end,
138 | color: Colors.white,
139 | size: size.width / 18.0,
140 | ),
141 | ),
142 | ),
143 | GestureDetector(
144 | onTap: () async {
145 | await _responce(true);
146 | },
147 | child: Container(
148 | height: size.width * .18,
149 | width: size.width * .18,
150 | decoration: BoxDecoration(
151 | shape: BoxShape.circle,
152 | color: Colors.green,
153 | ),
154 | child: Icon(
155 | Icons.call,
156 | color: Colors.white,
157 | size: size.width / 18.0,
158 | ),
159 | ),
160 | ),
161 | ],
162 | ),
163 | ],
164 | );
165 | },
166 | ),
167 | ],
168 | ),
169 | ),
170 | );
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/lib/src/page/home_page/home_page.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 | import 'package:cloud_firestore/cloud_firestore.dart';
4 | import 'package:firebase_messaging/firebase_messaging.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:project_message_demo/src/page/notification_page/notification_page.dart';
7 | import 'package:project_message_demo/src/page/receive_page/receive_page.dart';
8 |
9 | class HomePage extends StatefulWidget {
10 | final String uid;
11 | final Size size;
12 | HomePage({
13 | this.uid,
14 | this.size,
15 | });
16 | @override
17 | State createState() => _HomePageState();
18 | }
19 |
20 | class _HomePageState extends State with WidgetsBindingObserver {
21 | final Firestore _db = Firestore.instance;
22 | final FirebaseMessaging _fcm = FirebaseMessaging();
23 | StreamSubscription iosSubscription;
24 |
25 | _saveDeviceToken() async {
26 | // Get the current user
27 | String uid = widget.uid;
28 | print(widget.uid);
29 | // FirebaseUser user = await _auth.currentUser();
30 |
31 | // Get the token for this device
32 | String fcmToken = await _fcm.getToken();
33 |
34 | // Save it to Firestore
35 | if (fcmToken != null) {
36 | var tokens = _db.collection('users').document(uid);
37 |
38 | await tokens.updateData({
39 | 'token': fcmToken,
40 | });
41 | }
42 | }
43 |
44 | @override
45 | void initState() {
46 | super.initState();
47 |
48 | if (Platform.isIOS) {
49 | iosSubscription = _fcm.onIosSettingsRegistered.listen((data) {
50 | _saveDeviceToken();
51 | });
52 |
53 | _fcm.requestNotificationPermissions(IosNotificationSettings());
54 | } else {
55 | _saveDeviceToken();
56 | }
57 |
58 | _fcm.configure(
59 | onMessage: (Map message) async {
60 | message['notification']['title'] == 'Admin'
61 | ? print('lambiengcode')
62 | : showDialog(
63 | context: context,
64 | barrierDismissible: false, // user must tap button!
65 | builder: (BuildContext context) {
66 | return AlertDialog(
67 | title: Text(
68 | message['notification']['title'],
69 | style: TextStyle(
70 | color: Colors.black,
71 | fontSize: widget.size.width / 21.5,
72 | fontWeight: FontWeight.bold,
73 | ),
74 | ),
75 | content: SingleChildScrollView(
76 | child: ListBody(
77 | children: [
78 | Text(
79 | message['notification']['body'],
80 | style: TextStyle(
81 | color: Colors.grey.shade800,
82 | fontSize: widget.size.width / 25.0,
83 | fontWeight: FontWeight.w400,
84 | ),
85 | ),
86 | ],
87 | ),
88 | ),
89 | actions: [
90 | TextButton(
91 | child: Text('Close'),
92 | onPressed: () {
93 | Navigator.of(context).pop();
94 | },
95 | ),
96 | TextButton(
97 | child: Text('Notifications'),
98 | onPressed: () {
99 | Navigator.of(context).pop();
100 | Navigator.of(context).push(
101 | MaterialPageRoute(
102 | builder: (context) => NotificationPage(),
103 | ),
104 | );
105 | },
106 | ),
107 | ],
108 | );
109 | },
110 | );
111 | },
112 | );
113 | }
114 |
115 | @override
116 | Widget build(BuildContext context) {
117 | return Scaffold(
118 | body: ReceivePage(),
119 | );
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/lib/src/page/notification_page/notification_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_icons/flutter_icons.dart';
4 | import 'package:project_message_demo/src/widget/notification_widget/notification_item.dart';
5 | import 'package:provider/provider.dart';
6 |
7 | import '../../model/user.dart';
8 |
9 | class NotificationPage extends StatefulWidget {
10 | @override
11 | State createState() => _NotificationPageState();
12 | }
13 |
14 | class _NotificationPageState extends State {
15 | Future _updateNotification(index) async {
16 | Firestore.instance.runTransaction((Transaction transaction) async {
17 | DocumentSnapshot snapshot = await transaction.get(index);
18 | bool notification = snapshot['notifications'];
19 | await transaction.update(index, {
20 | 'notifications': !notification,
21 | });
22 | });
23 | }
24 |
25 | @override
26 | Widget build(BuildContext context) {
27 | final user = Provider.of(context);
28 | final sizeWidth = MediaQuery.of(context).size.width;
29 | return Scaffold(
30 | appBar: AppBar(
31 | brightness: Brightness.light,
32 | elevation: 1.5,
33 | backgroundColor: Colors.white,
34 | centerTitle: true,
35 | leading: IconButton(
36 | icon: Icon(
37 | Feather.arrow_left,
38 | color: Colors.grey.shade800,
39 | size: sizeWidth / 14.5,
40 | ),
41 | onPressed: () {
42 | Navigator.of(context).pop(context);
43 | },
44 | ),
45 | title: Text(
46 | 'Notifications',
47 | style: TextStyle(
48 | fontSize: sizeWidth / 18.8,
49 | fontWeight: FontWeight.bold,
50 | color: Colors.grey.shade800,
51 | height: 2,
52 | ),
53 | ),
54 | actions: [
55 | StreamBuilder(
56 | stream: Firestore.instance
57 | .collection('users')
58 | .where('id', isEqualTo: user.uid)
59 | .snapshots(),
60 | builder: (context, AsyncSnapshot snapshot) {
61 | if (!snapshot.hasData) {
62 | return IconButton(
63 | icon: Icon(
64 | Feather.bell,
65 | color: Colors.grey.shade800,
66 | size: sizeWidth / 16.5,
67 | ),
68 | onPressed: () {},
69 | );
70 | }
71 |
72 | return IconButton(
73 | icon: Icon(
74 | snapshot.data.documents[0]['notifications']
75 | ? Feather.bell
76 | : Feather.bell_off,
77 | color: Colors.grey.shade800,
78 | size: sizeWidth / 16.5,
79 | ),
80 | onPressed: () async {
81 | await _updateNotification(
82 | snapshot.data.documents[0].reference);
83 | },
84 | );
85 | },
86 | ),
87 | ],
88 | ),
89 | body: Container(
90 | child: Column(
91 | children: [
92 | Expanded(
93 | child: StreamBuilder(
94 | stream: Firestore.instance
95 | .collection('users')
96 | .where('id', isEqualTo: user.uid)
97 | .snapshots(),
98 | builder: (context, AsyncSnapshot snapshot) {
99 | if (!snapshot.hasData) {
100 | return Container();
101 | }
102 |
103 | return StreamBuilder(
104 | stream: Firestore.instance
105 | .collection('notifications')
106 | .where('key',
107 | isEqualTo: snapshot.data.documents[0]['key'])
108 | .orderBy('publishAt', descending: true)
109 | .snapshots(),
110 | builder: (context, AsyncSnapshot result) {
111 | if (!result.hasData) {
112 | return Container();
113 | }
114 |
115 | List docs = result.data.documents;
116 |
117 | docs
118 | .where((doc) {
119 | if (doc['all'] == false) {
120 | int count = 0;
121 | List members = doc['members'];
122 | for (int j = 0; j < members.length; j++) {
123 | if (user.uid == members[j]) {
124 | count++;
125 | }
126 | }
127 |
128 | return count == 0 ? true : false;
129 | }
130 | return false;
131 | }) // filter keys
132 | .toList() // create a copy to avoid concurrent modifications
133 | .forEach(docs.remove);
134 |
135 | return ListView.builder(
136 | itemCount: docs.length,
137 | itemBuilder: (context, index) {
138 | return NotificationItem(
139 | title: docs[index]['title'],
140 | body: docs[index]['body'],
141 | urlToImage: docs[index]['urlToImage'],
142 | );
143 | },
144 | );
145 | },
146 | );
147 | },
148 | ),
149 | ),
150 | ],
151 | ),
152 | ),
153 | );
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/lib/src/page/receive_page/receive_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_icons/flutter_icons.dart';
4 | import 'package:project_message_demo/src/animation/fade_animation.dart';
5 | import 'package:project_message_demo/src/model/user.dart';
6 | import 'package:project_message_demo/src/page/notification_page/notification_page.dart';
7 | import 'package:project_message_demo/src/page/user/profile_page.dart';
8 | import 'package:project_message_demo/src/widget/receive_widget/inbox_list.dart';
9 | import 'package:provider/provider.dart';
10 | import 'package:intl/intl.dart';
11 |
12 | class ReceivePage extends StatefulWidget {
13 | @override
14 | State createState() => _ReceivePageState();
15 | }
16 |
17 | class _ReceivePageState extends State {
18 | DateTime _fromDate;
19 | DateTime _toDate;
20 | List _states = [
21 | 'All',
22 | 'Accept',
23 | 'Reject',
24 | 'Missing',
25 | 'Rejected Call',
26 | ];
27 | String _state;
28 | String _from;
29 | String _to;
30 |
31 | @override
32 | void initState() {
33 | super.initState();
34 | _toDate = DateTime.now();
35 | _fromDate = _toDate.subtract(Duration(days: 14));
36 | _from = DateFormat('dd/MM/yyyy').format(_fromDate);
37 | _to = DateFormat('dd/MM/yyyy').format(_toDate);
38 | _state = _states[0];
39 | }
40 |
41 | Future _selectDateFrom(BuildContext context) async {
42 | final DateTime picked = await showDatePicker(
43 | context: context,
44 | initialDate: _fromDate,
45 | firstDate: DateTime(2020, 10),
46 | lastDate: DateTime.now());
47 | if (picked != null && picked != _fromDate)
48 | setState(() {
49 | if (_toDate.compareTo(picked) != -1) {
50 | _fromDate = picked;
51 | _from = DateFormat('dd/MM/yyyy').format(_fromDate);
52 | }
53 | });
54 |
55 | Navigator.of(context).pop(context);
56 | showFilterBottomSheet();
57 | }
58 |
59 | Future _selectDateTo(BuildContext context) async {
60 | final DateTime picked = await showDatePicker(
61 | context: context,
62 | initialDate: _toDate,
63 | firstDate: DateTime(2020, 10),
64 | lastDate: DateTime.now().add(Duration(days: 1)));
65 | if (picked != null && picked != _toDate)
66 | setState(() {
67 | if (_fromDate.compareTo(picked) != 1) {
68 | _toDate = picked;
69 | _to = DateFormat('dd/MM/yyyy').format(_toDate);
70 | }
71 | });
72 | Navigator.of(context).pop(context);
73 | showFilterBottomSheet();
74 | }
75 |
76 | void showFilterBottomSheet() {
77 | showModalBottomSheet(
78 | isScrollControlled: true,
79 | shape: RoundedRectangleBorder(
80 | borderRadius: BorderRadius.all(
81 | Radius.circular(12.0),
82 | ),
83 | ),
84 | context: context,
85 | builder: (context) {
86 | return _filterBottomSheet(context);
87 | },
88 | );
89 | }
90 |
91 | @override
92 | Widget build(BuildContext context) {
93 | Size size = MediaQuery.of(context).size;
94 | final user = Provider.of(context);
95 | return Scaffold(
96 | appBar: AppBar(
97 | brightness: Brightness.light,
98 | backgroundColor: Colors.white,
99 | elevation: 2.0,
100 | centerTitle: false,
101 | title: Row(
102 | mainAxisAlignment: MainAxisAlignment.start,
103 | crossAxisAlignment: CrossAxisAlignment.center,
104 | children: [
105 | StreamBuilder(
106 | stream: Firestore.instance
107 | .collection('users')
108 | .where('id', isEqualTo: user.uid)
109 | .snapshots(),
110 | builder: (BuildContext context,
111 | AsyncSnapshot snapshot) {
112 | if (!snapshot.hasData) {
113 | return Container();
114 | }
115 |
116 | String urlToImage = snapshot.data.documents[0]['urlToImage'];
117 |
118 | return Row(
119 | children: [
120 | CircleAvatar(
121 | backgroundImage: urlToImage == ''
122 | ? AssetImage('images/avt.jpg')
123 | : NetworkImage(urlToImage),
124 | radius: 16.96),
125 | SizedBox(
126 | width: 6.8,
127 | ),
128 | Text(
129 | "History",
130 | style: TextStyle(
131 | fontSize: size.width / 18.25,
132 | fontWeight: FontWeight.bold,
133 | color: Colors.black,
134 | ),
135 | ),
136 | ],
137 | );
138 | },
139 | ),
140 | ],
141 | ),
142 | actions: [
143 | IconButton(
144 | icon: Icon(
145 | Feather.sliders,
146 | size: size.width / 16.5,
147 | color: Colors.grey.shade800,
148 | ),
149 | onPressed: () {
150 | showFilterBottomSheet();
151 | },
152 | ),
153 | IconButton(
154 | icon: Icon(
155 | Feather.bell,
156 | size: size.width / 16.5,
157 | color: Colors.grey.shade800,
158 | ),
159 | onPressed: () {
160 | Navigator.of(context).push(
161 | MaterialPageRoute(
162 | builder: (context) => NotificationPage(),
163 | ),
164 | );
165 | },
166 | ),
167 | IconButton(
168 | icon: Icon(
169 | Feather.settings,
170 | size: size.width / 16.5,
171 | color: Colors.grey.shade800,
172 | ),
173 | onPressed: () {
174 | Navigator.of(context).push(
175 | MaterialPageRoute(builder: (context) => EditProfilePage()));
176 | },
177 | ),
178 | SizedBox(
179 | width: 4.0,
180 | ),
181 | ],
182 | ),
183 | body: Container(
184 | height: size.height,
185 | width: size.width,
186 | child: Column(
187 | children: [
188 | SizedBox(
189 | height: 12.0,
190 | ),
191 | Expanded(
192 | child: StreamBuilder(
193 | stream: _state == 'All'
194 | ? Firestore.instance
195 | .collection('requests')
196 | .where('receiveID', isEqualTo: user.uid)
197 | //.where('sortVal', isGreaterThanOrEqualTo: _fromDate.millisecondsSinceEpoch)
198 | .snapshots()
199 | : Firestore.instance
200 | .collection('requests')
201 | .where('receiveID', isEqualTo: user.uid)
202 | .where('responce', isEqualTo: _state)
203 | .snapshots(),
204 | builder: (BuildContext context,
205 | AsyncSnapshot snapshot) {
206 | if (!snapshot.hasData) {
207 | return Container();
208 | }
209 |
210 | List docs = snapshot.data.documents;
211 |
212 | //filter
213 | docs
214 | .where((doc) {
215 | Timestamp responsedTime = doc['responcedTime'];
216 | DateTime response = responsedTime.toDate();
217 | return response.compareTo(_toDate) == 1 ||
218 | response.compareTo(_fromDate) == -1;
219 | }) // filter keys
220 | .toList() // create a copy to avoid concurrent modifications
221 | .forEach(docs.remove);
222 | //sort
223 | for (int i = 0; i < docs.length - 1; i++) {
224 | for (int j = 0; j < docs.length - 1 - i; j++) {
225 | Timestamp t1 = docs[j]['responcedTime'];
226 | Timestamp t2 = docs[j + 1]['responcedTime'];
227 | if (t1.compareTo(t2) == -1) {
228 | DocumentSnapshot temp = docs[j];
229 | docs[j] = docs[j + 1];
230 | docs[j + 1] = temp;
231 | }
232 | }
233 | }
234 |
235 | return FadeAnimation(
236 | .25,
237 | InboxList(
238 | documents: docs,
239 | ),
240 | );
241 | },
242 | ),
243 | )
244 | ],
245 | ),
246 | ),
247 | );
248 | }
249 |
250 | Widget _filterBottomSheet(context) {
251 | Size size = MediaQuery.of(context).size;
252 | return Container(
253 | height: size.height * .3,
254 | child: Column(
255 | children: [
256 | SizedBox(
257 | height: 28.0,
258 | ),
259 | Row(
260 | children: [
261 | SizedBox(
262 | width: 20.0,
263 | ),
264 | Expanded(
265 | flex: 1,
266 | child: Text(
267 | 'From',
268 | style: TextStyle(
269 | color: Colors.grey.shade800,
270 | fontSize: size.width / 23.5,
271 | fontWeight: FontWeight.w600,
272 | ),
273 | ),
274 | ),
275 | Expanded(
276 | flex: 5,
277 | child: GestureDetector(
278 | onTap: () async {
279 | _selectDateFrom(context);
280 | },
281 | child: Container(
282 | padding: EdgeInsets.only(
283 | left: 18.0,
284 | right: 12.0,
285 | top: 12.0,
286 | bottom: 12.0,
287 | ),
288 | margin: EdgeInsets.symmetric(
289 | horizontal: 12.0,
290 | vertical: 8.0,
291 | ),
292 | decoration: BoxDecoration(
293 | borderRadius: BorderRadius.all(Radius.circular(6.0)),
294 | color: Colors.grey.shade50,
295 | boxShadow: [
296 | BoxShadow(
297 | color: Color(0xFFABBAD5),
298 | spreadRadius: .8,
299 | blurRadius: 2.0,
300 | offset: Offset(0, 2.0), // changes position of shadow
301 | ),
302 | ],
303 | ),
304 | child: Row(
305 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
306 | children: [
307 | Text(
308 | _from,
309 | style: TextStyle(
310 | color: Colors.grey.shade800,
311 | fontSize: size.width / 28.0,
312 | fontWeight: FontWeight.w400,
313 | ),
314 | ),
315 | Icon(
316 | Feather.calendar,
317 | size: size.width / 20,
318 | color: Colors.grey.shade700,
319 | ),
320 | ],
321 | ),
322 | ),
323 | ),
324 | ),
325 | ],
326 | ),
327 | Row(
328 | children: [
329 | SizedBox(
330 | width: 20.0,
331 | ),
332 | Expanded(
333 | flex: 1,
334 | child: Text(
335 | 'To',
336 | style: TextStyle(
337 | color: Colors.grey.shade800,
338 | fontSize: size.width / 23.5,
339 | fontWeight: FontWeight.w600,
340 | ),
341 | ),
342 | ),
343 | Expanded(
344 | flex: 5,
345 | child: GestureDetector(
346 | onTap: () async {
347 | await _selectDateTo(context);
348 | },
349 | child: Container(
350 | padding: EdgeInsets.only(
351 | left: 18.0,
352 | right: 12.0,
353 | top: 12.0,
354 | bottom: 12.0,
355 | ),
356 | margin: EdgeInsets.symmetric(
357 | horizontal: 12.0,
358 | vertical: 8.0,
359 | ),
360 | decoration: BoxDecoration(
361 | borderRadius: BorderRadius.all(Radius.circular(6.0)),
362 | color: Colors.grey.shade50,
363 | boxShadow: [
364 | BoxShadow(
365 | color: Color(0xFFABBAD5),
366 | spreadRadius: .8,
367 | blurRadius: 2.0,
368 | offset: Offset(0, 2.0), // changes position of shadow
369 | ),
370 | ],
371 | ),
372 | child: Row(
373 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
374 | children: [
375 | Text(
376 | _to,
377 | style: TextStyle(
378 | color: Colors.grey.shade800,
379 | fontSize: size.width / 28.0,
380 | fontWeight: FontWeight.w400,
381 | ),
382 | ),
383 | Icon(
384 | Feather.calendar,
385 | size: size.width / 20,
386 | color: Colors.grey.shade700,
387 | ),
388 | ],
389 | ),
390 | ),
391 | ),
392 | ),
393 | ],
394 | ),
395 | Row(
396 | children: [
397 | SizedBox(
398 | width: 20.0,
399 | ),
400 | Expanded(
401 | flex: 1,
402 | child: Text(
403 | 'State',
404 | style: TextStyle(
405 | color: Colors.grey.shade800,
406 | fontSize: size.width / 22.5,
407 | fontWeight: FontWeight.w600,
408 | ),
409 | ),
410 | ),
411 | Expanded(
412 | flex: 5,
413 | child: Container(
414 | padding: EdgeInsets.only(left: 18.0, right: 12.0),
415 | margin: EdgeInsets.symmetric(
416 | horizontal: 12.0,
417 | vertical: 8.0,
418 | ),
419 | decoration: BoxDecoration(
420 | borderRadius: BorderRadius.all(Radius.circular(6.0)),
421 | color: Colors.grey.shade50,
422 | boxShadow: [
423 | BoxShadow(
424 | color: Color(0xFFABBAD5),
425 | spreadRadius: .8,
426 | blurRadius: 2.0,
427 | offset: Offset(0, 2.0), // changes position of shadow
428 | ),
429 | ],
430 | ),
431 | child: DropdownButtonHideUnderline(
432 | child: DropdownButtonFormField(
433 | icon: Icon(
434 | Feather.hash,
435 | size: size.width / 20,
436 | color: Colors.grey.shade700,
437 | ),
438 | iconEnabledColor: Colors.grey.shade800,
439 | decoration: InputDecoration(
440 | border: InputBorder.none,
441 | ),
442 | value: _state,
443 | items: _states.map((state) {
444 | return DropdownMenuItem(
445 | value: state,
446 | child: Text(
447 | state,
448 | style: TextStyle(
449 | fontSize: size.width / 24,
450 | color: Colors.grey.shade800,
451 | ),
452 | ));
453 | }).toList(),
454 | onChanged: (val) {
455 | setState(() {
456 | _state = val;
457 | });
458 | },
459 | ),
460 | ),
461 | ),
462 | ),
463 | ],
464 | ),
465 | ],
466 | ),
467 | );
468 | }
469 | }
470 |
--------------------------------------------------------------------------------
/lib/src/page/receive_page/room_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 | import 'package:flutter/cupertino.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter/rendering.dart';
5 | import 'package:flutter_icons/flutter_icons.dart';
6 |
7 | class ChatRoomPage extends StatefulWidget {
8 | final String idRequest;
9 | final String idSend;
10 | final String idReceive;
11 | final String responce;
12 | final Timestamp publishAt;
13 | final Timestamp responcedTime;
14 | final String urlToImage;
15 |
16 | ChatRoomPage({
17 | this.idRequest,
18 | this.idSend,
19 | this.idReceive,
20 | this.publishAt,
21 | this.responcedTime,
22 | this.responce,
23 | this.urlToImage,
24 | });
25 |
26 | @override
27 | State createState() => _ChatRoomPageState();
28 | }
29 |
30 | class _ChatRoomPageState extends State {
31 | DateTime responcedTime;
32 | DateTime publishAt;
33 | String _publish;
34 | String _responced;
35 | Color _color;
36 |
37 | String checkDate(int input) {
38 | if (input < 10) {
39 | return '0$input';
40 | } else {
41 | return '$input';
42 | }
43 | }
44 |
45 | @override
46 | void initState() {
47 | super.initState();
48 | responcedTime = widget.responcedTime.toDate();
49 | publishAt = widget.publishAt.toDate();
50 | _color = widget.responce == 'Accept'
51 | ? Colors.green
52 | : widget.responce == 'Reject'
53 | ? Colors.blueAccent
54 | : widget.responce == 'Missing'
55 | ? Colors.redAccent
56 | : Colors.amber.shade700;
57 |
58 | _publish =
59 | '${checkDate(publishAt.hour)}:${checkDate(publishAt.minute)}:${checkDate(publishAt.second)} at ' +
60 | '${checkDate(publishAt.day)}/${checkDate(publishAt.month)}/${checkDate(publishAt.year)}';
61 |
62 | _responced =
63 | '${checkDate(responcedTime.hour)}:${checkDate(responcedTime.minute)}:${checkDate(responcedTime.second)} at ' +
64 | '${checkDate(responcedTime.day)}/${checkDate(responcedTime.month)}/${checkDate(responcedTime.year)}';
65 | }
66 |
67 | @override
68 | Widget build(BuildContext context) {
69 | Size size = MediaQuery.of(context).size;
70 |
71 | return Container(
72 | height: size.height * .77,
73 | child: Column(
74 | crossAxisAlignment: CrossAxisAlignment.start,
75 | children: [
76 | Container(
77 | height: 60.0,
78 | width: size.width,
79 | child: Row(
80 | crossAxisAlignment: CrossAxisAlignment.center,
81 | children: [
82 | SizedBox(
83 | width: 8.0,
84 | ),
85 | IconButton(
86 | icon: Icon(
87 | Feather.arrow_left,
88 | color: Colors.grey.shade700,
89 | size: size.width / 16.0,
90 | ),
91 | onPressed: () {
92 | Navigator.of(context).pop(context);
93 | },
94 | ),
95 | SizedBox(
96 | width: 24.0,
97 | ),
98 | Padding(
99 | padding: EdgeInsets.only(top: 2.0),
100 | child: Text(
101 | 'State :',
102 | style: TextStyle(
103 | fontSize: size.width / 20.2,
104 | color: Colors.grey.shade800,
105 | fontWeight: FontWeight.bold,
106 | ),
107 | ),
108 | ),
109 | SizedBox(
110 | width: 20.0,
111 | ),
112 | Icon(
113 | widget.responce == 'Accept'
114 | ? Icons.check
115 | : widget.responce == 'Reject'
116 | ? Icons.close
117 | : widget.responce == 'Missing'
118 | ? Icons.call_missed
119 | : Icons.call_end,
120 | color: _color,
121 | size: size.width / 16.0,
122 | ),
123 | ],
124 | ),
125 | ),
126 | Divider(
127 | height: .8,
128 | thickness: .8,
129 | color: Colors.grey.shade200,
130 | ),
131 | Container(
132 | height: size.height * .35,
133 | width: size.width,
134 | decoration: BoxDecoration(
135 | image: DecorationImage(
136 | image: widget.urlToImage == ''
137 | ? AssetImage(
138 | 'images/avt.jpg',
139 | )
140 | : NetworkImage(
141 | widget.urlToImage,
142 | ),
143 | fit: BoxFit.cover,
144 | ),
145 | ),
146 | ),
147 | SizedBox(
148 | height: 20.0,
149 | ),
150 | Padding(
151 | padding: EdgeInsets.symmetric(
152 | horizontal: 20.0,
153 | ),
154 | child: Column(
155 | crossAxisAlignment: CrossAxisAlignment.start,
156 | children: [
157 | RichText(
158 | overflow: TextOverflow.visible,
159 | text: TextSpan(children: [
160 | TextSpan(
161 | text: 'Responce\t\t:\t\t',
162 | style: TextStyle(
163 | fontSize: size.width / 24.0,
164 | color: Colors.grey.shade800,
165 | fontWeight: FontWeight.bold,
166 | ),
167 | ),
168 | TextSpan(
169 | text: widget.responce,
170 | style: TextStyle(
171 | fontSize: size.width / 24.0,
172 | color: _color,
173 | fontWeight: FontWeight.w600,
174 | ),
175 | ),
176 | ]),
177 | ),
178 | SizedBox(
179 | height: 12.0,
180 | ),
181 | RichText(
182 | overflow: TextOverflow.visible,
183 | text: TextSpan(children: [
184 | TextSpan(
185 | text: 'Responses Time\t:\t',
186 | style: TextStyle(
187 | fontSize: size.width / 24.0,
188 | color: Colors.grey.shade800,
189 | fontWeight: FontWeight.bold,
190 | ),
191 | ),
192 | TextSpan(
193 | text: _responced,
194 | style: TextStyle(
195 | fontSize: size.width / 24.0,
196 | color: Colors.grey.shade800,
197 | fontWeight: FontWeight.w600,
198 | ),
199 | ),
200 | ]),
201 | ),
202 | SizedBox(
203 | height: 12.0,
204 | ),
205 | RichText(
206 | overflow: TextOverflow.visible,
207 | text: TextSpan(children: [
208 | TextSpan(
209 | text: 'PublishAt\t:\t',
210 | style: TextStyle(
211 | fontSize: size.width / 24.0,
212 | color: Colors.grey.shade800,
213 | fontWeight: FontWeight.bold,
214 | ),
215 | ),
216 | TextSpan(
217 | text: _publish,
218 | style: TextStyle(
219 | fontSize: size.width / 24.0,
220 | color: Colors.grey.shade700,
221 | fontWeight: FontWeight.w600,
222 | ),
223 | ),
224 | ]),
225 | ),
226 | ],
227 | ),
228 | ),
229 | ],
230 | ),
231 | );
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/lib/src/page/request_page.dart/request_page.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_icons/flutter_icons.dart';
4 | import 'package:project_message_demo/src/model/user.dart';
5 | import 'package:project_message_demo/src/widget/search_widget/user_card.dart';
6 | import 'package:provider/provider.dart';
7 |
8 | class RequestPage extends StatefulWidget {
9 | @override
10 | State createState() => _RequestPageState();
11 | }
12 |
13 | class _RequestPageState extends State {
14 | String searchKey = '';
15 | @override
16 | Widget build(BuildContext context) {
17 | Size size = MediaQuery.of(context).size;
18 | final user = Provider.of(context);
19 | return Scaffold(
20 | appBar: AppBar(
21 | backgroundColor: Colors.white,
22 | centerTitle: false,
23 | elevation: 2.0,
24 | title: Row(
25 | children: [
26 | StreamBuilder(
27 | stream: Firestore.instance
28 | .collection('users')
29 | .where('id', isEqualTo: user.uid)
30 | .snapshots(),
31 | builder: (BuildContext context,
32 | AsyncSnapshot snapshot) {
33 | if (!snapshot.hasData) {
34 | return Container();
35 | }
36 |
37 | String urlToImage = snapshot.data.documents[0]['urlToImage'];
38 |
39 | return CircleAvatar(
40 | backgroundColor: Colors.blueAccent,
41 | radius: 16.98,
42 | child: CircleAvatar(
43 | backgroundImage: urlToImage == ''
44 | ? AssetImage('images/avt.jpg')
45 | : NetworkImage(urlToImage),
46 | radius: 16.88,
47 | ),
48 | );
49 | },
50 | ),
51 | Expanded(
52 | child: TextFormField(
53 | autofocus: true,
54 | style: TextStyle(
55 | color: Colors.black87,
56 | fontSize: size.width / 22.8,
57 | fontWeight: FontWeight.w400,
58 | ),
59 | onChanged: (val) {
60 | setState(() {
61 | searchKey = val.trim();
62 | });
63 | },
64 | decoration: InputDecoration(
65 | contentPadding: EdgeInsets.only(left: 16.0),
66 | hintText: 'Search',
67 | hintStyle: TextStyle(
68 | color: Colors.grey.shade600,
69 | fontSize: size.width / 22.5,
70 | fontWeight: FontWeight.w400,
71 | ),
72 | filled: true,
73 | fillColor: Colors.transparent,
74 | border: OutlineInputBorder(
75 | borderSide: BorderSide.none,
76 | ),
77 | ),
78 | ),
79 | ),
80 | ],
81 | ),
82 | actions: [
83 | IconButton(
84 | icon: Icon(
85 | Feather.sliders,
86 | size: size.width / 16.0,
87 | color: Colors.grey.shade800,
88 | ),
89 | onPressed: () {},
90 | ),
91 | ],
92 | ),
93 | body: searchKey.length == 0
94 | ? Container()
95 | : StreamBuilder(
96 | stream: Firestore.instance
97 | .collection('users')
98 | .orderBy('username', descending: false)
99 | .limit(200)
100 | .snapshots(),
101 | builder: (BuildContext context,
102 | AsyncSnapshot snapshot) {
103 | if (!snapshot.hasData) {
104 | return Container(
105 | child: Center(
106 | child: CircularProgressIndicator(),
107 | ),
108 | );
109 | }
110 |
111 | List docs = snapshot.data.documents;
112 |
113 | for (int i = 0; i < docs.length; i++) {
114 | if (docs[i]['phone']
115 | .toString()
116 | .toLowerCase()
117 | .startsWith(searchKey.toLowerCase())) {
118 | continue;
119 | } else {
120 | docs.removeAt(i);
121 | }
122 | }
123 |
124 | for (int i = 0; i < docs.length; i++) {
125 | if (user.uid == docs[i]['id']) {
126 | docs.removeAt(i);
127 | }
128 | }
129 |
130 | return docs.length == 0
131 | ? Container(
132 | height: 0.0,
133 | )
134 | : ListView.builder(
135 | padding: EdgeInsets.only(top: 12.5),
136 | itemCount: docs.length,
137 | itemBuilder: (context, index) {
138 | return UserCard(
139 | user: docs[index],
140 | );
141 | },
142 | );
143 | },
144 | ),
145 | );
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/lib/src/service/auth.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 | import 'package:firebase_auth/firebase_auth.dart';
3 | import 'package:project_message_demo/src/model/user.dart';
4 |
5 | class AuthService {
6 | final FirebaseAuth _auth = FirebaseAuth.instance;
7 | //final GoogleSignIn _googleSignIn = GoogleSignIn(scopes: ['email']);
8 |
9 | //create user obj based on FirebaseUser
10 | User _userFromFirebaseUser(FirebaseUser user) {
11 | return user != null ? User(uid: user.uid) : null;
12 | }
13 |
14 | //auth change user stream
15 | Stream get user {
16 | return _auth.onAuthStateChanged.map(_userFromFirebaseUser);
17 | }
18 |
19 | //sign in anon
20 | //cannot use in purchase
21 | Future signInAnon() async {
22 | try {
23 | AuthResult result = await _auth.signInAnonymously();
24 | FirebaseUser user = result.user;
25 |
26 | //create info client
27 | await _createDataUser(user.uid, user.uid, '', '', '');
28 |
29 | return _userFromFirebaseUser(user);
30 | } catch (e) {
31 | print(e.toString());
32 | return null;
33 | }
34 | }
35 |
36 | //sign in email and password
37 | Future signInWithEmailAndPassword(String email, String password) async {
38 | try {
39 | AuthResult result = await _auth.signInWithEmailAndPassword(
40 | email: email, password: password);
41 | FirebaseUser user = result.user;
42 |
43 | return _userFromFirebaseUser(user);
44 | } catch (e) {
45 | print(e.toString());
46 | return null;
47 | }
48 | }
49 |
50 | //register with email & password
51 | Future registerWithEmailAndPassword(String email, String password,
52 | String phone, String dept, String company) async {
53 | try {
54 | AuthResult result = await _auth.createUserWithEmailAndPassword(
55 | email: email, password: password);
56 | FirebaseUser user = result.user;
57 |
58 | //create info client
59 | await _createDataUser(email, user.uid, phone, dept, company);
60 |
61 | return _userFromFirebaseUser(user);
62 | } catch (e) {
63 | print(e.toString());
64 | }
65 | }
66 |
67 | // Future signInWithGoogle() async {
68 | // try{
69 | // GoogleSignInAccount googleUser = await _googleSignIn.signIn();
70 | // GoogleSignInAuthentication googleAuth = await googleUser.authentication;
71 | //
72 | // AuthCredential credential = GoogleAuthProvider.getCredential(
73 | // idToken: googleAuth.idToken,
74 | // accessToken: googleAuth.accessToken,
75 | // );
76 | //
77 | // AuthResult result = await _auth.signInWithCredential(credential);
78 | // FirebaseUser user = result.user;
79 | //
80 | // _updateDataUserGoogle(user.email, 'Your phone', user.uid, user.photoUrl, user.displayName);
81 | //
82 | // return _userFromFirebaseUser(user);
83 | // }catch(e){
84 | // print(e.toString());
85 | // return null;
86 | // }
87 | // }
88 |
89 | Future sendPasswordResetEmail(String email) async {
90 | return _auth.sendPasswordResetEmail(email: email);
91 | }
92 |
93 | //sign out
94 | Future signOut() async {
95 | try {
96 | return await _auth.signOut();
97 | } catch (e) {
98 | print(e.toString());
99 | return null;
100 | }
101 | }
102 |
103 | Future _createDataUser(email, uid, phone, dept, company) async {
104 | Firestore.instance.collection('users').document(uid).setData({
105 | 'email': email,
106 | 'id': uid,
107 | 'username': email.toString().substring(0, email.toString().length - 10),
108 | 'publishAt': DateTime.now(),
109 | 'phone': phone,
110 | 'urlToImage': '',
111 | 'dept': dept,
112 | 'key': company,
113 | });
114 | }
115 |
116 | void _createListUserGoogle(email, uid) {
117 | Firestore.instance.collection('usersGoogle').document(uid).setData({
118 | 'email': email,
119 | });
120 | }
121 |
122 | void _updateDataUserGoogle(email, phone, uid, url, displayName) {
123 | DocumentReference documentReference =
124 | Firestore.instance.collection('usersGoogle').document(uid);
125 |
126 | Firestore.instance.runTransaction((Transaction transaction) async {
127 | DocumentSnapshot snapshot = await transaction.get(documentReference);
128 | if (snapshot.exists) {
129 | print("USING NOW");
130 | } else {
131 | //create nor data
132 | _createDataUser(email, uid, phone, '', '');
133 | }
134 | });
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/lib/src/widget/general/cached_image.dart:
--------------------------------------------------------------------------------
1 | import 'package:cached_network_image/cached_network_image.dart';
2 | import 'package:flutter/material.dart';
3 |
4 | class CachedImage extends StatelessWidget {
5 | final String url;
6 |
7 | CachedImage({
8 | @required this.url,
9 | });
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return SizedBox(
14 | child: ClipRRect(
15 | borderRadius: BorderRadius.circular(6),
16 | child: CachedNetworkImage(
17 | imageUrl: url,
18 | placeholder: (context, url) =>
19 | Center(child: CircularProgressIndicator()),
20 | errorWidget: (context, url, error) => Center(
21 | child: Icon(
22 | Icons.error,
23 | color: Colors.white.withOpacity(.88),
24 | ),
25 | ),
26 | ),
27 | ),
28 | );
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lib/src/widget/general/loading.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | class Loading extends StatefulWidget {
4 | @override
5 | State createState() => _LoadingState();
6 | }
7 |
8 | class _LoadingState extends State {
9 | @override
10 | void initState() {
11 | super.initState();
12 | }
13 |
14 | @override
15 | Widget build(BuildContext context) {
16 | return Scaffold(
17 | body: Container(
18 | child: Center(
19 | child: CircularProgressIndicator(),
20 | ),
21 | ),
22 | );
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/lib/src/widget/general/photo_viewer.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'package:photo_view/photo_view.dart';
3 |
4 | class PhotoViewer extends StatefulWidget {
5 | final String image;
6 |
7 | PhotoViewer({this.image});
8 |
9 | @override
10 | State createState() => _PhotoViewerState();
11 | }
12 |
13 | class _PhotoViewerState extends State {
14 | @override
15 | Widget build(BuildContext context) {
16 | return Container(
17 | child: PhotoView(
18 | minScale: PhotoViewComputedScale.contained,
19 | maxScale: PhotoViewComputedScale.covered * 1.8,
20 | initialScale: PhotoViewComputedScale.contained,
21 | basePosition: Alignment.center,
22 | imageProvider: NetworkImage(widget.image),
23 | ));
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/src/widget/notification_widget/notification_item.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 |
3 | import '../general/photo_viewer.dart';
4 |
5 | class NotificationItem extends StatefulWidget {
6 | final String title;
7 | final String body;
8 | final String urlToImage;
9 | NotificationItem({
10 | this.title,
11 | this.body,
12 | this.urlToImage,
13 | });
14 | @override
15 | State createState() => _NotificationItemState();
16 | }
17 |
18 | class _NotificationItemState extends State {
19 | @override
20 | Widget build(BuildContext context) {
21 | final size = MediaQuery.of(context).size;
22 | return Container(
23 | margin: EdgeInsets.only(
24 | top: 10.0,
25 | ),
26 | padding: EdgeInsets.only(
27 | left: 20.0,
28 | right: 8.0,
29 | bottom: 10.0,
30 | ),
31 | decoration: BoxDecoration(
32 | border: Border(
33 | bottom: BorderSide(
34 | color: Colors.grey.shade300,
35 | width: .4,
36 | ))),
37 | child: Row(
38 | children: [
39 | Expanded(
40 | child: Column(
41 | crossAxisAlignment: CrossAxisAlignment.start,
42 | children: [
43 | Text(
44 | widget.title,
45 | style: TextStyle(
46 | color: Colors.blueAccent,
47 | fontSize: size.width / 20.5,
48 | fontWeight: FontWeight.bold,
49 | ),
50 | ),
51 | SizedBox(
52 | height: 4.0,
53 | ),
54 | Text(
55 | widget.body,
56 | style: TextStyle(
57 | color: Colors.grey.shade800,
58 | fontSize: size.width / 25.0,
59 | fontWeight: FontWeight.w400,
60 | ),
61 | ),
62 | ],
63 | ),
64 | ),
65 | SizedBox(
66 | width: 20.0,
67 | ),
68 | widget.urlToImage == ''
69 | ? Container()
70 | : GestureDetector(
71 | onTap: () {
72 | Navigator.of(context).push(
73 | MaterialPageRoute(
74 | builder: (context) => PhotoViewer(
75 | image: widget.urlToImage,
76 | ),
77 | ),
78 | );
79 | },
80 | child: Container(
81 | height: 55.0,
82 | width: 55.0,
83 | decoration: BoxDecoration(
84 | shape: BoxShape.circle,
85 | border: Border.all(
86 | color: Colors.blueGrey.shade100,
87 | width: 1.8,
88 | ),
89 | image: DecorationImage(
90 | image: NetworkImage(widget.urlToImage),
91 | fit: BoxFit.cover,
92 | ),
93 | ),
94 | ),
95 | ),
96 | ],
97 | ),
98 | );
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/lib/src/widget/receive_widget/build_chat_line.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:project_message_demo/src/widget/general/cached_image.dart';
4 | import 'package:project_message_demo/src/widget/general/photo_viewer.dart';
5 |
6 | class BuildChatLine extends StatefulWidget {
7 | final String message;
8 | final String type;
9 | final String name;
10 | final String idUser;
11 | final int hour;
12 | final int min;
13 | final int color;
14 | final bool isMe;
15 | final bool seen;
16 | final bool isLast;
17 | final Timestamp publishAt;
18 | final index;
19 |
20 | BuildChatLine(
21 | {this.message,
22 | this.type,
23 | this.name,
24 | this.hour,
25 | this.min,
26 | this.publishAt,
27 | this.isMe,
28 | this.seen,
29 | this.isLast,
30 | this.index,
31 | this.idUser,
32 | this.color});
33 |
34 | @override
35 | State createState() => _BuildChatLineState();
36 | }
37 |
38 | class _BuildChatLineState extends State {
39 | String time = '';
40 | bool showTime = false;
41 | int secondLeft = 0;
42 |
43 | Future _updateSeen() async {
44 | Firestore.instance.runTransaction((Transaction transaction) async {
45 | DocumentSnapshot snapshot = await transaction.get(widget.index);
46 | await transaction.update(widget.index, {
47 | 'seen': true,
48 | });
49 | });
50 | }
51 |
52 | @override
53 | void initState() {
54 | super.initState();
55 | if (widget.seen == false && widget.isMe == false) {
56 | _updateSeen();
57 | }
58 | var dateTime = DateTime.now();
59 | DateTime datePublish = widget.publishAt.toDate();
60 | secondLeft = dateTime.difference(datePublish).inSeconds;
61 | }
62 |
63 | @override
64 | Widget build(BuildContext context) {
65 | final sizeWidth = MediaQuery.of(context).size.width;
66 |
67 | void setTime() {
68 | if (widget.hour < 10 && widget.min < 10) {
69 | setState(() {
70 | time = '0${widget.hour}:0${widget.min}';
71 | });
72 | } else if (widget.hour < 10) {
73 | setState(() {
74 | time = '0${widget.hour}:${widget.min}';
75 | });
76 | } else if (widget.min < 10) {
77 | setState(() {
78 | time = '${widget.hour}:0${widget.min}';
79 | });
80 | } else {
81 | setState(() {
82 | time = '${widget.hour}:${widget.min}';
83 | });
84 | }
85 | }
86 |
87 | Future _deleteMessage() async {
88 | Firestore.instance.runTransaction((Transaction transaction) async {
89 | DocumentSnapshot snapshot = await transaction.get(widget.index);
90 | await transaction.update(widget.index, {
91 | 'message': 'This message has been deleted.',
92 | 'type': 'deleted',
93 | });
94 | });
95 | }
96 |
97 | return Row(
98 | mainAxisAlignment:
99 | widget.isMe ? MainAxisAlignment.end : MainAxisAlignment.start,
100 | children: [
101 | GestureDetector(
102 | onTap: () {
103 | if (widget.type == 'image') {
104 | Navigator.of(context).push(MaterialPageRoute(
105 | builder: (context) => PhotoViewer(
106 | image: widget.message,
107 | )));
108 | } else {
109 | setState(() {
110 | showTime = !showTime;
111 | });
112 | setTime();
113 | }
114 | },
115 | child: Column(
116 | crossAxisAlignment:
117 | widget.isMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
118 | children: [
119 | Row(
120 | crossAxisAlignment: CrossAxisAlignment.end,
121 | mainAxisAlignment: MainAxisAlignment.end,
122 | children: [
123 | senderLayout(),
124 | widget.isLast && widget.isMe
125 | ? widget.seen
126 | ? StreamBuilder(
127 | stream: Firestore.instance
128 | .collection('users')
129 | .where('id', isEqualTo: widget.idUser)
130 | .snapshots(),
131 | builder: (BuildContext context,
132 | AsyncSnapshot snapshot) {
133 | if (!snapshot.hasData) {
134 | return Container(
135 | height: 12.0,
136 | width: 12.0,
137 | decoration: BoxDecoration(
138 | shape: BoxShape.circle,
139 | color: Color(widget.color)),
140 | child: Icon(
141 | Icons.check,
142 | color: Colors.grey.shade400,
143 | size: 6.0,
144 | ),
145 | alignment: Alignment.center,
146 | );
147 | }
148 |
149 | String image =
150 | snapshot.data.documents[0]['urlToImage'];
151 |
152 | return Container(
153 | height: 12.0,
154 | width: 12.0,
155 | decoration: BoxDecoration(
156 | shape: BoxShape.circle,
157 | image: DecorationImage(
158 | image: image == ''
159 | ? AssetImage('images/avt.jpg')
160 | : NetworkImage(image),
161 | fit: BoxFit.cover)),
162 | );
163 | },
164 | )
165 | : Container(
166 | height: 12.0,
167 | width: 12.0,
168 | decoration: BoxDecoration(
169 | shape: BoxShape.circle,
170 | color: Color(widget.color),
171 | ),
172 | child: Icon(
173 | Icons.check,
174 | color: Colors.white,
175 | size: 6.0,
176 | ),
177 | alignment: Alignment.center,
178 | )
179 | : Container(
180 | height: 0.0,
181 | ),
182 | ],
183 | ),
184 | showTime
185 | ? Padding(
186 | padding: EdgeInsets.only(
187 | left: widget.isMe ? 0 : 8,
188 | right: widget.isMe ? 14 : 0,
189 | bottom: 4.0),
190 | child: Text(
191 | time,
192 | style: TextStyle(fontSize: sizeWidth / 38),
193 | ),
194 | )
195 | : Container(
196 | height: 0.0,
197 | ),
198 | ],
199 | ),
200 | ),
201 | ],
202 | );
203 | }
204 |
205 | Widget senderLayout() {
206 | return Container(
207 | margin: EdgeInsets.only(
208 | top: 6.0, right: widget.isMe && widget.isLast == false ? 10 : 0),
209 | constraints: BoxConstraints(
210 | maxWidth: MediaQuery.of(context).size.width * 0.65,
211 | maxHeight: MediaQuery.of(context).size.height * 0.4),
212 | decoration: BoxDecoration(
213 | color: widget.type == 'image'
214 | ? Colors.grey.shade200
215 | : widget.isMe
216 | ? Colors.grey.shade200
217 | : Colors.grey.shade300,
218 | borderRadius: widget.type == 'image'
219 | ? BorderRadius.all(Radius.circular(8.0))
220 | : BorderRadius.all(Radius.circular(30.0)),
221 | ),
222 | child: Padding(
223 | padding: widget.type == 'image'
224 | ? EdgeInsets.all(1.5)
225 | : EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0),
226 | child: getMessage(),
227 | ),
228 | );
229 | }
230 |
231 | getMessage() {
232 | return widget.type != 'image'
233 | ? Text(
234 | widget.type == 'text'
235 | ? widget.message
236 | : widget.isMe
237 | ? '${widget.message.replaceAll('username', 'You')}'
238 | : '${widget.message.replaceAll('username', widget.name)}',
239 | style: TextStyle(
240 | color: widget.isMe ? Colors.grey.shade800 : Colors.black,
241 | fontSize: MediaQuery.of(context).size.width / 24,
242 | fontWeight: FontWeight.w400),
243 | )
244 | : widget.message != null
245 | ? CachedImage(url: widget.message)
246 | : Text("Error!");
247 | }
248 | }
249 |
--------------------------------------------------------------------------------
/lib/src/widget/receive_widget/inbox_card.dart:
--------------------------------------------------------------------------------
1 | import 'package:cached_network_image/cached_network_image.dart';
2 | import 'package:cloud_firestore/cloud_firestore.dart';
3 | import 'package:flutter/material.dart';
4 | import 'package:flutter_icons/flutter_icons.dart';
5 | import 'package:project_message_demo/src/page/receive_page/room_page.dart';
6 |
7 | class InboxCard extends StatelessWidget {
8 | final String responce;
9 | final Timestamp publishAt;
10 | final String uid;
11 | final String roomID;
12 | final bool isMe;
13 | final bool seen;
14 | final bool request;
15 | final bool completed;
16 | final index;
17 | final DocumentSnapshot doc;
18 |
19 | InboxCard({
20 | this.uid,
21 | this.responce,
22 | this.publishAt,
23 | this.roomID,
24 | this.isMe,
25 | this.seen,
26 | this.request,
27 | this.index,
28 | this.completed,
29 | this.doc,
30 | });
31 |
32 | @override
33 | Widget build(BuildContext context) {
34 | int hour = publishAt.toDate().hour;
35 | int min = publishAt.toDate().minute;
36 | String time = '';
37 | String lastMes;
38 |
39 | lastMes = responce.replaceAll('''\n''', ' ');
40 | lastMes = lastMes.length > 23 ? lastMes.substring(0, 20) + '...' : lastMes;
41 |
42 | Color parseColor(String color) {
43 | String hex = color.replaceAll("#", "");
44 | if (hex.isEmpty) hex = "ffffff";
45 | if (hex.length == 3) {
46 | hex =
47 | '${hex.substring(0, 1)}${hex.substring(0, 1)}${hex.substring(1, 2)}${hex.substring(1, 2)}${hex.substring(2, 3)}${hex.substring(2, 3)}';
48 | }
49 | Color col = Color(int.parse(hex, radix: 16)).withOpacity(1.0);
50 | return col;
51 | }
52 |
53 | if (hour < 10 && min < 10) {
54 | time = '0$hour:0$min';
55 | } else if (hour < 10) {
56 | time = '0$hour:$min';
57 | } else if (min < 10) {
58 | time = '$hour:0$min';
59 | } else {
60 | time = '$hour:$min';
61 | }
62 |
63 | void showHistoryBottomSheet() {
64 | showModalBottomSheet(
65 | isScrollControlled: true,
66 | context: context,
67 | builder: (context) {
68 | return ChatRoomPage(
69 | urlToImage: doc['urlToImage'],
70 | idRequest: doc['id'],
71 | idSend: doc['idSend'],
72 | idReceive: doc['idReceive'],
73 | publishAt: doc['publishAt'],
74 | responcedTime: doc['responcedTime'],
75 | responce: doc['responce'],
76 | );
77 | });
78 | }
79 |
80 | final double sizeWidth = MediaQuery.of(context).size.width;
81 |
82 | return StreamBuilder(
83 | stream: Firestore.instance
84 | .collection('users')
85 | .where('id', isEqualTo: uid)
86 | .snapshots(),
87 | builder: (BuildContext context, AsyncSnapshot snapshot) {
88 | if (!snapshot.hasData) {
89 | return Container(
90 | padding: EdgeInsets.fromLTRB(14, 0, 14, 8),
91 | decoration: BoxDecoration(
92 | color: seen ? Colors.transparent : Colors.white.withOpacity(.08),
93 | border: Border(
94 | bottom: BorderSide(
95 | color: Colors.white.withOpacity(.3), width: 0.04)),
96 | ),
97 | child: Row(
98 | crossAxisAlignment: CrossAxisAlignment.center,
99 | children: [
100 | Container(
101 | height: sizeWidth / 6.4,
102 | width: sizeWidth / 6.4,
103 | decoration: BoxDecoration(
104 | border: Border.all(color: Colors.white10, width: 0.4),
105 | shape: BoxShape.circle,
106 | image: DecorationImage(
107 | image: AssetImage('images/avt.jpg'),
108 | fit: BoxFit.cover,
109 | ),
110 | ),
111 | ),
112 | SizedBox(
113 | width: 10.0,
114 | ),
115 | Container(
116 | width: sizeWidth * 0.70,
117 | child: Column(
118 | crossAxisAlignment: CrossAxisAlignment.start,
119 | children: [
120 | Text(
121 | 'Username',
122 | style: TextStyle(
123 | fontSize: sizeWidth / 21.5,
124 | fontWeight: FontWeight.bold),
125 | ),
126 | SizedBox(
127 | height: 6.0,
128 | ),
129 | Row(
130 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
131 | children: [
132 | Expanded(
133 | child: Text(
134 | isMe ? 'You: message' : 'Stranger: message',
135 | style: TextStyle(
136 | fontSize: sizeWidth / 28,
137 | ),
138 | ),
139 | ),
140 | SizedBox(
141 | width: 12.0,
142 | ),
143 | Text(
144 | time,
145 | style: TextStyle(
146 | fontSize: sizeWidth / 28,
147 | ),
148 | ),
149 | ],
150 | ),
151 | ],
152 | ),
153 | ),
154 | ],
155 | ),
156 | );
157 | }
158 |
159 | String username = snapshot.data.documents[0]['username'];
160 | String urlToImage = snapshot.data.documents[0]['urlToImage'];
161 |
162 | return GestureDetector(
163 | onTap: () {
164 | showHistoryBottomSheet();
165 | },
166 | child: Container(
167 | padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 6.5),
168 | decoration: BoxDecoration(
169 | color: Colors.transparent,
170 | ),
171 | child: Row(
172 | crossAxisAlignment: CrossAxisAlignment.center,
173 | children: [
174 | urlToImage == ''
175 | ? Container(
176 | padding: EdgeInsets.symmetric(horizontal: 4.0),
177 | height: sizeWidth / 6.5,
178 | width: sizeWidth / 6.5,
179 | decoration: BoxDecoration(
180 | shape: BoxShape.circle,
181 | image: DecorationImage(
182 | image: AssetImage('images/avt.jpg'),
183 | fit: BoxFit.cover),
184 | ),
185 | alignment: Alignment.bottomRight,
186 | )
187 | : CachedNetworkImage(
188 | imageBuilder: (BuildContext context, index) {
189 | return Container(
190 | padding: EdgeInsets.symmetric(horizontal: 8.0),
191 | height: sizeWidth / 6.5,
192 | width: sizeWidth / 6.5,
193 | decoration: BoxDecoration(
194 | shape: BoxShape.circle,
195 | image: DecorationImage(
196 | image: NetworkImage(urlToImage),
197 | fit: BoxFit.cover),
198 | ),
199 | alignment: Alignment.bottomRight,
200 | );
201 | },
202 | imageUrl: urlToImage,
203 | placeholder: (context, url) =>
204 | Center(child: CircularProgressIndicator()),
205 | errorWidget: (context, url, error) => Center(
206 | child: Icon(
207 | Icons.error,
208 | color: Colors.white.withOpacity(.88),
209 | ),
210 | ),
211 | ),
212 | SizedBox(
213 | width: 12.0,
214 | ),
215 | Container(
216 | width: sizeWidth * 0.70,
217 | child: Column(
218 | crossAxisAlignment: CrossAxisAlignment.start,
219 | children: [
220 | Row(
221 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
222 | crossAxisAlignment: CrossAxisAlignment.center,
223 | children: [
224 | Text(
225 | username,
226 | style: TextStyle(
227 | fontSize: sizeWidth / 20.5,
228 | fontWeight: FontWeight.bold,
229 | color: request
230 | ? Colors.redAccent
231 | : Colors.blueAccent,
232 | ),
233 | ),
234 | Text(
235 | time,
236 | style: TextStyle(
237 | color: Colors.grey.shade800,
238 | fontSize: sizeWidth / 28.0,
239 | fontWeight: isMe == false
240 | ? seen
241 | ? FontWeight.w400
242 | : FontWeight.bold
243 | : FontWeight.w400,
244 | ),
245 | ),
246 | ],
247 | ),
248 | SizedBox(
249 | height: 2.0,
250 | ),
251 | Row(
252 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
253 | crossAxisAlignment: CrossAxisAlignment.center,
254 | children: [
255 | Expanded(
256 | child: RichText(
257 | softWrap: true,
258 | overflow: TextOverflow.visible,
259 | text: TextSpan(
260 | children: [
261 | TextSpan(
262 | text: isMe ? 'You:\b' : '$username:\b',
263 | style: TextStyle(
264 | fontSize: sizeWidth / 26.0,
265 | fontWeight: FontWeight.w600,
266 | color: Colors.grey.shade800,
267 | ),
268 | ),
269 | TextSpan(
270 | text: lastMes,
271 | style: TextStyle(
272 | fontSize: sizeWidth / 26.0,
273 | fontWeight: isMe == false
274 | ? seen
275 | ? FontWeight.w400
276 | : FontWeight.bold
277 | : FontWeight.w400,
278 | color: Colors.grey.shade600,
279 | ),
280 | ),
281 | ],
282 | ),
283 | ),
284 | ),
285 | Icon(
286 | Feather.check,
287 | color: seen
288 | ? parseColor('#00CC00')
289 | : Colors.grey.shade600,
290 | size: sizeWidth / 18.5,
291 | )
292 | ],
293 | ),
294 | ],
295 | ),
296 | ),
297 | ],
298 | ),
299 | ),
300 | );
301 | },
302 | );
303 | }
304 | }
305 |
--------------------------------------------------------------------------------
/lib/src/widget/receive_widget/inbox_list.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:project_message_demo/src/model/user.dart';
4 | import 'package:project_message_demo/src/widget/receive_widget/inbox_card.dart';
5 | import 'package:provider/provider.dart';
6 |
7 | class InboxList extends StatefulWidget {
8 | final List documents;
9 |
10 | InboxList({this.documents});
11 |
12 | @override
13 | State createState() => _InboxListState();
14 | }
15 |
16 | class _InboxListState extends State {
17 | @override
18 | void initState() {
19 | super.initState();
20 | }
21 |
22 | @override
23 | Widget build(BuildContext context) {
24 | final user = Provider.of(context);
25 |
26 | return ListView.builder(
27 | padding: const EdgeInsets.only(top: 0.0),
28 | itemCount: widget.documents.length,
29 | itemBuilder: (context, index) {
30 | String room = widget.documents[index]['id'];
31 | String responce = widget.documents[index]['responce'];
32 |
33 | return InboxCard(
34 | responce: responce.length == 0 ? '[\"Image\"]' : responce,
35 | publishAt: widget.documents[index]['responcedTime'],
36 | roomID: room,
37 | uid: widget.documents[index]['idSend'],
38 | isMe: responce.length == 0 ? false : true,
39 | seen: true,
40 | request: widget.documents[index]['idSend'] == user.uid ? true : false,
41 | index: widget.documents[index].reference,
42 | completed: widget.documents[index]['completed'],
43 | doc: widget.documents[index],
44 | );
45 | },
46 | );
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib/src/widget/receive_widget/input_bottom.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 |
3 | import 'package:cloud_firestore/cloud_firestore.dart';
4 | import 'package:firebase_storage/firebase_storage.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:image_picker/image_picker.dart';
7 | import 'package:project_message_demo/src/model/user.dart';
8 | import 'package:provider/provider.dart';
9 |
10 | class InputBottom extends StatefulWidget {
11 | final bool request;
12 | final String idReceive;
13 | final String idRoom;
14 | final index;
15 | final bool available;
16 |
17 | InputBottom({
18 | this.request,
19 | this.idReceive,
20 | this.idRoom,
21 | this.index,
22 | this.available,
23 | });
24 |
25 | @override
26 | State createState() => _InputBottomState();
27 | }
28 |
29 | class _InputBottomState extends State {
30 | bool available = false;
31 |
32 | @override
33 | void initState() {
34 | super.initState();
35 | available = widget.available;
36 | }
37 |
38 | @override
39 | Widget build(BuildContext context) {
40 | return available == false
41 | ? _unAvailable(context)
42 | : widget.request
43 | ? _requestType(context)
44 | : _receiveType(context);
45 | }
46 |
47 | Future _sendMessage(
48 | idSend,
49 | idReceive,
50 | id,
51 | message,
52 | type,
53 | ) async {
54 | Firestore.instance.runTransaction((Transaction transaction) async {
55 | CollectionReference reference = Firestore.instance.collection("inboxs");
56 | await reference.add({
57 | 'idSend': idSend,
58 | 'receiveID': idReceive,
59 | 'id': id,
60 | 'publishAt': DateTime.now(),
61 | 'message': message,
62 | 'seen': false,
63 | 'type': type,
64 | 'hour': DateTime.now().hour,
65 | 'min': DateTime.now().minute,
66 | });
67 | });
68 |
69 | setState(() {
70 | available = false;
71 | });
72 | }
73 |
74 | Future _updateRoom() async {
75 | Firestore.instance.runTransaction((Transaction transaction) async {
76 | DocumentSnapshot snapshot = await transaction.get(widget.index);
77 | await transaction.update(widget.index, {
78 | 'completed': true,
79 | });
80 | });
81 | }
82 |
83 | //receive
84 | Future uploadImage(file) async {
85 | String fileName = DateTime.now().microsecondsSinceEpoch.toString();
86 | StorageReference firebaseStorageRef =
87 | FirebaseStorage.instance.ref().child('Images').child(fileName);
88 | StorageUploadTask uploadTask = firebaseStorageRef.putFile(file);
89 | StorageTaskSnapshot taskSnapshot = await uploadTask.onComplete;
90 | var downUrl = await taskSnapshot.ref.getDownloadURL();
91 | String url = downUrl.toString();
92 | return url;
93 | }
94 |
95 | Future _pickImage(ImageSource source, User user) async {
96 | File selected = await ImagePicker.pickImage(source: source);
97 | if (selected != null) {
98 | String message = await uploadImage(selected);
99 | await _sendMessage(
100 | user.uid, widget.idReceive, widget.idRoom, message, 'image');
101 | }
102 | }
103 |
104 | Widget _requestType(context) {
105 | Size size = MediaQuery.of(context).size;
106 | final user = Provider.of(context);
107 | return Container(
108 | child: Row(
109 | children: [
110 | Expanded(
111 | flex: 1,
112 | child: GestureDetector(
113 | onTap: () async {
114 | await _sendMessage(
115 | user.uid, widget.idReceive, widget.idRoom, 'False', 'text');
116 | },
117 | child: Container(
118 | decoration: BoxDecoration(
119 | color: Colors.redAccent,
120 | ),
121 | alignment: Alignment.center,
122 | padding: EdgeInsets.symmetric(vertical: 18.0),
123 | child: Text(
124 | 'False',
125 | style: TextStyle(
126 | color: Colors.white,
127 | fontWeight: FontWeight.w600,
128 | fontSize: size.width / 24.0,
129 | ),
130 | ),
131 | ),
132 | ),
133 | ),
134 | Expanded(
135 | flex: 1,
136 | child: GestureDetector(
137 | onTap: () async {
138 | await _sendMessage(
139 | user.uid,
140 | widget.idReceive,
141 | widget.idRoom,
142 | 'True',
143 | 'text',
144 | );
145 | await _updateRoom();
146 | },
147 | child: Container(
148 | decoration: BoxDecoration(color: Colors.blueAccent),
149 | alignment: Alignment.center,
150 | padding: EdgeInsets.symmetric(vertical: 18.0),
151 | child: Text(
152 | 'True',
153 | style: TextStyle(
154 | color: Colors.white,
155 | fontWeight: FontWeight.w600,
156 | fontSize: size.width / 24.0,
157 | ),
158 | ),
159 | ),
160 | ),
161 | ),
162 | ],
163 | ),
164 | );
165 | }
166 |
167 | Widget _receiveType(context) {
168 | Size size = MediaQuery.of(context).size;
169 | final user = Provider.of(context);
170 | return Container(
171 | child: Row(
172 | children: [
173 | Expanded(
174 | flex: 1,
175 | child: GestureDetector(
176 | onTap: () async {
177 | try {
178 | _pickImage(ImageSource.gallery, user);
179 | } on Exception catch (_) {
180 | throw Exception('Error on server');
181 | }
182 | },
183 | child: Container(
184 | decoration: BoxDecoration(
185 | color: Colors.grey,
186 | ),
187 | alignment: Alignment.center,
188 | padding: EdgeInsets.symmetric(vertical: 14.0),
189 | child: Icon(
190 | Icons.image,
191 | size: size.width / 14.0,
192 | color: Colors.white,
193 | ),
194 | ),
195 | ),
196 | ),
197 | Expanded(
198 | flex: 1,
199 | child: GestureDetector(
200 | onTap: () async {
201 | try {
202 | _pickImage(ImageSource.camera, user);
203 | } on Exception catch (_) {
204 | throw Exception('Error on server');
205 | }
206 | },
207 | child: Container(
208 | decoration: BoxDecoration(color: Colors.blueAccent),
209 | alignment: Alignment.center,
210 | padding: EdgeInsets.symmetric(vertical: 14.0),
211 | child: Icon(
212 | Icons.camera_alt,
213 | size: size.width / 14.0,
214 | color: Colors.white,
215 | ),
216 | ),
217 | ),
218 | ),
219 | ],
220 | ),
221 | );
222 | }
223 |
224 | Widget _unAvailable(context) {
225 | Size size = MediaQuery.of(context).size;
226 | return Container(
227 | decoration: BoxDecoration(
228 | color: Colors.black.withOpacity(.8),
229 | ),
230 | alignment: Alignment.center,
231 | padding: EdgeInsets.symmetric(vertical: 18.0),
232 | child: Text(
233 | 'Waiting Reply',
234 | style: TextStyle(
235 | color: Colors.white,
236 | fontWeight: FontWeight.w600,
237 | fontSize: size.width / 26.5,
238 | ),
239 | ),
240 | );
241 | }
242 | }
243 |
--------------------------------------------------------------------------------
/lib/src/widget/search_widget/user_card.dart:
--------------------------------------------------------------------------------
1 | import 'package:cloud_firestore/cloud_firestore.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:project_message_demo/src/model/user.dart';
4 | import 'package:provider/provider.dart';
5 |
6 | class UserCard extends StatefulWidget {
7 | final DocumentSnapshot user;
8 |
9 | UserCard({this.user});
10 |
11 | @override
12 | State createState() => _UserCardState();
13 | }
14 |
15 | class _UserCardState extends State {
16 | Future _request(
17 | idSend,
18 | idReceive,
19 | ) async {
20 | String id = idSend + DateTime.now().microsecondsSinceEpoch.toString();
21 | Firestore.instance.runTransaction((Transaction transaction) async {
22 | CollectionReference reference = Firestore.instance.collection("requests");
23 |
24 | await reference.add({
25 | 'idSend': idSend,
26 | 'receiveID': idReceive,
27 | 'id': id,
28 | 'publishAt': DateTime.now(),
29 | 'completed': false,
30 | 'responce': '',
31 | 'responcedTime': DateTime.now(),
32 | 'request': true,
33 | 'urlToImage': '',
34 | });
35 | });
36 | }
37 |
38 | @override
39 | Widget build(BuildContext context) {
40 | final sizeWidth = MediaQuery.of(context).size.width;
41 | final user = Provider.of(context);
42 |
43 | return GestureDetector(
44 | onTap: () {},
45 | child: Container(
46 | padding: EdgeInsets.symmetric(horizontal: 14.0, vertical: 8.0),
47 | child: Row(
48 | mainAxisAlignment: MainAxisAlignment.spaceBetween,
49 | children: [
50 | Row(
51 | mainAxisAlignment: MainAxisAlignment.start,
52 | crossAxisAlignment: CrossAxisAlignment.center,
53 | children: [
54 | CircleAvatar(
55 | backgroundImage: widget.user['urlToImage'] == ''
56 | ? AssetImage('images/avt.jpg')
57 | : NetworkImage(widget.user['urlToImage']),
58 | radius: 26.0,
59 | ),
60 | SizedBox(
61 | width: 12.0,
62 | ),
63 | Column(
64 | crossAxisAlignment: CrossAxisAlignment.start,
65 | children: [
66 | Text(
67 | widget.user['username'],
68 | style: TextStyle(
69 | color: Colors.black,
70 | fontSize: sizeWidth / 21.5,
71 | fontWeight: FontWeight.w600,
72 | ),
73 | ),
74 | SizedBox(
75 | height: 6.0,
76 | ),
77 | Text(
78 | widget.user['phone'],
79 | style: TextStyle(
80 | color: Colors.black,
81 | fontSize: sizeWidth / 25.0,
82 | fontWeight: FontWeight.w400,
83 | ),
84 | ),
85 | ],
86 | ),
87 | ],
88 | ),
89 | StreamBuilder(
90 | stream: Firestore.instance
91 | .collection('requests')
92 | .where('idSend', isEqualTo: user.uid)
93 | .where('receiveID', isEqualTo: widget.user['id'])
94 | .where('completed', isEqualTo: false)
95 | .snapshots(),
96 | builder: (BuildContext context,
97 | AsyncSnapshot snapshot) {
98 | if (!snapshot.hasData) {
99 | return Container();
100 | }
101 |
102 | int length = snapshot.data.documents.length;
103 |
104 | return GestureDetector(
105 | onTap: () async {
106 | if (length == 0) {
107 | await _request(user.uid, widget.user['id']);
108 | } else if (snapshot.data.documents[0]['id'] == '') {
109 | await _request(user.uid, widget.user['id']);
110 | } else {}
111 | },
112 | child: Container(
113 | padding: EdgeInsets.symmetric(
114 | horizontal: 16.0,
115 | vertical: 8.0,
116 | ),
117 | decoration: BoxDecoration(
118 | color: length == 0
119 | ? Colors.transparent
120 | : Colors.blueAccent,
121 | border: Border.all(
122 | color: length == 0
123 | ? Colors.grey.shade600
124 | : Colors.blueAccent,
125 | width: 1.0,
126 | ),
127 | borderRadius: BorderRadius.all(Radius.circular(
128 | 4.0,
129 | ))),
130 | child: Text(
131 | length == 0 ? 'Request' : 'Requested',
132 | style: TextStyle(
133 | color:
134 | length == 0 ? Colors.grey.shade800 : Colors.white,
135 | fontSize: sizeWidth / 28.8,
136 | ),
137 | ),
138 | alignment: Alignment.center,
139 | ),
140 | );
141 | },
142 | ),
143 | ],
144 | ),
145 | ),
146 | );
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | assets_audio_player:
5 | dependency: "direct main"
6 | description:
7 | name: assets_audio_player
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.0.13+1"
11 | assets_audio_player_web:
12 | dependency: transitive
13 | description:
14 | name: assets_audio_player_web
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "2.0.13+1"
18 | async:
19 | dependency: transitive
20 | description:
21 | name: async
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "2.5.0-nullsafety.3"
25 | boolean_selector:
26 | dependency: transitive
27 | description:
28 | name: boolean_selector
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "2.1.0-nullsafety.3"
32 | cached_network_image:
33 | dependency: "direct main"
34 | description:
35 | name: cached_network_image
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "2.3.3"
39 | characters:
40 | dependency: transitive
41 | description:
42 | name: characters
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.1.0-nullsafety.5"
46 | charcode:
47 | dependency: transitive
48 | description:
49 | name: charcode
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "1.2.0-nullsafety.3"
53 | clock:
54 | dependency: transitive
55 | description:
56 | name: clock
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "1.1.0-nullsafety.3"
60 | cloud_firestore:
61 | dependency: "direct main"
62 | description:
63 | name: cloud_firestore
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "0.13.7"
67 | cloud_firestore_platform_interface:
68 | dependency: transitive
69 | description:
70 | name: cloud_firestore_platform_interface
71 | url: "https://pub.dartlang.org"
72 | source: hosted
73 | version: "1.1.2"
74 | cloud_firestore_web:
75 | dependency: transitive
76 | description:
77 | name: cloud_firestore_web
78 | url: "https://pub.dartlang.org"
79 | source: hosted
80 | version: "0.1.1+2"
81 | cloud_functions:
82 | dependency: "direct main"
83 | description:
84 | name: cloud_functions
85 | url: "https://pub.dartlang.org"
86 | source: hosted
87 | version: "0.5.0"
88 | cloud_functions_platform_interface:
89 | dependency: transitive
90 | description:
91 | name: cloud_functions_platform_interface
92 | url: "https://pub.dartlang.org"
93 | source: hosted
94 | version: "1.1.0"
95 | cloud_functions_web:
96 | dependency: transitive
97 | description:
98 | name: cloud_functions_web
99 | url: "https://pub.dartlang.org"
100 | source: hosted
101 | version: "1.1.0"
102 | collection:
103 | dependency: transitive
104 | description:
105 | name: collection
106 | url: "https://pub.dartlang.org"
107 | source: hosted
108 | version: "1.15.0-nullsafety.5"
109 | convert:
110 | dependency: transitive
111 | description:
112 | name: convert
113 | url: "https://pub.dartlang.org"
114 | source: hosted
115 | version: "2.1.1"
116 | crypto:
117 | dependency: transitive
118 | description:
119 | name: crypto
120 | url: "https://pub.dartlang.org"
121 | source: hosted
122 | version: "2.1.5"
123 | cupertino_icons:
124 | dependency: "direct main"
125 | description:
126 | name: cupertino_icons
127 | url: "https://pub.dartlang.org"
128 | source: hosted
129 | version: "1.0.1+1"
130 | fake_async:
131 | dependency: transitive
132 | description:
133 | name: fake_async
134 | url: "https://pub.dartlang.org"
135 | source: hosted
136 | version: "1.2.0-nullsafety.3"
137 | ffi:
138 | dependency: transitive
139 | description:
140 | name: ffi
141 | url: "https://pub.dartlang.org"
142 | source: hosted
143 | version: "0.1.3"
144 | file:
145 | dependency: transitive
146 | description:
147 | name: file
148 | url: "https://pub.dartlang.org"
149 | source: hosted
150 | version: "5.2.1"
151 | firebase:
152 | dependency: transitive
153 | description:
154 | name: firebase
155 | url: "https://pub.dartlang.org"
156 | source: hosted
157 | version: "7.3.2"
158 | firebase_auth:
159 | dependency: "direct main"
160 | description:
161 | name: firebase_auth
162 | url: "https://pub.dartlang.org"
163 | source: hosted
164 | version: "0.16.1"
165 | firebase_auth_platform_interface:
166 | dependency: transitive
167 | description:
168 | name: firebase_auth_platform_interface
169 | url: "https://pub.dartlang.org"
170 | source: hosted
171 | version: "1.1.8"
172 | firebase_auth_web:
173 | dependency: transitive
174 | description:
175 | name: firebase_auth_web
176 | url: "https://pub.dartlang.org"
177 | source: hosted
178 | version: "0.1.3+1"
179 | firebase_core:
180 | dependency: transitive
181 | description:
182 | name: firebase_core
183 | url: "https://pub.dartlang.org"
184 | source: hosted
185 | version: "0.4.5"
186 | firebase_core_platform_interface:
187 | dependency: transitive
188 | description:
189 | name: firebase_core_platform_interface
190 | url: "https://pub.dartlang.org"
191 | source: hosted
192 | version: "1.0.4"
193 | firebase_core_web:
194 | dependency: transitive
195 | description:
196 | name: firebase_core_web
197 | url: "https://pub.dartlang.org"
198 | source: hosted
199 | version: "0.1.1+2"
200 | firebase_database:
201 | dependency: "direct main"
202 | description:
203 | name: firebase_database
204 | url: "https://pub.dartlang.org"
205 | source: hosted
206 | version: "3.1.6"
207 | firebase_messaging:
208 | dependency: "direct main"
209 | description:
210 | name: firebase_messaging
211 | url: "https://pub.dartlang.org"
212 | source: hosted
213 | version: "6.0.16"
214 | firebase_storage:
215 | dependency: "direct main"
216 | description:
217 | name: firebase_storage
218 | url: "https://pub.dartlang.org"
219 | source: hosted
220 | version: "3.1.6"
221 | flutter:
222 | dependency: "direct main"
223 | description: flutter
224 | source: sdk
225 | version: "0.0.0"
226 | flutter_blurhash:
227 | dependency: transitive
228 | description:
229 | name: flutter_blurhash
230 | url: "https://pub.dartlang.org"
231 | source: hosted
232 | version: "0.5.0"
233 | flutter_cache_manager:
234 | dependency: transitive
235 | description:
236 | name: flutter_cache_manager
237 | url: "https://pub.dartlang.org"
238 | source: hosted
239 | version: "2.0.0"
240 | flutter_icons:
241 | dependency: "direct main"
242 | description:
243 | name: flutter_icons
244 | url: "https://pub.dartlang.org"
245 | source: hosted
246 | version: "1.1.0"
247 | flutter_plugin_android_lifecycle:
248 | dependency: transitive
249 | description:
250 | name: flutter_plugin_android_lifecycle
251 | url: "https://pub.dartlang.org"
252 | source: hosted
253 | version: "1.0.11"
254 | flutter_test:
255 | dependency: "direct dev"
256 | description: flutter
257 | source: sdk
258 | version: "0.0.0"
259 | flutter_web_plugins:
260 | dependency: transitive
261 | description: flutter
262 | source: sdk
263 | version: "0.0.0"
264 | flutter_webrtc:
265 | dependency: "direct main"
266 | description:
267 | name: flutter_webrtc
268 | url: "https://pub.dartlang.org"
269 | source: hosted
270 | version: "0.2.8"
271 | http:
272 | dependency: transitive
273 | description:
274 | name: http
275 | url: "https://pub.dartlang.org"
276 | source: hosted
277 | version: "0.12.2"
278 | http_parser:
279 | dependency: transitive
280 | description:
281 | name: http_parser
282 | url: "https://pub.dartlang.org"
283 | source: hosted
284 | version: "3.1.4"
285 | image_picker:
286 | dependency: "direct main"
287 | description:
288 | name: image_picker
289 | url: "https://pub.dartlang.org"
290 | source: hosted
291 | version: "0.6.7+14"
292 | image_picker_platform_interface:
293 | dependency: transitive
294 | description:
295 | name: image_picker_platform_interface
296 | url: "https://pub.dartlang.org"
297 | source: hosted
298 | version: "1.1.1"
299 | intl:
300 | dependency: "direct main"
301 | description:
302 | name: intl
303 | url: "https://pub.dartlang.org"
304 | source: hosted
305 | version: "0.16.1"
306 | js:
307 | dependency: transitive
308 | description:
309 | name: js
310 | url: "https://pub.dartlang.org"
311 | source: hosted
312 | version: "0.6.3-nullsafety.3"
313 | matcher:
314 | dependency: transitive
315 | description:
316 | name: matcher
317 | url: "https://pub.dartlang.org"
318 | source: hosted
319 | version: "0.12.10-nullsafety.3"
320 | meta:
321 | dependency: transitive
322 | description:
323 | name: meta
324 | url: "https://pub.dartlang.org"
325 | source: hosted
326 | version: "1.3.0-nullsafety.6"
327 | nested:
328 | dependency: transitive
329 | description:
330 | name: nested
331 | url: "https://pub.dartlang.org"
332 | source: hosted
333 | version: "0.0.4"
334 | octo_image:
335 | dependency: transitive
336 | description:
337 | name: octo_image
338 | url: "https://pub.dartlang.org"
339 | source: hosted
340 | version: "0.3.0"
341 | path:
342 | dependency: transitive
343 | description:
344 | name: path
345 | url: "https://pub.dartlang.org"
346 | source: hosted
347 | version: "1.8.0-nullsafety.3"
348 | path_provider:
349 | dependency: transitive
350 | description:
351 | name: path_provider
352 | url: "https://pub.dartlang.org"
353 | source: hosted
354 | version: "1.6.24"
355 | path_provider_linux:
356 | dependency: transitive
357 | description:
358 | name: path_provider_linux
359 | url: "https://pub.dartlang.org"
360 | source: hosted
361 | version: "0.0.1+2"
362 | path_provider_macos:
363 | dependency: transitive
364 | description:
365 | name: path_provider_macos
366 | url: "https://pub.dartlang.org"
367 | source: hosted
368 | version: "0.0.4+6"
369 | path_provider_platform_interface:
370 | dependency: transitive
371 | description:
372 | name: path_provider_platform_interface
373 | url: "https://pub.dartlang.org"
374 | source: hosted
375 | version: "1.0.4"
376 | path_provider_windows:
377 | dependency: transitive
378 | description:
379 | name: path_provider_windows
380 | url: "https://pub.dartlang.org"
381 | source: hosted
382 | version: "0.0.4+3"
383 | pedantic:
384 | dependency: transitive
385 | description:
386 | name: pedantic
387 | url: "https://pub.dartlang.org"
388 | source: hosted
389 | version: "1.9.2"
390 | photo_view:
391 | dependency: "direct main"
392 | description:
393 | name: photo_view
394 | url: "https://pub.dartlang.org"
395 | source: hosted
396 | version: "0.9.2"
397 | platform:
398 | dependency: transitive
399 | description:
400 | name: platform
401 | url: "https://pub.dartlang.org"
402 | source: hosted
403 | version: "2.2.1"
404 | plugin_platform_interface:
405 | dependency: transitive
406 | description:
407 | name: plugin_platform_interface
408 | url: "https://pub.dartlang.org"
409 | source: hosted
410 | version: "1.0.3"
411 | process:
412 | dependency: transitive
413 | description:
414 | name: process
415 | url: "https://pub.dartlang.org"
416 | source: hosted
417 | version: "3.0.13"
418 | provider:
419 | dependency: "direct main"
420 | description:
421 | name: provider
422 | url: "https://pub.dartlang.org"
423 | source: hosted
424 | version: "4.3.2+2"
425 | quiver:
426 | dependency: transitive
427 | description:
428 | name: quiver
429 | url: "https://pub.dartlang.org"
430 | source: hosted
431 | version: "2.1.5"
432 | rflutter_alert:
433 | dependency: "direct main"
434 | description:
435 | name: rflutter_alert
436 | url: "https://pub.dartlang.org"
437 | source: hosted
438 | version: "1.1.0"
439 | rxdart:
440 | dependency: transitive
441 | description:
442 | name: rxdart
443 | url: "https://pub.dartlang.org"
444 | source: hosted
445 | version: "0.24.1"
446 | sdp_transform:
447 | dependency: "direct main"
448 | description:
449 | name: sdp_transform
450 | url: "https://pub.dartlang.org"
451 | source: hosted
452 | version: "0.2.0"
453 | simple_animations:
454 | dependency: "direct main"
455 | description:
456 | name: simple_animations
457 | url: "https://pub.dartlang.org"
458 | source: hosted
459 | version: "1.3.12"
460 | sky_engine:
461 | dependency: transitive
462 | description: flutter
463 | source: sdk
464 | version: "0.0.99"
465 | source_span:
466 | dependency: transitive
467 | description:
468 | name: source_span
469 | url: "https://pub.dartlang.org"
470 | source: hosted
471 | version: "1.8.0-nullsafety.4"
472 | sqflite:
473 | dependency: transitive
474 | description:
475 | name: sqflite
476 | url: "https://pub.dartlang.org"
477 | source: hosted
478 | version: "1.3.2+1"
479 | sqflite_common:
480 | dependency: transitive
481 | description:
482 | name: sqflite_common
483 | url: "https://pub.dartlang.org"
484 | source: hosted
485 | version: "1.0.2+1"
486 | stack_trace:
487 | dependency: transitive
488 | description:
489 | name: stack_trace
490 | url: "https://pub.dartlang.org"
491 | source: hosted
492 | version: "1.10.0-nullsafety.6"
493 | stream_channel:
494 | dependency: transitive
495 | description:
496 | name: stream_channel
497 | url: "https://pub.dartlang.org"
498 | source: hosted
499 | version: "2.1.0-nullsafety.3"
500 | string_scanner:
501 | dependency: transitive
502 | description:
503 | name: string_scanner
504 | url: "https://pub.dartlang.org"
505 | source: hosted
506 | version: "1.1.0-nullsafety.3"
507 | synchronized:
508 | dependency: transitive
509 | description:
510 | name: synchronized
511 | url: "https://pub.dartlang.org"
512 | source: hosted
513 | version: "2.2.0+2"
514 | term_glyph:
515 | dependency: transitive
516 | description:
517 | name: term_glyph
518 | url: "https://pub.dartlang.org"
519 | source: hosted
520 | version: "1.2.0-nullsafety.3"
521 | test_api:
522 | dependency: transitive
523 | description:
524 | name: test_api
525 | url: "https://pub.dartlang.org"
526 | source: hosted
527 | version: "0.2.19-nullsafety.6"
528 | typed_data:
529 | dependency: transitive
530 | description:
531 | name: typed_data
532 | url: "https://pub.dartlang.org"
533 | source: hosted
534 | version: "1.3.0-nullsafety.5"
535 | uuid:
536 | dependency: transitive
537 | description:
538 | name: uuid
539 | url: "https://pub.dartlang.org"
540 | source: hosted
541 | version: "2.2.2"
542 | vector_math:
543 | dependency: transitive
544 | description:
545 | name: vector_math
546 | url: "https://pub.dartlang.org"
547 | source: hosted
548 | version: "2.1.0-nullsafety.5"
549 | win32:
550 | dependency: transitive
551 | description:
552 | name: win32
553 | url: "https://pub.dartlang.org"
554 | source: hosted
555 | version: "1.7.4"
556 | xdg_directories:
557 | dependency: transitive
558 | description:
559 | name: xdg_directories
560 | url: "https://pub.dartlang.org"
561 | source: hosted
562 | version: "0.1.2"
563 | sdks:
564 | dart: ">=2.12.0-0.0 <3.0.0"
565 | flutter: ">=1.22.2"
566 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: project_message_demo
2 | description: A new Flutter project.
3 |
4 | # The following line prevents the package from being accidentally published to
5 | # pub.dev using `pub publish`. This is preferred for private packages.
6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev
7 |
8 | # The following defines the version and build number for your application.
9 | # A version number is three numbers separated by dots, like 1.2.43
10 | # followed by an optional build number separated by a +.
11 | # Both the version and the builder number may be overridden in flutter
12 | # build by specifying --build-name and --build-number, respectively.
13 | # In Android, build-name is used as versionName while build-number used as versionCode.
14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning
15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.
16 | # Read more about iOS versioning at
17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
18 | version: 1.0.0+1
19 |
20 | environment:
21 | sdk: ">=2.7.0 <3.0.0"
22 |
23 | dependencies:
24 | flutter:
25 | sdk: flutter
26 |
27 |
28 | # The following adds the Cupertino Icons font to your application.
29 | # Use with the CupertinoIcons class for iOS style icons.
30 | cupertino_icons: ^1.0.0
31 | firebase_auth:
32 | cloud_firestore: ^0.13.7
33 | simple_animations: ^1.3.3
34 | image_picker: ^0.6.4
35 | firebase_storage: ^3.1.5
36 | provider:
37 | intl: ^0.16.1
38 | photo_view: ^0.9.2
39 | firebase_messaging: ^6.0.16
40 | cloud_functions: ^0.5.0
41 | flutter_icons: ^1.0.0+1
42 | cached_network_image: ^2.2.0+1
43 | assets_audio_player: ^2.0.9+2
44 | firebase_database:
45 | flutter_webrtc: ^0.2.8
46 | sdp_transform: ^0.2.0
47 | rflutter_alert: ^1.0.3
48 |
49 | dev_dependencies:
50 | flutter_test:
51 | sdk: flutter
52 |
53 | # For information on the generic Dart part of this file, see the
54 | # following page: https://dart.dev/tools/pub/pubspec
55 |
56 | # The following section is specific to Flutter.
57 | flutter:
58 |
59 | # The following line ensures that the Material Icons font is
60 | # included with your application, so that you can use the icons in
61 | # the material Icons class.
62 | uses-material-design: true
63 |
64 | # To add assets to your application, add an assets section, like this:
65 | assets:
66 | - images/
67 | - assets/
68 |
69 | # An image asset can refer to one or more resolution-specific "variants", see
70 | # https://flutter.dev/assets-and-images/#resolution-aware.
71 |
72 | # For details regarding adding assets from package dependencies, see
73 | # https://flutter.dev/assets-and-images/#from-packages
74 |
75 | # To add custom fonts to your application, add a fonts section here,
76 | # in this "flutter" section. Each entry in this list should have a
77 | # "family" key with the font family name, and a "fonts" key with a
78 | # list giving the asset and other descriptors for the font. For
79 | # example:
80 | # fonts:
81 | # - family: Schyler
82 | # fonts:
83 | # - asset: fonts/Schyler-Regular.ttf
84 | # - asset: fonts/Schyler-Italic.ttf
85 | # style: italic
86 | # - family: Trajan Pro
87 | # fonts:
88 | # - asset: fonts/TrajanPro.ttf
89 | # - asset: fonts/TrajanPro_Bold.ttf
90 | # weight: 700
91 | #
92 | # For details regarding fonts from package dependencies,
93 | # see https://flutter.dev/custom-fonts/#from-packages
94 |
--------------------------------------------------------------------------------
/test/widget_test.dart:
--------------------------------------------------------------------------------
1 | // This is a basic Flutter widget test.
2 | //
3 | // To perform an interaction with a widget in your test, use the WidgetTester
4 | // utility that Flutter provides. For example, you can send tap and scroll
5 | // gestures. You can also use WidgetTester to find child widgets in the widget
6 | // tree, read text, and verify that the values of widget properties are correct.
7 |
8 | import 'package:flutter/material.dart';
9 | import 'package:flutter_test/flutter_test.dart';
10 |
11 | import 'package:project_message_demo/main.dart';
12 |
13 | void main() {
14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async {
15 | // Build our app and trigger a frame.
16 | await tester.pumpWidget(MyApp());
17 |
18 | // Verify that our counter starts at 0.
19 | expect(find.text('0'), findsOneWidget);
20 | expect(find.text('1'), findsNothing);
21 |
22 | // Tap the '+' icon and trigger a frame.
23 | await tester.tap(find.byIcon(Icons.add));
24 | await tester.pump();
25 |
26 | // Verify that our counter has incremented.
27 | expect(find.text('0'), findsNothing);
28 | expect(find.text('1'), findsOneWidget);
29 | });
30 | }
31 |
--------------------------------------------------------------------------------