├── .gitignore ├── .metadata ├── README.md ├── android ├── .gitignore ├── app │ ├── build.gradle │ ├── google-services.json │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── etechviral │ │ │ │ └── group_chat_room_responsive │ │ │ │ └── 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 ├── assets ├── background_img.png ├── bubble.json ├── congratulation.json ├── img.json ├── loading.json ├── logo.png ├── profile.png ├── profile_default.png └── shape.png ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── data │ ├── datasource │ │ └── firebase_remote_datasource.dart │ ├── model │ │ ├── text_message_model.dart │ │ └── user_model.dart │ └── repositories │ │ └── firebasee_repository_impl.dart ├── domain │ ├── entities │ │ ├── text_message_entity.dart │ │ └── user_entity.dart │ ├── repositories │ │ └── firebase_repository.dart │ └── usecase │ │ ├── get_create_current_user.dart │ │ ├── get_current_uid.dart │ │ ├── get_messages_usecase.dart │ │ ├── get_useres_usecase.dart │ │ ├── is_signin_usecase.dart │ │ ├── send_text_message_usecase.dart │ │ ├── sign_out_usecase.dart │ │ ├── signin_usecase.dart │ │ └── signup_usecase.dart ├── injection_container.dart ├── main.dart └── presentation │ ├── bloc │ ├── auth │ │ ├── auth_cubit.dart │ │ └── auth_state.dart │ ├── communication │ │ ├── communication_cubit.dart │ │ └── communication_state.dart │ ├── login │ │ ├── login_cubit.dart │ │ └── login_state.dart │ └── user │ │ ├── user_cubit.dart │ │ └── user_state.dart │ ├── pages │ ├── mobile │ │ ├── mobile_page.dart │ │ ├── single_chat_page_mobile.dart │ │ ├── welcome_page_mobile.dart │ │ └── widget │ │ │ ├── body_widget.dart │ │ │ └── mobile_message_layout.dart │ ├── tablet │ │ ├── single_chat_page_tablet.dart │ │ ├── tablet_page.dart │ │ ├── welcome_page_tablet.dart │ │ └── widgets │ │ │ ├── left_side_widget.dart │ │ │ └── right_side_widget.dart │ ├── web │ │ ├── single_chat_page_web.dart │ │ ├── web_page.dart │ │ ├── welcome_page_web.dart │ │ └── widgets │ │ │ ├── left_side_widget.dart │ │ │ └── right_side_widget.dart │ └── widget │ │ └── message_layout.dart │ └── screens │ ├── home_screen.dart │ ├── single_chat_screen.dart │ └── welcome_screen.dart ├── pubspec.lock ├── pubspec.yaml ├── test └── widget_test.dart └── web ├── favicon.png ├── icons ├── Icon-192.png └── Icon-512.png ├── index.html └── manifest.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | -------------------------------------------------------------------------------- /.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: 4732a214a7362a3781989977429f14202143b4f5 8 | channel: master 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Responsive Group Chat room (firebase + clean architecture) [Web, Mobile, Tablet] 2 | 3 | ### Show some and star the repo to support the project 4 | 5 | 6 | 7 | ### Preview 8 | 9 | ![ezgif com-crop(1)](https://user-images.githubusercontent.com/10207753/93014425-6e3e5880-f5ca-11ea-9df1-6a63a646bbc6.gif) 10 |
![ezgif com-crop(1)](https://user-images.githubusercontent.com/10207753/93014336-d3de1500-f5c9-11ea-82ab-d440a62e78b8.gif) 11 | 12 | ### Screenshots 13 | ### Web 14 | 15 |

16 | 17 |
18 |
19 | 20 |
21 |
22 | 23 |
24 |
25 |

26 | 27 | 28 | 29 | ### Mobile 30 |

31 | 32 | 33 | 34 | 35 | 36 |
37 |

38 | 39 |
40 |
41 | 42 | Flutter Responsive Group Chat Room (with Firebase + Clean Architecture) complete course 43 |

44 |
45 | 46 | ### How to Create a Flutter Web project 47 | 48 | 49 |
50 | switch to the master channel run the following command 51 | 52 | ``1. flutter channel master``
53 | Then upgrade your flutter to the latest version from master.
54 | `` 2. flutter upgrade``
55 | Then enable web support.
56 | `` 3. flutter config --enable-web``
57 | 58 | Now when you create a project it'll be web enabled and you can run it in the browser. to verify that run this cmd.
59 | 60 | ``flutter devices`` 61 |
62 | 63 | Then 64 | ``flutter create xyz_project_name`` 65 |
66 | 67 | ### Packages 68 | 69 | [responsive_builder :](https://pub.dev/packages/responsive_builder) The responsive builder package contains widgets that allows you to create a readable responsive UI. The package is inspired by the Responsive UI Flutter series created by FilledStacks. 70 | 71 | [flutter_bloc :](https://pub.dev/packages/flutter_bloc) Widgets that make it easy to integrate blocs and cubits into [Flutter](https://flutter.dev/). Built to work with [package:bloc](https://pub.dev/packages/bloc). The Bloc library provides very good 🦄 tooling and compared to other state management solutions that use Streams, it's a pure gem. 72 | 73 | [get_it :](https://pub.dev/packages/get_it) This is a simple **Service Locator** for Dart and Flutter projects with some additional goodies highly inspired by [Splat](https://github.com/reactiveui/splat). It can be used instead of `InheritedWidget` or `Provider` to access objects e.g. from your UI. 74 | 75 | [equatable:](https://pub.dev/packages/equatable) Being able to compare objects in `Dart` often involves having to override the `==` operator as well as `hashCode`. 76 | 77 | [firebase :](https://firebase.google.com/) is the most amazing back-end as a service (BaaS) developed by Google for mobile and web application development that helps you build, improve, grow your app and deliver richer app experiences. 78 | 79 | 80 | ### # The Clean Architecture [proposed by our friendly Uncle Bob](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html) 81 | 82 |

83 | 84 |

85 | 86 | 87 | [@AmirKhan](https://github.com/amirk3321) , Youtube : [@eTechViral](https://www.youtube.com/channel/UCO6gMNHYhRqyzbskNh4gG_A) , Twitter : [@AmirKhan](https://twitter.com/__Meer___) 88 | -------------------------------------------------------------------------------- /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.etechviral.group_chat_room_responsive" 42 | minSdkVersion 16 43 | targetSdkVersion 29 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | multiDexEnabled true 47 | } 48 | 49 | buildTypes { 50 | release { 51 | // TODO: Add your own signing config for the release build. 52 | // Signing with the debug keys for now, so `flutter run --release` works. 53 | signingConfig signingConfigs.debug 54 | } 55 | } 56 | } 57 | 58 | flutter { 59 | source '../..' 60 | } 61 | 62 | dependencies { 63 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 64 | } 65 | apply plugin: 'com.google.gms.google-services' -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "112386489891", 4 | "firebase_url": "https://groupchatroomresponsive.firebaseio.com", 5 | "project_id": "groupchatroomresponsive", 6 | "storage_bucket": "groupchatroomresponsive.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:112386489891:android:2ce216ec89b8b9ca2f3b96", 12 | "android_client_info": { 13 | "package_name": "com.etechviral.group_chat_room_responsive" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "112386489891-o9j4fif092a5ccur03cs0fbaub10hbbk.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyBgHLtSxgpgP49CCIyQ1uJmGEkcU_AsQcQ" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "112386489891-o9j4fif092a5ccur03cs0fbaub10hbbk.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 | 12 | 19 | 23 | 27 | 32 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/etechviral/group_chat_room_responsive/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.etechviral.group_chat_room_responsive 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/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/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 'com.google.gms:google-services:4.3.3' 11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | google() 18 | jcenter() 19 | } 20 | } 21 | 22 | rootProject.buildDir = '../build' 23 | subprojects { 24 | project.buildDir = "${rootProject.buildDir}/${project.name}" 25 | } 26 | subprojects { 27 | project.evaluationDependsOn(':app') 28 | } 29 | 30 | task clean(type: Delete) { 31 | delete rootProject.buildDir 32 | } 33 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.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 | -------------------------------------------------------------------------------- /assets/background_img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/assets/background_img.png -------------------------------------------------------------------------------- /assets/bubble.json: -------------------------------------------------------------------------------- 1 | {"v":"5.6.5","fr":25,"ip":0,"op":200,"w":1000,"h":1000,"nm":"ballz","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 4","sr":1,"ks":{"o":{"a":0,"k":51,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[478,502.5,0],"to":[-9.667,-0.417,0],"ti":[17.333,-1.083,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":23,"s":[420,500,0],"to":[-17.333,1.083,0],"ti":[6.25,-6.667,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":43,"s":[374,509,0],"to":[-6.25,6.667,0],"ti":[-10.667,-8.167,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":58,"s":[382.5,540,0],"to":[10.667,8.167,0],"ti":[-19.917,0.667,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":70,"s":[438,558,0],"to":[19.917,-0.667,0],"ti":[-2.667,6,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81,"s":[502,536,0],"to":[2.667,-6,0],"ti":[12.75,1.5,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":88,"s":[454,522,0],"to":[-12.75,-1.5,0],"ti":[5.333,-3.333,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104,"s":[425.5,527,0],"to":[-5.333,3.333,0],"ti":[-9,-6.917,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":111,"s":[422,542,0],"to":[9,6.917,0],"ti":[-8.333,1.667,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":121,"s":[479.5,568.5,0],"to":[8.333,-1.667,0],"ti":[-11,7.167,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":131,"s":[472,532,0],"to":[11,-7.167,0],"ti":[-9.5,4.417,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":145,"s":[545.5,525.5,0],"to":[9.5,-4.417,0],"ti":[3.917,-1.75,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":157,"s":[529,505.5,0],"to":[-3.917,1.75,0],"ti":[8.25,0.25,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":178,"s":[522,536,0],"to":[-8.25,-0.25,0],"ti":[7.083,5.333,0]},{"t":199,"s":[479.5,504,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[294,274],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.654901960784,0.796078431373,0.850980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[241,-357],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":200,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 3","sr":1,"ks":{"o":{"a":0,"k":51,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[545.25,483.5,0],"to":[5.458,-4.25,0],"ti":[-20.458,-0.75,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":23,"s":[578,458,0],"to":[20.458,0.75,0],"ti":[-19.333,-12.917,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":45,"s":[668,488,0],"to":[19.333,12.917,0],"ti":[1.083,-3.5,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":62,"s":[694,535.5,0],"to":[-0.403,1.303,0],"ti":[5.23,5.938,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":70,"s":[655.624,535.627,0],"to":[-8.823,-10.017,0],"ti":[-19.906,2.756,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":88,"s":[667,509.5,0],"to":[11.075,-1.533,0],"ti":[-9.167,-13.083,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":110,"s":[728,538,0],"to":[9.167,13.083,0],"ti":[7.667,-7.667,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":131,"s":[722,588,0],"to":[-7.667,7.667,0],"ti":[9.333,7.333,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":144,"s":[682,584,0],"to":[-9.333,-7.333,0],"ti":[14,6.667,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":157,"s":[666,544,0],"to":[-14,-6.667,0],"ti":[20.167,10.083,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":178,"s":[598,544,0],"to":[-20.167,-10.083,0],"ti":[-5.625,13.708,0]},{"t":199,"s":[545,483.5,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[166,164],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.654901960784,0.796078431373,0.850980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-135,166],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[208.434,207.5],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":200,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 2","sr":1,"ks":{"o":{"a":0,"k":51,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[488,483.5,0],"to":[3.5,-12.75,0],"ti":[17.333,7.333,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":23,"s":[470,452,0],"to":[-10.245,-4.334,0],"ti":[16.399,-1.959,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":36,"s":[428.007,456.366,0],"to":[-11.346,1.355,0],"ti":[7.77,-2.863,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":45,"s":[396,456,0],"to":[-19,7,0],"ti":[-0.333,-13.667,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":62,"s":[356,494,0],"to":[0.333,13.667,0],"ti":[-11,1,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":88,"s":[398,538,0],"to":[11,-1,0],"ti":[-11.083,1.083,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":109,"s":[422,488,0],"to":[11.083,-1.083,0],"ti":[-1.667,-15.667,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":131,"s":[464.5,531.5,0],"to":[1.667,15.667,0],"ti":[-3.333,-10.333,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":145,"s":[432,582,0],"to":[3.333,10.333,0],"ti":[-0.667,6.667,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":157,"s":[484.5,593.5,0],"to":[0.667,-6.667,0],"ti":[-0.583,18.583,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":177,"s":[436,542,0],"to":[0.583,-18.583,0],"ti":[1.833,25.417,0]},{"t":199,"s":[488,482,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[142,138],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.654901960784,0.796078431373,0.850980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[193,-99],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":200,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":51,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":0,"s":[500,500,0],"to":[-3.333,6.667,0],"ti":[-5,-12.333,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":28,"s":[480,540,0],"to":[5,12.333,0],"ti":[-11.667,2,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":53,"s":[530,574,0],"to":[11.667,-2,0],"ti":[-4,16.333,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":81,"s":[550,528,0],"to":[4,-16.333,0],"ti":[-10,7.667,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":104,"s":[554,476,0],"to":[10,-7.667,0],"ti":[-9,7.667,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":121,"s":[610,482,0],"to":[9,-7.667,0],"ti":[9.667,6.333,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":138,"s":[608,430,0],"to":[-9.667,-6.333,0],"ti":[3,-12.333,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":151,"s":[552,444,0],"to":[-3,12.333,0],"ti":[8,-9.333,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":174,"s":[590,504,0],"to":[-8,9.333,0],"ti":[14.333,0.667,0]},{"t":199,"s":[504,500,0]}],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[310,310],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":0,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.654901960784,0.796078431373,0.850980392157,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-233,-223],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":200,"st":0,"bm":0}],"markers":[]} -------------------------------------------------------------------------------- /assets/congratulation.json: -------------------------------------------------------------------------------- 1 | {"v":"5.7.0","fr":29.9700012207031,"ip":0,"op":90.0000036657751,"w":800,"h":280,"nm":"바보","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Shape Layer 1","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[33,179,0],"ix":2},"a":{"a":0,"k":[-367,39,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.455,0.455,0.667],"y":[1.003,1.003,1]},"o":{"x":[0.028,0.028,0.333],"y":[1.519,1.519,0]},"t":0,"s":[0,0,100]},{"t":41.0000016699642,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-46,-10],[-6,24]],"o":[[0,0],[46,10],[6,-24]],"v":[[-366,38],[-284,-2],[-214,-26]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.995294129615,0.69033873315,0.284927338245,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":9,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60.0000024438501,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"L_하늘 작은원","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[275.335,64.716,0],"ix":2},"a":{"a":0,"k":[6.913,6.914,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.035,0.035,0.667],"y":[1.362,1.362,1]},"o":{"x":[0.008,0.008,0.333],"y":[1.31,1.31,0]},"t":3,"s":[0,0,100]},{"t":48.0000019550801,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.076,-2.534],[2.534,2.076],[-2.075,2.535],[-2.533,-2.076]],"o":[[-2.076,2.534],[-2.534,-2.076],[2.076,-2.533],[2.534,2.075]],"v":[[4.588,3.758],[-3.758,4.588],[-4.589,-3.759],[3.758,-4.588]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.579999976065,0.855000035903,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.913,6.914],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60.0000024438501,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"L_주황원","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[83.223,248.127,0],"ix":2},"a":{"a":0,"k":[8.312,8.313,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.15,0.15,0.667],"y":[1.281,1.281,1]},"o":{"x":[0.008,0.008,0.333],"y":[1.556,1.556,0]},"t":3,"s":[0,0,100]},{"t":48.0000019550801,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.3,-4.287],[4.287,0.301],[-0.301,4.286],[-4.287,-0.3]],"o":[[-0.3,4.287],[-4.286,-0.301],[0.3,-4.288],[4.287,0.301]],"v":[[7.762,0.543],[-0.544,7.761],[-7.761,-0.543],[0.544,-7.762]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.666999966491,0.224000010771,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[8.312,8.313],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60.0000024438501,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"L_구불초록","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[173.967,224.639,0],"ix":2},"a":{"a":0,"k":[49.171,23.215,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.198,0.198,0.667],"y":[1.264,1.264,1]},"o":{"x":[0.007,0.007,0.333],"y":[2.046,2.046,0]},"t":3,"s":[0,0,100]},{"t":48.0000019550801,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-23.191,5.575],[-13.242,-16.528]],"o":[[0,0],[23.19,-5.575],[0,0]],"v":[[-40.223,-8.177],[-4.986,2.692],[40.223,8.26]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.246999987434,1,0.776000019148,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8.379,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[61.171,29.215],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60.0000024438501,"st":0,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"L_네모 핑크","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[213.323,41.8,0],"ix":2},"a":{"a":0,"k":[27.087,13.782,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.034,0.034,0.667],"y":[1.395,1.395,1]},"o":{"x":[0.045,0.045,0.333],"y":[1.911,1.911,0]},"t":3,"s":[0,0,100]},{"t":48.0000019550801,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.026,-0.378],[0,0],[-0.727,0.32],[-14.856,-9.275],[-0.283,0.564],[0,0],[0.558,0.239],[23.944,-11.679]],"o":[[0,0],[-0.056,0.793],[6.262,-2.76],[0.535,0.335],[0,0],[0.273,-0.543],[-4.956,-2.117],[-0.341,0.166]],"v":[[-25.861,-0.969],[-26.781,12.17],[-25.31,13.212],[19.094,10.582],[20.615,10.177],[26.564,-1.691],[26.044,-3.131],[-25.265,-1.852]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.952999997606,0.579999976065,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.087,13.782],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60.0000024438501,"st":0,"bm":0},{"ddd":0,"ind":6,"ty":4,"nm":"L_네모 보라","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[99.071,205.298,0],"ix":2},"a":{"a":0,"k":[5.757,24.766,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.086,0.086,0.667],"y":[1.129,1.129,1]},"o":{"x":[0.007,0.007,0.333],"y":[1.458,1.458,0]},"t":3,"s":[0,0,100]},{"t":48.0000019550801,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.034,-0.479],[0,0],[-0.674,0.082],[0,0],[-0.036,0.516],[0,0],[0.703,-0.152],[0,0]],"o":[[0,0],[-0.048,0.677],[0,0],[0.513,-0.063],[0,0],[0.05,-0.718],[0,0],[-0.47,0.101]],"v":[[-18.27,-4.705],[-19.46,12.287],[-18.253,13.434],[17.085,9.129],[18.031,8.133],[19.458,-12.233],[18.152,-13.364],[-17.421,-5.684]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.709999952129,0.694000004787,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[19.758,13.766],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60.0000024438501,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":4,"nm":"L_네모 하늘","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[103.245,56.206,0],"ix":2},"a":{"a":0,"k":[18.443,31.294,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.153,0.153,0.667],"y":[1.127,1.127,1]},"o":{"x":[0.008,0.008,0.333],"y":[1.529,1.529,0]},"t":3,"s":[0,0,100]},{"t":48.0000019550801,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.059,0.411],[-9.703,14.021],[-0.246,-0.009],[0,0],[0.158,-0.452],[-1.201,-12.74],[0.434,0.03],[0,0]],"o":[[0.853,-5.952],[0.14,-0.203],[0,0],[0.478,0.019],[-2.283,6.557],[0.04,0.434],[0,0],[-0.414,-0.029]],"v":[[-18.134,28.815],[-1.617,-30.732],[-0.997,-31.035],[17.388,-30.305],[18.035,-29.362],[3.15,30.252],[2.39,31.014],[-17.476,29.622]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.666999966491,0.870999983245,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[18.443,31.294],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60.0000024438501,"st":0,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":"R_하늘 작은원","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[706.334,124.716,0],"ix":2},"a":{"a":0,"k":[6.913,6.914,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.469,0.469,0.667],"y":[1,1,1]},"o":{"x":[0.02,0.02,0.333],"y":[1.991,1.991,0]},"t":5,"s":[0,0,100]},{"t":50.0000020365418,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[2.075,-2.534],[2.535,2.076],[-2.075,2.535],[-2.534,-2.076]],"o":[[-2.076,2.534],[-2.534,-2.076],[2.076,-2.533],[2.534,2.075]],"v":[[4.589,3.758],[-3.759,4.588],[-4.588,-3.759],[3.759,-4.588]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.579999976065,0.855000035903,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.913,6.914],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60.0000024438501,"st":0,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"R_하늘 원","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[596.5,17.5,0],"ix":2},"a":{"a":0,"k":[10.25,10.25,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.469,0.469,0.667],"y":[1,1,1]},"o":{"x":[0.02,0.02,0.333],"y":[1.991,1.991,0]},"t":0,"s":[0,0,100]},{"t":45.0000018328876,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-5.523],[5.522,0],[0,5.523],[-5.522,0]],"o":[[0,5.523],[-5.522,0],[0,-5.523],[5.522,0]],"v":[[10,0],[0,10],[-10,0],[0,-10]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.579999976065,0.855000035903,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[10.25,10.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60.0000024438501,"st":0,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"R_노랑 원","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[598,190,0],"ix":2},"a":{"a":0,"k":[5.75,5.75,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.469,0.469,0.667],"y":[1,1,1]},"o":{"x":[0.02,0.02,0.333],"y":[1.991,1.991,0]},"t":3,"s":[0,0,100]},{"t":48.0000019550801,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-3.038],[3.037,0],[0,3.038],[-3.037,0]],"o":[[0,3.038],[-3.037,0],[0,-3.038],[3.037,0]],"v":[[5.5,0],[0,5.5],[-5.5,0],[0,-5.5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.917999985639,0.62400004069,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[5.75,5.75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60.0000024438501,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"R_구불핑ㅋ","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":-12.353,"ix":10},"p":{"a":0,"k":[704,192.5,0],"ix":2},"a":{"a":0,"k":[112.5,120,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.469,0.469,0.667],"y":[1,1,1]},"o":{"x":[0.02,0.02,0.333],"y":[1.991,1.991,0]},"t":3,"s":[0,0,100]},{"t":48.0000019550801,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[-51,-3],[0,0]],"o":[[0,0],[51,3],[0,0]],"v":[[-54,-57.5],[3,-2.5],[39,57.5]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[1,0.579999976065,0.859000052658,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":7,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[71.5,75],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60.0000024438501,"st":0,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":"R_네모 블루","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[648.11,211.289,0],"ix":2},"a":{"a":0,"k":[18.822,20.282,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.469,0.469,0.667],"y":[1,1,1]},"o":{"x":[0.02,0.02,0.333],"y":[1.991,1.991,0]},"t":0,"s":[0,0,100]},{"t":45.0000018328876,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0.667,-0.351],[0,0],[-0.587,-0.759],[0,0],[-0.639,0.73],[0,0],[0.443,0.583],[0,0]],"o":[[0,0],[-0.848,0.446],[0,0],[0.594,0.768],[0,0],[0.483,-0.551],[0,0],[-0.456,-0.6]],"v":[[-1.266,-19.681],[-17.484,-11.145],[-17.985,-8.835],[3.744,19.229],[6.124,19.302],[18.061,5.659],[18.129,3.709],[0.68,-19.249]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.74900004069,0.791999966491,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[18.822,20.282],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60.0000024438501,"st":0,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"R_직선 초록","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":21.354,"ix":10},"p":{"a":0,"k":[639,98.396,0],"ix":2},"a":{"a":0,"k":[98.25,32.561,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.469,0.469,0.667],"y":[1,1,1]},"o":{"x":[0.02,0.02,0.333],"y":[1.991,1.991,0]},"t":0,"s":[0,0,100]},{"t":45.0000018328876,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-0.377],[0,0],[-0.52,0.13],[-50.335,-23.243],[0,0.6],[0,0],[0.252,0.148],[61.187,-15.753]],"o":[[0,0],[0,0.536],[7.822,-1.964],[0.545,0.252],[0,0],[0,-0.292],[-4.69,-2.737],[-0.365,0.095]],"v":[[-56,-3.754],[-56,9.539],[-54.983,10.339],[54.833,20.059],[56,19.319],[56,8.073],[55.597,7.364],[-55.376,-4.558]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.246999987434,1,0.776000019148,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[56.25,20.561],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60.0000024438501,"st":0,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":"R_직선 주황","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":33.443,"ix":10},"p":{"a":0,"k":[738.954,116.383,0],"ix":2},"a":{"a":0,"k":[98.204,33.6,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.469,0.469,0.667],"y":[1,1,1]},"o":{"x":[0.02,0.02,0.333],"y":[1.991,1.991,0]},"t":3,"s":[0,0,100]},{"t":48.0000019550801,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-0.516],[0,0],[-0.458,-0.04],[-35.646,-21.989],[-0.204,0.51],[0,0],[0.296,0.246],[74.082,5.655]],"o":[[0,0],[0,0.46],[7.551,0.654],[0.468,0.289],[0,0],[0.143,-0.358],[-3.949,-3.284],[-0.515,-0.039]],"v":[[-53.954,-19.426],[-53.954,-9.199],[-53.152,-8.317],[49.156,20.061],[50.437,19.64],[53.811,11.204],[53.559,10.197],[-52.993,-20.311]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,0.666999966491,0.224000010771,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[54.204,20.6],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":60.0000024438501,"st":0,"bm":0}],"markers":[]} -------------------------------------------------------------------------------- /assets/loading.json: -------------------------------------------------------------------------------- 1 | {"v":"5.7.1","fr":30,"ip":0,"op":110,"w":108,"h":108,"nm":"匹配","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":15,"s":[80]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":40,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":60,"s":[70]},{"t":75,"s":[100]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[54,54,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":15,"s":[80,80,100]},{"t":40,"s":[100,100,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[12,12],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.129411771894,0.501960813999,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":110,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":40,"s":[70]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":45,"s":[20]},{"t":50,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[54,54,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":10,"s":[80,80,100]},{"t":50,"s":[300,300,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[12,12],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.701960784314,0.827450980392,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":110,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":45,"s":[100]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":75,"s":[70]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":80,"s":[20]},{"t":85,"s":[0]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[54,54,0],"ix":2},"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":45,"s":[80,80,100]},{"t":85,"s":[300,300,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[12,12],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"椭圆路径 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.701960784314,0.827450980392,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"填充 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[300,300],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"变换"}],"nm":"3","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":110,"st":0,"bm":0}],"markers":[]} -------------------------------------------------------------------------------- /assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/assets/logo.png -------------------------------------------------------------------------------- /assets/profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/assets/profile.png -------------------------------------------------------------------------------- /assets/profile_default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/assets/profile_default.png -------------------------------------------------------------------------------- /assets/shape.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/assets/shape.png -------------------------------------------------------------------------------- /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 "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/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/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/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/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/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/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/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/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/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/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/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/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/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/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/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/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/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/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/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/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/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/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/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/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/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/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/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/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/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/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | group_chat_room_responsive 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/data/datasource/firebase_remote_datasource.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:firebase_auth/firebase_auth.dart'; 3 | import 'package:group_chat_room_responsive/data/model/text_message_model.dart'; 4 | import 'package:group_chat_room_responsive/data/model/user_model.dart'; 5 | import 'package:group_chat_room_responsive/domain/entities/text_message_entity.dart'; 6 | 7 | abstract class FirebaseRemoteDataSource { 8 | Future signUp(String email, String password); 9 | 10 | Future signIn(String email, String password); 11 | 12 | Future signOut(); 13 | 14 | Future isSignIn(); 15 | 16 | Future getCurrentUid(); 17 | 18 | Future getCreateCurrentUser( 19 | String email, String name, String profileUrl); 20 | 21 | Future sendTextMessage(TextMessageEntity textMessage); 22 | 23 | Stream> getUsers(); 24 | 25 | Stream> getMessages(); 26 | } 27 | 28 | class FirebaseRemoteDataSourceImpl implements FirebaseRemoteDataSource { 29 | final FirebaseAuth _auth = FirebaseAuth.instance; 30 | final _userCollection = FirebaseFirestore.instance.collection("users"); 31 | final _globalChatChannelCollection = 32 | FirebaseFirestore.instance.collection("globalChatChannel"); 33 | 34 | final String channelId = "8ghYhb9YN58qQeSBy2MG"; 35 | 36 | @override 37 | Future getCurrentUid() async => _auth.currentUser.uid; 38 | 39 | @override 40 | Future isSignIn() async => _auth.currentUser.uid != null; 41 | 42 | @override 43 | Future signIn(String email, String password) async { 44 | await _auth.signInWithEmailAndPassword(email: email, password: password); 45 | } 46 | 47 | @override 48 | Future signUp(String email, String password) async { 49 | await _auth.createUserWithEmailAndPassword( 50 | email: email, password: password); 51 | } 52 | 53 | @override 54 | Future getCreateCurrentUser( 55 | String email, String name, String profileUrl) async { 56 | _userCollection.doc(_auth.currentUser.uid).get().then((user) { 57 | if (!user.exists) { 58 | final newUser = UserModel( 59 | name: name, 60 | email: email, 61 | uid: _auth.currentUser.uid, 62 | profileUrl: profileUrl, 63 | ).toDocument(); 64 | _userCollection.doc(_auth.currentUser.uid).set(newUser); 65 | return; 66 | } else { 67 | print("User Already exists"); 68 | return; 69 | } 70 | }); 71 | } 72 | 73 | @override 74 | Stream> getMessages() { 75 | return _globalChatChannelCollection 76 | .doc(channelId) 77 | .collection("messages") 78 | .orderBy("time") 79 | .snapshots() 80 | .map((querySnapshot) => querySnapshot.docs 81 | .map((docSnapshot) => TextMessageModel.fromSnapshot(docSnapshot)) 82 | .toList()); 83 | } 84 | 85 | @override 86 | Stream> getUsers() { 87 | return _userCollection.snapshots().map( 88 | (querySnapShot) => querySnapShot.docs 89 | .map((docSnapshot) => UserModel.fromSnapshot(docSnapshot)) 90 | .toList(), 91 | ); 92 | } 93 | 94 | @override 95 | Future sendTextMessage(TextMessageEntity textMessage) async { 96 | final newMessage = TextMessageModel( 97 | message: textMessage.message, 98 | recipientId: textMessage.recipientId, 99 | time: textMessage.time, 100 | receiverName: textMessage.receiverName, 101 | senderId: textMessage.senderId, 102 | senderName: textMessage.senderName, 103 | type: textMessage.type, 104 | ); 105 | _globalChatChannelCollection 106 | .doc(channelId) 107 | .collection("messages") 108 | .add(newMessage.toDocument()); 109 | } 110 | 111 | @override 112 | Future signOut() async{ 113 | await _auth.signOut(); 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /lib/data/model/text_message_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:cloud_firestore_platform_interface/src/timestamp.dart'; 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:group_chat_room_responsive/domain/entities/text_message_entity.dart'; 5 | 6 | class TextMessageModel extends TextMessageEntity { 7 | TextMessageModel({String recipientId, String senderId, String senderName, 8 | String type="TEXT", Timestamp time, String message, String receiverName}) 9 | : super(recipientId, senderId, senderName, type, time, message, receiverName); 10 | 11 | factory TextMessageModel.fromJson(Map json){ 12 | return TextMessageModel( 13 | recipientId: json['recipientId'], 14 | message:json['message'], 15 | time: json['time'], 16 | receiverName:json['receiverName'], 17 | senderId:json['senderId'], 18 | senderName:json['senderName'], 19 | type:json['type'], 20 | ); 21 | } 22 | 23 | factory TextMessageModel.fromSnapshot(DocumentSnapshot documentSnapshot){ 24 | return TextMessageModel( 25 | time: documentSnapshot.data()['time'], 26 | message:documentSnapshot.data()['message'], 27 | receiverName: documentSnapshot.data()['receiverName'], 28 | recipientId: documentSnapshot.data()['recipientId'], 29 | senderId:documentSnapshot.data()['senderId'], 30 | senderName:documentSnapshot.data()['senderName'], 31 | type:documentSnapshot.data()['type'], 32 | ); 33 | } 34 | 35 | Map toDocument(){ 36 | return { 37 | "recipientId":recipientId, 38 | "senderId":senderId, 39 | "senderName":senderName, 40 | "type":type, 41 | "time":time, 42 | "message":message, 43 | "receiverName":receiverName 44 | }; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/data/model/user_model.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:group_chat_room_responsive/domain/entities/user_entity.dart'; 3 | 4 | class UserModel extends UserEntity { 5 | UserModel({String name, String email, String uid, String profileUrl}) 6 | : super(name, email, uid, profileUrl); 7 | 8 | factory UserModel.fromJson(Map json){ 9 | return UserModel( 10 | name: json['name'], 11 | profileUrl: json['profileUrl'], 12 | email: json['email'], 13 | uid: json['uid'] 14 | ); 15 | } 16 | factory UserModel.fromSnapshot(DocumentSnapshot documentSnapshot){ 17 | return UserModel( 18 | name: documentSnapshot.data()['name'], 19 | uid: documentSnapshot.data()['uid'], 20 | email: documentSnapshot.data()['email'], 21 | profileUrl: documentSnapshot.data()['profileUrl'], 22 | ); 23 | } 24 | Map toDocument(){ 25 | return { 26 | "name" :name, 27 | "uid" :uid, 28 | "email":email, 29 | "profileUrl":profileUrl, 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/data/repositories/firebasee_repository_impl.dart: -------------------------------------------------------------------------------- 1 | import 'package:group_chat_room_responsive/data/datasource/firebase_remote_datasource.dart'; 2 | import 'package:group_chat_room_responsive/domain/entities/text_message_entity.dart'; 3 | import 'package:group_chat_room_responsive/domain/entities/user_entity.dart'; 4 | import 'package:group_chat_room_responsive/domain/repositories/firebase_repository.dart'; 5 | 6 | class FireBaseRepositoryImpl implements FireBaseRepository { 7 | final FirebaseRemoteDataSource firebaseRemoteDataSource; 8 | 9 | FireBaseRepositoryImpl({this.firebaseRemoteDataSource}); 10 | 11 | @override 12 | Future getCurrentUid() async => 13 | await firebaseRemoteDataSource.getCurrentUid(); 14 | 15 | @override 16 | Future isSignIn() async => 17 | await firebaseRemoteDataSource.isSignIn(); 18 | 19 | @override 20 | Future signIn(String email, String password) async => 21 | await firebaseRemoteDataSource.signIn(email, password); 22 | 23 | @override 24 | Future signUp(String email, String password) async => 25 | await firebaseRemoteDataSource.signUp(email, password); 26 | 27 | @override 28 | Future getCreateCurrentUser(String email, String name, 29 | String profileUrl)async { 30 | return await firebaseRemoteDataSource.getCreateCurrentUser(email, name, profileUrl); 31 | } 32 | 33 | @override 34 | Stream> getMessages() { 35 | return firebaseRemoteDataSource.getMessages(); 36 | } 37 | 38 | @override 39 | Stream> getUsers() { 40 | return firebaseRemoteDataSource.getUsers(); 41 | } 42 | 43 | @override 44 | Future sendTextMessage(TextMessageEntity textMessage) { 45 | return firebaseRemoteDataSource.sendTextMessage(textMessage); 46 | } 47 | 48 | @override 49 | Future signOut() => firebaseRemoteDataSource.signOut(); 50 | 51 | } 52 | -------------------------------------------------------------------------------- /lib/domain/entities/text_message_entity.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:cloud_firestore/cloud_firestore.dart'; 4 | import 'package:equatable/equatable.dart'; 5 | 6 | class TextMessageEntity extends Equatable{ 7 | final String recipientId; 8 | final String senderId; 9 | final String senderName; 10 | final String type; 11 | final Timestamp time; 12 | final String message; 13 | final String receiverName; 14 | 15 | TextMessageEntity(this.recipientId, this.senderId, this.senderName, this.type, this.time, this.message, this.receiverName); 16 | 17 | @override 18 | // TODO: implement props 19 | List get props => [recipientId, senderId, senderName, type, time, message, receiverName]; 20 | 21 | } -------------------------------------------------------------------------------- /lib/domain/entities/user_entity.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:equatable/equatable.dart'; 3 | 4 | class UserEntity extends Equatable{ 5 | final String name; 6 | final String email; 7 | final String uid; 8 | final String profileUrl; 9 | 10 | UserEntity(this.name, this.email, this.uid, this.profileUrl); 11 | 12 | @override 13 | // TODO: implement props 14 | List get props => [name,email,uid,profileUrl]; 15 | } -------------------------------------------------------------------------------- /lib/domain/repositories/firebase_repository.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:group_chat_room_responsive/domain/entities/text_message_entity.dart'; 4 | import 'package:group_chat_room_responsive/domain/entities/user_entity.dart'; 5 | 6 | abstract class FireBaseRepository{ 7 | Future signUp(String email,String password); 8 | Future signIn(String email,String password); 9 | Future isSignIn(); 10 | Future signOut(); 11 | Future getCurrentUid(); 12 | Future getCreateCurrentUser( 13 | String email, String name, String profileUrl); 14 | Future sendTextMessage(TextMessageEntity textMessage); 15 | Stream> getUsers(); 16 | Stream> getMessages(); 17 | } -------------------------------------------------------------------------------- /lib/domain/usecase/get_create_current_user.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:group_chat_room_responsive/domain/repositories/firebase_repository.dart'; 4 | 5 | class GetCreateCurrentUser{ 6 | final FireBaseRepository fireBaseRepository; 7 | 8 | GetCreateCurrentUser({this.fireBaseRepository}); 9 | 10 | Future call(String email,String name,String profileUrl)async{ 11 | fireBaseRepository.getCreateCurrentUser(email, name, profileUrl); 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /lib/domain/usecase/get_current_uid.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:group_chat_room_responsive/domain/repositories/firebase_repository.dart'; 4 | 5 | class GetCurrentUid{ 6 | final FireBaseRepository repository; 7 | 8 | GetCurrentUid({this.repository}); 9 | 10 | Future call() async => await repository.getCurrentUid(); 11 | 12 | } -------------------------------------------------------------------------------- /lib/domain/usecase/get_messages_usecase.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:group_chat_room_responsive/domain/entities/text_message_entity.dart'; 4 | import 'package:group_chat_room_responsive/domain/repositories/firebase_repository.dart'; 5 | 6 | class GetMessagesUseCase { 7 | final FireBaseRepository repository; 8 | 9 | GetMessagesUseCase({this.repository}); 10 | 11 | Stream> call() => repository.getMessages(); 12 | 13 | } -------------------------------------------------------------------------------- /lib/domain/usecase/get_useres_usecase.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:group_chat_room_responsive/domain/entities/user_entity.dart'; 4 | import 'package:group_chat_room_responsive/domain/repositories/firebase_repository.dart'; 5 | 6 | class GetUsersUseCase { 7 | final FireBaseRepository repository; 8 | 9 | GetUsersUseCase({this.repository}); 10 | 11 | Stream> call() => repository.getUsers(); 12 | 13 | 14 | } -------------------------------------------------------------------------------- /lib/domain/usecase/is_signin_usecase.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:group_chat_room_responsive/domain/repositories/firebase_repository.dart'; 3 | 4 | class IsSignInUseCase{ 5 | final FireBaseRepository repository; 6 | 7 | IsSignInUseCase(this.repository); 8 | 9 | Future call() async => repository.isSignIn(); 10 | } -------------------------------------------------------------------------------- /lib/domain/usecase/send_text_message_usecase.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:group_chat_room_responsive/domain/entities/text_message_entity.dart'; 4 | import 'package:group_chat_room_responsive/domain/repositories/firebase_repository.dart'; 5 | 6 | class SendTextMessageUseCase{ 7 | final FireBaseRepository repository; 8 | 9 | SendTextMessageUseCase({this.repository}); 10 | 11 | Future call(TextMessageEntity textMessage){ 12 | return repository.sendTextMessage(textMessage); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /lib/domain/usecase/sign_out_usecase.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | import 'package:group_chat_room_responsive/domain/repositories/firebase_repository.dart'; 5 | 6 | class SignOutUseCase{ 7 | final FireBaseRepository repository; 8 | 9 | SignOutUseCase({this.repository}); 10 | 11 | Future call()async{ 12 | return await repository.signOut(); 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /lib/domain/usecase/signin_usecase.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:group_chat_room_responsive/domain/repositories/firebase_repository.dart'; 4 | 5 | class SignInUseCase{ 6 | final FireBaseRepository repository; 7 | 8 | SignInUseCase({this.repository}); 9 | 10 | Future call(String email,String password){ 11 | return repository.signIn(email, password); 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /lib/domain/usecase/signup_usecase.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:group_chat_room_responsive/domain/repositories/firebase_repository.dart'; 4 | 5 | class SignUpUseCase{ 6 | final FireBaseRepository repository; 7 | 8 | SignUpUseCase({this.repository}); 9 | 10 | Future call(String email,String password) async{ 11 | return repository.signUp(email, password); 12 | } 13 | } -------------------------------------------------------------------------------- /lib/injection_container.dart: -------------------------------------------------------------------------------- 1 | import 'package:get_it/get_it.dart'; 2 | import 'package:group_chat_room_responsive/data/datasource/firebase_remote_datasource.dart'; 3 | import 'package:group_chat_room_responsive/data/repositories/firebasee_repository_impl.dart'; 4 | import 'package:group_chat_room_responsive/domain/repositories/firebase_repository.dart'; 5 | import 'package:group_chat_room_responsive/domain/usecase/get_create_current_user.dart'; 6 | import 'package:group_chat_room_responsive/domain/usecase/get_current_uid.dart'; 7 | import 'package:group_chat_room_responsive/domain/usecase/get_messages_usecase.dart'; 8 | import 'package:group_chat_room_responsive/domain/usecase/get_useres_usecase.dart'; 9 | import 'package:group_chat_room_responsive/domain/usecase/is_signin_usecase.dart'; 10 | import 'package:group_chat_room_responsive/domain/usecase/send_text_message_usecase.dart'; 11 | import 'package:group_chat_room_responsive/domain/usecase/sign_out_usecase.dart'; 12 | import 'package:group_chat_room_responsive/domain/usecase/sign_out_usecase.dart'; 13 | import 'package:group_chat_room_responsive/domain/usecase/signin_usecase.dart'; 14 | import 'package:group_chat_room_responsive/presentation/bloc/auth/auth_cubit.dart'; 15 | import 'package:group_chat_room_responsive/presentation/bloc/communication/communication_cubit.dart'; 16 | import 'package:group_chat_room_responsive/presentation/bloc/login/login_cubit.dart'; 17 | 18 | import 'domain/usecase/signup_usecase.dart'; 19 | import 'presentation/bloc/user/user_cubit.dart'; 20 | 21 | final sl = GetIt.instance; 22 | 23 | Future init() async { 24 | //Features bloc, 25 | sl.registerFactory(() => 26 | AuthCubit(isSignInUseCase: sl.call(), getCurrentUidUseCase: sl.call())); 27 | sl.registerFactory(() => LoginCubit( 28 | signInUseCase: sl.call(), 29 | signUpUseCase: sl.call(), 30 | getCreateCurrentUser: sl.call(), 31 | signOutUseCase: sl.call(), 32 | )); 33 | sl.registerFactory(() => UserCubit(usersUseCase: sl.call())); 34 | sl.registerFactory(() => CommunicationCubit( 35 | getMessagesUseCase: sl.call(), sendTextMessageUseCase: sl.call())); 36 | //!useCase 37 | sl.registerLazySingleton(() => IsSignInUseCase(sl.call())); 38 | sl.registerLazySingleton( 39 | () => GetCurrentUid(repository: sl.call())); 40 | sl.registerLazySingleton( 41 | () => GetCreateCurrentUser(fireBaseRepository: sl.call())); 42 | sl.registerLazySingleton( 43 | () => SignInUseCase(repository: sl.call())); 44 | sl.registerLazySingleton( 45 | () => SignUpUseCase(repository: sl.call())); 46 | sl.registerLazySingleton( 47 | () => GetUsersUseCase(repository: sl.call())); 48 | sl.registerLazySingleton( 49 | () => GetMessagesUseCase(repository: sl.call())); 50 | sl.registerLazySingleton( 51 | () => SendTextMessageUseCase(repository: sl.call())); 52 | sl.registerLazySingleton( 53 | () => SignOutUseCase(repository: sl.call())); 54 | 55 | //repository 56 | sl.registerLazySingleton( 57 | () => FireBaseRepositoryImpl(firebaseRemoteDataSource: sl.call())); 58 | //dataSource 59 | sl.registerLazySingleton( 60 | () => FirebaseRemoteDataSourceImpl()); 61 | //external 62 | //e.g final sharedPreference=await SharedPreferences.getInstance(); 63 | } 64 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_bloc/flutter_bloc.dart'; 3 | import 'package:group_chat_room_responsive/presentation/bloc/auth/auth_cubit.dart'; 4 | 5 | import 'presentation/bloc/communication/communication_cubit.dart'; 6 | import 'presentation/bloc/login/login_cubit.dart'; 7 | import 'presentation/bloc/user/user_cubit.dart'; 8 | import 'presentation/screens/home_screen.dart'; 9 | import 'injection_container.dart' as di; 10 | import 'presentation/screens/welcome_screen.dart'; 11 | import 'package:firebase_core/firebase_core.dart'; 12 | 13 | 14 | 15 | void main() async{ 16 | WidgetsFlutterBinding.ensureInitialized(); 17 | await Firebase.initializeApp(); 18 | await di.init(); 19 | runApp(MyApp()); 20 | } 21 | 22 | class MyApp extends StatelessWidget { 23 | @override 24 | Widget build(BuildContext context) { 25 | 26 | 27 | return MultiBlocProvider( 28 | providers: [ 29 | BlocProvider( 30 | create: (_) => di.sl()..appStarted(), 31 | ), 32 | BlocProvider( 33 | create: (_) => di.sl(), 34 | ), 35 | BlocProvider( 36 | create: (_) => di.sl(), 37 | ), 38 | BlocProvider( 39 | create: (_) => di.sl(), 40 | ) 41 | ], 42 | child: MaterialApp( 43 | title: 'Flutter Group Chat Room Responsive', 44 | debugShowCheckedModeBanner: false, 45 | routes: { 46 | "/" : (context){ 47 | return BlocBuilder( 48 | builder: (context,authState){ 49 | if (authState is Authenticated){ 50 | return WelcomeScreen(uid:authState.uid); 51 | } 52 | if (authState is UnAuthenticated){ 53 | return HomeScreen(); 54 | } 55 | return Container(); 56 | }, 57 | ); 58 | } 59 | }, 60 | ), 61 | ); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/presentation/bloc/auth/auth_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import 'package:group_chat_room_responsive/domain/usecase/get_current_uid.dart'; 4 | import 'package:group_chat_room_responsive/domain/usecase/is_signin_usecase.dart'; 5 | 6 | part 'auth_state.dart'; 7 | 8 | class AuthCubit extends Cubit { 9 | final IsSignInUseCase isSignInUseCase; 10 | final GetCurrentUid getCurrentUidUseCase; 11 | AuthCubit({this.isSignInUseCase,this.getCurrentUidUseCase}) : super(AuthInitial()); 12 | 13 | 14 | Future appStarted()async{ 15 | 16 | try{ 17 | final isSignIn=await isSignInUseCase.call(); 18 | print("Sign $isSignIn"); 19 | if (isSignIn==true){ 20 | final currentUid=await getCurrentUidUseCase.call(); 21 | emit(Authenticated(uid: currentUid)); 22 | }else{ 23 | emit(UnAuthenticated()); 24 | } 25 | }catch(_){ 26 | print("heelo appStarted catch"); 27 | emit(UnAuthenticated()); 28 | } 29 | } 30 | Future loggedIn() async{ 31 | final currentUid=await getCurrentUidUseCase.call(); 32 | emit(Authenticated(uid: currentUid)); 33 | } 34 | Future loggedOut() async { 35 | emit(UnAuthenticated()); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/presentation/bloc/auth/auth_state.dart: -------------------------------------------------------------------------------- 1 | part of 'auth_cubit.dart'; 2 | 3 | abstract class AuthState extends Equatable { 4 | const AuthState(); 5 | } 6 | 7 | class AuthInitial extends AuthState { 8 | @override 9 | List get props => []; 10 | } 11 | class Authenticated extends AuthState{ 12 | final String uid; 13 | 14 | Authenticated({this.uid}); 15 | @override 16 | // TODO: implement props 17 | List get props => [uid]; 18 | } 19 | class UnAuthenticated extends AuthState{ 20 | 21 | 22 | @override 23 | // TODO: implement props 24 | List get props => []; 25 | } 26 | -------------------------------------------------------------------------------- /lib/presentation/bloc/communication/communication_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:bloc/bloc.dart'; 4 | import 'package:cloud_firestore/cloud_firestore.dart'; 5 | import 'package:equatable/equatable.dart'; 6 | import 'package:group_chat_room_responsive/domain/entities/text_message_entity.dart'; 7 | import 'package:group_chat_room_responsive/domain/usecase/get_messages_usecase.dart'; 8 | import 'package:group_chat_room_responsive/domain/usecase/send_text_message_usecase.dart'; 9 | 10 | part 'communication_state.dart'; 11 | 12 | class CommunicationCubit extends Cubit { 13 | final GetMessagesUseCase getMessagesUseCase; 14 | final SendTextMessageUseCase sendTextMessageUseCase; 15 | CommunicationCubit({this.getMessagesUseCase,this.sendTextMessageUseCase}) : super(CommunicationInitial()); 16 | 17 | Future sendTextMsg({String name,String uid,String message})async{ 18 | try{ 19 | await sendTextMessageUseCase.call(TextMessageEntity("", uid, name, "TEXT", Timestamp.now(), message, "")); 20 | }on SocketException catch(_){} 21 | } 22 | Future getTextMessages()async{ 23 | try{ 24 | final messages=getMessagesUseCase.call(); 25 | messages.listen((msg) { 26 | emit(CommunicationLoaded(messages: msg)); 27 | }); 28 | }on SocketException catch(_){} 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/presentation/bloc/communication/communication_state.dart: -------------------------------------------------------------------------------- 1 | part of 'communication_cubit.dart'; 2 | 3 | abstract class CommunicationState extends Equatable { 4 | const CommunicationState(); 5 | } 6 | 7 | class CommunicationInitial extends CommunicationState { 8 | @override 9 | List get props => []; 10 | } 11 | 12 | class CommunicationLoading extends CommunicationState{ 13 | @override 14 | // TODO: implement props 15 | List get props => []; 16 | 17 | } 18 | 19 | class CommunicationLoaded extends CommunicationState{ 20 | final List messages; 21 | 22 | CommunicationLoaded({this.messages}); 23 | @override 24 | // TODO: implement props 25 | List get props => [messages]; 26 | } -------------------------------------------------------------------------------- /lib/presentation/bloc/login/login_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'package:bloc/bloc.dart'; 2 | import 'package:equatable/equatable.dart'; 3 | import 'package:group_chat_room_responsive/domain/usecase/get_create_current_user.dart'; 4 | import 'package:group_chat_room_responsive/domain/usecase/sign_out_usecase.dart'; 5 | import 'package:group_chat_room_responsive/domain/usecase/signin_usecase.dart'; 6 | import 'package:group_chat_room_responsive/domain/usecase/signup_usecase.dart'; 7 | import 'dart:io'; 8 | 9 | part 'login_state.dart'; 10 | 11 | class LoginCubit extends Cubit { 12 | final SignUpUseCase signUpUseCase; 13 | final SignInUseCase signInUseCase; 14 | final GetCreateCurrentUser getCreateCurrentUser; 15 | final SignOutUseCase signOutUseCase; 16 | LoginCubit({this.signInUseCase,this.signUpUseCase,this.getCreateCurrentUser,this.signOutUseCase}) : super(LoginInitial()); 17 | 18 | Future submitLogin({String email,String password})async{ 19 | emit(LoginLoading()); 20 | try{ 21 | await signInUseCase.call(email, password); 22 | emit(LoginSuccess()); 23 | }on SocketException catch(e){ 24 | emit(LoginFailure(e.message)); 25 | }catch(_){ 26 | emit(LoginFailure("firebase exception")); 27 | } 28 | } 29 | Future submitRegistration({String email,String password,String name})async{ 30 | emit(LoginLoading()); 31 | try{ 32 | await signUpUseCase.call(email, password); 33 | await getCreateCurrentUser.call(email, name, ""); 34 | emit(LoginSuccess()); 35 | }on SocketException catch(e){ 36 | emit(LoginFailure(e.message)); 37 | }catch(_){ 38 | emit(LoginFailure("firebase exception")); 39 | } 40 | } 41 | Future submitSignOut()async{ 42 | try{ 43 | await signOutUseCase.call(); 44 | }on SocketException catch(_){} 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/presentation/bloc/login/login_state.dart: -------------------------------------------------------------------------------- 1 | part of 'login_cubit.dart'; 2 | 3 | abstract class LoginState extends Equatable { 4 | const LoginState(); 5 | } 6 | 7 | class LoginInitial extends LoginState { 8 | @override 9 | List get props => []; 10 | } 11 | 12 | class LoginLoading extends LoginState{ 13 | @override 14 | // TODO: implement props 15 | List get props => []; 16 | 17 | } 18 | class LoginFailure extends LoginState{ 19 | final String errorMessage; 20 | 21 | LoginFailure(this.errorMessage); 22 | @override 23 | // TODO: implement props 24 | List get props => [this.errorMessage]; 25 | 26 | } 27 | class LoginSuccess extends LoginState{ 28 | @override 29 | // TODO: implement props 30 | List get props => []; 31 | } -------------------------------------------------------------------------------- /lib/presentation/bloc/user/user_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:bloc/bloc.dart'; 4 | import 'package:equatable/equatable.dart'; 5 | import 'package:group_chat_room_responsive/domain/entities/user_entity.dart'; 6 | import 'package:group_chat_room_responsive/domain/usecase/get_useres_usecase.dart'; 7 | 8 | part 'user_state.dart'; 9 | 10 | class UserCubit extends Cubit { 11 | final GetUsersUseCase usersUseCase; 12 | UserCubit({this.usersUseCase}) : super(UserInitial()); 13 | 14 | Future getUsers()async{ 15 | try{ 16 | final user=usersUseCase.call(); 17 | user.listen((users) { 18 | emit(UserLoaded(users: users)); 19 | }); 20 | }on SocketException catch(_){} 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/presentation/bloc/user/user_state.dart: -------------------------------------------------------------------------------- 1 | part of 'user_cubit.dart'; 2 | 3 | abstract class UserState extends Equatable { 4 | const UserState(); 5 | } 6 | 7 | class UserInitial extends UserState { 8 | @override 9 | List get props => []; 10 | } 11 | class UserLoaded extends UserState{ 12 | final List users; 13 | 14 | UserLoaded({this.users}); 15 | @override 16 | // TODO: implement props 17 | List get props => [users]; 18 | } 19 | class UserLoading extends UserState{ 20 | @override 21 | // TODO: implement props 22 | List get props => throw UnimplementedError(); 23 | } 24 | -------------------------------------------------------------------------------- /lib/presentation/pages/mobile/mobile_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:group_chat_room_responsive/presentation/pages/mobile/widget/body_widget.dart'; 3 | import 'package:responsive_builder/responsive_builder.dart'; 4 | 5 | class MobilePage extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | return ResponsiveBuilder( 9 | builder: (context,sizingInformation){ 10 | return Scaffold( 11 | body: Container( 12 | child: BodyWidgetMobile(sizingInformation: sizingInformation,), 13 | ), 14 | ); 15 | }, 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/presentation/pages/mobile/single_chat_page_mobile.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:bubble/bubble.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:group_chat_room_responsive/presentation/bloc/communication/communication_cubit.dart'; 7 | import 'package:group_chat_room_responsive/presentation/pages/mobile/widget/mobile_message_layout.dart'; 8 | import 'package:group_chat_room_responsive/presentation/pages/widget/message_layout.dart'; 9 | import 'package:intl/intl.dart'; 10 | import 'package:responsive_builder/responsive_builder.dart'; 11 | 12 | class SingleChatPageMobile extends StatefulWidget { 13 | final String uid; 14 | final String userName; 15 | 16 | const SingleChatPageMobile({Key key, this.uid, this.userName}) : super(key: key); 17 | 18 | @override 19 | _SingleChatPageMobileState createState() => _SingleChatPageMobileState(); 20 | } 21 | 22 | class _SingleChatPageMobileState extends State { 23 | TextEditingController _messageController = TextEditingController(); 24 | ScrollController _scrollController=ScrollController(); 25 | @override 26 | void initState() { 27 | BlocProvider.of(context).getTextMessages(); 28 | super.initState(); 29 | } 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | return BlocBuilder( 34 | builder: (context,state){ 35 | if (state is CommunicationLoaded){ 36 | return _bodyWidget(state); 37 | } 38 | return _loadingWidget(); 39 | }, 40 | ); 41 | } 42 | Widget _bodyWidget(CommunicationLoaded messages) { 43 | return Scaffold( 44 | body: Stack( 45 | children: [ 46 | Positioned.fill( 47 | child: Image.asset( 48 | "assets/background_img.png", 49 | fit: BoxFit.cover, 50 | ), 51 | ), 52 | Column( 53 | children: [ 54 | _headerWidget(), 55 | _listMessagesWidget(messages), 56 | _sendTextMessageWidget(), 57 | ], 58 | ), 59 | ], 60 | ), 61 | ); 62 | } 63 | Widget _loadingWidget(){ 64 | return Scaffold( 65 | body: Stack( 66 | children: [ 67 | Positioned.fill( 68 | child: Image.asset( 69 | "assets/background_img.png", 70 | fit: BoxFit.cover, 71 | ), 72 | ), 73 | Column( 74 | children: [ 75 | _headerWidget(), 76 | Expanded(child: Center(child: CircularProgressIndicator(),)), 77 | _sendTextMessageWidget(), 78 | ], 79 | ), 80 | ], 81 | ), 82 | ); 83 | } 84 | Widget _headerWidget() { 85 | return Container( 86 | padding: EdgeInsets.only(left: 15,right: 15,top: 20), 87 | height: 70, 88 | width: double.infinity, 89 | decoration: BoxDecoration( 90 | gradient: LinearGradient( 91 | colors: [ 92 | Colors.indigo[400], 93 | Colors.blue[300], 94 | ], 95 | )), 96 | child: Row( 97 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 98 | children: [ 99 | Row( 100 | children: [ 101 | Container( 102 | width: 40, height: 40, child: Image.asset("assets/logo.png")), 103 | Text( 104 | "Global Chat Room", 105 | style: TextStyle( 106 | fontSize: 18, 107 | color: Colors.white, 108 | ), 109 | ), 110 | ], 111 | ), 112 | Text( 113 | "${widget.userName}", 114 | style: TextStyle( 115 | fontSize: 22, 116 | color: Colors.white, 117 | ), 118 | ) 119 | ], 120 | ), 121 | ); 122 | } 123 | 124 | Widget _listMessagesWidget(CommunicationLoaded messages) { 125 | Timer( 126 | Duration(milliseconds: 100), 127 | () => _scrollController 128 | .jumpTo(_scrollController.position.maxScrollExtent)); 129 | return Expanded( 130 | child: Container( 131 | margin: EdgeInsets.symmetric(horizontal: 20), 132 | child: ListView.builder( 133 | controller: _scrollController, 134 | shrinkWrap: true, 135 | itemCount: messages.messages.length, 136 | itemBuilder: (_, index) { 137 | return messages.messages[index].senderId == 138 | widget.uid 139 | ? MobileMessageLayout( 140 | type: messages.messages[index].type, 141 | senderId: messages.messages[index].senderId, 142 | senderName: 143 | messages.messages[index].senderName, 144 | text: messages.messages[index].message, 145 | time: DateFormat('hh:mm a').format( 146 | messages.messages[index].time.toDate()), 147 | color: Colors.green[300], 148 | align: TextAlign.left, 149 | nip: BubbleNip.rightTop, 150 | boxAlignment: CrossAxisAlignment.end, 151 | boxMainAxisAlignment: MainAxisAlignment.end, 152 | uid: widget.uid,) 153 | : MobileMessageLayout( 154 | type: messages.messages[index].type, 155 | senderName: 156 | messages.messages[index].senderName, 157 | text: messages.messages[index].message, 158 | time: DateFormat('hh:mm a').format( 159 | messages.messages[index].time.toDate()), 160 | color: Colors.blue, 161 | align: TextAlign.left, 162 | nip: BubbleNip.leftTop, 163 | boxAlignment: CrossAxisAlignment.start, 164 | boxMainAxisAlignment: MainAxisAlignment.start, 165 | ); 166 | }, 167 | ), 168 | ), 169 | ); 170 | } 171 | 172 | Widget _sendTextMessageWidget() { 173 | return Padding( 174 | padding: const EdgeInsets.only(bottom: 5), 175 | child: Container( 176 | padding: EdgeInsets.symmetric(horizontal: 20), 177 | height: 60, 178 | width: double.infinity, 179 | decoration: BoxDecoration( 180 | color: Colors.white, 181 | borderRadius: BorderRadius.all(Radius.circular(0.0)), 182 | border: Border.all(color: Colors.black.withOpacity(.4), width: 2)), 183 | child: Row( 184 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 185 | children: [ 186 | Row( 187 | children: [ 188 | _emojiWidget(), 189 | SizedBox( 190 | width: 8, 191 | ), 192 | _textFieldWidget(), 193 | ], 194 | ), 195 | Row( 196 | children: [ 197 | _micWidget(), 198 | SizedBox( 199 | width: 8, 200 | ), 201 | _sendMessageButton(), 202 | ], 203 | ) 204 | ], 205 | ), 206 | ), 207 | ); 208 | } 209 | 210 | _emojiWidget() { 211 | return Container( 212 | height: 40, 213 | width: 40, 214 | decoration: BoxDecoration( 215 | color: Colors.black.withOpacity(.2), 216 | borderRadius: BorderRadius.all(Radius.circular(40))), 217 | child: Icon( 218 | Icons.emoji_symbols, 219 | color: Colors.white, 220 | ), 221 | ); 222 | } 223 | 224 | _micWidget() { 225 | return Container( 226 | height: 40, 227 | width: 40, 228 | decoration: BoxDecoration( 229 | color: Colors.black.withOpacity(.2), 230 | borderRadius: BorderRadius.all(Radius.circular(40))), 231 | child: Icon( 232 | Icons.mic, 233 | color: Colors.white, 234 | ), 235 | ); 236 | } 237 | 238 | _textFieldWidget() { 239 | return ResponsiveBuilder( 240 | builder: (_, sizingInformation) { 241 | return Container( 242 | width: sizingInformation.screenSize.width * 0.40, 243 | child: ConstrainedBox( 244 | constraints: BoxConstraints( 245 | maxHeight: 60, 246 | ), 247 | child: Scrollbar( 248 | child: TextField( 249 | controller: _messageController, 250 | maxLines: null, 251 | decoration: InputDecoration( 252 | border: InputBorder.none, 253 | hintText: "Type Feel Free <3 ..."), 254 | ), 255 | ), 256 | ), 257 | ); 258 | }, 259 | ); 260 | } 261 | 262 | Widget _sendMessageButton() { 263 | return InkWell( 264 | onTap: () { 265 | if (_messageController.text.isNotEmpty) { 266 | _sendTextMessage(); 267 | _messageController.clear(); 268 | } 269 | }, 270 | child: Container( 271 | height: 40, 272 | width: 40, 273 | decoration: BoxDecoration( 274 | color: Colors.green, 275 | borderRadius: BorderRadius.all(Radius.circular(40))), 276 | child: Icon( 277 | Icons.send, 278 | color: Colors.white, 279 | ), 280 | ), 281 | ); 282 | } 283 | 284 | void _sendTextMessage() { 285 | BlocProvider.of(context).sendTextMsg( 286 | uid: widget.uid, 287 | name: widget.userName, 288 | message: _messageController.text); 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /lib/presentation/pages/mobile/welcome_page_mobile.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:group_chat_room_responsive/data/model/user_model.dart'; 5 | import 'package:group_chat_room_responsive/presentation/bloc/auth/auth_cubit.dart'; 6 | import 'package:group_chat_room_responsive/presentation/bloc/login/login_cubit.dart'; 7 | import 'package:group_chat_room_responsive/presentation/bloc/user/user_cubit.dart'; 8 | import 'package:group_chat_room_responsive/presentation/screens/single_chat_screen.dart'; 9 | import 'package:lottie/lottie.dart'; 10 | 11 | class WelcomePageMobile extends StatefulWidget { 12 | final String uid; 13 | 14 | const WelcomePageMobile({Key key, this.uid}) : super(key: key); 15 | 16 | @override 17 | _WelcomePageMobileState createState() => _WelcomePageMobileState(); 18 | } 19 | 20 | class _WelcomePageMobileState extends State { 21 | @override 22 | void initState() { 23 | BlocProvider.of(context).getUsers(); 24 | super.initState(); 25 | } 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return BlocBuilder(builder: (_, state) { 30 | if (state is UserLoaded) { 31 | return _bodyWidget(state); 32 | } 33 | return _loadingWidget(); 34 | }); 35 | } 36 | 37 | Widget _bodyWidget(UserLoaded users) { 38 | final user = users.users.firstWhere((user) => user.uid == widget.uid, 39 | orElse: () => UserModel()); 40 | return Scaffold( 41 | body: Stack( 42 | children: [ 43 | Container( 44 | decoration: BoxDecoration( 45 | gradient: LinearGradient( 46 | colors: [ 47 | Colors.indigo[400], 48 | Colors.blue[300], 49 | ], 50 | )), 51 | ), 52 | Align( 53 | alignment: Alignment.topCenter, 54 | child: Lottie.asset("assets/congratulation.json"), 55 | ), 56 | Align( 57 | alignment: Alignment.bottomRight, 58 | child: Lottie.asset("assets/bubble.json"), 59 | ), 60 | Align( 61 | alignment: Alignment.topCenter, 62 | child: Container( 63 | margin: EdgeInsets.only(top: 80), 64 | child: Text( 65 | "Welcome ${user.name}", 66 | style: TextStyle( 67 | fontSize: 20, 68 | color: Colors.white, 69 | fontWeight: FontWeight.bold, 70 | ), 71 | )), 72 | ), 73 | _joinGlobalChatButton(user.name), 74 | _logOutWidget(), 75 | ], 76 | ), 77 | ); 78 | } 79 | 80 | Widget _loadingWidget() { 81 | return Scaffold( 82 | body: Stack( 83 | children: [ 84 | Container( 85 | decoration: BoxDecoration( 86 | gradient: LinearGradient( 87 | colors: [ 88 | Colors.indigo[400], 89 | Colors.blue[300], 90 | ], 91 | )), 92 | ), 93 | Align( 94 | alignment: Alignment.center, 95 | child: CircularProgressIndicator(), 96 | ), 97 | ], 98 | ), 99 | ); 100 | } 101 | 102 | Widget _joinGlobalChatButton(String name) { 103 | return Align( 104 | alignment: Alignment.center, 105 | child: Column( 106 | mainAxisAlignment: MainAxisAlignment.center, 107 | children: [ 108 | Text( 109 | "Join Us For Fun", 110 | style: TextStyle( 111 | fontSize: 20, 112 | fontWeight: FontWeight.bold, 113 | ), 114 | ), 115 | SizedBox( 116 | height: 30, 117 | ), 118 | InkWell( 119 | onTap: (){ 120 | Navigator.push( 121 | context, 122 | MaterialPageRoute( 123 | builder: (_) => SingleChatScreen(username: name,uid: widget.uid,), 124 | ), 125 | ); 126 | }, 127 | child: Container( 128 | width: 180, 129 | height: 60, 130 | alignment: Alignment.center, 131 | decoration: BoxDecoration( 132 | color: Colors.white.withOpacity(.3), 133 | borderRadius: BorderRadius.all(Radius.circular(20)), 134 | border: Border.all(color: Colors.white60, width: 2)), 135 | child: Text( 136 | "Join", 137 | style: TextStyle(fontSize: 20), 138 | ), 139 | ), 140 | ), 141 | ], 142 | ), 143 | ); 144 | } 145 | 146 | Widget _logOutWidget() { 147 | return Align( 148 | alignment: Alignment.bottomLeft, 149 | child: InkWell( 150 | onTap: (){ 151 | //TODO:Logout 152 | BlocProvider.of(context).loggedOut(); 153 | BlocProvider.of(context).submitSignOut(); 154 | }, 155 | child: Container( 156 | margin: EdgeInsets.only(left: 15,bottom: 15), 157 | padding: EdgeInsets.all(10), 158 | decoration: BoxDecoration( 159 | color: Colors.white.withOpacity(.3), 160 | borderRadius: BorderRadius.circular(30), 161 | ), 162 | child: Icon( 163 | Icons.exit_to_app, 164 | size: 30, 165 | ), 166 | ), 167 | ), 168 | ); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /lib/presentation/pages/mobile/widget/body_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:group_chat_room_responsive/presentation/bloc/auth/auth_cubit.dart'; 5 | import 'package:group_chat_room_responsive/presentation/bloc/login/login_cubit.dart'; 6 | import 'package:lottie/lottie.dart'; 7 | import 'package:responsive_builder/responsive_builder.dart'; 8 | 9 | class BodyWidgetMobile extends StatefulWidget { 10 | final SizingInformation sizingInformation; 11 | 12 | const BodyWidgetMobile({Key key, this.sizingInformation}) : super(key: key); 13 | 14 | @override 15 | _BodyWidgetMobileState createState() => _BodyWidgetMobileState(); 16 | } 17 | 18 | class _BodyWidgetMobileState extends State { 19 | 20 | TextEditingController _nameController; 21 | TextEditingController _emailController; 22 | TextEditingController _passwordController; 23 | 24 | bool isLoginPage=true; 25 | 26 | @override 27 | void initState() { 28 | _nameController=TextEditingController(); 29 | _emailController=TextEditingController(); 30 | _passwordController=TextEditingController(); 31 | super.initState(); 32 | } 33 | @override 34 | void dispose() { 35 | _nameController.dispose(); 36 | _emailController.dispose(); 37 | _passwordController.dispose(); 38 | super.dispose(); 39 | } 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | return BlocConsumer( 44 | builder: (context, state) { 45 | if (state is LoginLoading) { 46 | return _loadingWidget(); 47 | } 48 | return _bodyWidget(); 49 | }, 50 | listener: (context, state) { 51 | if (state is LoginSuccess){ 52 | BlocProvider.of(context).loggedIn(); 53 | } 54 | }, 55 | ); 56 | } 57 | Widget _loadingWidget(){ 58 | return SingleChildScrollView( 59 | child: Container( 60 | decoration: BoxDecoration( 61 | color: Colors.white, 62 | ), 63 | padding: EdgeInsets.symmetric(horizontal: 15, vertical: 20), 64 | child: Column( 65 | mainAxisAlignment: MainAxisAlignment.center, 66 | children: [ 67 | Text( 68 | "WELCOME TO eTechViral", 69 | style: TextStyle( 70 | fontSize: 30, color: Colors.black,fontWeight: FontWeight.bold), 71 | ), 72 | Text( 73 | "Flutter is changing the app development world. if you want to improve your flutter skill then join our channel because we try to upload one video per week. eTechViral :- flutter and dart app development tutorials crafted to make you win jobs.", 74 | textAlign: TextAlign.center, 75 | style: TextStyle(color: Colors.black54, fontSize: 16), 76 | ), 77 | SizedBox( 78 | height: 15, 79 | ), 80 | _imageWidget(), 81 | SizedBox( 82 | height: 15, 83 | ), 84 | _fromWidget(), 85 | SizedBox( 86 | height: 15, 87 | ), 88 | _buttonWidget(), 89 | SizedBox( 90 | height: 40, 91 | ), 92 | Align( 93 | alignment: Alignment.bottomCenter, 94 | child: Lottie.asset('assets/loading.json'), 95 | ), 96 | ], 97 | ), 98 | ), 99 | ); 100 | } 101 | Widget _bodyWidget(){ 102 | return SingleChildScrollView( 103 | child: Container( 104 | decoration: BoxDecoration( 105 | color: Colors.white, 106 | ), 107 | padding: EdgeInsets.symmetric(horizontal: 15, vertical: 20), 108 | child: Column( 109 | mainAxisAlignment: MainAxisAlignment.center, 110 | children: [ 111 | Text( 112 | "WELCOME TO eTechViral", 113 | style: TextStyle( 114 | fontSize: 30, color: Colors.black,fontWeight: FontWeight.bold), 115 | ), 116 | Text( 117 | "Flutter is changing the app development world. if you want to improve your flutter skill then join our channel because we try to upload one video per week. eTechViral :- flutter and dart app development tutorials crafted to make you win jobs.", 118 | textAlign: TextAlign.center, 119 | style: TextStyle(color: Colors.black54, fontSize: 16), 120 | ), 121 | SizedBox( 122 | height: 15, 123 | ), 124 | _imageWidget(), 125 | SizedBox( 126 | height: 15, 127 | ), 128 | _fromWidget(), 129 | SizedBox( 130 | height: 15, 131 | ), 132 | _buttonWidget(), 133 | SizedBox( 134 | height: 40, 135 | ), 136 | Align( 137 | alignment: Alignment.bottomCenter, 138 | child: _rowTextWidget(), 139 | ), 140 | ], 141 | ), 142 | ), 143 | ); 144 | } 145 | Widget _imageWidget() { 146 | return Container( 147 | height: 60, 148 | width: 60, 149 | child: Image.asset("assets/profile.png"), 150 | ); 151 | } 152 | 153 | Widget _fromWidget() { 154 | return Column( 155 | children: [ 156 | isLoginPage==true?Text("",style: TextStyle(fontSize: 0),):Container( 157 | height: 60, 158 | alignment: Alignment.center, 159 | decoration: BoxDecoration( 160 | borderRadius: BorderRadius.all(Radius.circular(40)), 161 | border: Border.all(color: Colors.grey, width: 1.0), 162 | ), 163 | child: TextField( 164 | controller: _nameController, 165 | decoration: InputDecoration( 166 | border: InputBorder.none, 167 | hintText: "User Name", 168 | prefixIcon: Icon(Icons.person_outline), 169 | ), 170 | ), 171 | ), 172 | isLoginPage==true?Text("",style: TextStyle(fontSize: 0),):SizedBox( 173 | height: 20, 174 | ), 175 | Container( 176 | height: 60, 177 | alignment: Alignment.center, 178 | decoration: BoxDecoration( 179 | borderRadius: BorderRadius.all(Radius.circular(40)), 180 | border: Border.all(color: Colors.grey, width: 1.0), 181 | ), 182 | child: TextField( 183 | controller: _emailController, 184 | decoration: InputDecoration( 185 | border: InputBorder.none, 186 | hintText: "Email Address", 187 | prefixIcon: Icon(Icons.alternate_email), 188 | ), 189 | ), 190 | ), 191 | SizedBox( 192 | height: 20, 193 | ), 194 | Container( 195 | height: 60, 196 | alignment: Alignment.center, 197 | decoration: BoxDecoration( 198 | borderRadius: BorderRadius.all(Radius.circular(40)), 199 | border: Border.all(color: Colors.grey, width: 1.0), 200 | ), 201 | child: TextField( 202 | controller: _passwordController, 203 | obscureText: true, 204 | decoration: InputDecoration( 205 | border: InputBorder.none, 206 | hintText: "Password", 207 | prefixIcon: Icon(Icons.lock_outline), 208 | ), 209 | ), 210 | ), 211 | ], 212 | ); 213 | } 214 | 215 | Widget _buttonWidget() { 216 | return InkWell( 217 | onTap: (){ 218 | if (isLoginPage == true) { 219 | _submitLogin(); 220 | } else { 221 | _submitRegistration(); 222 | } 223 | }, 224 | child: Container( 225 | margin: EdgeInsets.symmetric(horizontal: 10), 226 | width: widget.sizingInformation.screenSize.width, 227 | alignment: Alignment.center, 228 | padding: EdgeInsets.symmetric(vertical: 14), 229 | decoration: BoxDecoration( 230 | color: Colors.indigo, 231 | borderRadius: BorderRadius.all(Radius.circular(50)), 232 | ), 233 | child: Text( 234 | isLoginPage==true?"LOGIN":"REGISTER", 235 | style: TextStyle(fontSize: 18, color: Colors.white), 236 | ), 237 | ), 238 | ); 239 | } 240 | 241 | Widget _rowTextWidget() { 242 | return Row( 243 | mainAxisAlignment: MainAxisAlignment.center, 244 | children: [ 245 | Text( 246 | isLoginPage==true? "Don't have account?" : "Have an account?", 247 | style: TextStyle(fontSize: 16, color: Colors.indigo[400]), 248 | ), 249 | InkWell( 250 | onTap: (){ 251 | setState(() { 252 | if (isLoginPage==true) 253 | isLoginPage=false; 254 | else 255 | isLoginPage=true; 256 | }); 257 | }, 258 | child: Text( 259 | isLoginPage==true ?" Sign Up":" Sign In", 260 | style: TextStyle(fontSize: 16, color: Colors.indigo,fontWeight: FontWeight.bold), 261 | ), 262 | ), 263 | ], 264 | ); 265 | } 266 | void _submitLogin() { 267 | if (_emailController.text.isNotEmpty && 268 | _passwordController.text.isNotEmpty) { 269 | BlocProvider.of(context).submitLogin( 270 | email: _emailController.text, 271 | password: _passwordController.text, 272 | ); 273 | } 274 | } 275 | 276 | void _submitRegistration() { 277 | if (_emailController.text.isNotEmpty && 278 | _passwordController.text.isNotEmpty && 279 | _nameController.text.isNotEmpty) { 280 | BlocProvider.of(context).submitRegistration( 281 | email: _emailController.text, 282 | password: _passwordController.text, 283 | name: _nameController.text, 284 | ); 285 | } 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /lib/presentation/pages/mobile/widget/mobile_message_layout.dart: -------------------------------------------------------------------------------- 1 | import 'package:bubble/bubble.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | 5 | class MobileMessageLayout extends StatelessWidget { 6 | final String uid; 7 | final String type; 8 | final text; 9 | final time; 10 | final color; 11 | final align; 12 | final boxAlignment; 13 | final nip; 14 | final senderName; 15 | final senderId; 16 | final boxMainAxisAlignment; 17 | 18 | const MobileMessageLayout({ 19 | Key key, 20 | this.uid, 21 | this.type, 22 | this.text, 23 | this.time, 24 | this.color, 25 | this.align, 26 | this.boxAlignment, 27 | this.nip, 28 | this.senderName, 29 | this.senderId, 30 | this.boxMainAxisAlignment, 31 | }) : super(key: key); 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | return Column( 36 | crossAxisAlignment: boxAlignment, 37 | children: [ 38 | Row( 39 | crossAxisAlignment: CrossAxisAlignment.start, 40 | mainAxisAlignment: boxMainAxisAlignment, 41 | children: [ 42 | color == Colors.blue 43 | ?Container( 44 | height: 35, 45 | width: 35, 46 | decoration: BoxDecoration( 47 | border: Border.all(color: Colors.black,width: 2), 48 | borderRadius: BorderRadius.all(Radius.circular(55)) 49 | ), 50 | child: Image.asset("assets/profile_default.png"), 51 | ):Text("",style: TextStyle(fontSize: 0),), 52 | Container( 53 | padding: EdgeInsets.all(8), 54 | margin: EdgeInsets.all(3), 55 | child: Bubble( 56 | color: color, 57 | nip: nip, 58 | child: Column( 59 | crossAxisAlignment: CrossAxisAlignment.start, 60 | children: [ 61 | color == Colors.green[300] 62 | ? Text( 63 | "Me", 64 | textAlign: align, 65 | style: TextStyle( 66 | fontSize: 18, fontWeight: FontWeight.bold), 67 | ) 68 | : Text( 69 | senderName, 70 | textAlign: align, 71 | style: TextStyle( 72 | fontSize: 18, fontWeight: FontWeight.bold), 73 | ), 74 | ConstrainedBox( 75 | constraints: BoxConstraints( 76 | maxWidth: 250 77 | ), 78 | child: Text( 79 | text==""?"":text, 80 | textAlign: align, 81 | style: TextStyle(fontSize: 18), 82 | ), 83 | ), 84 | Text( 85 | time, 86 | textAlign: align, 87 | style: TextStyle(fontSize: 14), 88 | ) 89 | ], 90 | ), 91 | ), 92 | ), 93 | ], 94 | ) 95 | ], 96 | ); 97 | } 98 | } 99 | 100 | -------------------------------------------------------------------------------- /lib/presentation/pages/tablet/single_chat_page_tablet.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:bubble/bubble.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:group_chat_room_responsive/presentation/bloc/communication/communication_cubit.dart'; 7 | import 'package:group_chat_room_responsive/presentation/pages/widget/message_layout.dart'; 8 | import 'package:intl/intl.dart'; 9 | import 'package:responsive_builder/responsive_builder.dart'; 10 | 11 | class SingleChatPageTablet extends StatefulWidget { 12 | final String uid; 13 | final String userName; 14 | 15 | const SingleChatPageTablet({Key key, this.uid, this.userName}) : super(key: key); 16 | 17 | @override 18 | _SingleChatPageTabletState createState() => _SingleChatPageTabletState(); 19 | } 20 | 21 | class _SingleChatPageTabletState extends State { 22 | TextEditingController _messageController = TextEditingController(); 23 | ScrollController _scrollController=ScrollController(); 24 | 25 | @override 26 | void initState() { 27 | BlocProvider.of(context).getTextMessages(); 28 | super.initState(); 29 | } 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | return BlocBuilder( 34 | builder: (context,state){ 35 | if (state is CommunicationLoaded){ 36 | return _bodyWidget(state); 37 | } 38 | return _loadingWidget(); 39 | }, 40 | ); 41 | } 42 | Widget _bodyWidget(CommunicationLoaded messages) { 43 | return Scaffold( 44 | body: Stack( 45 | children: [ 46 | Positioned.fill( 47 | child: Image.asset( 48 | "assets/background_img.png", 49 | fit: BoxFit.cover, 50 | ), 51 | ), 52 | Column( 53 | children: [ 54 | _headerWidget(), 55 | _listMessagesWidget(messages), 56 | _sendTextMessageWidget(), 57 | ], 58 | ), 59 | ], 60 | ), 61 | ); 62 | } 63 | Widget _loadingWidget(){ 64 | return Scaffold( 65 | body: Stack( 66 | children: [ 67 | Positioned.fill( 68 | child: Image.asset( 69 | "assets/background_img.png", 70 | fit: BoxFit.cover, 71 | ), 72 | ), 73 | Column( 74 | children: [ 75 | _headerWidget(), 76 | Expanded(child: Center(child: CircularProgressIndicator(),)), 77 | _sendTextMessageWidget(), 78 | ], 79 | ), 80 | ], 81 | ), 82 | ); 83 | } 84 | Widget _headerWidget() { 85 | return Container( 86 | padding: EdgeInsets.symmetric(horizontal: 80), 87 | height: 60, 88 | width: double.infinity, 89 | decoration: BoxDecoration( 90 | gradient: LinearGradient( 91 | colors: [ 92 | Colors.indigo[400], 93 | Colors.blue[300], 94 | ], 95 | )), 96 | child: Row( 97 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 98 | children: [ 99 | Row( 100 | children: [ 101 | Container( 102 | width: 40, height: 40, child: Image.asset("assets/logo.png")), 103 | Text( 104 | "Global Chat Room", 105 | style: TextStyle( 106 | fontSize: 22, 107 | color: Colors.white, 108 | ), 109 | ), 110 | ], 111 | ), 112 | Text( 113 | "${widget.userName}", 114 | style: TextStyle( 115 | fontSize: 22, 116 | color: Colors.white, 117 | ), 118 | ) 119 | ], 120 | ), 121 | ); 122 | } 123 | 124 | Widget _listMessagesWidget(CommunicationLoaded messages) { 125 | Timer( 126 | Duration(milliseconds: 100), 127 | () => _scrollController 128 | .jumpTo(_scrollController.position.maxScrollExtent)); 129 | return Expanded( 130 | child: Container( 131 | margin: EdgeInsets.symmetric(horizontal: 20), 132 | child: ListView.builder( 133 | controller: _scrollController, 134 | shrinkWrap: true, 135 | itemCount: messages.messages.length, 136 | itemBuilder: (_, index) { 137 | return messages.messages[index].senderId == 138 | widget.uid 139 | ? MessageLayout( 140 | type: messages.messages[index].type, 141 | senderId: messages.messages[index].senderId, 142 | senderName: 143 | messages.messages[index].senderName, 144 | text: messages.messages[index].message, 145 | time: DateFormat('hh:mm a').format( 146 | messages.messages[index].time.toDate()), 147 | color: Colors.green[300], 148 | align: TextAlign.left, 149 | nip: BubbleNip.rightTop, 150 | boxAlignment: CrossAxisAlignment.end, 151 | boxMainAxisAlignment: MainAxisAlignment.end, 152 | uid: widget.uid,) 153 | : MessageLayout( 154 | type: messages.messages[index].type, 155 | senderName: 156 | messages.messages[index].senderName, 157 | text: messages.messages[index].message, 158 | time: DateFormat('hh:mm a').format( 159 | messages.messages[index].time.toDate()), 160 | color: Colors.blue, 161 | align: TextAlign.left, 162 | nip: BubbleNip.leftTop, 163 | boxAlignment: CrossAxisAlignment.start, 164 | boxMainAxisAlignment: MainAxisAlignment.start, 165 | ); 166 | }, 167 | ), 168 | ), 169 | ); 170 | } 171 | 172 | Widget _sendTextMessageWidget() { 173 | return Padding( 174 | padding: const EdgeInsets.only(bottom: 5), 175 | child: Container( 176 | padding: EdgeInsets.symmetric(horizontal: 80), 177 | height: 60, 178 | width: double.infinity, 179 | decoration: BoxDecoration( 180 | color: Colors.white, 181 | borderRadius: BorderRadius.all(Radius.circular(0.0)), 182 | border: Border.all(color: Colors.black.withOpacity(.4), width: 2)), 183 | child: Row( 184 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 185 | children: [ 186 | Row( 187 | children: [ 188 | _emojiWidget(), 189 | SizedBox( 190 | width: 8, 191 | ), 192 | _textFieldWidget(), 193 | ], 194 | ), 195 | Row( 196 | children: [ 197 | _micWidget(), 198 | SizedBox( 199 | width: 8, 200 | ), 201 | _sendMessageButton(), 202 | ], 203 | ) 204 | ], 205 | ), 206 | ), 207 | ); 208 | } 209 | 210 | _emojiWidget() { 211 | return Container( 212 | height: 50, 213 | width: 50, 214 | decoration: BoxDecoration( 215 | color: Colors.black.withOpacity(.2), 216 | borderRadius: BorderRadius.all(Radius.circular(40))), 217 | child: Icon( 218 | Icons.emoji_symbols, 219 | color: Colors.white, 220 | ), 221 | ); 222 | } 223 | 224 | _micWidget() { 225 | return Container( 226 | height: 50, 227 | width: 50, 228 | decoration: BoxDecoration( 229 | color: Colors.black.withOpacity(.2), 230 | borderRadius: BorderRadius.all(Radius.circular(40))), 231 | child: Icon( 232 | Icons.mic, 233 | color: Colors.white, 234 | ), 235 | ); 236 | } 237 | 238 | _textFieldWidget() { 239 | return ResponsiveBuilder( 240 | builder: (_, sizingInformation) { 241 | return Container( 242 | width: sizingInformation.screenSize.width * 0.45, 243 | child: ConstrainedBox( 244 | constraints: BoxConstraints( 245 | maxHeight: 60, 246 | ), 247 | child: Scrollbar( 248 | child: TextField( 249 | controller: _messageController, 250 | maxLines: null, 251 | decoration: InputDecoration( 252 | border: InputBorder.none, 253 | hintText: "Type Feel Free <3 ..."), 254 | ), 255 | ), 256 | ), 257 | ); 258 | }, 259 | ); 260 | } 261 | 262 | Widget _sendMessageButton() { 263 | return InkWell( 264 | onTap: () { 265 | if (_messageController.text.isNotEmpty) { 266 | _sendTextMessage(); 267 | _messageController.clear(); 268 | } 269 | }, 270 | child: Container( 271 | height: 50, 272 | width: 50, 273 | decoration: BoxDecoration( 274 | color: Colors.green, 275 | borderRadius: BorderRadius.all(Radius.circular(40))), 276 | child: Icon( 277 | Icons.send, 278 | color: Colors.white, 279 | ), 280 | ), 281 | ); 282 | } 283 | 284 | void _sendTextMessage() { 285 | BlocProvider.of(context).sendTextMsg( 286 | uid: widget.uid, 287 | name: widget.userName, 288 | message: _messageController.text); 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /lib/presentation/pages/tablet/tablet_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:group_chat_room_responsive/presentation/pages/tablet/widgets/left_side_widget.dart'; 3 | import 'package:group_chat_room_responsive/presentation/pages/tablet/widgets/right_side_widget.dart'; 4 | import 'package:responsive_builder/responsive_builder.dart'; 5 | 6 | class TabletPage extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | return ResponsiveBuilder( 10 | builder: (context,sizingInformation){ 11 | return Scaffold( 12 | body: Row( 13 | children: [ 14 | LeftSideWidget(sizingInformation: sizingInformation,), 15 | Expanded(child: RightSideWidget( 16 | sizingInformation: sizingInformation, 17 | )), 18 | ], 19 | ), 20 | ); 21 | }, 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/presentation/pages/tablet/welcome_page_tablet.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:group_chat_room_responsive/data/model/user_model.dart'; 5 | import 'package:group_chat_room_responsive/presentation/bloc/auth/auth_cubit.dart'; 6 | import 'package:group_chat_room_responsive/presentation/bloc/login/login_cubit.dart'; 7 | import 'package:group_chat_room_responsive/presentation/bloc/user/user_cubit.dart'; 8 | import 'package:group_chat_room_responsive/presentation/pages/tablet/single_chat_page_tablet.dart'; 9 | import 'package:group_chat_room_responsive/presentation/screens/single_chat_screen.dart'; 10 | import 'package:lottie/lottie.dart'; 11 | 12 | class WelcomePageTablet extends StatefulWidget { 13 | final String uid; 14 | 15 | const WelcomePageTablet({Key key, this.uid}) : super(key: key); 16 | @override 17 | _WelcomePageTabletState createState() => _WelcomePageTabletState(); 18 | } 19 | 20 | class _WelcomePageTabletState extends State { 21 | 22 | @override 23 | void initState() { 24 | BlocProvider.of(context).getUsers(); 25 | super.initState(); 26 | } 27 | @override 28 | Widget build(BuildContext context) { 29 | return BlocBuilder(builder: (_,state){ 30 | if (state is UserLoaded){ 31 | return _bodyWidget(state); 32 | } 33 | return _loadingWidget(); 34 | }); 35 | } 36 | 37 | Widget _bodyWidget(UserLoaded users){ 38 | final user = users.users.firstWhere((user) => user.uid == widget.uid,orElse: () => UserModel()); 39 | return Scaffold( 40 | body: Stack( 41 | children: [ 42 | Container( 43 | decoration: BoxDecoration( 44 | gradient: LinearGradient( 45 | colors: [ 46 | Colors.indigo[400], 47 | Colors.blue[300], 48 | ], 49 | )), 50 | ), 51 | Align( 52 | alignment: Alignment.topCenter, 53 | child: Lottie.asset("assets/congratulation.json"), 54 | ), 55 | Align( 56 | alignment: Alignment.bottomRight, 57 | child: Lottie.asset("assets/bubble.json"), 58 | ), 59 | Align( 60 | alignment: Alignment.topCenter, 61 | child: Container( 62 | margin: EdgeInsets.only(top: 100), 63 | child: Text( 64 | "Welcome ${user.name}", 65 | style: TextStyle( 66 | fontSize: 25, 67 | color: Colors.white, 68 | fontWeight: FontWeight.bold, 69 | ), 70 | )), 71 | ), 72 | _joinGlobalChatButton(user.name), 73 | _logOutWidget(), 74 | ], 75 | ), 76 | ); 77 | } 78 | 79 | Widget _loadingWidget(){ 80 | return Scaffold( 81 | body: Stack( 82 | children: [ 83 | Container( 84 | decoration: BoxDecoration( 85 | gradient: LinearGradient( 86 | colors: [ 87 | Colors.indigo[400], 88 | Colors.blue[300], 89 | ], 90 | )), 91 | ), 92 | Align( 93 | alignment: Alignment.center, 94 | child: CircularProgressIndicator(), 95 | ), 96 | ], 97 | ), 98 | ); 99 | } 100 | 101 | Widget _joinGlobalChatButton(String name) { 102 | return Align( 103 | alignment: Alignment.center, 104 | child: Column( 105 | mainAxisAlignment: MainAxisAlignment.center, 106 | children: [ 107 | Text( 108 | "Join Us For Fun", 109 | style: TextStyle( 110 | fontSize: 25, 111 | fontWeight: FontWeight.bold, 112 | ), 113 | ), 114 | SizedBox( 115 | height: 30, 116 | ), 117 | InkWell( 118 | onTap: (){ 119 | Navigator.push( 120 | context, 121 | MaterialPageRoute( 122 | builder: (_) => SingleChatScreen(username: name,uid: widget.uid,), 123 | ), 124 | ); 125 | }, 126 | child: Container( 127 | width: 250, 128 | height: 70, 129 | alignment: Alignment.center, 130 | decoration: BoxDecoration( 131 | color: Colors.white.withOpacity(.3), 132 | borderRadius: BorderRadius.all(Radius.circular(20)), 133 | border: Border.all(color: Colors.white60, width: 2)), 134 | child: Text( 135 | "Join", 136 | style: TextStyle(fontSize: 25), 137 | ), 138 | ), 139 | ), 140 | ], 141 | ), 142 | ); 143 | } 144 | Widget _logOutWidget() { 145 | return Align( 146 | alignment: Alignment.bottomLeft, 147 | child: InkWell( 148 | onTap: (){ 149 | //TODO:Logout 150 | BlocProvider.of(context).loggedOut(); 151 | BlocProvider.of(context).submitSignOut(); 152 | }, 153 | child: Container( 154 | margin: EdgeInsets.only(left: 15,bottom: 15), 155 | padding: EdgeInsets.all(10), 156 | decoration: BoxDecoration( 157 | color: Colors.white.withOpacity(.3), 158 | borderRadius: BorderRadius.circular(30), 159 | ), 160 | child: Icon( 161 | Icons.exit_to_app, 162 | size: 30, 163 | ), 164 | ), 165 | ), 166 | ); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /lib/presentation/pages/tablet/widgets/left_side_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/painting.dart'; 3 | import 'package:flutter/widgets.dart'; 4 | import 'package:lottie/lottie.dart'; 5 | import 'package:responsive_builder/responsive_builder.dart'; 6 | 7 | class LeftSideWidget extends StatelessWidget { 8 | final SizingInformation sizingInformation; 9 | 10 | const LeftSideWidget({Key key, this.sizingInformation}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Container( 15 | width: sizingInformation.screenSize.width * 0.55, 16 | height: double.infinity, 17 | decoration: BoxDecoration( 18 | gradient: LinearGradient( 19 | colors: [ 20 | Colors.indigo[400], 21 | Colors.blue[300], 22 | ], 23 | )), 24 | child: Container( 25 | child: Stack( 26 | children: [ 27 | _loginButton(), 28 | _bgImageWidget(), 29 | _welcomeTextWidget(), 30 | Positioned( 31 | left: -150, 32 | bottom: -150, 33 | child: Image.asset( 34 | "assets/shape.png", 35 | color: Colors.white.withOpacity(.2), 36 | )) 37 | ], 38 | ), 39 | ), 40 | ); 41 | } 42 | 43 | Widget _loginButton() { 44 | return Align( 45 | alignment: Alignment.topRight, 46 | child: Container( 47 | margin: EdgeInsets.symmetric(horizontal: 20, vertical: 20), 48 | padding: EdgeInsets.symmetric(horizontal: 20, vertical: 5), 49 | decoration: BoxDecoration( 50 | borderRadius: BorderRadius.all(Radius.circular(15)), 51 | border: Border.all(color: Colors.white60, width: 1.50), 52 | ), 53 | child: Text( 54 | "LOGIN", 55 | style: TextStyle(fontSize: 16, color: Colors.white60), 56 | )), 57 | ); 58 | } 59 | 60 | _bgImageWidget() { 61 | return Align( 62 | alignment: Alignment.center, 63 | child: Lottie.asset("assets/img.json"), 64 | ); 65 | } 66 | 67 | Widget _welcomeTextWidget() { 68 | return Positioned( 69 | left: 35, 70 | bottom: 30, 71 | child: Column( 72 | crossAxisAlignment: CrossAxisAlignment.start, 73 | children: [ 74 | Container( 75 | width: sizingInformation.screenSize.width * 0.45, 76 | child: Text( 77 | "WELCOME TO eTechViral", 78 | style: TextStyle( 79 | fontSize: 30, color: Colors.white, fontWeight: FontWeight.bold), 80 | ), 81 | ), 82 | SizedBox( 83 | height: 20, 84 | ), 85 | Container( 86 | width: sizingInformation.screenSize.width * 0.45, 87 | child: Text( 88 | "Flutter is changing the app development world. if you want to improve your flutter skill then join our channel because we try to upload one video per week. eTechViral :- flutter and dart app development tutorials crafted to make you win jobs.", 89 | textAlign: TextAlign.start, 90 | style: TextStyle(color: Colors.white60, fontSize: 16), 91 | ), 92 | ), 93 | SizedBox( 94 | height: 20, 95 | ), 96 | Container( 97 | padding: EdgeInsets.symmetric(horizontal: 20, vertical: 5), 98 | decoration: BoxDecoration( 99 | borderRadius: BorderRadius.all(Radius.circular(15)), 100 | border: Border.all(color: Colors.white60, width: 1.50), 101 | ), 102 | child: Text( 103 | "SIGN IN", 104 | style: TextStyle(fontSize: 16, color: Colors.white60), 105 | )), 106 | ], 107 | ), 108 | ); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /lib/presentation/pages/tablet/widgets/right_side_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:group_chat_room_responsive/presentation/bloc/auth/auth_cubit.dart'; 5 | import 'package:group_chat_room_responsive/presentation/bloc/login/login_cubit.dart'; 6 | import 'package:lottie/lottie.dart'; 7 | import 'package:responsive_builder/responsive_builder.dart'; 8 | 9 | class RightSideWidget extends StatefulWidget { 10 | final SizingInformation sizingInformation; 11 | 12 | const RightSideWidget({Key key, this.sizingInformation}) : super(key: key); 13 | 14 | @override 15 | _RightSideWidgetState createState() => _RightSideWidgetState(); 16 | } 17 | 18 | class _RightSideWidgetState extends State { 19 | 20 | TextEditingController _nameController; 21 | TextEditingController _emailController; 22 | TextEditingController _passwordController; 23 | 24 | bool isLoginPage=true; 25 | 26 | @override 27 | void initState() { 28 | _nameController=TextEditingController(); 29 | _emailController=TextEditingController(); 30 | _passwordController=TextEditingController(); 31 | super.initState(); 32 | } 33 | @override 34 | void dispose() { 35 | _nameController.dispose(); 36 | _emailController.dispose(); 37 | _passwordController.dispose(); 38 | super.dispose(); 39 | } 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | return BlocConsumer( 44 | builder: (context, state) { 45 | if (state is LoginLoading) { 46 | return _loadingWidget(); 47 | } 48 | return _bodyWidget(); 49 | }, 50 | listener: (context, state) { 51 | if (state is LoginSuccess){ 52 | BlocProvider.of(context).loggedIn(); 53 | } 54 | }, 55 | ); 56 | } 57 | Widget _bodyWidget(){ 58 | return Container( 59 | decoration: BoxDecoration( 60 | color: Colors.white, 61 | ), 62 | height: double.infinity, 63 | padding: EdgeInsets.symmetric(horizontal: 15, vertical: 20), 64 | child: Column( 65 | mainAxisAlignment: MainAxisAlignment.center, 66 | children: [ 67 | _imageWidget(), 68 | SizedBox( 69 | height: 15, 70 | ), 71 | _fromWidget(), 72 | SizedBox( 73 | height: 15, 74 | ), 75 | _buttonWidget(), 76 | SizedBox( 77 | height: 40, 78 | ), 79 | Align( 80 | alignment: Alignment.bottomCenter, 81 | child: _rowTextWidget(), 82 | ), 83 | ], 84 | ), 85 | ); 86 | } 87 | Widget _loadingWidget(){ 88 | return Container( 89 | decoration: BoxDecoration( 90 | color: Colors.white, 91 | ), 92 | height: double.infinity, 93 | padding: EdgeInsets.symmetric(horizontal: 15, vertical: 20), 94 | child: Column( 95 | mainAxisAlignment: MainAxisAlignment.center, 96 | children: [ 97 | _imageWidget(), 98 | SizedBox( 99 | height: 15, 100 | ), 101 | _fromWidget(), 102 | SizedBox( 103 | height: 15, 104 | ), 105 | _buttonWidget(), 106 | SizedBox( 107 | height: 40, 108 | ), 109 | Align( 110 | alignment: Alignment.bottomCenter, 111 | child: Lottie.asset("assets/loading.json"), 112 | ), 113 | ], 114 | ), 115 | ); 116 | } 117 | Widget _imageWidget() { 118 | return Container( 119 | height: 60, 120 | width: 60, 121 | child: Image.asset("assets/profile.png"), 122 | ); 123 | } 124 | 125 | Widget _fromWidget() { 126 | return Column( 127 | children: [ 128 | isLoginPage==true?Text("",style: TextStyle(fontSize: 0),):Container( 129 | height: 60, 130 | alignment: Alignment.center, 131 | decoration: BoxDecoration( 132 | borderRadius: BorderRadius.all(Radius.circular(40)), 133 | border: Border.all(color: Colors.grey, width: 1.0), 134 | ), 135 | child: TextField( 136 | controller: _nameController, 137 | decoration: InputDecoration( 138 | border: InputBorder.none, 139 | hintText: "User Name", 140 | prefixIcon: Icon(Icons.person_outline), 141 | ), 142 | ), 143 | ), 144 | isLoginPage==true?Text("",style: TextStyle(fontSize: 0),):SizedBox( 145 | height: 20, 146 | ), 147 | Container( 148 | height: 60, 149 | alignment: Alignment.center, 150 | decoration: BoxDecoration( 151 | borderRadius: BorderRadius.all(Radius.circular(40)), 152 | border: Border.all(color: Colors.grey, width: 1.0), 153 | ), 154 | child: TextField( 155 | controller: _emailController, 156 | decoration: InputDecoration( 157 | border: InputBorder.none, 158 | hintText: "Email Address", 159 | prefixIcon: Icon(Icons.alternate_email), 160 | ), 161 | ), 162 | ), 163 | SizedBox( 164 | height: 20, 165 | ), 166 | Container( 167 | height: 60, 168 | alignment: Alignment.center, 169 | decoration: BoxDecoration( 170 | borderRadius: BorderRadius.all(Radius.circular(40)), 171 | border: Border.all(color: Colors.grey, width: 1.0), 172 | ), 173 | child: TextField( 174 | controller: _passwordController, 175 | obscureText: true, 176 | decoration: InputDecoration( 177 | border: InputBorder.none, 178 | hintText: "Password", 179 | prefixIcon: Icon(Icons.lock_outline), 180 | ), 181 | ), 182 | ), 183 | ], 184 | ); 185 | } 186 | 187 | Widget _buttonWidget() { 188 | return InkWell( 189 | onTap: (){ 190 | if (isLoginPage == true) { 191 | _submitLogin(); 192 | } else { 193 | _submitRegistration(); 194 | } 195 | }, 196 | child: Container( 197 | margin: EdgeInsets.symmetric(horizontal: 10), 198 | width: widget.sizingInformation.screenSize.width, 199 | alignment: Alignment.center, 200 | padding: EdgeInsets.symmetric(vertical: 14), 201 | decoration: BoxDecoration( 202 | color: Colors.indigo, 203 | borderRadius: BorderRadius.all(Radius.circular(50)), 204 | ), 205 | child: Text( 206 | isLoginPage==true?"LOGIN":"REGISTER", 207 | style: TextStyle(fontSize: 18, color: Colors.white), 208 | ), 209 | ), 210 | ); 211 | } 212 | 213 | Widget _rowTextWidget() { 214 | return Row( 215 | mainAxisAlignment: MainAxisAlignment.center, 216 | children: [ 217 | Text( 218 | isLoginPage==true? "Don't have account?" : "Have an account?", 219 | style: TextStyle(fontSize: 16, color: Colors.indigo[400]), 220 | ), 221 | InkWell( 222 | onTap: (){ 223 | setState(() { 224 | if (isLoginPage==true) 225 | isLoginPage=false; 226 | else 227 | isLoginPage=true; 228 | }); 229 | }, 230 | child: Text( 231 | isLoginPage==true ?" Sign Up":" Sign In", 232 | style: TextStyle(fontSize: 16, color: Colors.indigo,fontWeight: FontWeight.bold), 233 | ), 234 | ), 235 | ], 236 | ); 237 | } 238 | void _submitLogin() { 239 | if (_emailController.text.isNotEmpty && 240 | _passwordController.text.isNotEmpty) { 241 | BlocProvider.of(context).submitLogin( 242 | email: _emailController.text, 243 | password: _passwordController.text, 244 | ); 245 | } 246 | } 247 | 248 | void _submitRegistration() { 249 | if (_emailController.text.isNotEmpty && 250 | _passwordController.text.isNotEmpty && 251 | _nameController.text.isNotEmpty) { 252 | BlocProvider.of(context).submitRegistration( 253 | email: _emailController.text, 254 | password: _passwordController.text, 255 | name: _nameController.text, 256 | ); 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /lib/presentation/pages/web/single_chat_page_web.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:bubble/bubble.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:group_chat_room_responsive/presentation/bloc/communication/communication_cubit.dart'; 7 | import 'package:group_chat_room_responsive/presentation/pages/widget/message_layout.dart'; 8 | import 'package:intl/intl.dart'; 9 | import 'package:responsive_builder/responsive_builder.dart'; 10 | 11 | class SingleChatPageWeb extends StatefulWidget { 12 | final String uid; 13 | final String userName; 14 | 15 | const SingleChatPageWeb({Key key, this.uid, this.userName}) : super(key: key); 16 | 17 | @override 18 | _SingleChatPageWebState createState() => _SingleChatPageWebState(); 19 | } 20 | 21 | class _SingleChatPageWebState extends State { 22 | TextEditingController _messageController = TextEditingController(); 23 | ScrollController _scrollController =ScrollController(); 24 | 25 | @override 26 | void initState() { 27 | BlocProvider.of(context).getTextMessages(); 28 | super.initState(); 29 | } 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | return BlocBuilder( 34 | builder: (context,state){ 35 | if (state is CommunicationLoaded){ 36 | return _bodyWidget(state); 37 | } 38 | return _loadingWidget(); 39 | }, 40 | ); 41 | } 42 | Widget _bodyWidget(CommunicationLoaded messages) { 43 | return Scaffold( 44 | body: Stack( 45 | children: [ 46 | Positioned.fill( 47 | child: Image.asset( 48 | "assets/background_img.png", 49 | fit: BoxFit.cover, 50 | ), 51 | ), 52 | Column( 53 | children: [ 54 | _headerWidget(), 55 | _listMessagesWidget(messages), 56 | _sendTextMessageWidget(), 57 | ], 58 | ), 59 | ], 60 | ), 61 | ); 62 | } 63 | Widget _loadingWidget(){ 64 | return Scaffold( 65 | body: Stack( 66 | children: [ 67 | Positioned.fill( 68 | child: Image.asset( 69 | "assets/background_img.png", 70 | fit: BoxFit.cover, 71 | ), 72 | ), 73 | Column( 74 | children: [ 75 | _headerWidget(), 76 | Expanded(child: Center(child: CircularProgressIndicator(),)), 77 | _sendTextMessageWidget(), 78 | ], 79 | ), 80 | ], 81 | ), 82 | ); 83 | } 84 | Widget _headerWidget() { 85 | return Container( 86 | padding: EdgeInsets.symmetric(horizontal: 80), 87 | height: 60, 88 | width: double.infinity, 89 | decoration: BoxDecoration( 90 | gradient: LinearGradient( 91 | colors: [ 92 | Colors.indigo[400], 93 | Colors.blue[300], 94 | ], 95 | )), 96 | child: Row( 97 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 98 | children: [ 99 | Row( 100 | children: [ 101 | Container( 102 | width: 40, height: 40, child: Image.asset("assets/logo.png")), 103 | Text( 104 | "Global Chat Room", 105 | style: TextStyle( 106 | fontSize: 22, 107 | color: Colors.white, 108 | ), 109 | ), 110 | ], 111 | ), 112 | Text( 113 | "${widget.userName}", 114 | style: TextStyle( 115 | fontSize: 22, 116 | color: Colors.white, 117 | ), 118 | ) 119 | ], 120 | ), 121 | ); 122 | } 123 | 124 | Widget _listMessagesWidget(CommunicationLoaded messages) { 125 | Timer( 126 | Duration(milliseconds: 100), 127 | () => _scrollController 128 | .jumpTo(_scrollController.position.maxScrollExtent)); 129 | 130 | return Expanded( 131 | child: Container( 132 | margin: EdgeInsets.symmetric(horizontal: 20), 133 | child: ListView.builder( 134 | controller: _scrollController, 135 | shrinkWrap: true, 136 | itemCount: messages.messages.length, 137 | itemBuilder: (_, index) { 138 | return messages.messages[index].senderId == 139 | widget.uid 140 | ? MessageLayout( 141 | type: messages.messages[index].type, 142 | senderId: messages.messages[index].senderId, 143 | senderName: 144 | messages.messages[index].senderName, 145 | text: messages.messages[index].message, 146 | time: DateFormat('hh:mm a').format( 147 | messages.messages[index].time.toDate()), 148 | color: Colors.green[300], 149 | align: TextAlign.left, 150 | nip: BubbleNip.rightTop, 151 | boxAlignment: CrossAxisAlignment.end, 152 | boxMainAxisAlignment: MainAxisAlignment.end, 153 | uid: widget.uid,) 154 | : MessageLayout( 155 | type: messages.messages[index].type, 156 | senderName: 157 | messages.messages[index].senderName, 158 | text: messages.messages[index].message, 159 | time: DateFormat('hh:mm a').format( 160 | messages.messages[index].time.toDate()), 161 | color: Colors.blue, 162 | align: TextAlign.left, 163 | nip: BubbleNip.leftTop, 164 | boxAlignment: CrossAxisAlignment.start, 165 | boxMainAxisAlignment: MainAxisAlignment.start, 166 | ); 167 | }, 168 | ), 169 | ), 170 | ); 171 | } 172 | 173 | Widget _sendTextMessageWidget() { 174 | return Padding( 175 | padding: const EdgeInsets.only(bottom: 5), 176 | child: Container( 177 | padding: EdgeInsets.symmetric(horizontal: 80), 178 | height: 60, 179 | width: double.infinity, 180 | decoration: BoxDecoration( 181 | color: Colors.white, 182 | borderRadius: BorderRadius.all(Radius.circular(0.0)), 183 | border: Border.all(color: Colors.black.withOpacity(.4), width: 2)), 184 | child: Row( 185 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 186 | children: [ 187 | Row( 188 | children: [ 189 | _emojiWidget(), 190 | SizedBox( 191 | width: 8, 192 | ), 193 | _textFieldWidget(), 194 | ], 195 | ), 196 | Row( 197 | children: [ 198 | _micWidget(), 199 | SizedBox( 200 | width: 8, 201 | ), 202 | _sendMessageButton(), 203 | ], 204 | ) 205 | ], 206 | ), 207 | ), 208 | ); 209 | } 210 | 211 | _emojiWidget() { 212 | return Container( 213 | height: 50, 214 | width: 50, 215 | decoration: BoxDecoration( 216 | color: Colors.black.withOpacity(.2), 217 | borderRadius: BorderRadius.all(Radius.circular(40))), 218 | child: Icon( 219 | Icons.emoji_symbols, 220 | color: Colors.white, 221 | ), 222 | ); 223 | } 224 | 225 | _micWidget() { 226 | return Container( 227 | height: 50, 228 | width: 50, 229 | decoration: BoxDecoration( 230 | color: Colors.black.withOpacity(.2), 231 | borderRadius: BorderRadius.all(Radius.circular(40))), 232 | child: Icon( 233 | Icons.mic, 234 | color: Colors.white, 235 | ), 236 | ); 237 | } 238 | 239 | _textFieldWidget() { 240 | return ResponsiveBuilder( 241 | builder: (_, sizingInformation) { 242 | return Container( 243 | width: sizingInformation.screenSize.width * 0.65, 244 | child: ConstrainedBox( 245 | constraints: BoxConstraints( 246 | maxHeight: 60, 247 | ), 248 | child: Scrollbar( 249 | child: TextField( 250 | controller: _messageController, 251 | maxLines: null, 252 | decoration: InputDecoration( 253 | border: InputBorder.none, 254 | hintText: "Type Feel Free <3 ..."), 255 | ), 256 | ), 257 | ), 258 | ); 259 | }, 260 | ); 261 | } 262 | 263 | Widget _sendMessageButton() { 264 | return InkWell( 265 | onTap: () { 266 | if (_messageController.text.isNotEmpty) { 267 | _sendTextMessage(); 268 | _messageController.clear(); 269 | } 270 | }, 271 | child: Container( 272 | height: 50, 273 | width: 50, 274 | decoration: BoxDecoration( 275 | color: Colors.green, 276 | borderRadius: BorderRadius.all(Radius.circular(40))), 277 | child: Icon( 278 | Icons.send, 279 | color: Colors.white, 280 | ), 281 | ), 282 | ); 283 | } 284 | 285 | void _sendTextMessage() { 286 | BlocProvider.of(context).sendTextMsg( 287 | uid: widget.uid, 288 | name: widget.userName, 289 | message: _messageController.text); 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /lib/presentation/pages/web/web_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:group_chat_room_responsive/presentation/pages/web/widgets/left_side_widget.dart'; 3 | import 'package:group_chat_room_responsive/presentation/pages/web/widgets/right_side_widget.dart'; 4 | import 'package:responsive_builder/responsive_builder.dart'; 5 | 6 | class WebPage extends StatelessWidget { 7 | final SizingInformation sizingInformation; 8 | 9 | const WebPage({Key key, this.sizingInformation}) : super(key: key); 10 | @override 11 | Widget build(BuildContext context) { 12 | return Scaffold( 13 | body: Container( 14 | child: Row( 15 | children: [ 16 | LeftSideWidget(sizingInformation: sizingInformation,), 17 | Expanded(child: RightSideWidget( 18 | sizingInformation: sizingInformation, 19 | )), 20 | ], 21 | ), 22 | ), 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/presentation/pages/web/welcome_page_web.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:group_chat_room_responsive/data/model/user_model.dart'; 5 | import 'package:group_chat_room_responsive/presentation/bloc/auth/auth_cubit.dart'; 6 | import 'package:group_chat_room_responsive/presentation/bloc/login/login_cubit.dart'; 7 | import 'package:group_chat_room_responsive/presentation/bloc/user/user_cubit.dart'; 8 | import 'package:group_chat_room_responsive/presentation/pages/web/single_chat_page_web.dart'; 9 | import 'package:group_chat_room_responsive/presentation/screens/single_chat_screen.dart'; 10 | import 'package:lottie/lottie.dart'; 11 | 12 | class WelcomePageWeb extends StatefulWidget { 13 | final String uid; 14 | 15 | const WelcomePageWeb({Key key, this.uid}) : super(key: key); 16 | 17 | @override 18 | _WelcomePageWebState createState() => _WelcomePageWebState(); 19 | } 20 | 21 | class _WelcomePageWebState extends State { 22 | @override 23 | void initState() { 24 | BlocProvider.of(context).getUsers(); 25 | super.initState(); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return BlocBuilder(builder: (_, state) { 31 | if (state is UserLoaded) { 32 | return _bodyWidget(state); 33 | } 34 | return _loadingWidget(); 35 | }); 36 | } 37 | 38 | Widget _bodyWidget(UserLoaded users) { 39 | final user = users.users.firstWhere((user) => user.uid == widget.uid, 40 | orElse: () => UserModel()); 41 | return Scaffold( 42 | body: Stack( 43 | children: [ 44 | Container( 45 | decoration: BoxDecoration( 46 | gradient: LinearGradient( 47 | colors: [ 48 | Colors.indigo[400], 49 | Colors.blue[300], 50 | ], 51 | )), 52 | ), 53 | Align( 54 | alignment: Alignment.topCenter, 55 | child: Lottie.asset( 56 | "assets/congratulation.json", 57 | ), 58 | ), 59 | Align( 60 | alignment: Alignment.bottomRight, 61 | child: Lottie.asset("assets/bubble.json"), 62 | ), 63 | Align( 64 | alignment: Alignment.topCenter, 65 | child: Container( 66 | margin: EdgeInsets.only(top: 150), 67 | child: Text( 68 | "Welcome ${user.name}", 69 | style: TextStyle( 70 | fontSize: 30, 71 | color: Colors.white, 72 | fontWeight: FontWeight.bold, 73 | ), 74 | )), 75 | ), 76 | _joinGlobalChatButton(name: user.name), 77 | _logOutWidget(), 78 | ], 79 | ), 80 | ); 81 | } 82 | 83 | Widget _loadingWidget() { 84 | return Scaffold( 85 | body: Stack( 86 | children: [ 87 | Container( 88 | decoration: BoxDecoration( 89 | gradient: LinearGradient( 90 | colors: [ 91 | Colors.indigo[400], 92 | Colors.blue[300], 93 | ], 94 | )), 95 | ), 96 | Align( 97 | alignment: Alignment.center, 98 | child: CircularProgressIndicator(), 99 | ), 100 | ], 101 | ), 102 | ); 103 | } 104 | 105 | Widget _joinGlobalChatButton({String name}) { 106 | return Align( 107 | alignment: Alignment.center, 108 | child: Center( 109 | child: Column( 110 | mainAxisAlignment: MainAxisAlignment.center, 111 | children: [ 112 | Text( 113 | "Join Us For Fun", 114 | style: TextStyle( 115 | fontSize: 30, 116 | fontWeight: FontWeight.bold, 117 | ), 118 | ), 119 | SizedBox( 120 | height: 30, 121 | ), 122 | InkWell( 123 | onTap: () { 124 | Navigator.push( 125 | context, 126 | MaterialPageRoute( 127 | builder: (_) => SingleChatScreen(username: name,uid: widget.uid,), 128 | ), 129 | ); 130 | }, 131 | child: Container( 132 | width: 250, 133 | height: 70, 134 | alignment: Alignment.center, 135 | decoration: BoxDecoration( 136 | color: Colors.white.withOpacity(.3), 137 | borderRadius: BorderRadius.all(Radius.circular(20)), 138 | border: Border.all(color: Colors.white60, width: 2)), 139 | child: Text( 140 | "Join", 141 | style: TextStyle(fontSize: 30), 142 | ), 143 | ), 144 | ), 145 | ], 146 | ), 147 | ), 148 | ); 149 | } 150 | 151 | Widget _logOutWidget() { 152 | return Align( 153 | alignment: Alignment.bottomLeft, 154 | child: InkWell( 155 | onTap: () { 156 | //TODO:Logout 157 | BlocProvider.of(context).loggedOut(); 158 | BlocProvider.of(context).submitSignOut(); 159 | }, 160 | child: Container( 161 | margin: EdgeInsets.only(left: 15, bottom: 15), 162 | padding: EdgeInsets.all(10), 163 | decoration: BoxDecoration( 164 | color: Colors.white.withOpacity(.3), 165 | borderRadius: BorderRadius.circular(30), 166 | ), 167 | child: Icon( 168 | Icons.exit_to_app, 169 | size: 30, 170 | ), 171 | ), 172 | ), 173 | ); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /lib/presentation/pages/web/widgets/left_side_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/painting.dart'; 3 | import 'package:flutter/widgets.dart'; 4 | import 'package:lottie/lottie.dart'; 5 | import 'package:responsive_builder/responsive_builder.dart'; 6 | 7 | class LeftSideWidget extends StatelessWidget { 8 | final SizingInformation sizingInformation; 9 | 10 | const LeftSideWidget({Key key, this.sizingInformation}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Container( 15 | width: sizingInformation.screenSize.width * 0.65, 16 | height: double.infinity, 17 | decoration: BoxDecoration( 18 | gradient: LinearGradient( 19 | colors: [ 20 | Colors.indigo[400], 21 | Colors.blue[300], 22 | ], 23 | )), 24 | child: Container( 25 | child: Stack( 26 | children: [ 27 | _loginButton(), 28 | _bgImageWidget(), 29 | _welcomeTextWidget(), 30 | Positioned( 31 | left: -150, 32 | bottom: -150, 33 | child: Image.asset( 34 | "assets/shape.png", 35 | color: Colors.white.withOpacity(.2), 36 | )) 37 | ], 38 | ), 39 | ), 40 | ); 41 | } 42 | 43 | Widget _loginButton() { 44 | return Align( 45 | alignment: Alignment.topRight, 46 | child: Container( 47 | margin: EdgeInsets.symmetric(horizontal: 20, vertical: 20), 48 | padding: EdgeInsets.symmetric(horizontal: 20, vertical: 5), 49 | decoration: BoxDecoration( 50 | borderRadius: BorderRadius.all(Radius.circular(15)), 51 | border: Border.all(color: Colors.white60, width: 1.50), 52 | ), 53 | child: Text( 54 | "LOGIN", 55 | style: TextStyle(fontSize: 16, color: Colors.white60), 56 | )), 57 | ); 58 | } 59 | 60 | _bgImageWidget() { 61 | return Align( 62 | alignment: Alignment.center, 63 | child: Lottie.asset("assets/img.json"), 64 | ); 65 | } 66 | 67 | Widget _welcomeTextWidget() { 68 | return Positioned( 69 | left: 35, 70 | bottom: 30, 71 | child: Column( 72 | crossAxisAlignment: CrossAxisAlignment.start, 73 | children: [ 74 | Text( 75 | "WELCOME TO eTechViral", 76 | style: TextStyle( 77 | fontSize: 30, color: Colors.white, fontWeight: FontWeight.bold), 78 | ), 79 | SizedBox( 80 | height: 20, 81 | ), 82 | Container( 83 | width: sizingInformation.screenSize.width * 0.60, 84 | child: Text( 85 | "Flutter is changing the app development world. if you want to improve your flutter skill then join our channel because we try to upload one video per week. eTechViral :- flutter and dart app development tutorials crafted to make you win jobs.", 86 | textAlign: TextAlign.start, 87 | style: TextStyle(color: Colors.white60, fontSize: 16), 88 | ), 89 | ), 90 | SizedBox( 91 | height: 20, 92 | ), 93 | Container( 94 | padding: EdgeInsets.symmetric(horizontal: 20, vertical: 5), 95 | decoration: BoxDecoration( 96 | borderRadius: BorderRadius.all(Radius.circular(15)), 97 | border: Border.all(color: Colors.white60, width: 1.50), 98 | ), 99 | child: Text( 100 | "SIGN IN", 101 | style: TextStyle(fontSize: 16, color: Colors.white60), 102 | )), 103 | ], 104 | ), 105 | ); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/presentation/pages/web/widgets/right_side_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:group_chat_room_responsive/presentation/bloc/auth/auth_cubit.dart'; 5 | import 'package:group_chat_room_responsive/presentation/bloc/login/login_cubit.dart'; 6 | import 'package:lottie/lottie.dart'; 7 | import 'package:responsive_builder/responsive_builder.dart'; 8 | 9 | class RightSideWidget extends StatefulWidget { 10 | final SizingInformation sizingInformation; 11 | 12 | const RightSideWidget({Key key, this.sizingInformation}) : super(key: key); 13 | 14 | @override 15 | _RightSideWidgetState createState() => _RightSideWidgetState(); 16 | } 17 | 18 | class _RightSideWidgetState extends State { 19 | TextEditingController _nameController; 20 | TextEditingController _emailController; 21 | TextEditingController _passwordController; 22 | 23 | bool isLoginPage = true; 24 | 25 | @override 26 | void initState() { 27 | _nameController = TextEditingController(); 28 | _emailController = TextEditingController(); 29 | _passwordController = TextEditingController(); 30 | super.initState(); 31 | } 32 | 33 | @override 34 | void dispose() { 35 | _nameController.dispose(); 36 | _emailController.dispose(); 37 | _passwordController.dispose(); 38 | super.dispose(); 39 | } 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | return BlocConsumer( 44 | builder: (context, state) { 45 | if (state is LoginLoading) { 46 | return _loadingWidget(); 47 | } 48 | return _bodyWidget(); 49 | }, 50 | listener: (context, state) { 51 | if (state is LoginSuccess){ 52 | BlocProvider.of(context).loggedIn(); 53 | } 54 | }, 55 | ); 56 | } 57 | 58 | Widget _loadingWidget() { 59 | return Container( 60 | decoration: BoxDecoration( 61 | color: Colors.white, 62 | ), 63 | height: double.infinity, 64 | padding: EdgeInsets.symmetric(horizontal: 15, vertical: 20), 65 | child: Column( 66 | mainAxisAlignment: MainAxisAlignment.center, 67 | children: [ 68 | _imageWidget(), 69 | SizedBox( 70 | height: 15, 71 | ), 72 | _fromWidget(), 73 | SizedBox( 74 | height: 15, 75 | ), 76 | _buttonWidget(), 77 | SizedBox( 78 | height: 40, 79 | ), 80 | Align( 81 | alignment: Alignment.bottomCenter, 82 | child: _rowTextWidget(), 83 | ), 84 | Align( 85 | alignment: Alignment.bottomCenter, 86 | child: Lottie.asset("assets/loading.json"), 87 | ), 88 | ], 89 | ), 90 | ); 91 | } 92 | 93 | Widget _bodyWidget() { 94 | return Container( 95 | decoration: BoxDecoration( 96 | color: Colors.white, 97 | ), 98 | height: double.infinity, 99 | padding: EdgeInsets.symmetric(horizontal: 15, vertical: 20), 100 | child: Column( 101 | mainAxisAlignment: MainAxisAlignment.center, 102 | children: [ 103 | _imageWidget(), 104 | SizedBox( 105 | height: 15, 106 | ), 107 | _fromWidget(), 108 | SizedBox( 109 | height: 15, 110 | ), 111 | _buttonWidget(), 112 | SizedBox( 113 | height: 40, 114 | ), 115 | Align( 116 | alignment: Alignment.bottomCenter, 117 | child: _rowTextWidget(), 118 | ), 119 | ], 120 | ), 121 | ); 122 | } 123 | 124 | Widget _imageWidget() { 125 | return Container( 126 | height: 60, 127 | width: 60, 128 | child: Image.asset("assets/profile.png"), 129 | ); 130 | } 131 | 132 | Widget _fromWidget() { 133 | return Column( 134 | children: [ 135 | isLoginPage == true 136 | ? Text( 137 | "", 138 | style: TextStyle(fontSize: 0), 139 | ) 140 | : Container( 141 | height: 60, 142 | alignment: Alignment.center, 143 | decoration: BoxDecoration( 144 | borderRadius: BorderRadius.all(Radius.circular(40)), 145 | border: Border.all(color: Colors.grey, width: 1.0), 146 | ), 147 | child: TextField( 148 | controller: _nameController, 149 | decoration: InputDecoration( 150 | border: InputBorder.none, 151 | hintText: "User Name", 152 | prefixIcon: Icon(Icons.person_outline), 153 | ), 154 | ), 155 | ), 156 | isLoginPage == true 157 | ? Text( 158 | "", 159 | style: TextStyle(fontSize: 0), 160 | ) 161 | : SizedBox( 162 | height: 20, 163 | ), 164 | Container( 165 | height: 60, 166 | alignment: Alignment.center, 167 | decoration: BoxDecoration( 168 | borderRadius: BorderRadius.all(Radius.circular(40)), 169 | border: Border.all(color: Colors.grey, width: 1.0), 170 | ), 171 | child: TextField( 172 | controller: _emailController, 173 | decoration: InputDecoration( 174 | border: InputBorder.none, 175 | hintText: "Email Address", 176 | prefixIcon: Icon(Icons.alternate_email), 177 | ), 178 | ), 179 | ), 180 | SizedBox( 181 | height: 20, 182 | ), 183 | Container( 184 | height: 60, 185 | alignment: Alignment.center, 186 | decoration: BoxDecoration( 187 | borderRadius: BorderRadius.all(Radius.circular(40)), 188 | border: Border.all(color: Colors.grey, width: 1.0), 189 | ), 190 | child: TextField( 191 | controller: _passwordController, 192 | obscureText: true, 193 | decoration: InputDecoration( 194 | border: InputBorder.none, 195 | hintText: "Password", 196 | prefixIcon: Icon(Icons.lock_outline), 197 | ), 198 | ), 199 | ), 200 | ], 201 | ); 202 | } 203 | 204 | Widget _buttonWidget() { 205 | return InkWell( 206 | onTap: () { 207 | if (isLoginPage == true) { 208 | _submitLogin(); 209 | } else { 210 | _submitRegistration(); 211 | } 212 | }, 213 | child: Container( 214 | margin: EdgeInsets.symmetric(horizontal: 10), 215 | width: widget.sizingInformation.screenSize.width, 216 | alignment: Alignment.center, 217 | padding: EdgeInsets.symmetric(vertical: 14), 218 | decoration: BoxDecoration( 219 | color: Colors.indigo, 220 | borderRadius: BorderRadius.all(Radius.circular(50)), 221 | ), 222 | child: Text( 223 | isLoginPage == true ? "LOGIN" : "REGISTER", 224 | style: TextStyle(fontSize: 18, color: Colors.white), 225 | ), 226 | ), 227 | ); 228 | } 229 | 230 | Widget _rowTextWidget() { 231 | return Row( 232 | mainAxisAlignment: MainAxisAlignment.center, 233 | children: [ 234 | Text( 235 | isLoginPage == true ? "Don't have account?" : "Have an account?", 236 | style: TextStyle(fontSize: 16, color: Colors.indigo[400]), 237 | ), 238 | InkWell( 239 | onTap: () { 240 | setState(() { 241 | if (isLoginPage == true) 242 | isLoginPage = false; 243 | else 244 | isLoginPage = true; 245 | }); 246 | }, 247 | child: Text( 248 | isLoginPage == true ? " Sign Up" : " Sign In", 249 | style: TextStyle( 250 | fontSize: 16, 251 | color: Colors.indigo, 252 | fontWeight: FontWeight.bold), 253 | ), 254 | ), 255 | ], 256 | ); 257 | } 258 | 259 | void _submitLogin() { 260 | if (_emailController.text.isNotEmpty && 261 | _passwordController.text.isNotEmpty) { 262 | BlocProvider.of(context).submitLogin( 263 | email: _emailController.text, 264 | password: _passwordController.text, 265 | ); 266 | } 267 | } 268 | 269 | void _submitRegistration() { 270 | if (_emailController.text.isNotEmpty && 271 | _passwordController.text.isNotEmpty && 272 | _nameController.text.isNotEmpty) { 273 | BlocProvider.of(context).submitRegistration( 274 | email: _emailController.text, 275 | password: _passwordController.text, 276 | name: _nameController.text, 277 | ); 278 | } 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /lib/presentation/pages/widget/message_layout.dart: -------------------------------------------------------------------------------- 1 | import 'package:bubble/bubble.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class MessageLayout extends StatelessWidget { 5 | final String uid; 6 | final String type; 7 | final text; 8 | final time; 9 | final color; 10 | final align; 11 | final boxAlignment; 12 | final nip; 13 | final senderName; 14 | final senderId; 15 | final boxMainAxisAlignment; 16 | 17 | const MessageLayout({ 18 | Key key, 19 | this.uid, 20 | this.type, 21 | this.text, 22 | this.time, 23 | this.color, 24 | this.align, 25 | this.boxAlignment, 26 | this.nip, 27 | this.senderName, 28 | this.senderId, 29 | this.boxMainAxisAlignment, 30 | }) : super(key: key); 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | return Column( 35 | crossAxisAlignment: boxAlignment, 36 | children: [ 37 | Row( 38 | crossAxisAlignment: CrossAxisAlignment.start, 39 | mainAxisAlignment: boxMainAxisAlignment, 40 | children: [ 41 | color == Colors.blue 42 | ?Container( 43 | height: 55, 44 | width: 55, 45 | decoration: BoxDecoration( 46 | border: Border.all(color: Colors.black,width: 2), 47 | borderRadius: BorderRadius.all(Radius.circular(55)) 48 | ), 49 | child: Image.asset("assets/profile_default.png"), 50 | ):Text("",style: TextStyle(fontSize: 0),), 51 | Container( 52 | padding: EdgeInsets.all(8), 53 | margin: EdgeInsets.all(3), 54 | child: Bubble( 55 | color: color, 56 | nip: nip, 57 | child: Column( 58 | crossAxisAlignment: CrossAxisAlignment.start, 59 | children: [ 60 | color == Colors.green[300] 61 | ? Text( 62 | "Me", 63 | textAlign: align, 64 | style: TextStyle( 65 | fontSize: 18, fontWeight: FontWeight.bold), 66 | ) 67 | : Text( 68 | senderName, 69 | textAlign: align, 70 | style: TextStyle( 71 | fontSize: 18, fontWeight: FontWeight.bold), 72 | ), 73 | ConstrainedBox( 74 | constraints: BoxConstraints( 75 | maxWidth: 400 76 | ), 77 | child: Text( 78 | text==""?"":text, 79 | textAlign: align, 80 | style: TextStyle(fontSize: 18), 81 | ), 82 | ), 83 | Text( 84 | time, 85 | textAlign: align, 86 | style: TextStyle(fontSize: 14), 87 | ) 88 | ], 89 | ), 90 | ), 91 | ), 92 | color != Colors.blue 93 | ?Container( 94 | height: 55, 95 | width: 55, 96 | decoration: BoxDecoration( 97 | border: Border.all(color: Colors.black,width: 2), 98 | borderRadius: BorderRadius.all(Radius.circular(55)) 99 | ), 100 | child: Image.asset("assets/profile_default.png"), 101 | ):Text("",style: TextStyle(fontSize: 0),), 102 | ], 103 | ) 104 | ], 105 | ); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/presentation/screens/home_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:group_chat_room_responsive/presentation/pages/mobile/mobile_page.dart'; 3 | import 'package:group_chat_room_responsive/presentation/pages/tablet/tablet_page.dart'; 4 | import 'package:group_chat_room_responsive/presentation/pages/web/web_page.dart'; 5 | import 'package:responsive_builder/responsive_builder.dart'; 6 | 7 | class HomeScreen extends StatelessWidget { 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return ResponsiveBuilder( 12 | builder: (context,sizingInformation){ 13 | if (sizingInformation.isDesktop){ 14 | return WebPage(sizingInformation: sizingInformation,); 15 | } 16 | if (sizingInformation.isTablet){ 17 | return TabletPage(); 18 | } 19 | return MobilePage(); 20 | }, 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/presentation/screens/single_chat_screen.dart: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:group_chat_room_responsive/presentation/pages/mobile/single_chat_page_mobile.dart'; 5 | import 'package:group_chat_room_responsive/presentation/pages/tablet/single_chat_page_tablet.dart'; 6 | import 'package:group_chat_room_responsive/presentation/pages/web/single_chat_page_web.dart'; 7 | import 'package:responsive_builder/responsive_builder.dart'; 8 | 9 | class SingleChatScreen extends StatelessWidget { 10 | final String username; 11 | final String uid; 12 | 13 | const SingleChatScreen({Key key, this.username, this.uid}) : super(key: key); 14 | @override 15 | Widget build(BuildContext context) { 16 | return ResponsiveBuilder( 17 | builder: (context,sizingInformation){ 18 | if (sizingInformation.isDesktop){ 19 | return SingleChatPageWeb(uid: uid,userName: username,); 20 | } 21 | if (sizingInformation.isTablet){ 22 | return SingleChatPageTablet(uid: uid,userName: username,); 23 | } 24 | return SingleChatPageMobile(userName: username,uid: uid,); 25 | }, 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/presentation/screens/welcome_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:group_chat_room_responsive/presentation/pages/mobile/welcome_page_mobile.dart'; 3 | import 'package:group_chat_room_responsive/presentation/pages/tablet/welcome_page_tablet.dart'; 4 | import 'package:group_chat_room_responsive/presentation/pages/web/welcome_page_web.dart'; 5 | import 'package:responsive_builder/responsive_builder.dart'; 6 | 7 | class WelcomeScreen extends StatelessWidget { 8 | final String uid; 9 | 10 | const WelcomeScreen({Key key, this.uid}) : super(key: key); 11 | @override 12 | Widget build(BuildContext context) { 13 | return ResponsiveBuilder( 14 | builder: (context,sizingInformation){ 15 | if (sizingInformation.isDesktop){ 16 | return WelcomePageWeb(uid: uid,); 17 | } 18 | if (sizingInformation.isTablet){ 19 | return WelcomePageTablet(uid: uid,); 20 | } 21 | return WelcomePageMobile(uid: uid,); 22 | }, 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | archive: 5 | dependency: transitive 6 | description: 7 | name: archive 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.0.13" 11 | args: 12 | dependency: transitive 13 | description: 14 | name: args 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "1.6.0" 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" 25 | bloc: 26 | dependency: transitive 27 | description: 28 | name: bloc 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "6.0.3" 32 | boolean_selector: 33 | dependency: transitive 34 | description: 35 | name: boolean_selector 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "2.1.0-nullsafety" 39 | bubble: 40 | dependency: "direct main" 41 | description: 42 | name: bubble 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.1.9+1" 46 | characters: 47 | dependency: transitive 48 | description: 49 | name: characters 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.1.0-nullsafety.2" 53 | charcode: 54 | dependency: transitive 55 | description: 56 | name: charcode 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.2.0-nullsafety" 60 | clock: 61 | dependency: transitive 62 | description: 63 | name: clock 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "1.1.0-nullsafety" 67 | cloud_firestore: 68 | dependency: "direct main" 69 | description: 70 | name: cloud_firestore 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "0.14.0+2" 74 | cloud_firestore_platform_interface: 75 | dependency: transitive 76 | description: 77 | name: cloud_firestore_platform_interface 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "2.0.1" 81 | cloud_firestore_web: 82 | dependency: "direct main" 83 | description: 84 | name: cloud_firestore_web 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "0.2.0+1" 88 | collection: 89 | dependency: transitive 90 | description: 91 | name: collection 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "1.15.0-nullsafety.2" 95 | convert: 96 | dependency: transitive 97 | description: 98 | name: convert 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "2.1.1" 102 | crypto: 103 | dependency: transitive 104 | description: 105 | name: crypto 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "2.1.5" 109 | cupertino_icons: 110 | dependency: "direct main" 111 | description: 112 | name: cupertino_icons 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "1.0.0" 116 | equatable: 117 | dependency: "direct main" 118 | description: 119 | name: equatable 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "1.2.4" 123 | fake_async: 124 | dependency: transitive 125 | description: 126 | name: fake_async 127 | url: "https://pub.dartlang.org" 128 | source: hosted 129 | version: "1.1.0-nullsafety" 130 | firebase: 131 | dependency: transitive 132 | description: 133 | name: firebase 134 | url: "https://pub.dartlang.org" 135 | source: hosted 136 | version: "7.3.0" 137 | firebase_auth: 138 | dependency: "direct main" 139 | description: 140 | name: firebase_auth 141 | url: "https://pub.dartlang.org" 142 | source: hosted 143 | version: "0.18.0+1" 144 | firebase_auth_platform_interface: 145 | dependency: transitive 146 | description: 147 | name: firebase_auth_platform_interface 148 | url: "https://pub.dartlang.org" 149 | source: hosted 150 | version: "2.0.1" 151 | firebase_auth_web: 152 | dependency: "direct main" 153 | description: 154 | name: firebase_auth_web 155 | url: "https://pub.dartlang.org" 156 | source: hosted 157 | version: "0.3.0+1" 158 | firebase_core: 159 | dependency: transitive 160 | description: 161 | name: firebase_core 162 | url: "https://pub.dartlang.org" 163 | source: hosted 164 | version: "0.5.0" 165 | firebase_core_platform_interface: 166 | dependency: transitive 167 | description: 168 | name: firebase_core_platform_interface 169 | url: "https://pub.dartlang.org" 170 | source: hosted 171 | version: "2.0.0" 172 | firebase_core_web: 173 | dependency: transitive 174 | description: 175 | name: firebase_core_web 176 | url: "https://pub.dartlang.org" 177 | source: hosted 178 | version: "0.2.0" 179 | flutter: 180 | dependency: "direct main" 181 | description: flutter 182 | source: sdk 183 | version: "0.0.0" 184 | flutter_bloc: 185 | dependency: "direct main" 186 | description: 187 | name: flutter_bloc 188 | url: "https://pub.dartlang.org" 189 | source: hosted 190 | version: "6.0.5" 191 | flutter_plugin_android_lifecycle: 192 | dependency: transitive 193 | description: 194 | name: flutter_plugin_android_lifecycle 195 | url: "https://pub.dartlang.org" 196 | source: hosted 197 | version: "1.0.8" 198 | flutter_shimmer: 199 | dependency: "direct main" 200 | description: 201 | name: flutter_shimmer 202 | url: "https://pub.dartlang.org" 203 | source: hosted 204 | version: "1.3.0" 205 | flutter_test: 206 | dependency: "direct dev" 207 | description: flutter 208 | source: sdk 209 | version: "0.0.0" 210 | flutter_web_plugins: 211 | dependency: transitive 212 | description: flutter 213 | source: sdk 214 | version: "0.0.0" 215 | font_awesome_flutter: 216 | dependency: "direct main" 217 | description: 218 | name: font_awesome_flutter 219 | url: "https://pub.dartlang.org" 220 | source: hosted 221 | version: "8.8.1" 222 | get_it: 223 | dependency: "direct main" 224 | description: 225 | name: get_it 226 | url: "https://pub.dartlang.org" 227 | source: hosted 228 | version: "4.0.4" 229 | http: 230 | dependency: transitive 231 | description: 232 | name: http 233 | url: "https://pub.dartlang.org" 234 | source: hosted 235 | version: "0.12.2" 236 | http_parser: 237 | dependency: transitive 238 | description: 239 | name: http_parser 240 | url: "https://pub.dartlang.org" 241 | source: hosted 242 | version: "3.1.4" 243 | image_picker: 244 | dependency: "direct main" 245 | description: 246 | name: image_picker 247 | url: "https://pub.dartlang.org" 248 | source: hosted 249 | version: "0.6.7+7" 250 | image_picker_platform_interface: 251 | dependency: transitive 252 | description: 253 | name: image_picker_platform_interface 254 | url: "https://pub.dartlang.org" 255 | source: hosted 256 | version: "1.1.0" 257 | intl: 258 | dependency: "direct main" 259 | description: 260 | name: intl 261 | url: "https://pub.dartlang.org" 262 | source: hosted 263 | version: "0.16.1" 264 | js: 265 | dependency: transitive 266 | description: 267 | name: js 268 | url: "https://pub.dartlang.org" 269 | source: hosted 270 | version: "0.6.2" 271 | logging: 272 | dependency: transitive 273 | description: 274 | name: logging 275 | url: "https://pub.dartlang.org" 276 | source: hosted 277 | version: "0.11.4" 278 | lottie: 279 | dependency: "direct main" 280 | description: 281 | name: lottie 282 | url: "https://pub.dartlang.org" 283 | source: hosted 284 | version: "0.6.0" 285 | matcher: 286 | dependency: transitive 287 | description: 288 | name: matcher 289 | url: "https://pub.dartlang.org" 290 | source: hosted 291 | version: "0.12.10-nullsafety" 292 | meta: 293 | dependency: transitive 294 | description: 295 | name: meta 296 | url: "https://pub.dartlang.org" 297 | source: hosted 298 | version: "1.3.0-nullsafety.2" 299 | nested: 300 | dependency: transitive 301 | description: 302 | name: nested 303 | url: "https://pub.dartlang.org" 304 | source: hosted 305 | version: "0.0.4" 306 | path: 307 | dependency: transitive 308 | description: 309 | name: path 310 | url: "https://pub.dartlang.org" 311 | source: hosted 312 | version: "1.8.0-nullsafety" 313 | pedantic: 314 | dependency: transitive 315 | description: 316 | name: pedantic 317 | url: "https://pub.dartlang.org" 318 | source: hosted 319 | version: "1.9.2" 320 | plugin_platform_interface: 321 | dependency: transitive 322 | description: 323 | name: plugin_platform_interface 324 | url: "https://pub.dartlang.org" 325 | source: hosted 326 | version: "1.0.2" 327 | provider: 328 | dependency: transitive 329 | description: 330 | name: provider 331 | url: "https://pub.dartlang.org" 332 | source: hosted 333 | version: "4.3.2+2" 334 | quiver: 335 | dependency: transitive 336 | description: 337 | name: quiver 338 | url: "https://pub.dartlang.org" 339 | source: hosted 340 | version: "2.1.3" 341 | responsive_builder: 342 | dependency: "direct main" 343 | description: 344 | name: responsive_builder 345 | url: "https://pub.dartlang.org" 346 | source: hosted 347 | version: "0.3.0" 348 | sky_engine: 349 | dependency: transitive 350 | description: flutter 351 | source: sdk 352 | version: "0.0.99" 353 | source_span: 354 | dependency: transitive 355 | description: 356 | name: source_span 357 | url: "https://pub.dartlang.org" 358 | source: hosted 359 | version: "1.8.0-nullsafety" 360 | stack_trace: 361 | dependency: transitive 362 | description: 363 | name: stack_trace 364 | url: "https://pub.dartlang.org" 365 | source: hosted 366 | version: "1.10.0-nullsafety" 367 | stream_channel: 368 | dependency: transitive 369 | description: 370 | name: stream_channel 371 | url: "https://pub.dartlang.org" 372 | source: hosted 373 | version: "2.1.0-nullsafety" 374 | string_scanner: 375 | dependency: transitive 376 | description: 377 | name: string_scanner 378 | url: "https://pub.dartlang.org" 379 | source: hosted 380 | version: "1.1.0-nullsafety" 381 | term_glyph: 382 | dependency: transitive 383 | description: 384 | name: term_glyph 385 | url: "https://pub.dartlang.org" 386 | source: hosted 387 | version: "1.2.0-nullsafety" 388 | test_api: 389 | dependency: transitive 390 | description: 391 | name: test_api 392 | url: "https://pub.dartlang.org" 393 | source: hosted 394 | version: "0.2.19-nullsafety" 395 | typed_data: 396 | dependency: transitive 397 | description: 398 | name: typed_data 399 | url: "https://pub.dartlang.org" 400 | source: hosted 401 | version: "1.3.0-nullsafety.2" 402 | vector_math: 403 | dependency: transitive 404 | description: 405 | name: vector_math 406 | url: "https://pub.dartlang.org" 407 | source: hosted 408 | version: "2.1.0-nullsafety.2" 409 | sdks: 410 | dart: ">=2.10.0-0.0.dev <2.10.0" 411 | flutter: ">=1.16.0 <2.0.0" 412 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: group_chat_room_responsive 2 | description: A new Flutter application. 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 | responsive_builder: ^0.3.0 27 | font_awesome_flutter: ^8.8.1 28 | #message shape bubble 29 | bubble: ^1.1.9+1 30 | #state management 31 | flutter_bloc: ^6.0.5 32 | equatable: ^1.2.4 33 | #dateFormat 34 | intl: ^0.16.1 35 | #image picker 36 | image_picker: ^0.6.7+7 37 | #firebase 38 | cloud_firestore: ^0.14.0+2 39 | firebase_auth: ^0.18.0+1 40 | firebase_auth_web: ^0.3.0+1 41 | cloud_firestore_web: ^0.2.0+1 42 | lottie: ^0.6.0 43 | get_it: ^4.0.4 44 | flutter_shimmer: ^1.3.0 45 | 46 | # The following adds the Cupertino Icons font to your application. 47 | # Use with the CupertinoIcons class for iOS style icons. 48 | cupertino_icons: ^1.0.0 49 | 50 | dev_dependencies: 51 | flutter_test: 52 | sdk: flutter 53 | 54 | # For information on the generic Dart part of this file, see the 55 | # following page: https://dart.dev/tools/pub/pubspec 56 | 57 | # The following section is specific to Flutter. 58 | flutter: 59 | 60 | # The following line ensures that the Material Icons font is 61 | # included with your application, so that you can use the icons in 62 | # the material Icons class. 63 | uses-material-design: true 64 | 65 | # To add assets to your application, add an assets section, like this: 66 | assets: 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:group_chat_room_responsive/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 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/amirk3321/group_chat_room_responsive/e6ef8e99e2b46d36bc1247ba4863d6cf3d438e15/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | group_chat_room_responsive 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 41 | 44 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "group_chat_room_responsive", 3 | "short_name": "group_chat_room_responsive", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter application.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ] 23 | } 24 | --------------------------------------------------------------------------------