├── android ├── .gradle │ ├── 4.10.2 │ │ ├── gc.properties │ │ ├── fileChanges │ │ │ └── last-build.bin │ │ ├── fileHashes │ │ │ ├── fileHashes.bin │ │ │ ├── fileHashes.lock │ │ │ └── resourceHashesCache.bin │ │ ├── fileContent │ │ │ └── fileContent.lock │ │ ├── javaCompile │ │ │ ├── classAnalysis.bin │ │ │ ├── jarAnalysis.bin │ │ │ ├── javaCompile.lock │ │ │ └── taskHistory.bin │ │ └── taskHistory │ │ │ ├── taskHistory.bin │ │ │ └── taskHistory.lock │ ├── vcs-1 │ │ └── gc.properties │ └── buildOutputCleanup │ │ ├── cache.properties │ │ ├── outputFiles.bin │ │ └── buildOutputCleanup.lock ├── key.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradle.properties ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── 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 │ │ │ │ │ ├── strings.xml │ │ │ │ │ └── styles.xml │ │ │ │ └── drawable │ │ │ │ │ └── launch_background.xml │ │ │ ├── java │ │ │ │ ├── com │ │ │ │ │ └── blackhole │ │ │ │ │ │ └── chat_app │ │ │ │ │ │ └── MainActivity.java │ │ │ │ └── io │ │ │ │ │ └── flutter │ │ │ │ │ └── plugins │ │ │ │ │ └── GeneratedPluginRegistrant.java │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── google-services.json │ └── build.gradle ├── local.properties ├── settings.gradle ├── build.gradle ├── chat_app_android.iml ├── gradlew.bat └── gradlew ├── images ├── logo.png ├── smiley1.png ├── sticker1.png ├── sticker2.png ├── sticker3.png ├── sticker4.png ├── font_filled.png └── font_outline.png ├── fonts ├── B612Mono.ttf ├── RobotoMono.ttf ├── UbuntuMono.ttf └── Ubuntu-Regular.ttf ├── ios ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ ├── Generated.xcconfig │ └── AppFrameworkInfo.plist ├── Runner │ ├── AppDelegate.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── 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-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── main.m │ ├── GeneratedPluginRegistrant.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ ├── Info.plist │ └── GeneratedPluginRegistrant.m ├── Runner.xcworkspace │ └── contents.xcworkspacedata ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme └── Podfile ├── lib ├── controllers │ ├── json_controller.dart │ ├── image_compresser.dart │ ├── chats_controller.dart │ ├── user_controller.dart │ └── stories_controller.dart ├── const.dart ├── models │ ├── smiley_data.dart │ ├── basic.dart │ ├── image_data.dart │ ├── camera_file.dart │ ├── text_data.dart │ ├── user.g.dart │ ├── user.dart │ ├── story.dart │ ├── chat.dart │ ├── chat.g.dart │ └── story.g.dart ├── widgets │ ├── loading_widget.dart │ ├── search_widget.dart │ ├── recent_chat_widget.dart │ ├── user_widget.dart │ ├── ease_in_widget.dart │ ├── color_box.dart │ ├── profile_picture_widget.dart │ ├── switch_icon.dart │ ├── camera_button.dart │ ├── rounded_button.dart │ ├── moving_widget.dart │ ├── thumbnail_widget.dart │ ├── story_image_widget.dart │ ├── storyline_widget.dart │ ├── add_story_button.dart │ ├── chat_widget.dart │ ├── wave_user_widget.dart │ ├── chat_input_widget.dart │ ├── login_button.dart │ ├── smiley_add_widget.dart │ ├── stories_widget.dart │ ├── story_widget.dart │ ├── story_create_widget.dart │ └── text_add_widget.dart ├── screens │ ├── discover_screen.dart │ ├── chats_screen.dart │ ├── stories_screen.dart │ ├── people_screen.dart │ ├── search_screen.dart │ ├── splash_screen.dart │ ├── main_screen.dart │ ├── chat_screen.dart │ └── story_create_screen.dart └── main.dart ├── .idea ├── runConfigurations │ └── main_dart.xml ├── modules.xml ├── libraries │ ├── Dart_SDK.xml │ └── Flutter_Plugins.xml └── codeStyles │ └── Project.xml ├── .metadata ├── chat_app.iml ├── LICENSE ├── test └── widget_test.dart ├── README.md ├── .flutter-plugins ├── pubspec.yaml └── .gitignore /android/.gradle/4.10.2/gc.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /android/.gradle/vcs-1/gc.properties: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /android/.gradle/4.10.2/fileChanges/last-build.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/images/logo.png -------------------------------------------------------------------------------- /fonts/B612Mono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/fonts/B612Mono.ttf -------------------------------------------------------------------------------- /images/smiley1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/images/smiley1.png -------------------------------------------------------------------------------- /images/sticker1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/images/sticker1.png -------------------------------------------------------------------------------- /images/sticker2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/images/sticker2.png -------------------------------------------------------------------------------- /images/sticker3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/images/sticker3.png -------------------------------------------------------------------------------- /images/sticker4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/images/sticker4.png -------------------------------------------------------------------------------- /fonts/RobotoMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/fonts/RobotoMono.ttf -------------------------------------------------------------------------------- /fonts/UbuntuMono.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/fonts/UbuntuMono.ttf -------------------------------------------------------------------------------- /images/font_filled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/images/font_filled.png -------------------------------------------------------------------------------- /fonts/Ubuntu-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/fonts/Ubuntu-Regular.ttf -------------------------------------------------------------------------------- /images/font_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/images/font_outline.png -------------------------------------------------------------------------------- /android/.gradle/buildOutputCleanup/cache.properties: -------------------------------------------------------------------------------- 1 | #Thu Jun 06 16:47:40 IST 2019 2 | gradle.version=4.10.2 3 | -------------------------------------------------------------------------------- /android/key.properties: -------------------------------------------------------------------------------- 1 | storePassword=android 2 | keyPassword=android 3 | keyAlias=key 4 | storeFile=C:/key.jks -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | android.enableR8=true 5 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /android/.gradle/4.10.2/fileHashes/fileHashes.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/.gradle/4.10.2/fileHashes/fileHashes.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/fileHashes/fileHashes.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/.gradle/4.10.2/fileHashes/fileHashes.lock -------------------------------------------------------------------------------- /android/.gradle/4.10.2/fileContent/fileContent.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/.gradle/4.10.2/fileContent/fileContent.lock -------------------------------------------------------------------------------- /android/.gradle/4.10.2/javaCompile/classAnalysis.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/.gradle/4.10.2/javaCompile/classAnalysis.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/javaCompile/jarAnalysis.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/.gradle/4.10.2/javaCompile/jarAnalysis.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/javaCompile/javaCompile.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/.gradle/4.10.2/javaCompile/javaCompile.lock -------------------------------------------------------------------------------- /android/.gradle/4.10.2/javaCompile/taskHistory.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/.gradle/4.10.2/javaCompile/taskHistory.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/taskHistory/taskHistory.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/.gradle/4.10.2/taskHistory/taskHistory.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/taskHistory/taskHistory.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/.gradle/4.10.2/taskHistory/taskHistory.lock -------------------------------------------------------------------------------- /android/.gradle/buildOutputCleanup/outputFiles.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/.gradle/buildOutputCleanup/outputFiles.bin -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/.gradle/4.10.2/fileHashes/resourceHashesCache.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/.gradle/4.10.2/fileHashes/resourceHashesCache.bin -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/.gradle/buildOutputCleanup/buildOutputCleanup.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/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/developerpaaji/chat_app/HEAD/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/developerpaaji/chat_app/HEAD/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/developerpaaji/chat_app/HEAD/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/developerpaaji/chat_app/HEAD/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/developerpaaji/chat_app/HEAD/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/developerpaaji/chat_app/HEAD/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/developerpaaji/chat_app/HEAD/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/developerpaaji/chat_app/HEAD/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/developerpaaji/chat_app/HEAD/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/developerpaaji/chat_app/HEAD/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/developerpaaji/chat_app/HEAD/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/developerpaaji/chat_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /lib/controllers/json_controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | class JsonController { 4 | static dynamic convertToJson(json) { 5 | return jsonDecode(jsonEncode(json)); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/developerpaaji/chat_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /lib/const.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | final themeColor = Color(0xfff5a623); 4 | final primaryColor = Color(0xff203152); 5 | final greyColor = Color(0xffaeaeae); 6 | final greyColor2 = Color(0xffE8E8E8); -------------------------------------------------------------------------------- /android/local.properties: -------------------------------------------------------------------------------- 1 | sdk.dir=/Users/bhavneetsingh/Library/Android/sdk 2 | flutter.sdk=/Users/bhavneetsingh/Downloads/flutter 3 | flutter.buildMode=debug 4 | flutter.versionName=1.0.0 5 | flutter.versionCode=1 -------------------------------------------------------------------------------- /lib/models/smiley_data.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'basic.dart'; 3 | 4 | class SmileyData extends Basic{ 5 | final String smileyPath; 6 | 7 | SmileyData({this.smileyPath}) : super(DateTime.now().toString()); 8 | 9 | } -------------------------------------------------------------------------------- /lib/models/basic.dart: -------------------------------------------------------------------------------- 1 | 2 | abstract class Basic{ 3 | final String id; 4 | 5 | Basic(this.id); 6 | 7 | bool operator==(o)=> o is Basic&&o.id==id; 8 | @override 9 | int get hashCode => id.hashCode; 10 | } -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /lib/models/image_data.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:meta/meta.dart'; 3 | 4 | import 'basic.dart'; 5 | 6 | class ImageData extends Basic{ 7 | final String imagePath; 8 | 9 | ImageData({@required this.imagePath}) : super(DateTime.now().toString()); 10 | } -------------------------------------------------------------------------------- /ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Chat App 4 | 1331371627010098 5 | fb1331371627010098 6 | -------------------------------------------------------------------------------- /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-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /lib/models/camera_file.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:meta/meta.dart'; 4 | 5 | enum CameraFileType{ 6 | Image, 7 | Video 8 | } 9 | 10 | class CameraFile{ 11 | final CameraFileType type; 12 | final File file; 13 | 14 | CameraFile({@required this.type,@required this.file }); 15 | } -------------------------------------------------------------------------------- /.idea/runConfigurations/main_dart.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.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: 7a4c33425ddd78c54aba07d86f3f9a4a0051769b 8 | channel: beta 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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/Flutter/Generated.xcconfig: -------------------------------------------------------------------------------- 1 | // This is a generated file; do not edit or check into version control. 2 | FLUTTER_ROOT=E:\flutter 3 | FLUTTER_APPLICATION_PATH=E:\chat_app\chat_app 4 | FLUTTER_TARGET=lib/main.dart 5 | FLUTTER_BUILD_DIR=build 6 | SYMROOT=${SOURCE_ROOT}/../build\ios 7 | FLUTTER_FRAMEWORK_DIR=E:\flutter\bin\cache\artifacts\engine\ios 8 | FLUTTER_BUILD_NAME=1.0.0 9 | FLUTTER_BUILD_NUMBER=1 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /lib/models/text_data.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:flutter/material.dart'; 3 | 4 | import 'basic.dart'; 5 | 6 | class TextData extends Basic{ 7 | 8 | static const EdgeInsets TEXT_PADDING=const EdgeInsets.all(12.0); 9 | 10 | final String data; 11 | final BoxDecoration decoration; 12 | final TextStyle textStyle; 13 | 14 | TextData({@required this.data,@required this.decoration,@required this.textStyle}):super(DateTime.now().toString()); 15 | 16 | } -------------------------------------------------------------------------------- /ios/Runner/GeneratedPluginRegistrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | #ifndef GeneratedPluginRegistrant_h 6 | #define GeneratedPluginRegistrant_h 7 | 8 | #import 9 | 10 | NS_ASSUME_NONNULL_BEGIN 11 | 12 | @interface GeneratedPluginRegistrant : NSObject 13 | + (void)registerWithRegistry:(NSObject*)registry; 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | #endif /* GeneratedPluginRegistrant_h */ 18 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/blackhole/chat_app/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.blackhole.chat_app; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /lib/controllers/image_compresser.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'dart:typed_data'; 3 | import 'package:image/image.dart' as Im; 4 | import 'package:path_provider/path_provider.dart'; 5 | import 'dart:math' as Math; 6 | import 'package:flutter_native_image/flutter_native_image.dart'; 7 | 8 | class ImageCompressor{ 9 | 10 | static Future compressImage(File file) async { 11 | try{ 12 | File compressedFile = await FlutterNativeImage.compressImage(file.path, 13 | quality: 75, percentage: 50); 14 | print("Size ${compressedFile.statSync().size}"); 15 | return compressedFile.readAsBytesSync(); 16 | } 17 | catch(e){ 18 | print("Excpetion $e"); 19 | 20 | return null; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /lib/models/user.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'user.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | User _$UserFromJson(Map json) { 10 | return User(json['displayName'] as String, json['profilePicture'] as String, 11 | json['id'] as String, json['isActive'] as bool); 12 | } 13 | 14 | Map _$UserToJson(User instance) => { 15 | 'displayName': instance.displayName, 16 | 'profilePicture': instance.profilePicture, 17 | 'id': instance.id, 18 | 'isActive': instance.isActive 19 | }; 20 | -------------------------------------------------------------------------------- /lib/widgets/loading_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class LoadingWidget extends StatefulWidget { 4 | final Function onDisposed; 5 | final Function onInit; 6 | const LoadingWidget({Key key, this.onDisposed, this.onInit}) : super(key: key); 7 | @override 8 | State createState() => _LoadingWidgetState(); 9 | 10 | } 11 | 12 | class _LoadingWidgetState extends State { 13 | @override 14 | void initState() { 15 | super.initState(); 16 | widget.onInit(); 17 | } 18 | @override 19 | Widget build(BuildContext context) { 20 | print("Start Loading"); 21 | return Center(child: CircularProgressIndicator()); 22 | } 23 | @override 24 | void dispose() { 25 | widget.onDisposed(); 26 | super.dispose(); 27 | } 28 | } -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | maven { url 'https://plugins.gradle.org/m2/' } 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.2.1' 10 | classpath 'com.google.gms:google-services:4.2.0' 11 | classpath 'gradle.plugin.com.onesignal:onesignal-gradle-plugin:[0.12.1, 0.99.99]' 12 | } 13 | } 14 | 15 | allprojects { 16 | 17 | repositories { 18 | google() 19 | jcenter() 20 | } 21 | } 22 | 23 | rootProject.buildDir = '../build' 24 | subprojects { 25 | project.buildDir = "${rootProject.buildDir}/${project.name}" 26 | } 27 | subprojects { 28 | project.evaluationDependsOn(':app') 29 | } 30 | 31 | task clean(type: Delete) { 32 | delete rootProject.buildDir 33 | } 34 | -------------------------------------------------------------------------------- /lib/models/user.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/controllers/json_controller.dart'; 2 | import 'package:json_annotation/json_annotation.dart'; 3 | 4 | part 'user.g.dart'; 5 | 6 | @JsonSerializable() 7 | class User { 8 | String displayName; 9 | String profilePicture; 10 | String id; 11 | bool isActive; 12 | 13 | User(this.displayName, this.profilePicture, this.id, this.isActive); 14 | 15 | factory User.fromJson(Map json) => 16 | _$UserFromJson(JsonController.convertToJson(json)); 17 | 18 | factory User.fromNamed( 19 | {String displayName, 20 | String profilePicture, 21 | String id, 22 | bool isActive}) => 23 | User(displayName, profilePicture, id, isActive); 24 | 25 | Map toJson() => _$UserToJson(this); 26 | 27 | String get firstName => displayName.split(" ")[0]; 28 | } 29 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /chat_app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /lib/models/story.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/controllers/json_controller.dart'; 2 | import 'package:chat_app/models/user.dart'; 3 | import 'package:json_annotation/json_annotation.dart'; 4 | 5 | part 'story.g.dart'; 6 | 7 | @JsonSerializable() 8 | class Story { 9 | User user; 10 | String id; 11 | bool isSeen; 12 | List data; 13 | DateTime publishedAt; 14 | Story( 15 | this.id, 16 | this.user, 17 | this.isSeen, 18 | this.data, 19 | this.publishedAt, 20 | ); 21 | 22 | factory Story.fromJson(Map json) => 23 | _$StoryFromJson(JsonController.convertToJson(json)); 24 | Map toJson() => _$StoryToJson(this); 25 | } 26 | 27 | @JsonSerializable(explicitToJson: true) 28 | class DataType { 29 | static const String IMAGE = "image"; 30 | final String link; 31 | final String type; 32 | 33 | DataType(this.link, this.type); 34 | 35 | Map toJson() => _$DataTypeToJson(this); 36 | 37 | factory DataType.fromJson(json) => _$DataTypeFromJson(json); 38 | } 39 | -------------------------------------------------------------------------------- /lib/screens/discover_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/widgets/rounded_button.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class DiscoverScreen extends StatefulWidget { 5 | @override 6 | State createState() => _DiscoverScreenState(); 7 | } 8 | 9 | class _DiscoverScreenState extends State { 10 | @override 11 | Widget build(BuildContext context) { 12 | return Scaffold( 13 | body: Center( 14 | child: Column( 15 | mainAxisSize: MainAxisSize.min, 16 | children: [ 17 | Text( 18 | "Feature is not available yet", 19 | style: TextStyle(fontSize: 18.0, fontWeight: FontWeight.w600), 20 | ), 21 | Padding( 22 | padding: const EdgeInsets.only(top:8.0), 23 | child: Text( 24 | "Contribute to app github repository", 25 | style: TextStyle(fontSize: 16.0,), 26 | ), 27 | ), 28 | RoundedButton("Contribute",onTap: (){},nextIcon: true,) 29 | ], 30 | ), 31 | ), 32 | ); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Bhavneet Singh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /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:chat_app/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 | -------------------------------------------------------------------------------- /lib/widgets/search_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SearchWidget extends StatelessWidget { 4 | @override 5 | Widget build(BuildContext context) { 6 | return GestureDetector( 7 | onTap: (){ 8 | Navigator.of(context).pushNamed('/search'); 9 | }, 10 | child: Container( 11 | width: MediaQuery.of(context).size.width, 12 | decoration: BoxDecoration(color: Color(0xffeeeeee),borderRadius: BorderRadius.circular(25.0)), 13 | padding: EdgeInsets.all(4.0), 14 | margin: EdgeInsets.symmetric(vertical: 8.0,horizontal: 16.0), 15 | child: Row( 16 | mainAxisSize: MainAxisSize.min, 17 | children: [ 18 | Padding( 19 | padding: const EdgeInsets.all(8.0), 20 | child: Icon(Icons.search,size: 22.0,color: Colors.blueGrey), 21 | ), 22 | Padding( 23 | padding: const EdgeInsets.only(left:4.0), 24 | child: Text("Search",style: TextStyle(fontSize: 16.0,color: Colors.blueGrey,fontWeight: FontWeight.w500),), 25 | ) 26 | ], 27 | ), 28 | ), 29 | ); 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /lib/models/chat.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/controllers/json_controller.dart'; 2 | import 'package:chat_app/models/user.dart'; 3 | import 'package:json_annotation/json_annotation.dart'; 4 | 5 | part 'chat.g.dart'; 6 | 7 | @JsonSerializable() 8 | class Chat { 9 | static const String TEXT = "text"; 10 | String type; 11 | dynamic content; 12 | User from, to; 13 | bool isSeen; 14 | DateTime publishedAt; 15 | String groupId; 16 | Chat(this.content, this.from, this.to, this.isSeen, this.publishedAt, 17 | this.type, this.groupId); 18 | 19 | factory Chat.fromJson(Map json) => 20 | _$ChatFromJson(JsonController.convertToJson(json)); 21 | factory Chat.fromNamed( 22 | {String content, 23 | User from, 24 | User to, 25 | bool isSeen, 26 | DateTime publishedAt, 27 | String type = TEXT}) => 28 | Chat(content, from, to, isSeen, publishedAt, type, ""); 29 | 30 | /// `toJson` is the convention for a class to declare support for serialization 31 | /// to JSON. The implementation simply calls the private, generated 32 | /// helper method `_$ChatToJson`. 33 | Map toJson() => _$ChatToJson(this); 34 | } 35 | -------------------------------------------------------------------------------- /lib/models/chat.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'chat.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Chat _$ChatFromJson(Map json) { 10 | return Chat( 11 | json['content'], 12 | json['from'] == null 13 | ? null 14 | : User.fromJson(json['from'] as Map), 15 | json['to'] == null 16 | ? null 17 | : User.fromJson(json['to'] as Map), 18 | json['isSeen'] as bool, 19 | json['publishedAt'] == null 20 | ? null 21 | : DateTime.parse(json['publishedAt'] as String), 22 | json['type'] as String, 23 | json['groupId'] as String); 24 | } 25 | 26 | Map _$ChatToJson(Chat instance) => { 27 | 'type': instance.type, 28 | 'content': instance.content, 29 | 'from': instance.from, 30 | 'to': instance.to, 31 | 'isSeen': instance.isSeen, 32 | 'publishedAt': instance.publishedAt?.toIso8601String(), 33 | 'groupId': instance.groupId 34 | }; 35 | -------------------------------------------------------------------------------- /lib/widgets/recent_chat_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/controllers/user_controller.dart'; 2 | import 'package:chat_app/models/chat.dart'; 3 | import 'package:chat_app/models/user.dart'; 4 | import 'package:chat_app/screens/chat_screen.dart'; 5 | import 'package:chat_app/widgets/user_widget.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | class RecentChatWidget extends StatefulWidget { 9 | final Chat chat; 10 | 11 | const RecentChatWidget({Key key, this.chat}) : super(key: key); 12 | @override 13 | State createState() => _RecentChatWidgetState(); 14 | 15 | } 16 | 17 | class _RecentChatWidgetState extends State { 18 | @override 19 | Widget build(BuildContext context) { 20 | return ListTile( 21 | onTap: (){ 22 | Navigator.of(context).push(MaterialPageRoute(builder: (context)=>ChatScreen(friend: friend,))); 23 | }, 24 | leading: UserWidget(user: friend,), 25 | title: Text(friend.displayName,style: TextStyle(fontWeight: FontWeight.w600),), 26 | subtitle: Text(widget.chat.content,maxLines: 1,overflow: TextOverflow.ellipsis,), 27 | ); 28 | } 29 | 30 | User get friend=>widget.chat.from.id==UserController.user.id?widget.chat.to:widget.chat.from; 31 | } -------------------------------------------------------------------------------- /lib/widgets/user_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/models/user.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class UserWidget extends StatelessWidget { 5 | final User user; 6 | final double size; 7 | const UserWidget({Key key, @required this.user, this.size = 26.0}) 8 | : super(key: key); 9 | @override 10 | Widget build(BuildContext context) { 11 | return Stack( 12 | alignment: Alignment.bottomRight, 13 | children: [ 14 | CircleAvatar( 15 | backgroundImage: NetworkImage(user.profilePicture), 16 | radius: size, 17 | ), 18 | user.isActive 19 | ? Container( 20 | decoration: 21 | BoxDecoration(color: Colors.white, shape: BoxShape.circle), 22 | padding: EdgeInsets.all(2.5), 23 | child: Container( 24 | decoration: BoxDecoration( 25 | color: Colors.yellow, shape: BoxShape.circle), 26 | width: size / 2, 27 | height: size / 2, 28 | ), 29 | ) 30 | : Container( 31 | width: 0.0, 32 | height: 0.0, 33 | ) 34 | ], 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/widgets/ease_in_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class EaseInWidget extends StatefulWidget { 4 | final Widget child; 5 | final Function onTap; 6 | const EaseInWidget({Key key,@required this.child,@required this.onTap}) : super(key: key); 7 | @override 8 | State createState() => _EaseInWidgetState(); 9 | 10 | } 11 | 12 | class _EaseInWidgetState extends State with TickerProviderStateMixin{ 13 | AnimationController controller; 14 | AnimationeaseInAnimation; 15 | @override 16 | void initState() { 17 | super.initState(); 18 | controller=AnimationController(vsync: this,duration: Duration(milliseconds: 200,),value: 1.0); 19 | easeInAnimation=Tween(begin:1.0,end:0.95).animate( 20 | CurvedAnimation(parent: controller, curve: Curves.easeIn,), 21 | ); 22 | controller.reverse(); 23 | } 24 | @override 25 | Widget build(BuildContext context) { 26 | return GestureDetector( 27 | 28 | onTap: (){ 29 | controller.forward().then((val){ 30 | controller.reverse().then((val){ 31 | widget.onTap(); 32 | }); 33 | }); 34 | }, 35 | child: ScaleTransition(scale: easeInAnimation,child: widget.child,), 36 | ); 37 | } 38 | @override 39 | void dispose() { 40 | controller.dispose(); 41 | super.dispose(); 42 | } 43 | } -------------------------------------------------------------------------------- /android/chat_app_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /lib/models/story.g.dart: -------------------------------------------------------------------------------- 1 | // GENERATED CODE - DO NOT MODIFY BY HAND 2 | 3 | part of 'story.dart'; 4 | 5 | // ************************************************************************** 6 | // JsonSerializableGenerator 7 | // ************************************************************************** 8 | 9 | Story _$StoryFromJson(Map json) { 10 | return Story( 11 | json['id'] as String, 12 | json['user'] == null 13 | ? null 14 | : User.fromJson(json['user'] as Map), 15 | json['isSeen'] as bool, 16 | (json['data'] as List) 17 | ?.map((e) => 18 | e == null ? null : DataType.fromJson(e as Map)) 19 | ?.toList(), 20 | json['publishedAt'] == null 21 | ? null 22 | : DateTime.parse(json['publishedAt'] as String)); 23 | } 24 | 25 | Map _$StoryToJson(Story instance) => { 26 | 'user': instance.user, 27 | 'id': instance.id, 28 | 'isSeen': instance.isSeen, 29 | 'data': instance.data, 30 | 'publishedAt': instance.publishedAt?.toIso8601String() 31 | }; 32 | 33 | DataType _$DataTypeFromJson(Map json) { 34 | return DataType(json['link'] as String, json['type'] as String); 35 | } 36 | 37 | Map _$DataTypeToJson(DataType instance) => 38 | {'link': instance.link, 'type': instance.type}; 39 | -------------------------------------------------------------------------------- /lib/widgets/color_box.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'ease_in_widget.dart'; 4 | 5 | class ColorBox extends StatefulWidget { 6 | final double size; 7 | final Color color; 8 | final bool smallBorder; 9 | final Function onTap; 10 | const ColorBox({Key key, this.size=32.0,@required this.color, this.onTap, this.smallBorder=true}) : super(key: key); 11 | @override 12 | State createState() => _ColorBoxState(); 13 | } 14 | 15 | class _ColorBoxState extends State { 16 | final double smallBorder=3.0,bigBorder=4.5; 17 | double border=3.0; 18 | @override 19 | void initState() { 20 | super.initState(); 21 | border=widget.smallBorder?smallBorder:bigBorder; 22 | } 23 | @override 24 | Widget build(BuildContext context) { 25 | border=widget.smallBorder?smallBorder:bigBorder; 26 | return EaseInWidget( 27 | onTap: (){ 28 | widget.onTap(); 29 | setState(() { 30 | border=4.5; 31 | }); 32 | }, 33 | child: AnimatedContainer( 34 | duration: Duration(milliseconds: 200), 35 | margin: EdgeInsets.symmetric(horizontal: 8.0), 36 | decoration: BoxDecoration(border: Border.all(color: Colors.white,width: border),shape: BoxShape.circle), 37 | child: Container( 38 | width: widget.size, 39 | height: widget.size, 40 | decoration: BoxDecoration(color: widget.color,shape: BoxShape.circle), 41 | ), 42 | ), 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/widgets/profile_picture_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/controllers/user_controller.dart'; 2 | import 'package:firebase_auth/firebase_auth.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class ProfilePictureWidget extends StatefulWidget { 6 | final double size; 7 | 8 | const ProfilePictureWidget({Key key, this.size = 50.0}) : super(key: key); 9 | @override 10 | State createState() => _ProfilePictureWidgetState(); 11 | } 12 | 13 | class _ProfilePictureWidgetState extends State { 14 | String profilePic; 15 | @override 16 | void initState() { 17 | super.initState(); 18 | UserController.getUser().then((user) { 19 | setState(() { 20 | profilePic = user.profilePicture; 21 | }); 22 | }); 23 | } 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | double size = widget.size; 28 | return Container( 29 | height: size, 30 | width: size, 31 | decoration: 32 | BoxDecoration(color: Color(0xffeeeeee), shape: BoxShape.circle), 33 | child: profilePic != null 34 | ? ClipRRect( 35 | child: FadeInImage.assetNetwork( 36 | placeholder: "", 37 | image: profilePic, 38 | width: size, 39 | height: size, 40 | fit: BoxFit.cover, 41 | ), 42 | borderRadius: BorderRadius.circular(size), 43 | ) 44 | : null, 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "690424244397", 4 | "firebase_url": "https://chat-9b859.firebaseio.com", 5 | "project_id": "chat-9b859", 6 | "storage_bucket": "chat-9b859.appspot.com" 7 | }, 8 | "client": [ 9 | { 10 | "client_info": { 11 | "mobilesdk_app_id": "1:690424244397:android:5b8c02327ef8480d", 12 | "android_client_info": { 13 | "package_name": "com.blackhole.chat_app" 14 | } 15 | }, 16 | "oauth_client": [ 17 | { 18 | "client_id": "690424244397-rtaovr80tc5q6jh1k4do5pjquod66kkt.apps.googleusercontent.com", 19 | "client_type": 3 20 | } 21 | ], 22 | "api_key": [ 23 | { 24 | "current_key": "AIzaSyCId3bXit5z1iCf92L-KzY1mpHLc34LQC4" 25 | } 26 | ], 27 | "services": { 28 | "appinvite_service": { 29 | "other_platform_oauth_client": [ 30 | { 31 | "client_id": "690424244397-rtaovr80tc5q6jh1k4do5pjquod66kkt.apps.googleusercontent.com", 32 | "client_type": 3 33 | }, 34 | { 35 | "client_id": "690424244397-pon848ikmlu1mik26es51ut51bq5sac2.apps.googleusercontent.com", 36 | "client_type": 2, 37 | "ios_info": { 38 | "bundle_id": "com.blackhole.chatApp" 39 | } 40 | } 41 | ] 42 | } 43 | } 44 | } 45 | ], 46 | "configuration_version": "1" 47 | } -------------------------------------------------------------------------------- /lib/widgets/switch_icon.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class SwitchIcon extends StatefulWidget { 6 | final double size; 7 | final Function onTap; 8 | const SwitchIcon({Key key, this.size = 28.0,@required this.onTap}) : super(key: key); 9 | @override 10 | State createState() => _SwitchIconState(); 11 | } 12 | 13 | class _SwitchIconState extends State 14 | with TickerProviderStateMixin { 15 | AnimationController controller; 16 | @override 17 | void initState() { 18 | super.initState(); 19 | controller = 20 | AnimationController(vsync: this, duration: Duration(seconds: 1)); 21 | } 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return AnimatedBuilder( 26 | animation: controller, 27 | child: InkWell( 28 | onTap: () { 29 | widget.onTap(); 30 | if(controller.value==0.0){ 31 | controller.forward(); 32 | } 33 | else{ 34 | controller.reverse(); 35 | } 36 | }, 37 | child: Padding( 38 | padding: const EdgeInsets.all(4.0), 39 | child: Icon( 40 | Icons.switch_camera, 41 | color: Colors.white, 42 | size: 28.0, 43 | ), 44 | )), 45 | builder: (context, child) { 46 | return Transform.rotate( 47 | angle: pi * controller.value, 48 | child: child, 49 | ); 50 | }, 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/widgets/camera_button.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import 'ease_in_widget.dart'; 5 | 6 | class CameraButton extends StatefulWidget { 7 | final dynamic takePicture; 8 | const CameraButton( 9 | {Key key, 10 | @required this.takePicture,}) 11 | : super(key: key); 12 | @override 13 | State createState() => _CameraButtonState(); 14 | } 15 | 16 | class _CameraButtonState extends State 17 | with TickerProviderStateMixin { 18 | double bigSize = 72.0, smallSize = 64.0; 19 | double borderRadius = 72.0; 20 | bool camera = true; 21 | AnimationController _controller; 22 | @override 23 | void initState() { 24 | _controller = 25 | AnimationController(vsync: this, duration: Duration(seconds: 6)); 26 | super.initState(); 27 | 28 | } 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | double size=62.0; 33 | return EaseInWidget( 34 | onTap: (){ 35 | takePicture(); 36 | }, 37 | child: Container( 38 | margin: EdgeInsets.symmetric(vertical: 8.0), 39 | padding: EdgeInsets.all(4.0), 40 | decoration: BoxDecoration(color: Colors.transparent,shape: BoxShape.circle,border: Border.all(color: Colors.white.withOpacity(0.9),width: 4.0)), 41 | child: Container( 42 | width: size, 43 | height: size, 44 | decoration: BoxDecoration(color: Colors.white70,shape: BoxShape.circle), 45 | ), 46 | ), 47 | ); 48 | } 49 | 50 | 51 | void takePicture(){ 52 | widget.takePicture().then((val){ 53 | 54 | }); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /lib/widgets/rounded_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class RoundedButton extends StatelessWidget 4 | { 5 | final String data; 6 | final Function onTap; 7 | final bool nextIcon; 8 | const RoundedButton(this.data,{Key key,@required this.onTap, this.nextIcon=false,}) : super(key: key); 9 | @override 10 | Widget build(BuildContext context) { 11 | return InkWell( 12 | onTap: onTap, 13 | child: Material( 14 | color: Colors.transparent, 15 | child: Column( 16 | mainAxisSize: MainAxisSize.min, 17 | children: [ 18 | Container( 19 | alignment: Alignment.center, 20 | padding: const EdgeInsets.symmetric(vertical: 18.0), 21 | width: MediaQuery.of(context).size.width*5/6, 22 | decoration: BoxDecoration(color: Theme.of(context).primaryColor,borderRadius: BorderRadius.circular(32.0),shape: BoxShape.rectangle,boxShadow: [BoxShadow(color: Colors.pinkAccent.withOpacity(0.2),blurRadius: 25.0,spreadRadius: 0.2,offset: Offset(0.00, 1.0))]), 23 | child: Row( 24 | mainAxisSize: MainAxisSize.min, 25 | children: [ 26 | Text(data,style: TextStyle(color: Colors.white,fontWeight: FontWeight.bold,fontSize: 18.0),), 27 | Padding( 28 | padding: const EdgeInsets.only(left:8.0), 29 | child: nextIcon?Icon(Icons.arrow_forward,color: Colors.white,):Container(width: 0.0,height: 0.0,), 30 | ) 31 | ], 32 | ), 33 | ), 34 | ], 35 | ), 36 | ), 37 | ); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/screens/camera_screen.dart'; 2 | import 'package:chat_app/screens/chat_screen.dart'; 3 | import 'package:chat_app/screens/search_screen.dart'; 4 | import 'package:chat_app/screens/splash_screen.dart'; 5 | import 'package:chat_app/screens/main_screen.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | import 'models/user.dart'; 9 | 10 | void main() => runApp(MyApp()); 11 | 12 | class MyApp extends StatelessWidget { 13 | // This widget is the root of your application. 14 | @override 15 | Widget build(BuildContext context) { 16 | return MaterialApp( 17 | title: 'Flutter Demo', 18 | debugShowCheckedModeBanner: false, 19 | theme: ThemeData( 20 | scaffoldBackgroundColor: Colors.white, 21 | appBarTheme: AppBarTheme( 22 | brightness: Brightness.light, 23 | color: Colors.white, 24 | elevation: 0.0, 25 | iconTheme: IconThemeData(color: Colors.black), 26 | actionsIconTheme: IconThemeData(color: Colors.black)), 27 | fontFamily: "Ubuntu", 28 | primarySwatch: Colors.blue, 29 | primaryColor: Color(0xff0084FF)), 30 | home: SplashScreen(), 31 | routes: { 32 | '/main': (context) => MainScreen(), 33 | '/camera': (context) => CameraScreen(), 34 | '/search':(context)=>SearchScreen(), 35 | '/chat': (context) => ChatScreen( 36 | friend: User( 37 | "Bhavneet Singh", 38 | "https://avatars0.githubusercontent.com/u/31070108?s=460&v=4", 39 | "singhbhavneet", 40 | false), 41 | ) 42 | }, 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/screens/chats_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/controllers/chats_controller.dart'; 2 | import 'package:chat_app/controllers/stories_controller.dart'; 3 | import 'package:chat_app/models/chat.dart'; 4 | import 'package:chat_app/widgets/chat_widget.dart'; 5 | import 'package:chat_app/widgets/recent_chat_widget.dart'; 6 | import 'package:chat_app/widgets/search_widget.dart'; 7 | import 'package:chat_app/widgets/stories_widget.dart'; 8 | import 'package:flutter/material.dart'; 9 | 10 | class ChatsScreen extends StatefulWidget { 11 | @override 12 | State createState() => _ChatsScreenState(); 13 | } 14 | 15 | class _ChatsScreenState extends State { 16 | @override 17 | void initState() { 18 | super.initState(); 19 | } 20 | @override 21 | Widget build(BuildContext context) { 22 | return Column( 23 | mainAxisSize: MainAxisSize.min, 24 | children: [ 25 | Padding( 26 | padding: const EdgeInsets.symmetric(vertical: 8.0), 27 | child: StoriesWidget( 28 | rounded: true, 29 | ), 30 | ), 31 | StreamBuilder( 32 | stream: ChatsController.getChats(), 33 | builder: (context,snapshot){ 34 | if(!snapshot.hasData){ 35 | return Center(child: CircularProgressIndicator(),); 36 | } 37 | Listchats=snapshot.data; 38 | return ListView.builder( 39 | itemBuilder: (context, index) =>RecentChatWidget( 40 | chat: chats[index], 41 | ), 42 | itemCount:chats.length, 43 | shrinkWrap: true, 44 | physics: ClampingScrollPhysics(), 45 | ); 46 | }, 47 | ) 48 | ], 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/widgets/moving_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class MovingWidget extends StatefulWidget { 4 | final Widget child; 5 | final Offset deleteOffset; 6 | final Function() onRemove; 7 | const MovingWidget({Key key,@required this.child,@required this.deleteOffset,@required this.onRemove}) : super(key: key); 8 | @override 9 | State createState() => _MovingWidgetState(); 10 | 11 | } 12 | 13 | class _MovingWidgetState extends State { 14 | Offset globalPosition; 15 | double opacity=1.0; 16 | @override 17 | void initState() { 18 | super.initState(); 19 | 20 | } 21 | @override 22 | Widget build(BuildContext context) { 23 | if(globalPosition==null){ 24 | globalPosition=Offset(MediaQuery.of(context).size.width/2, MediaQuery.of(context).size.height/2); 25 | } 26 | return AnimatedPositioned( 27 | duration: Duration(milliseconds: 100), 28 | top: globalPosition.dy, 29 | left: globalPosition.dx, 30 | child: GestureDetector( 31 | onPanEnd: (details){ 32 | if(isRemovable){ 33 | widget.onRemove(); 34 | } 35 | }, 36 | onPanUpdate: (DragUpdateDetails details){ 37 | if(isRemovable){ 38 | print("Delete"); 39 | setState(() { 40 | opacity=0.4; 41 | }); 42 | } 43 | else if(opacity!=1.0){ 44 | setState(() { 45 | opacity=1.0; 46 | }); 47 | } 48 | setState(() { 49 | print(details.globalPosition); 50 | globalPosition=details.globalPosition; 51 | }); 52 | }, 53 | child: Opacity(opacity: opacity,child: widget.child), 54 | ), 55 | ); 56 | } 57 | bool get isRemovable{ 58 | return (globalPosition-widget.deleteOffset).distance<20.0; 59 | } 60 | } -------------------------------------------------------------------------------- /lib/widgets/thumbnail_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:chat_app/screens/story_create_screen.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:image_picker/image_picker.dart'; 5 | 6 | class ThumbnailWidget extends StatefulWidget { 7 | final double size; 8 | final String imagePath; 9 | 10 | const ThumbnailWidget( 11 | {Key key, 12 | @required this.imagePath, 13 | this.size = 32.0}) 14 | : super(key: key); 15 | @override 16 | State createState() => _ThumbnailWidgetState(); 17 | } 18 | 19 | class _ThumbnailWidgetState extends State { 20 | String thumb; 21 | @override 22 | void initState() { 23 | super.initState(); 24 | 25 | } 26 | Future getImage() async { 27 | var image = await ImagePicker.pickImage(source: ImageSource.gallery); 28 | if(image==null){ 29 | return; 30 | } 31 | Navigator.of(context).push(MaterialPageRoute(builder: (context)=>StoryCreateScreen(imagePath: image.path,))); 32 | } 33 | @override 34 | Widget build(BuildContext context) { 35 | thumb=widget.imagePath; 36 | return GestureDetector( 37 | onTap: getImage, 38 | child: Container( 39 | width: widget.size, 40 | height: widget.size, 41 | decoration: BoxDecoration( 42 | color: Colors.black, 43 | border: Border.all(color: Colors.white, width: 1.5), 44 | borderRadius: BorderRadius.circular(8.0)), 45 | child: thumb != null 46 | ? ClipRRect( 47 | borderRadius: BorderRadius.circular(8.0), 48 | child: Image.file( 49 | File(thumb), 50 | fit: BoxFit.cover, 51 | width: 75.0, 52 | height: 75.0, 53 | ), 54 | ) 55 | : null), 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chat_app 2 | A flutter chat app built with Firestore and Firebase Cloud. It is clone of messenger.User can create stories,chat and search in real time. 3 | 4 | I am Bhavneet Singh a freelancer who is always excited to work on new projects. If you have interesting work for me then contact me at singhbhavneetdeveloper@gmail.com. 5 | 6 | ## Screenshots 7 | 8 | ![Webp.net-resizeimage.jpg](https://www.dropbox.com/s/55ij1icjcrvcgx1/Webp.net-resizeimage.jpg?dl=0&raw=1) ![Webp.net-resizeimage (1).jpg](https://www.dropbox.com/s/kru3c1zpizo3fqv/Webp.net-resizeimage%20%281%29.jpg?dl=0&raw=1) 9 | 10 | ![Screenshot_20190707-005942.jpg](https://www.dropbox.com/s/lurztjoftz8gzcd/Screenshot_20190707-005942.jpg?dl=0&raw=1) ![Screenshot_20190707-005956.jpg](https://www.dropbox.com/s/7abs3s6oh7osgwi/Screenshot_20190707-005956.jpg?dl=0&raw=1) 11 | 12 | ![Screenshot (Jul 7, 2019 01_21_41).jpg](https://www.dropbox.com/s/4owt7bsezgmv30a/Screenshot%20%28Jul%207%2C%202019%2001_21_41%29.jpg?dl=0&raw=1) ![Screenshot_20190707-010427.jpg](https://www.dropbox.com/s/ri95au4bqiqpkr8/Screenshot_20190707-010427.jpg?dl=0&raw=1) 13 | 14 | ![Screenshot_20190707-005933.jpg](https://www.dropbox.com/s/rmrchlv770sxml2/Screenshot_20190707-005933.jpg?dl=0&raw=1) ![Screenshot_20190707-010549.jpg](https://www.dropbox.com/s/6b2whnbm0rmugj3/Screenshot_20190707-010549.jpg?dl=0&raw=1) 15 | 16 | ## Credits 17 | 18 | 1) Firestore and Flutter tutorial - https://www.youtube.com/watch?v=DqJ_KjFzL9I 19 | 2) Creating Image from widget - https://medium.com/flutter-community/export-your-widget-to-image-with-flutter-dc7ecfa6bafb 20 | 3) Image Compressing - https://github.com/btastic/flutter_native_image 21 | 4) Camera - https://www.youtube.com/watch?v=ZkpHzbOm-s0&t=2s 22 | 5) Complicated Animations - https://www.youtube.com/channel/UCtWyVkPpb8An90SNDTNF0Pg 23 | 6) Height and Width of Child - https://medium.com/@diegoveloper/flutter-widget-size-and-position-b0a9ffed9407 24 | 25 | -------------------------------------------------------------------------------- /lib/widgets/story_image_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:chat_app/widgets/loading_widget.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:cached_network_image/cached_network_image.dart'; 6 | import 'package:flutter_cache_manager/flutter_cache_manager.dart'; 7 | 8 | class StoryImageWidget extends StatelessWidget { 9 | final String image; 10 | final Function onLoaded; 11 | const StoryImageWidget({Key key,@required this.image,@required this.onLoaded}) : super(key: key); 12 | @override 13 | Widget build(BuildContext context) { 14 | DefaultCacheManager cacheManager=DefaultCacheManager(); 15 | cacheManager.emptyCache(); 16 | return Container( 17 | color: Colors.redAccent, 18 | width: MediaQuery.of(context).size.width, 19 | height: MediaQuery.of(context).size.height, 20 | child: Stack( 21 | fit: StackFit.expand, 22 | alignment: Alignment.center, 23 | children: [ 24 | Image.network(image,fit: BoxFit.fill,), 25 | BackdropFilter( 26 | filter: ImageFilter.blur( 27 | sigmaX: 75.0, 28 | sigmaY: 75.0, 29 | ), 30 | child: new Container( 31 | decoration: 32 | new BoxDecoration(color: Colors.white.withOpacity(0.5))), 33 | ), 34 | CachedNetworkImage( 35 | imageUrl: image, 36 | fit: BoxFit.cover, 37 | fadeInDuration: Duration(milliseconds: 250), 38 | fadeInCurve: Curves.easeIn, 39 | cacheManager: cacheManager, 40 | placeholder: (context,path)=>LoadingWidget( 41 | onDisposed: (){ 42 | print("Loaded $image"); 43 | onLoaded(true); 44 | }, 45 | onInit:(){ 46 | onLoaded(false); 47 | } 48 | ), 49 | ) 50 | ], 51 | ), 52 | ); 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /.idea/libraries/Dart_SDK.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /lib/widgets/storyline_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class StorylineWidget extends StatefulWidget { 4 | final AnimationController controller; 5 | final bool completed; 6 | const StorylineWidget({Key key,@required this.controller,this.completed=false}) : super(key: key); 7 | @override 8 | State createState() => _StorylineWidgetState(); 9 | 10 | } 11 | class _StorylineWidgetState extends State { 12 | @override 13 | Widget build(BuildContext context) { 14 | return Padding( 15 | padding: const EdgeInsets.symmetric(horizontal: 2.0), 16 | child: LayoutBuilder( 17 | builder: (context,constraints){ 18 | double width=constraints.maxWidth; 19 | double height=2.0; 20 | return widget.controller!=null?AnimatedBuilder( 21 | animation: widget.controller, 22 | builder: (context,child)=>Stack( 23 | alignment: Alignment.centerLeft, 24 | children: [ 25 | Container( 26 | height: height, 27 | width: width, 28 | decoration: BoxDecoration( 29 | color: Colors.white54, 30 | borderRadius: BorderRadius.circular(3.0), 31 | ), 32 | ), 33 | Container( 34 | height: height, 35 | width: widget.controller!=null?width*widget.controller.value:width, 36 | decoration: BoxDecoration( 37 | color: Colors.white, 38 | borderRadius: BorderRadius.circular(3.0), 39 | ), 40 | ) 41 | ], 42 | ), 43 | child: Container(width: 0.0,height: 0.0,), 44 | ):Container( 45 | height: height, 46 | width: width, 47 | decoration: BoxDecoration( 48 | color: widget.completed?Colors.white:Colors.white24, 49 | borderRadius: BorderRadius.circular(3.0), 50 | ), 51 | ); 52 | }, 53 | ), 54 | ); 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSMicrophoneUsageDescription 6 | This application needs to access your microphone 7 | NSSpeechRecognitionUsageDescription 8 | This application needs the speech recognition permission 9 | NSPhotoLibraryUsageDescription 10 | Can I read your library? 11 | NSCameraUsageDescription 12 | Can I use the camera please? 13 | NSMicrophoneUsageDescription 14 | Can I use the mic please? 15 | CFBundleDevelopmentRegion 16 | en 17 | CFBundleExecutable 18 | $(EXECUTABLE_NAME) 19 | CFBundleIdentifier 20 | $(PRODUCT_BUNDLE_IDENTIFIER) 21 | CFBundleInfoDictionaryVersion 22 | 6.0 23 | CFBundleName 24 | chat_app 25 | CFBundlePackageType 26 | APPL 27 | CFBundleShortVersionString 28 | $(FLUTTER_BUILD_NAME) 29 | CFBundleSignature 30 | ???? 31 | CFBundleVersion 32 | $(FLUTTER_BUILD_NUMBER) 33 | LSRequiresIPhoneOS 34 | 35 | UILaunchStoryboardName 36 | LaunchScreen 37 | UIMainStoryboardFile 38 | Main 39 | UISupportedInterfaceOrientations 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationLandscapeLeft 43 | UIInterfaceOrientationLandscapeRight 44 | 45 | UISupportedInterfaceOrientations~ipad 46 | 47 | UIInterfaceOrientationPortrait 48 | UIInterfaceOrientationPortraitUpsideDown 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UIViewControllerBasedStatusBarAppearance 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /lib/widgets/add_story_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'ease_in_widget.dart'; 4 | 5 | class AddStoryButton extends StatelessWidget { 6 | final double size; 7 | final bool rounded; 8 | const AddStoryButton({Key key, this.size=32.0, this.rounded=true}) : super(key: key); 9 | @override 10 | Widget build(BuildContext context) { 11 | return EaseInWidget(onTap: (){ 12 | Navigator.of(context).pushNamed('/camera'); 13 | },child: rounded?_buildRounded():_buildRectangleButton()); 14 | } 15 | Widget _buildRectangleButton(){ 16 | double width=100.0; 17 | return Container( 18 | decoration: BoxDecoration(color: Color(0xffeeeeee),borderRadius: BorderRadius.circular(4.0)), 19 | margin: EdgeInsets.only(left: 12.0), 20 | child: Stack( 21 | children: [ 22 | Container( 23 | width: width, 24 | padding: const EdgeInsets.all(8.0), 25 | child: Column( 26 | crossAxisAlignment: CrossAxisAlignment.start, 27 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 28 | children: [ 29 | Icon(Icons.add_circle,color: Colors.black,), 30 | Expanded( 31 | child: Container( 32 | width: width, 33 | 34 | alignment: Alignment.bottomLeft, 35 | child: Text('Add to your story',maxLines: 2,style: TextStyle(color: Colors.black,fontSize: 12.0),), 36 | ), 37 | ) 38 | ], 39 | ), 40 | ) 41 | ], 42 | ), 43 | ); 44 | } 45 | Widget _buildRounded(){ 46 | return Column( 47 | mainAxisSize: MainAxisSize.min, 48 | children: [ 49 | Container( 50 | width: size, 51 | height: size, 52 | alignment: Alignment.center, 53 | decoration: BoxDecoration(color: Color(0xffeeeeee),shape: BoxShape.circle), 54 | child: Icon(Icons.add,color: Colors.black,size:size*2.5/4,), 55 | ), 56 | Padding( 57 | padding: const EdgeInsets.only(top:6.0), 58 | child: Text("Add Story",style: TextStyle(color: Colors.black38,fontSize: 12.0),), 59 | ), 60 | ], 61 | ); 62 | } 63 | } -------------------------------------------------------------------------------- /lib/screens/stories_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/models/story.dart'; 2 | import 'package:chat_app/screens/story_screen.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | 6 | class StoriesScreen extends StatefulWidget { 7 | final List stories; 8 | final int initalPage; 9 | const StoriesScreen({Key key, @required this.stories, this.initalPage = 0}) 10 | : super(key: key); 11 | @override 12 | State createState() => _StoriesScreenState(); 13 | } 14 | 15 | class _StoriesScreenState extends State { 16 | PageController controller; 17 | @override 18 | void initState() { 19 | super.initState(); 20 | controller = PageController( 21 | initialPage: widget.initalPage, 22 | ); 23 | SystemChrome.setEnabledSystemUIOverlays([]); 24 | } 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return WillPopScope( 29 | onWillPop: () async { 30 | Navigator.of(context).popUntil(ModalRoute.withName('/main')); 31 | return widget.stories == null; 32 | }, 33 | child: Scaffold( 34 | backgroundColor: Colors.black, 35 | body: widget.stories == null 36 | ? Container( 37 | child: Text("Stories does not exist"), 38 | ) 39 | : PageView.builder( 40 | controller: controller, 41 | itemBuilder: (context, index) => StoryScreen( 42 | onCompleted: () { 43 | if (index == widget.stories.length - 1) { 44 | Navigator.of(context) 45 | .popUntil(ModalRoute.withName('/main')); 46 | } 47 | print("Next Page"); 48 | controller.animateToPage(index + 1, 49 | duration: Duration(seconds: 1), 50 | curve: Curves.easeInCubic); 51 | }, 52 | story: widget.stories[index], 53 | ), 54 | itemCount: widget.stories?.length ?? 0, 55 | ), 56 | ), 57 | ); 58 | } 59 | 60 | @override 61 | void dispose() { 62 | SystemChrome.setEnabledSystemUIOverlays( 63 | [SystemUiOverlay.top, SystemUiOverlay.bottom]); 64 | super.dispose(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /.idea/libraries/Flutter_Plugins.xml: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /lib/widgets/chat_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/models/chat.dart'; 2 | import 'package:chat_app/screens/chat_screen.dart' as chatFile; 3 | import 'package:chat_app/widgets/user_widget.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class ChatWidget extends StatelessWidget { 7 | final Chat chat; 8 | final bool isReceived; 9 | final bool showUser; 10 | const ChatWidget({Key key,@required this.chat,this.isReceived=false, this.showUser=true}) : super(key: key); 11 | @override 12 | Widget build(BuildContext context) { 13 | return Column( 14 | crossAxisAlignment: CrossAxisAlignment.end, 15 | mainAxisSize: MainAxisSize.min, 16 | children: [ 17 | Padding( 18 | padding: EdgeInsets.symmetric(vertical: 6.0,horizontal: 12.0), 19 | child: isReceived?_buildReceivedMessage(context):_buildSentMessage(context), 20 | ), 21 | ], 22 | ); 23 | } 24 | Widget _buildSentMessage(BuildContext context){ 25 | Color sendColor=Color(0xff0084FF); 26 | return Container( 27 | alignment: Alignment.centerRight, 28 | child: Container( 29 | constraints: BoxConstraints(maxWidth:MediaQuery.of(context).size.width*3/4 ), 30 | padding: EdgeInsets.all(12.0), 31 | decoration: BoxDecoration(color: sendColor,borderRadius: BorderRadius.circular(25.0),), 32 | child: Text(chat.content,style: TextStyle(fontSize: 18.0,color: Colors.white),), 33 | ), 34 | ); 35 | } 36 | 37 | Widget _buildReceivedMessage(BuildContext context){ 38 | Color receivedColor=Color(0x99eeeeee); 39 | return Container( 40 | alignment: Alignment.centerLeft, 41 | child: Row( 42 | children: [ 43 | isReceived&&showUser?Padding( 44 | padding: const EdgeInsets.only(right: 8.0), 45 | child: CircleAvatar(backgroundImage: NetworkImage(chat.from.profilePicture),radius: 12.0,), 46 | ):Container(width: 32.0,height: 24.0,), 47 | Container( 48 | constraints: BoxConstraints(maxWidth:MediaQuery.of(context).size.width*3/4 ), 49 | padding: EdgeInsets.all(12.0), 50 | decoration: BoxDecoration(color: receivedColor,borderRadius: BorderRadius.circular(25.0),), 51 | child: Text(chat.content,style: TextStyle(fontSize: 18.0,color: Colors.black),), 52 | ), 53 | ], 54 | ), 55 | ); 56 | } 57 | } -------------------------------------------------------------------------------- /lib/widgets/wave_user_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:chat_app/models/user.dart'; 4 | import 'package:chat_app/widgets/user_widget.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | import 'ease_in_widget.dart'; 8 | 9 | class WaveUserWidget extends StatefulWidget { 10 | final User user; 11 | 12 | const WaveUserWidget({Key key, @required this.user}) : super(key: key); 13 | @override 14 | State createState() => _WaveUserWidgetState(); 15 | } 16 | 17 | class _WaveUserWidgetState extends State with TickerProviderStateMixin{ 18 | bool isWaved=false; 19 | AnimationController controller; 20 | Animation bounceAnimation; 21 | @override 22 | void initState() { 23 | super.initState(); 24 | controller=AnimationController(vsync: this,duration: Duration(milliseconds: 400)); 25 | bounceAnimation=CurvedAnimation(parent: controller, curve: Curves.bounceIn); 26 | } 27 | @override 28 | Widget build(BuildContext context) { 29 | Color color=isWaved?Color(0xff3b5998):Colors.black; 30 | return ListTile( 31 | leading: UserWidget( 32 | user: widget.user, 33 | size: 18.0, 34 | ), 35 | title: Text( 36 | widget.user.displayName, 37 | style: TextStyle(fontSize: 14.0), 38 | ), 39 | trailing: EaseInWidget( 40 | onTap: (){ 41 | isWaved=true; 42 | controller.animateTo(0.5).then((val){ 43 | controller.reverse(); 44 | setState(() { 45 | 46 | }); 47 | }); 48 | }, 49 | child: Padding( 50 | padding: const EdgeInsets.all(8.0), 51 | child: Container( 52 | width: 28.0, 53 | height: 28.0, 54 | child: Center( 55 | child: AnimatedBuilder( 56 | animation: bounceAnimation, 57 | builder: (context,child){ 58 | return Transform.rotate(angle: pi*controller.value,child: child,); 59 | }, 60 | child: Icon( 61 | Icons.thumb_up, 62 | size: 16.0, 63 | color: color, 64 | ), 65 | ), 66 | ), 67 | decoration: BoxDecoration(color: color.withOpacity(0.2),shape: BoxShape.circle), 68 | ), 69 | ), 70 | ), 71 | ); 72 | } 73 | @override 74 | void dispose() { 75 | controller.dispose(); 76 | super.dispose(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/widgets/chat_input_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ChatInputWidget extends StatefulWidget { 4 | final Function(String) onSubmitted; 5 | 6 | const ChatInputWidget({Key key,@required this.onSubmitted}) : super(key: key); 7 | @override 8 | State createState() => _ChatInputWidgetState(); 9 | 10 | } 11 | 12 | class _ChatInputWidgetState extends State { 13 | TextEditingController editingController=TextEditingController(); 14 | FocusNode focusNode=FocusNode(); 15 | @override 16 | void initState() { 17 | super.initState(); 18 | editingController.addListener((){ 19 | if(mounted){ 20 | setState(() { 21 | 22 | }); 23 | } 24 | }); 25 | } 26 | @override 27 | Widget build(BuildContext context) { 28 | return Row( 29 | children: [ 30 | Expanded( 31 | child: Container( 32 | padding: EdgeInsets.symmetric(vertical: 2.0,horizontal: 8.0), 33 | decoration: BoxDecoration(color: Color(0xff3b5998).withOpacity(0.06),borderRadius: BorderRadius.circular(32.0),), 34 | margin: EdgeInsets.all(12.0), 35 | child: Row( 36 | children: [ 37 | Row( 38 | mainAxisSize: MainAxisSize.min, 39 | children: [ 40 | IconButton(icon: Icon(Icons.add_circle,color: Color(0xff3b5998),), onPressed: (){},), 41 | ], 42 | ), 43 | Expanded(child: TextField( 44 | decoration: InputDecoration( 45 | border:InputBorder.none, 46 | hintText: "Message...", 47 | ), 48 | focusNode: focusNode, 49 | textInputAction: TextInputAction.send, 50 | controller: editingController, 51 | onSubmitted: sendMessage, 52 | )), 53 | IconButton(icon: Icon(isTexting?Icons.send:Icons.keyboard_voice), onPressed: (){ 54 | sendMessage(editingController.text); 55 | },color: Color(0xff3b5998),), 56 | ], 57 | ), 58 | ), 59 | ), 60 | 61 | ], 62 | ); 63 | } 64 | bool get isTexting=>editingController.text.length!=0; 65 | 66 | void sendMessage(String message){ 67 | if(!isTexting){ 68 | return; 69 | } 70 | widget.onSubmitted(message); 71 | editingController.text=''; 72 | focusNode.unfocus(); 73 | } 74 | } -------------------------------------------------------------------------------- /lib/screens/people_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/controllers/chats_controller.dart'; 2 | import 'package:chat_app/controllers/user_controller.dart'; 3 | import 'package:chat_app/models/user.dart'; 4 | import 'package:chat_app/widgets/stories_widget.dart'; 5 | import 'package:chat_app/widgets/wave_user_widget.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | class PeopleScreen extends StatefulWidget { 9 | @override 10 | State createState() => _PeopleScreenState(); 11 | 12 | } 13 | 14 | class _PeopleScreenState extends State { 15 | @override 16 | Widget build(BuildContext context) { 17 | return Column( 18 | mainAxisSize: MainAxisSize.min, 19 | crossAxisAlignment: CrossAxisAlignment.start, 20 | children: [ 21 | Padding( 22 | padding: const EdgeInsets.symmetric(vertical: 8.0), 23 | child: StoriesWidget( 24 | rounded: false, 25 | ), 26 | ), 27 | Padding( 28 | padding: const EdgeInsets.symmetric(vertical: 8.0,horizontal: 20.0), 29 | child: Text("Active",style: TextStyle(fontSize: 16.0,color: Colors.black54),), 30 | ), 31 | FutureBuilder( 32 | future: UserController.getActiveUsers(), 33 | builder: (context,snapshot){ 34 | if(!snapshot.hasData){ 35 | return Center( 36 | child: CircularProgressIndicator(), 37 | ); 38 | } 39 | List data=snapshot.data; 40 | if(data.length==0){ 41 | return Center( 42 | child: Column( 43 | mainAxisSize: MainAxisSize.min, 44 | children: [ 45 | Padding( 46 | padding: const EdgeInsets.all(8.0), 47 | child: Text("Want to connect with developers",style: TextStyle(fontSize: 16.0),textAlign: TextAlign.center,), 48 | ), 49 | FlatButton(onPressed: (){}, child: Text("Invite Here")) 50 | ], 51 | ), 52 | ); 53 | } 54 | return ListView.builder( 55 | padding: EdgeInsets.symmetric(horizontal: 8.0), 56 | itemBuilder: (context, index) => WaveUserWidget( 57 | user:data[index], 58 | ), 59 | itemCount: data.length, 60 | shrinkWrap: true, 61 | physics: ClampingScrollPhysics(), 62 | ); 63 | }, 64 | ) 65 | ], 66 | ); 67 | } 68 | 69 | } -------------------------------------------------------------------------------- /lib/widgets/login_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class LoginButton extends StatefulWidget { 4 | final String text; 5 | final Function onPressed; 6 | final Color color; 7 | final Widget leading; 8 | const LoginButton(this.text, 9 | {Key key, 10 | @required this.onPressed, 11 | this.color = Colors.white, 12 | @required this.leading}) 13 | : super(key: key); 14 | 15 | @override 16 | State createState() => _LoginButtonState(); 17 | } 18 | 19 | class _LoginButtonState extends State { 20 | @override 21 | Widget build(BuildContext context) { 22 | return Column( 23 | mainAxisSize: MainAxisSize.min, 24 | children: [ 25 | Container( 26 | decoration: BoxDecoration( 27 | color: widget.color, 28 | borderRadius: BorderRadius.circular(16.0), 29 | boxShadow: [ 30 | BoxShadow( 31 | color: Colors.pinkAccent.withOpacity(0.1), 32 | blurRadius: 4.0, 33 | spreadRadius: 0.1, 34 | offset: Offset(0.5, 0.8)) 35 | ]), 36 | margin: EdgeInsets.all(0.0), 37 | child: Material( 38 | color: Colors.transparent, 39 | child: InkWell( 40 | onTap: widget.onPressed, 41 | child: Container( 42 | alignment: Alignment.center, 43 | padding: EdgeInsets.symmetric(vertical: 18.0, horizontal: 16.0), 44 | child: Row( 45 | children: [ 46 | widget.leading, 47 | Expanded( 48 | child: Padding( 49 | padding: const EdgeInsets.only(left: 8.0), 50 | child: Text( 51 | widget.text, 52 | textAlign: TextAlign.center, 53 | style: TextStyle( 54 | fontWeight: FontWeight.w500, 55 | fontSize: 17.0, 56 | color: widget.color == Colors.white 57 | ? Colors.black 58 | : Colors.white), 59 | ), 60 | ), 61 | ), 62 | Container( 63 | width: 24.0, 64 | height: 24.0, 65 | ) 66 | ], 67 | ), 68 | ), 69 | ), 70 | ), 71 | ), 72 | ], 73 | ); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /lib/screens/search_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/controllers/user_controller.dart'; 2 | import 'package:chat_app/models/user.dart'; 3 | import 'package:chat_app/screens/chat_screen.dart'; 4 | import 'package:chat_app/widgets/user_widget.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | class SearchScreen extends StatefulWidget { 8 | @override 9 | State createState() => _SearchScreenState(); 10 | } 11 | 12 | class _SearchScreenState extends State { 13 | TextEditingController controller = TextEditingController(); 14 | 15 | @override 16 | void initState() { 17 | super.initState(); 18 | controller.addListener(() { 19 | setState(() {}); 20 | }); 21 | } 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return Scaffold( 26 | appBar: AppBar( 27 | elevation: 1.0, 28 | iconTheme: IconThemeData(color: Theme.of(context).primaryColor), 29 | actionsIconTheme: IconThemeData(color: Theme.of(context).primaryColor), 30 | title: TextField( 31 | controller: controller, 32 | autofocus: true, 33 | decoration: InputDecoration( 34 | border: InputBorder.none, hintText: "Search here"), 35 | style: TextStyle(fontSize: 18.0), 36 | onSubmitted: (query) { 37 | UserController.searchUsers(query); 38 | }, 39 | ), 40 | actions: [ 41 | IconButton( 42 | icon: Icon(isTyping ? Icons.clear : Icons.mic), 43 | onPressed: () { 44 | if (isTyping) { 45 | controller.text = ''; 46 | } 47 | }) 48 | ], 49 | ), 50 | body: FutureBuilder( 51 | future: UserController.searchUsers(controller.text), 52 | builder: (context, snapshot) { 53 | if (!snapshot.hasData) { 54 | return Center(child: CircularProgressIndicator()); 55 | } 56 | List users = snapshot.data; 57 | print("Resi;t ${users.length}"); 58 | return ListView.builder( 59 | itemBuilder: (context, index) => ListTile( 60 | onTap: (){ 61 | Navigator.of(context).push(MaterialPageRoute(builder: (context)=>ChatScreen(friend: users[index],))); 62 | }, 63 | leading: UserWidget( 64 | size: 18.0, 65 | user: users[index], 66 | ), 67 | contentPadding: EdgeInsets.symmetric(vertical: 8.0,horizontal: 16.0), 68 | title: Text(users[index].displayName,style: TextStyle(fontWeight: FontWeight.w700),), 69 | ), 70 | itemCount: users.length, 71 | shrinkWrap: true, 72 | ); 73 | }), 74 | ); 75 | } 76 | 77 | bool get isTyping => controller.text.length != 0; 78 | } 79 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 11 | 12 | 13 | 17 | 24 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 43 | 44 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /lib/widgets/smiley_add_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/models/smiley_data.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import 'ease_in_widget.dart'; 5 | 6 | class SmileyAddWidget extends StatefulWidget { 7 | final Function(SmileyData) onSelected; 8 | 9 | const SmileyAddWidget({Key key, @required this.onSelected}) : super(key: key); 10 | @override 11 | State createState() => _SmileyAddWidgetState(); 12 | } 13 | 14 | class _SmileyAddWidgetState extends State { 15 | final GlobalKey key = GlobalKey(); 16 | AnimationController controller; 17 | List smilies = [ 18 | "sticker1", 19 | "sticker2", 20 | "sticker3", 21 | "sticker4" 22 | ]; 23 | @override 24 | void initState() { 25 | super.initState(); 26 | } 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return WillPopScope( 31 | onWillPop: () { 32 | widget.onSelected(null); 33 | }, 34 | child: Scaffold( 35 | key: key, 36 | backgroundColor: Colors.transparent, 37 | appBar: AppBar( 38 | backgroundColor: Colors.transparent, 39 | elevation: 0.0, 40 | automaticallyImplyLeading: false, 41 | actions: [ 42 | FlatButton( 43 | onPressed: () { 44 | widget.onSelected(null); 45 | }, 46 | child: Text( 47 | "Done", 48 | style: TextStyle( 49 | fontSize: 16.0, 50 | fontWeight: FontWeight.normal, 51 | color: Colors.white), 52 | )) 53 | ], 54 | ), 55 | body: Container( 56 | decoration: BoxDecoration( 57 | color: Colors.black54, 58 | borderRadius: BorderRadius.only( 59 | topRight: Radius.circular(16.0), 60 | topLeft: Radius.circular(16.0))), 61 | width: MediaQuery.of(context).size.width, 62 | height: MediaQuery.of(context).size.height, 63 | child: GridView.builder( 64 | gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( 65 | crossAxisCount: 2), 66 | itemBuilder: (context, index) { 67 | return EaseInWidget( 68 | onTap: (){ 69 | widget.onSelected(SmileyData(smileyPath: "images/${smilies[index]}.png")); 70 | }, 71 | child: Padding( 72 | padding: const EdgeInsets.all(8.0), 73 | child: Image.asset( 74 | "images/${smilies[index]}.png", 75 | ), 76 | ), 77 | ); 78 | }, 79 | itemCount: smilies.length, 80 | shrinkWrap: true, 81 | ), 82 | ), 83 | ), 84 | ); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /.flutter-plugins: -------------------------------------------------------------------------------- 1 | # This is a generated file; do not edit or check into version control. 2 | camera=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/camera-0.5.7+4/ 3 | cloud_firestore=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/cloud_firestore-0.12.11/ 4 | emoji_picker=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/emoji_picker-0.0.3/ 5 | firebase_auth=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_auth-0.16.0/ 6 | firebase_auth_web=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_auth_web-0.1.2/ 7 | firebase_core=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core-0.4.4+3/ 8 | firebase_core_web=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_core_web-0.1.1+2/ 9 | firebase_database=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_database-3.1.5/ 10 | firebase_storage=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/firebase_storage-3.1.5/ 11 | flutter_facebook_login=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_facebook_login-1.2.0/ 12 | flutter_native_image=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/git/flutter_native_image-884d50a6cd4b88d78a849dc7ccd70eaf23fd0cab/ 13 | flutter_plugin_android_lifecycle=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_plugin_android_lifecycle-1.0.7/ 14 | fluttertoast=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/fluttertoast-4.0.1/ 15 | image_picker=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/image_picker-0.6.6+1/ 16 | onesignal=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/onesignal-1.1.0/ 17 | path_provider=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-1.6.7/ 18 | path_provider_macos=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.4+2/ 19 | permission_handler=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-4.4.0+hotfix.4/ 20 | shared_preferences=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences-0.5.7+1/ 21 | shared_preferences_macos=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_macos-0.0.1+8/ 22 | shared_preferences_web=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/shared_preferences_web-0.1.2+5/ 23 | sqflite=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/sqflite-1.3.0+1/ 24 | url_launcher=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-5.4.5/ 25 | url_launcher_macos=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.0.1+5/ 26 | url_launcher_web=/Users/bhavneetsingh/Downloads/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_web-0.1.1+4/ 27 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /lib/widgets/stories_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/controllers/stories_controller.dart'; 2 | import 'package:chat_app/models/story.dart'; 3 | import 'package:chat_app/screens/stories_screen.dart'; 4 | import 'package:chat_app/widgets/story_widget.dart'; 5 | import 'package:firebase_auth/firebase_auth.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | import 'add_story_button.dart'; 9 | 10 | class StoriesWidget extends StatefulWidget { 11 | final bool rounded; 12 | const StoriesWidget({Key key, this.rounded = false}) : super(key: key); 13 | @override 14 | State createState() => _StoriesWidgetState(); 15 | } 16 | 17 | class _StoriesWidgetState extends State { 18 | FirebaseUser user; 19 | @override 20 | void initState() { 21 | super.initState(); 22 | getCurrentUser(); 23 | } 24 | 25 | void getCurrentUser() async { 26 | this.user = await FirebaseAuth.instance.currentUser(); 27 | setState(() {}); 28 | } 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | return Container( 33 | height: widget.rounded ? 81.0 : 140.0, 34 | margin: EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0), 35 | alignment: Alignment.center, 36 | child: user == null 37 | ? CircularProgressIndicator() 38 | : StreamBuilder>( 39 | stream: StoriesController.getStories(), 40 | builder: (context, snapshot) { 41 | if (snapshot.data == null) { 42 | return Row( 43 | children: [ 44 | AddStoryButton( 45 | rounded: widget.rounded, 46 | size: 52.0, 47 | ), 48 | Expanded( 49 | child: Center(child: CircularProgressIndicator())), 50 | ], 51 | ); 52 | } 53 | List stories = snapshot.data; 54 | stories = stories 55 | .where((val) => 56 | val != null && val.data != null && val.data.length != 0) 57 | .toList(); 58 | return ListView.builder( 59 | itemBuilder: (context, index) { 60 | if (index == 0) { 61 | return AddStoryButton( 62 | rounded: widget.rounded, 63 | size: 52.0, 64 | ); 65 | } 66 | return StoryWidget( 67 | size: 52.0, 68 | rounded: widget.rounded, 69 | current: stories[index - 1].user.id == user.email, 70 | story: stories[index - 1], 71 | onTap: () { 72 | Navigator.of(context).push(MaterialPageRoute( 73 | builder: (context) => StoriesScreen( 74 | stories: stories, 75 | initalPage: index - 1, 76 | ))); 77 | }, 78 | ); 79 | }, 80 | itemCount: stories.length + 1, 81 | scrollDirection: Axis.horizontal, 82 | physics: BouncingScrollPhysics(), 83 | ); 84 | }, 85 | ), 86 | ); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /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 from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | def keystoreProperties = new Properties() 28 | def keystorePropertiesFile = rootProject.file('key.properties') 29 | if (keystorePropertiesFile.exists()) { 30 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 31 | } 32 | 33 | android { 34 | compileSdkVersion 28 35 | 36 | lintOptions { 37 | disable 'InvalidPackage' 38 | } 39 | 40 | defaultConfig { 41 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 42 | applicationId "com.blackhole.chat_app" 43 | minSdkVersion 21 44 | targetSdkVersion 28 45 | versionCode flutterVersionCode.toInteger() 46 | versionName flutterVersionName 47 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 48 | } 49 | // signingConfigs { 50 | // release { 51 | // keyAlias keystoreProperties['keyAlias'] 52 | // keyPassword keystoreProperties['keyPassword'] 53 | // storeFile file(keystoreProperties['storeFile']) 54 | // storePassword keystoreProperties['storePassword'] 55 | // } 56 | // } 57 | buildTypes { 58 | release { 59 | signingConfig signingConfigs.debug 60 | } 61 | } 62 | } 63 | 64 | flutter { 65 | source '../..' 66 | } 67 | 68 | dependencies { 69 | testImplementation 'junit:junit:4.12' 70 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 71 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 72 | } 73 | configurations.all { 74 | resolutionStrategy { 75 | resolutionStrategy.eachDependency { details -> 76 | if (details.requested.group == 'androidx.core') { 77 | details.useVersion "1.0.1" 78 | } 79 | if (details.requested.group == 'androidx.lifecycle') { 80 | details.useVersion "2.0.0" 81 | } 82 | if (details.requested.group == 'androidx.versionedparcelable') { 83 | details.useVersion "1.0.0" 84 | } 85 | if (details.requested.group == 'androidx.fragment') { 86 | details.useVersion "1.0.0" 87 | } 88 | if (details.requested.group == 'androidx.appcompat') { 89 | details.useVersion "1.0.1" 90 | } 91 | } 92 | } 93 | } 94 | apply plugin: 'com.google.gms.google-services' 95 | apply plugin: 'com.onesignal.androidsdk.onesignal-gradle-plugin' -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | platform :ios, '11.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | generated_key_values = {} 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) do |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | generated_key_values[podname] = podpath 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | end 32 | generated_key_values 33 | end 34 | 35 | target 'Runner' do 36 | # Flutter Pod 37 | 38 | copied_flutter_dir = File.join(__dir__, 'Flutter') 39 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') 40 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') 41 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) 42 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. 43 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. 44 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. 45 | 46 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') 47 | unless File.exist?(generated_xcode_build_settings_path) 48 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" 49 | end 50 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) 51 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; 52 | 53 | unless File.exist?(copied_framework_path) 54 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) 55 | end 56 | unless File.exist?(copied_podspec_path) 57 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) 58 | end 59 | end 60 | 61 | # Keep pod path relative so it can be checked into Podfile.lock. 62 | pod 'Flutter', :path => 'Flutter' 63 | 64 | # Plugin Pods 65 | 66 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 67 | # referring to absolute paths on developers' machines. 68 | system('rm -rf .symlinks') 69 | system('mkdir -p .symlinks/plugins') 70 | plugin_pods = parse_KV_file('../.flutter-plugins') 71 | plugin_pods.each do |name, path| 72 | symlink = File.join('.symlinks', 'plugins', name) 73 | File.symlink(path, symlink) 74 | pod name, :path => File.join(symlink, 'ios') 75 | end 76 | end 77 | 78 | # Prevent Cocoapods from embedding a second Flutter framework and causing an error with the new Xcode build system. 79 | install! 'cocoapods', :disable_input_output_paths => true 80 | 81 | post_install do |installer| 82 | installer.pods_project.targets.each do |target| 83 | target.build_configurations.each do |config| 84 | config.build_settings['ENABLE_BITCODE'] = 'NO' 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | xmlns:android 11 | 12 | ^$ 13 | 14 | 15 | 16 |
17 |
18 | 19 | 20 | 21 | xmlns:.* 22 | 23 | ^$ 24 | 25 | 26 | BY_NAME 27 | 28 |
29 |
30 | 31 | 32 | 33 | .*:id 34 | 35 | http://schemas.android.com/apk/res/android 36 | 37 | 38 | 39 |
40 |
41 | 42 | 43 | 44 | .*:name 45 | 46 | http://schemas.android.com/apk/res/android 47 | 48 | 49 | 50 |
51 |
52 | 53 | 54 | 55 | name 56 | 57 | ^$ 58 | 59 | 60 | 61 |
62 |
63 | 64 | 65 | 66 | style 67 | 68 | ^$ 69 | 70 | 71 | 72 |
73 |
74 | 75 | 76 | 77 | .* 78 | 79 | ^$ 80 | 81 | 82 | BY_NAME 83 | 84 |
85 |
86 | 87 | 88 | 89 | .* 90 | 91 | http://schemas.android.com/apk/res/android 92 | 93 | 94 | ANDROID_ATTRIBUTE_ORDER 95 | 96 |
97 |
98 | 99 | 100 | 101 | .* 102 | 103 | .* 104 | 105 | 106 | BY_NAME 107 | 108 |
109 |
110 |
111 |
112 |
113 |
-------------------------------------------------------------------------------- /lib/controllers/chats_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/controllers/user_controller.dart'; 2 | import 'package:chat_app/models/user.dart'; 3 | import 'package:chat_app/models/chat.dart'; 4 | import 'package:cloud_firestore/cloud_firestore.dart'; 5 | //import 'package:crypto/crypto.dart' as crypto; 6 | //import 'dart:convert'; 7 | //import 'package:firebase_auth/firebase_auth.dart'; 8 | class ChatsController { 9 | static const String CHATS="chats",RECENT="recentChats",MESSAGES="messages"; 10 | 11 | static Stream> getChats() async*{ 12 | Firestore firestore=Firestore.instance; 13 | User user=await UserController.getUser(); 14 | ListactiveUsers=await UserController.getActiveUsers(); 15 | await for(QuerySnapshot snap in firestore.collection(RECENT).document(user.id).collection("history").snapshots()){ 16 | try{ 17 | Listchats=snap.documents.map((doc)=>Chat.fromJson(Map.from(doc.data))).toList(); 18 | chats.forEach((chat){ 19 | chat.to.isActive=false; 20 | chat.from.isActive=false; 21 | if(chat.to.id!=user.id){ 22 | activeUsers.forEach((temp){ 23 | if(temp.id==chat.to.id){ 24 | chat.to.isActive=true; 25 | } 26 | }); 27 | } 28 | else{ 29 | activeUsers.forEach((temp){ 30 | if(temp.id==chat.from.id){ 31 | chat.from.isActive=true; 32 | } 33 | }); 34 | } 35 | }); 36 | yield chats; 37 | } 38 | catch(e){ 39 | print(e); 40 | } 41 | } 42 | } 43 | static Future sendMessage(Chat chat)async{ 44 | try{ 45 | Firestore fireStore=Firestore.instance; 46 | String id=getUniqueId(chat.from.id,chat.to.id); 47 | print("ID $id"); 48 | chat.groupId=id; 49 | fireStore.collection(MESSAGES).add(chat.toJson()); 50 | await saveRecentChat(chat); 51 | return true; 52 | } 53 | catch(e){ 54 | print("Exception $e"); 55 | return false; 56 | } 57 | } 58 | static Future saveRecentChat(Chat chat)async{ 59 | Listids=[chat.from.id,chat.to.id]; 60 | for(String id in ids){ 61 | Firestore fireStore=Firestore.instance; 62 | Query query=fireStore.collection(RECENT).document(id).collection("history").where("groupId",isEqualTo: getUniqueId(chat.from.id, chat.to.id)); 63 | QuerySnapshot documents=await query.getDocuments(); 64 | if(documents.documents.length!=0){ 65 | DocumentSnapshot documentSnapshot=documents.documents[0]; 66 | documentSnapshot.reference.setData(chat.toJson()); 67 | } 68 | else{ 69 | fireStore.collection(RECENT).document(id).collection("history").add(chat.toJson()); 70 | } 71 | } 72 | } 73 | static String getUniqueId(String i1,String i2){ 74 | if(i1.compareTo(i2)<=-1){ 75 | return i1+i2; 76 | } 77 | else{ 78 | return i2+i1; 79 | } 80 | } 81 | static Stream> listenChat(User from,User to)async*{ 82 | Firestore firestore=Firestore.instance; 83 | await for(QuerySnapshot snap in firestore.collection("messages").where("groupId",isEqualTo: getUniqueId(from.id,to.id,)).snapshots()){ 84 | try{ 85 | Listchats=snap.documents.map((doc)=>Chat.fromJson(Map.from(doc.data))).toList(); 86 | yield chats; 87 | } 88 | catch(e){ 89 | print(e); 90 | } 91 | } 92 | } 93 | static String filterVal(String val){ 94 | ListinCorrects=[":","#","\$","[","]","."]; 95 | String filtered=val; 96 | inCorrects.forEach((val){ 97 | filtered=filtered.replaceAll(val, ""); 98 | }); 99 | return filtered; 100 | } 101 | } -------------------------------------------------------------------------------- /android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java: -------------------------------------------------------------------------------- 1 | package io.flutter.plugins; 2 | 3 | import io.flutter.plugin.common.PluginRegistry; 4 | import io.flutter.plugins.camera.CameraPlugin; 5 | import io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin; 6 | import com.jeffg.emoji_picker.EmojiPickerPlugin; 7 | import io.flutter.plugins.firebaseauth.FirebaseAuthPlugin; 8 | import io.flutter.plugins.firebase.core.FirebaseCorePlugin; 9 | import io.flutter.plugins.firebase.database.FirebaseDatabasePlugin; 10 | import io.flutter.plugins.firebase.storage.FirebaseStoragePlugin; 11 | import com.roughike.facebooklogin.facebooklogin.FacebookLoginPlugin; 12 | import com.example.flutternativeimage.FlutterNativeImagePlugin; 13 | import io.flutter.plugins.flutter_plugin_android_lifecycle.FlutterAndroidLifecyclePlugin; 14 | import io.github.ponnamkarthik.toast.fluttertoast.FluttertoastPlugin; 15 | import io.flutter.plugins.imagepicker.ImagePickerPlugin; 16 | import com.onesignal.flutter.OneSignalPlugin; 17 | import io.flutter.plugins.pathprovider.PathProviderPlugin; 18 | import com.baseflow.permissionhandler.PermissionHandlerPlugin; 19 | import io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin; 20 | import com.tekartik.sqflite.SqflitePlugin; 21 | import io.flutter.plugins.urllauncher.UrlLauncherPlugin; 22 | 23 | /** 24 | * Generated file. Do not edit. 25 | */ 26 | public final class GeneratedPluginRegistrant { 27 | public static void registerWith(PluginRegistry registry) { 28 | if (alreadyRegisteredWith(registry)) { 29 | return; 30 | } 31 | CameraPlugin.registerWith(registry.registrarFor("io.flutter.plugins.camera.CameraPlugin")); 32 | CloudFirestorePlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin")); 33 | EmojiPickerPlugin.registerWith(registry.registrarFor("com.jeffg.emoji_picker.EmojiPickerPlugin")); 34 | FirebaseAuthPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebaseauth.FirebaseAuthPlugin")); 35 | FirebaseCorePlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebase.core.FirebaseCorePlugin")); 36 | FirebaseDatabasePlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebase.database.FirebaseDatabasePlugin")); 37 | FirebaseStoragePlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebase.storage.FirebaseStoragePlugin")); 38 | FacebookLoginPlugin.registerWith(registry.registrarFor("com.roughike.facebooklogin.facebooklogin.FacebookLoginPlugin")); 39 | FlutterNativeImagePlugin.registerWith(registry.registrarFor("com.example.flutternativeimage.FlutterNativeImagePlugin")); 40 | FlutterAndroidLifecyclePlugin.registerWith(registry.registrarFor("io.flutter.plugins.flutter_plugin_android_lifecycle.FlutterAndroidLifecyclePlugin")); 41 | FluttertoastPlugin.registerWith(registry.registrarFor("io.github.ponnamkarthik.toast.fluttertoast.FluttertoastPlugin")); 42 | ImagePickerPlugin.registerWith(registry.registrarFor("io.flutter.plugins.imagepicker.ImagePickerPlugin")); 43 | OneSignalPlugin.registerWith(registry.registrarFor("com.onesignal.flutter.OneSignalPlugin")); 44 | PathProviderPlugin.registerWith(registry.registrarFor("io.flutter.plugins.pathprovider.PathProviderPlugin")); 45 | PermissionHandlerPlugin.registerWith(registry.registrarFor("com.baseflow.permissionhandler.PermissionHandlerPlugin")); 46 | SharedPreferencesPlugin.registerWith(registry.registrarFor("io.flutter.plugins.sharedpreferences.SharedPreferencesPlugin")); 47 | SqflitePlugin.registerWith(registry.registrarFor("com.tekartik.sqflite.SqflitePlugin")); 48 | UrlLauncherPlugin.registerWith(registry.registrarFor("io.flutter.plugins.urllauncher.UrlLauncherPlugin")); 49 | } 50 | 51 | private static boolean alreadyRegisteredWith(PluginRegistry registry) { 52 | final String key = GeneratedPluginRegistrant.class.getCanonicalName(); 53 | if (registry.hasPlugin(key)) { 54 | return true; 55 | } 56 | registry.registrarFor(key); 57 | return false; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: chat_app 2 | description: A flutter application for chatting 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 1.0.0+1 15 | 16 | environment: 17 | sdk: ">=2.1.0 <3.0.0" 18 | 19 | dependencies: 20 | flutter: 21 | sdk: flutter 22 | 23 | # The following adds the Cupertino Icons font to your application. 24 | # Use with the CupertinoIcons class for iOS style icons. 25 | cupertino_icons: ^0.1.2 26 | firebase_auth: 27 | flutter_facebook_login: ^1.1.1 28 | http: 29 | json_annotation: ^2.0.0 30 | camera: ^0.5.2+1 31 | path_provider: 32 | permission_handler: ^4.4.0 33 | photo_view: 34 | cloud_firestore: ^0.12.5+1 35 | firebase_storage: 36 | firebase_database: ^3.0.2 37 | image_picker: ^0.6.0+9 38 | cached_network_image: 39 | fluttertoast: 40 | intl: 41 | shared_preferences: 42 | image: 43 | onesignal: ^1.1.0 44 | flutter_native_image: 45 | git: https://github.com/btastic/flutter_native_image.git 46 | url_launcher: 47 | dev_dependencies: 48 | flutter_test: 49 | sdk: flutter 50 | build_runner: ^1.0.0 51 | json_serializable: ^2.0.0 52 | emoji_picker: ^0.0.2 53 | # For information on the generic Dart part of this file, see the 54 | # following page: https://www.dartlang.org/tools/pub/pubspec 55 | 56 | # The following section is specific to Flutter. 57 | flutter: 58 | 59 | # The following line ensures that the Material Icons font is 60 | # included with your application, so that you can use the icons in 61 | # the material Icons class. 62 | uses-material-design: true 63 | 64 | # To add assets to your application, add an assets section, like this: 65 | assets: 66 | - images/font_outline.png 67 | - images/font_filled.png 68 | - images/logo.png 69 | - images/sticker1.png 70 | - images/sticker2.png 71 | - images/sticker3.png 72 | - images/sticker4.png 73 | 74 | # An image asset can refer to one or more resolution-specific "variants", see 75 | # https://flutter.dev/assets-and-images/#resolution-aware. 76 | 77 | # For details regarding adding assets from package dependencies, see 78 | # https://flutter.dev/assets-and-images/#from-packages 79 | 80 | # To add custom fonts to your application, add a fonts section here, 81 | # in this "flutter" section. Each entry in this list should have a 82 | # "family" key with the font family name, and a "fonts" key with a 83 | # list giving the asset and other descriptors for the font. For 84 | # example: 85 | fonts: 86 | - family: Ubunutu 87 | fonts: 88 | - asset: fonts/Ubuntu-Regular.ttf 89 | - family: UbunutuMono 90 | fonts: 91 | - asset: fonts/UbuntuMono.ttf 92 | - family: RobotoMono 93 | fonts: 94 | - asset: fonts/RobotoMono.ttf 95 | - family: B612Mono 96 | fonts: 97 | - asset: fonts/B612Mono.ttf 98 | # - asset: fonts/Schyler-Italic.ttf 99 | # style: italic 100 | # - family: Trajan Pro 101 | # fonts: 102 | # - asset: fonts/TrajanPro.ttf 103 | # - asset: fonts/TrajanPro_Bold.ttf 104 | # weight: 700 105 | # 106 | # For details regarding fonts from package dependencies, 107 | # see https://flutter.dev/custom-fonts/#from-packages 108 | -------------------------------------------------------------------------------- /lib/controllers/user_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/models/chat.dart'; 2 | import 'package:chat_app/models/user.dart'; 3 | import 'package:cloud_firestore/cloud_firestore.dart'; 4 | import 'package:firebase_auth/firebase_auth.dart'; 5 | import 'package:flutter_facebook_login/flutter_facebook_login.dart'; 6 | 7 | import 'chats_controller.dart'; 8 | 9 | class UserController { 10 | static Firestore fireStore = Firestore.instance; 11 | static User user; 12 | 13 | static open() async { 14 | user = await getUser(); 15 | } 16 | 17 | static Future updateUser(bool isActive) async { 18 | User user = await getUser(); 19 | if (user != null) { 20 | user.isActive = isActive; 21 | DocumentReference documentReference = 22 | fireStore.collection("users").document(user.id); 23 | var data = await documentReference.get(); 24 | if (data.data == null) { 25 | await ChatsController.sendMessage(Chat.fromNamed( 26 | from: User.fromNamed( 27 | displayName: "Bhavneet Singh", 28 | id: "bhavi736@gmail.com", 29 | isActive: false, 30 | profilePicture: 31 | "https://graph.facebook.com/1714582255355192/picture"), 32 | to: user, 33 | isSeen: false, 34 | type: "text", 35 | publishedAt: DateTime.now(), 36 | content: 37 | "Hey there developer.Thank you for installing this app.To add new conversation just click on search bar")); 38 | } 39 | List indexes = [""]; 40 | for (int i = 1; i <= user.displayName.length; i++) { 41 | String subString = user.displayName.substring(0, i).toLowerCase(); 42 | indexes.add(subString); 43 | } 44 | Map json = user.toJson(); 45 | json["indexes"] = indexes; 46 | documentReference.setData(json); 47 | } 48 | } 49 | 50 | static Future getUser() async { 51 | FirebaseUser user = await FirebaseAuth.instance.currentUser(); 52 | if (user == null) { 53 | return null; 54 | } 55 | return User(user.displayName, user.photoUrl, user.email ?? user.uid, false); 56 | } 57 | 58 | static Future isLoggedIn() async { 59 | User user = await getUser(); 60 | return user != null; 61 | } 62 | 63 | static Future logOut() async { 64 | var facebookLogin = FacebookLogin(); 65 | await facebookLogin.logOut(); 66 | await FirebaseAuth.instance.signOut(); 67 | return true; 68 | } 69 | 70 | static Future> searchUsers(String query) async { 71 | print("Searching"); 72 | var val = await fireStore 73 | .collection("users") 74 | .where("indexes", arrayContains: query.toLowerCase()) 75 | .getDocuments(); 76 | var documents = val.documents; 77 | if (documents.length > 0) { 78 | try { 79 | print("Documents ${documents.length}"); 80 | return documents.map((document) { 81 | User user = User.fromJson(Map.from(document.data)); 82 | print(user.displayName); 83 | return user; 84 | }).toList(); 85 | } catch (e) { 86 | print("Exception $e"); 87 | } 88 | } 89 | return []; 90 | } 91 | 92 | static Future> getActiveUsers() async { 93 | print("Active Users"); 94 | var val = await fireStore 95 | .collection("users") 96 | .where("isActive", isEqualTo: true) 97 | .getDocuments(); 98 | var documents = val.documents; 99 | print("Documents ${documents.length}"); 100 | if (documents.length > 0) { 101 | try { 102 | print("Active ${documents.length}"); 103 | return documents.map((document) { 104 | User user = User.fromJson(Map.from(document.data)); 105 | print("User ${user.displayName}"); 106 | return user; 107 | }).toList(); 108 | } catch (e) { 109 | print("Exception $e"); 110 | return []; 111 | } 112 | } 113 | return []; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /lib/controllers/stories_controller.dart: -------------------------------------------------------------------------------- 1 | import 'dart:developer'; 2 | import 'dart:typed_data'; 3 | import 'package:cloud_firestore/cloud_firestore.dart'; 4 | import 'package:chat_app/models/user.dart'; 5 | import 'package:chat_app/models/story.dart'; 6 | import 'package:firebase_storage/firebase_storage.dart'; 7 | import 'package:firebase_auth/firebase_auth.dart'; 8 | 9 | import '../models/story.dart'; 10 | import '../models/story.dart'; 11 | import '../models/story.dart'; 12 | import '../models/story.dart'; 13 | 14 | class StoriesController { 15 | static Future storeImage(Uint8List data) async { 16 | try { 17 | StorageReference reference = 18 | FirebaseStorage.instance.ref().child(DateTime.now().toString()); 19 | StorageUploadTask uploadTask = reference.putData(data); 20 | StorageTaskSnapshot snapshot = await uploadTask.onComplete; 21 | String imagePath = snapshot.storageMetadata.path; 22 | await createStory(imagePath); 23 | return snapshot.error == 0; 24 | } catch (e) { 25 | return false; 26 | } 27 | } 28 | 29 | static const String STORIES = "stories"; 30 | static const String BASE_URl = 31 | "https://firebasestorage.googleapis.com/v0/b/chat-9b859.appspot.com/o/"; 32 | static Firestore firestore = Firestore.instance; 33 | 34 | static Future createStory(String path) async { 35 | try { 36 | String imagePath = "$BASE_URl$path?alt=media"; 37 | FirebaseUser authUser = await FirebaseAuth.instance.currentUser(); 38 | List names = authUser.displayName.split(" "); 39 | User user = 40 | User(authUser.displayName, authUser.photoUrl, authUser.email, false); 41 | Story story = Story( 42 | filterVal(user.id), 43 | user, 44 | false, 45 | [DataType(imagePath, DataType.IMAGE)], 46 | DateTime.now(), 47 | ); 48 | CollectionReference reference = 49 | firestore.collection(filterVal(STORIES)).reference(); 50 | DocumentSnapshot documentSnapshot = 51 | await reference.document(filterVal(user.id)).get(); 52 | if (documentSnapshot.data == null) { 53 | reference.document(filterVal(user.id)).setData(story.toJson()); 54 | } else { 55 | DocumentReference docReference = reference.document(filterVal(user.id)); 56 | log( 57 | "Dat ${filterVal(user.id)}a", 58 | ); 59 | log("Data ${Story.fromJson(documentSnapshot.data).toJson()}"); 60 | List data = Story.fromJson(documentSnapshot.data).data; 61 | log("Data ${data.length}"); 62 | data.add(DataType(imagePath, DataType.IMAGE)); 63 | await docReference.updateData({ 64 | "data": data.map((val) => val.toJson()).toList(), 65 | "isSeen": false 66 | }); 67 | } 68 | 69 | return true; 70 | } catch (e) { 71 | log(e, error: e); 72 | return false; 73 | } 74 | } 75 | 76 | static String filterVal(String val) { 77 | List inCorrects = [":", "#", "\$", "[", "]", "."]; 78 | String filtered = val; 79 | inCorrects.forEach((val) { 80 | filtered = filtered.replaceAll(val, ""); 81 | }); 82 | return filtered; 83 | } 84 | 85 | static Stream> getStories() async* { 86 | await for (QuerySnapshot snap 87 | in firestore.collection(STORIES).snapshots()) { 88 | List result = []; 89 | snap.documents.forEach((snap) async { 90 | try { 91 | Story story = Story.fromJson(Map.from(snap.data)); 92 | result.add(story); 93 | } catch (e) { 94 | print("Baby $e"); 95 | } 96 | }); 97 | yield result; 98 | } 99 | } 100 | 101 | static Future updateStory(Story story, bool isSeen) async { 102 | try { 103 | firestore 104 | .collection(STORIES) 105 | .document(story.id) 106 | .updateData({"isSeen": isSeen}); 107 | return true; 108 | } catch (e) { 109 | return false; 110 | } 111 | } 112 | 113 | static List filterStories(List stories) { 114 | Map> newStories = {}; 115 | List result = []; 116 | stories.forEach((story) { 117 | if (!newStories.containsKey(story.user.id)) { 118 | newStories.putIfAbsent(story.user.id, () => []); 119 | } 120 | newStories[story.user.id].add(story); 121 | }); 122 | newStories.forEach((val, list) { 123 | if (list.length == 0) { 124 | return; 125 | } 126 | User user = list[0].user; 127 | Story story = Story("", user, false, [], DateTime.now()); 128 | stories.forEach((temp) { 129 | story.data.addAll(temp.data); 130 | }); 131 | result.add(story); 132 | }); 133 | return result; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /ios/Runner/GeneratedPluginRegistrant.m: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | #import "GeneratedPluginRegistrant.h" 6 | 7 | #if __has_include() 8 | #import 9 | #else 10 | @import camera; 11 | #endif 12 | 13 | #if __has_include() 14 | #import 15 | #else 16 | @import cloud_firestore; 17 | #endif 18 | 19 | #if __has_include() 20 | #import 21 | #else 22 | @import emoji_picker; 23 | #endif 24 | 25 | #if __has_include() 26 | #import 27 | #else 28 | @import firebase_auth; 29 | #endif 30 | 31 | #if __has_include() 32 | #import 33 | #else 34 | @import firebase_core; 35 | #endif 36 | 37 | #if __has_include() 38 | #import 39 | #else 40 | @import firebase_database; 41 | #endif 42 | 43 | #if __has_include() 44 | #import 45 | #else 46 | @import firebase_storage; 47 | #endif 48 | 49 | #if __has_include() 50 | #import 51 | #else 52 | @import flutter_facebook_login; 53 | #endif 54 | 55 | #if __has_include() 56 | #import 57 | #else 58 | @import flutter_native_image; 59 | #endif 60 | 61 | #if __has_include() 62 | #import 63 | #else 64 | @import fluttertoast; 65 | #endif 66 | 67 | #if __has_include() 68 | #import 69 | #else 70 | @import image_picker; 71 | #endif 72 | 73 | #if __has_include() 74 | #import 75 | #else 76 | @import onesignal; 77 | #endif 78 | 79 | #if __has_include() 80 | #import 81 | #else 82 | @import path_provider; 83 | #endif 84 | 85 | #if __has_include() 86 | #import 87 | #else 88 | @import permission_handler; 89 | #endif 90 | 91 | #if __has_include() 92 | #import 93 | #else 94 | @import shared_preferences; 95 | #endif 96 | 97 | #if __has_include() 98 | #import 99 | #else 100 | @import sqflite; 101 | #endif 102 | 103 | #if __has_include() 104 | #import 105 | #else 106 | @import url_launcher; 107 | #endif 108 | 109 | @implementation GeneratedPluginRegistrant 110 | 111 | + (void)registerWithRegistry:(NSObject*)registry { 112 | [CameraPlugin registerWithRegistrar:[registry registrarForPlugin:@"CameraPlugin"]]; 113 | [FLTCloudFirestorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTCloudFirestorePlugin"]]; 114 | [EmojiPickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"EmojiPickerPlugin"]]; 115 | [FLTFirebaseAuthPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseAuthPlugin"]]; 116 | [FLTFirebaseCorePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseCorePlugin"]]; 117 | [FLTFirebaseDatabasePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseDatabasePlugin"]]; 118 | [FLTFirebaseStoragePlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTFirebaseStoragePlugin"]]; 119 | [FacebookLoginPlugin registerWithRegistrar:[registry registrarForPlugin:@"FacebookLoginPlugin"]]; 120 | [FlutterNativeImagePlugin registerWithRegistrar:[registry registrarForPlugin:@"FlutterNativeImagePlugin"]]; 121 | [FluttertoastPlugin registerWithRegistrar:[registry registrarForPlugin:@"FluttertoastPlugin"]]; 122 | [FLTImagePickerPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTImagePickerPlugin"]]; 123 | [OneSignalPlugin registerWithRegistrar:[registry registrarForPlugin:@"OneSignalPlugin"]]; 124 | [FLTPathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTPathProviderPlugin"]]; 125 | [PermissionHandlerPlugin registerWithRegistrar:[registry registrarForPlugin:@"PermissionHandlerPlugin"]]; 126 | [FLTSharedPreferencesPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTSharedPreferencesPlugin"]]; 127 | [SqflitePlugin registerWithRegistrar:[registry registrarForPlugin:@"SqflitePlugin"]]; 128 | [FLTURLLauncherPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTURLLauncherPlugin"]]; 129 | } 130 | 131 | @end 132 | -------------------------------------------------------------------------------- /lib/widgets/story_widget.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/models/story.dart'; 2 | import 'package:chat_app/widgets/user_widget.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'ease_in_widget.dart'; 6 | 7 | class StoryWidget extends StatelessWidget { 8 | final Story story; 9 | final double size; 10 | final Function onTap; 11 | final bool rounded; 12 | final bool current; 13 | const StoryWidget( 14 | {Key key, 15 | @required this.story, 16 | @required this.size, 17 | @required this.onTap, 18 | this.rounded = true, 19 | this.current = false}) 20 | : super(key: key); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return EaseInWidget( 25 | onTap: onTap, child: rounded ? _buildRounded() : _buildRectangle()); 26 | } 27 | 28 | Widget _buildRectangle() { 29 | double width = 100.0; 30 | return Container( 31 | color: Color(0xffeeeeee), 32 | margin: EdgeInsets.only(left: 12.0), 33 | child: Stack( 34 | children: [ 35 | ClipRRect( 36 | borderRadius: BorderRadius.circular(4.0), 37 | child: Image.network( 38 | story.data[0].link, 39 | width: width, 40 | fit: BoxFit.cover, 41 | ), 42 | ), 43 | Container( 44 | width: width, 45 | padding: const EdgeInsets.all(8.0), 46 | child: Column( 47 | crossAxisAlignment: CrossAxisAlignment.start, 48 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 49 | children: [ 50 | UserWidget( 51 | user: story.user, 52 | size: 12.0, 53 | ), 54 | Expanded( 55 | child: Container( 56 | width: width, 57 | alignment: Alignment.bottomLeft, 58 | child: Text( 59 | !current 60 | ? story.user.displayName.split(" ")[0] 61 | : "Your Story", 62 | maxLines: 2, 63 | style: TextStyle(color: Colors.white, fontSize: 12.0), 64 | ), 65 | ), 66 | ) 67 | ], 68 | ), 69 | ) 70 | ], 71 | ), 72 | ); 73 | } 74 | 75 | Widget _buildRounded() { 76 | double diff = 6.0; 77 | return Container( 78 | margin: EdgeInsets.only(left: 16.0), 79 | child: Column( 80 | mainAxisSize: MainAxisSize.min, 81 | children: [ 82 | Container( 83 | decoration: BoxDecoration( 84 | shape: BoxShape.circle, 85 | border: Border.all( 86 | color: !story.isSeen 87 | ? Colors.blue 88 | : Color( 89 | 0xffeeeeee, 90 | ), 91 | width: 3.0)), 92 | child: Stack( 93 | alignment: Alignment.center, 94 | children: [ 95 | Container( 96 | width: size - diff, 97 | height: size - diff, 98 | decoration: BoxDecoration( 99 | color: Color(0xffeeeeeee), shape: BoxShape.circle), 100 | child: ClipRRect( 101 | borderRadius: BorderRadius.circular(size), 102 | child: FadeInImage.assetNetwork( 103 | placeholder: 'assets/placeholder.png', 104 | image: story.user.profilePicture, 105 | width: size - diff, 106 | height: size - diff, 107 | fit: BoxFit.cover, 108 | ), 109 | ), 110 | ), 111 | Container( 112 | width: size, 113 | height: size, 114 | alignment: Alignment.bottomRight, 115 | child: story.user.isActive 116 | ? Container( 117 | width: 16.0, 118 | height: 16.0, 119 | alignment: Alignment.center, 120 | child: Container( 121 | width: 10.0, 122 | height: 10.0, 123 | decoration: BoxDecoration( 124 | color: Colors.yellow, shape: BoxShape.circle), 125 | ), 126 | decoration: BoxDecoration( 127 | color: Colors.white, shape: BoxShape.circle), 128 | ) 129 | : null, 130 | ) 131 | ], 132 | ), 133 | ), 134 | Padding( 135 | padding: const EdgeInsets.only(top: 4.0), 136 | child: Text( 137 | !current ? story.user.firstName : "Your Story", 138 | style: TextStyle( 139 | color: !story.isSeen ? Colors.black : Colors.black38, 140 | fontSize: 12.0), 141 | ), 142 | ), 143 | ], 144 | ), 145 | ); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /lib/widgets/story_create_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'dart:typed_data'; 4 | import 'dart:ui' as ui; 5 | import 'dart:ui'; 6 | import 'package:chat_app/controllers/image_compresser.dart'; 7 | import 'package:chat_app/controllers/stories_controller.dart'; 8 | import 'package:chat_app/models/basic.dart'; 9 | import 'package:chat_app/models/image_data.dart'; 10 | import 'package:chat_app/models/smiley_data.dart'; 11 | import 'package:chat_app/models/text_data.dart'; 12 | import 'package:chat_app/widgets/moving_widget.dart'; 13 | import 'package:flutter/material.dart'; 14 | import 'package:flutter/rendering.dart'; 15 | import 'package:path_provider/path_provider.dart'; 16 | 17 | class StoryCreateWidget extends StatefulWidget { 18 | final List children; 19 | final Offset deleteOffset; 20 | final Function(StoryCreateController) onCompleted; 21 | 22 | const StoryCreateWidget( 23 | {Key key, 24 | @required this.children, 25 | @required this.deleteOffset, 26 | @required this.onCompleted}) 27 | : super(key: key); 28 | 29 | @override 30 | State createState() => _StoryCreateWidgetState(); 31 | } 32 | 33 | class _StoryCreateWidgetState extends State { 34 | @override 35 | void initState() { 36 | super.initState(); 37 | WidgetsBinding.instance.addPostFrameCallback((val) { 38 | widget.onCompleted(StoryCreateController._(this)); 39 | }); 40 | } 41 | 42 | GlobalKey _globalKey = new GlobalKey(); 43 | 44 | @override 45 | Widget build(BuildContext context) { 46 | print("children story ${widget.children.length}"); 47 | 48 | return RepaintBoundary( 49 | key: _globalKey, 50 | child: Container( 51 | color: Colors.white12, 52 | child: Stack( 53 | fit: StackFit.expand, 54 | children: children, 55 | ), 56 | ), 57 | ); 58 | } 59 | 60 | List get children { 61 | List subs = []; 62 | widget.children.forEach((basic) { 63 | Widget subChild; 64 | if (basic is TextData) { 65 | subChild = createTextWidget(basic); 66 | } else if (basic is ImageData) { 67 | subChild = createImageWidget(basic); 68 | } else if (basic is SmileyData) { 69 | subChild = createSmileyWidget(basic); 70 | } 71 | if (basic is ImageData) { 72 | subs.add(subChild); 73 | } else { 74 | Widget child = MovingWidget( 75 | child: subChild, 76 | deleteOffset: widget.deleteOffset, 77 | onRemove: () { 78 | setState(() { 79 | widget.children.remove(basic); 80 | }); 81 | }, 82 | ); 83 | subs.add(child); 84 | } 85 | }); 86 | return subs; 87 | } 88 | 89 | Widget createTextWidget(TextData basic) { 90 | return Container( 91 | decoration: basic.decoration, 92 | padding: TextData.TEXT_PADDING, 93 | child: Text( 94 | basic.data, 95 | style: basic.textStyle, 96 | ), 97 | ); 98 | } 99 | 100 | Widget createImageWidget(ImageData basic) { 101 | return Stack( 102 | fit: StackFit.expand, 103 | children: [ 104 | Image.file( 105 | File( 106 | basic.imagePath, 107 | ), 108 | fit: BoxFit.fill, 109 | ), 110 | BackdropFilter( 111 | filter: ImageFilter.blur( 112 | sigmaX: 75.0, 113 | sigmaY: 75.0, 114 | ), 115 | child: new Container( 116 | decoration: 117 | new BoxDecoration(color: Colors.white.withOpacity(0.5))), 118 | ), 119 | Image.file( 120 | File( 121 | basic.imagePath, 122 | ), 123 | fit: BoxFit.fitWidth, 124 | ), 125 | ], 126 | ); 127 | } 128 | 129 | Widget createSmileyWidget(SmileyData basic) { 130 | return Image.asset( 131 | basic.smileyPath, 132 | height: 100.0, 133 | width: 100.0, 134 | ); 135 | } 136 | 137 | Future capturePNG() async { 138 | try { 139 | print('inside'); 140 | RenderRepaintBoundary boundary = 141 | (_globalKey.currentContext.findRenderObject()); 142 | 143 | ui.Image image = await boundary.toImage(pixelRatio: 3.0); 144 | ByteData byteData = 145 | await image.toByteData(format: ui.ImageByteFormat.png); 146 | var pngBytes = byteData.buffer.asUint8List(); 147 | final directory = await getTemporaryDirectory(); 148 | final String dirPath = '${directory.path}/Pictures'; 149 | final String filePath = '$dirPath/${timestamp()}.png'; 150 | print(filePath); 151 | File file = File(filePath); 152 | await file.writeAsBytes(pngBytes); 153 | await StoriesController.storeImage(file.readAsBytesSync()); 154 | return true; 155 | } catch (e) { 156 | print("False"); 157 | print(e); 158 | return false; 159 | } 160 | } 161 | 162 | Future get _imgFile async { 163 | final directory = await getExternalStorageDirectory(); 164 | final String dirPath = '${directory.path}/Pictures/flutter_test'; 165 | await Directory(dirPath).create(recursive: true); 166 | final String filePath = '$dirPath/${timestamp()}.png'; 167 | File file = File(filePath); 168 | file.create(recursive: true); 169 | print("Path ${file.path}"); 170 | return file; 171 | } 172 | 173 | String timestamp() => DateTime.now().millisecondsSinceEpoch.toString(); 174 | } 175 | 176 | class StoryCreateController { 177 | _StoryCreateWidgetState _state; 178 | 179 | StoryCreateController._(this._state); 180 | 181 | Future createStory() { 182 | return _state.capturePNG(); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /lib/screens/splash_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/controllers/user_controller.dart'; 2 | import 'package:chat_app/widgets/login_button.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | import 'package:flutter_facebook_login/flutter_facebook_login.dart'; 6 | import 'package:firebase_auth/firebase_auth.dart'; 7 | import 'package:onesignal/onesignal.dart'; 8 | 9 | class SplashScreen extends StatefulWidget { 10 | @override 11 | State createState() => _SplashScreenState(); 12 | } 13 | 14 | class _SplashScreenState extends State { 15 | bool isLoggedIn; 16 | @override 17 | void initState() { 18 | super.initState(); 19 | SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark.copyWith( 20 | statusBarColor: Colors.white, statusBarBrightness: Brightness.dark)); 21 | UserController.isLoggedIn().then((val) { 22 | setState(() { 23 | isLoggedIn = val; 24 | }); 25 | if (isLoggedIn) { 26 | Navigator.of(context).pushNamed('/main'); 27 | } 28 | }); 29 | WidgetsBinding.instance 30 | .addObserver(LifecycleEventHandler(resumeCallBack: () async { 31 | print("Resume"); 32 | UserController.updateUser(true); 33 | }, suspendingCallBack: () async { 34 | print("Stopped"); 35 | UserController.updateUser(false); 36 | })); 37 | OneSignal.shared.init("48eaffb3-f899-4b01-8de5-c60029af39dc"); 38 | OneSignal.shared 39 | .setInFocusDisplayType(OSNotificationDisplayType.notification); 40 | } 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | double imageSize = MediaQuery.of(context).size.width / 3; 45 | return Scaffold( 46 | body: Center( 47 | child: Image.asset( 48 | "images/logo.png", 49 | width: imageSize, 50 | height: imageSize, 51 | fit: BoxFit.cover, 52 | ), 53 | ), 54 | bottomNavigationBar: Padding( 55 | padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), 56 | child: Column( 57 | mainAxisSize: MainAxisSize.min, 58 | children: [ 59 | isLoggedIn == null 60 | ? CircularProgressIndicator() 61 | : !isLoggedIn 62 | ? LoginButton("Continue with Facebook", 63 | color: Color(0xff3b5998), 64 | onPressed: initiateFacebookLogin, 65 | leading: Image.network( 66 | "http://www.logospng.com/images/16/social-facebook-1489-1488-1494-1500-1497-1491-1505-1493-1499-1504-1493-1514-1508-1512-1505-1493-1501-1491-1497-1490-1497-1496-1500-1497-buzzlead-16424.png", 67 | color: Colors.white, 68 | width: 24.0, 69 | height: 24.0, 70 | )) 71 | : Container( 72 | width: 0.0, 73 | height: 0.0, 74 | ), 75 | Padding( 76 | padding: const EdgeInsets.only(top: 20.0), 77 | child: Text( 78 | "By continuing, You agree to our Private Policy.", 79 | style: TextStyle( 80 | color: Colors.black45, 81 | fontSize: 14.0, 82 | fontWeight: FontWeight.w500), 83 | textAlign: TextAlign.center, 84 | ), 85 | ) 86 | ], 87 | ), 88 | ), 89 | ); 90 | } 91 | 92 | void initiateFacebookLogin() async { 93 | var facebookLogin = FacebookLogin(); 94 | var result = await facebookLogin 95 | .logInWithReadPermissions(['email', 'public_profile']); 96 | switch (result.status) { 97 | case FacebookLoginStatus.error: 98 | print("Error"); 99 | break; 100 | case FacebookLoginStatus.cancelledByUser: 101 | print("CancelledByUser"); 102 | break; 103 | case FacebookLoginStatus.loggedIn: 104 | final token = result.accessToken.token; 105 | final AuthCredential credential = 106 | FacebookAuthProvider.getCredential(accessToken: token); 107 | print("Success"); 108 | final AuthResult authResult = 109 | await FirebaseAuth.instance.signInWithCredential(credential); 110 | FirebaseUser user = authResult.user; 111 | print("User $user"); 112 | setState(() { 113 | isLoggedIn = true; 114 | }); 115 | await UserController.updateUser(true); 116 | if (user != null) { 117 | Navigator.of(context).pushNamed('/main'); 118 | } 119 | break; 120 | } 121 | } 122 | } 123 | 124 | class LifecycleEventHandler extends WidgetsBindingObserver { 125 | LifecycleEventHandler({this.resumeCallBack, this.suspendingCallBack}); 126 | 127 | final Function resumeCallBack; 128 | final Function suspendingCallBack; 129 | 130 | // @override 131 | // Future didPopRoute() 132 | 133 | // @override 134 | // void didHaveMemoryPressure() 135 | 136 | @override 137 | Future didChangeAppLifecycleState(AppLifecycleState state) async { 138 | switch (state) { 139 | case AppLifecycleState.inactive: 140 | case AppLifecycleState.paused: 141 | await suspendingCallBack(); 142 | break; 143 | case AppLifecycleState.resumed: 144 | await resumeCallBack(); 145 | break; 146 | default: 147 | return; 148 | } 149 | } 150 | 151 | // @override 152 | // void didChangeLocale(Locale locale) 153 | 154 | // @override 155 | // void didChangeTextScaleFactor() 156 | 157 | // @override 158 | // void didChangeMetrics(); 159 | 160 | // @override 161 | // Future didPushRoute(String route) 162 | } 163 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/xcode,android,flutter,androidstudio 3 | # Edit at https://www.gitignore.io/?templates=xcode,android,flutter,androidstudio 4 | 5 | ### Android ### 6 | # Built application files 7 | *.apk 8 | *.ap_ 9 | *.aab 10 | 11 | # Files for the ART/Dalvik VM 12 | *.dex 13 | 14 | # Java class files 15 | *.class 16 | 17 | # Generated files 18 | bin/ 19 | gen/ 20 | out/ 21 | release/ 22 | 23 | # Gradle files 24 | .gradle/ 25 | build/ 26 | 27 | # Local configuration file (sdk path, etc) 28 | local.properties 29 | 30 | # Proguard folder generated by Eclipse 31 | proguard/ 32 | 33 | # Log Files 34 | *.log 35 | 36 | # Android Studio Navigation editor temp files 37 | .navigation/ 38 | 39 | # Android Studio captures folder 40 | captures/ 41 | 42 | # IntelliJ 43 | *.iml 44 | .idea/workspace.xml 45 | .idea/tasks.xml 46 | .idea/gradle.xml 47 | .idea/assetWizardSettings.xml 48 | .idea/dictionaries 49 | .idea/libraries 50 | # Android Studio 3 in .gitignore file. 51 | .idea/caches 52 | .idea/modules.xml 53 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 54 | .idea/navEditor.xml 55 | 56 | # Keystore files 57 | # Uncomment the following lines if you do not want to check your keystore files in. 58 | #*.jks 59 | #*.keystore 60 | 61 | # External native build folder generated in Android Studio 2.2 and later 62 | .externalNativeBuild 63 | 64 | # Google Services (e.g. APIs or Firebase) 65 | # google-services.json 66 | 67 | # Freeline 68 | freeline.py 69 | freeline/ 70 | freeline_project_description.json 71 | 72 | # fastlane 73 | fastlane/report.xml 74 | fastlane/Preview.html 75 | fastlane/screenshots 76 | fastlane/test_output 77 | fastlane/readme.md 78 | 79 | # Version control 80 | vcs.xml 81 | 82 | # lint 83 | lint/intermediates/ 84 | lint/generated/ 85 | lint/outputs/ 86 | lint/tmp/ 87 | # lint/reports/ 88 | 89 | ### Android Patch ### 90 | gen-external-apklibs 91 | output.json 92 | 93 | # Replacement of .externalNativeBuild directories introduced 94 | # with Android Studio 3.5. 95 | .cxx/ 96 | 97 | ### Flutter ### 98 | # Flutter/Dart/Pub related 99 | **/doc/api/ 100 | .dart_tool/ 101 | .flutter-plugins 102 | .flutter-plugins-dependencies 103 | .packages 104 | .pub-cache/ 105 | .pub/ 106 | 107 | # Android related 108 | **/android/**/gradle-wrapper.jar 109 | **/android/.gradle 110 | **/android/captures/ 111 | **/android/gradlew 112 | **/android/gradlew.bat 113 | **/android/local.properties 114 | **/android/**/GeneratedPluginRegistrant.java 115 | 116 | # iOS/XCode related 117 | **/ios/**/*.mode1v3 118 | **/ios/**/*.mode2v3 119 | **/ios/**/*.moved-aside 120 | **/ios/**/*.pbxuser 121 | **/ios/**/*.perspectivev3 122 | **/ios/**/*sync/ 123 | **/ios/**/.sconsign.dblite 124 | **/ios/**/.tags* 125 | **/ios/**/.vagrant/ 126 | **/ios/**/DerivedData/ 127 | **/ios/**/Icon? 128 | **/ios/**/Pods/ 129 | **/ios/**/.symlinks/ 130 | **/ios/**/profile 131 | **/ios/**/xcuserdata 132 | **/ios/.generated/ 133 | **/ios/Flutter/App.framework 134 | **/ios/Flutter/Flutter.framework 135 | **/ios/Flutter/Generated.xcconfig 136 | **/ios/Flutter/app.flx 137 | **/ios/Flutter/app.zip 138 | **/ios/Flutter/flutter_assets/ 139 | **/ios/ServiceDefinitions.json 140 | **/ios/Runner/GeneratedPluginRegistrant.* 141 | 142 | # Exceptions to above rules. 143 | !**/ios/**/default.mode1v3 144 | !**/ios/**/default.mode2v3 145 | !**/ios/**/default.pbxuser 146 | !**/ios/**/default.perspectivev3 147 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 148 | 149 | ### Xcode ### 150 | # Xcode 151 | # 152 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 153 | 154 | ## User settings 155 | xcuserdata/ 156 | 157 | ## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) 158 | *.xcscmblueprint 159 | *.xccheckout 160 | 161 | ## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) 162 | DerivedData/ 163 | *.moved-aside 164 | *.pbxuser 165 | !default.pbxuser 166 | *.mode1v3 167 | !default.mode1v3 168 | *.mode2v3 169 | !default.mode2v3 170 | *.perspectivev3 171 | !default.perspectivev3 172 | 173 | ## Xcode Patch 174 | *.xcodeproj/* 175 | !*.xcodeproj/project.pbxproj 176 | !*.xcodeproj/xcshareddata/ 177 | !*.xcworkspace/contents.xcworkspacedata 178 | /*.gcno 179 | 180 | ### Xcode Patch ### 181 | **/xcshareddata/WorkspaceSettings.xcsettings 182 | 183 | ### AndroidStudio ### 184 | # Covers files to be ignored for android development using Android Studio. 185 | 186 | # Built application files 187 | 188 | # Files for the ART/Dalvik VM 189 | 190 | # Java class files 191 | 192 | # Generated files 193 | 194 | # Gradle files 195 | .gradle 196 | 197 | # Signing files 198 | .signing/ 199 | 200 | # Local configuration file (sdk path, etc) 201 | 202 | # Proguard folder generated by Eclipse 203 | 204 | # Log Files 205 | 206 | # Android Studio 207 | /*/build/ 208 | /*/local.properties 209 | /*/out 210 | /*/*/build 211 | /*/*/production 212 | *.ipr 213 | *~ 214 | *.swp 215 | 216 | # Android Patch 217 | 218 | # External native build folder generated in Android Studio 2.2 and later 219 | 220 | # NDK 221 | obj/ 222 | 223 | # IntelliJ IDEA 224 | *.iws 225 | /out/ 226 | 227 | # User-specific configurations 228 | .idea/caches/ 229 | .idea/libraries/ 230 | .idea/shelf/ 231 | .idea/.name 232 | .idea/compiler.xml 233 | .idea/copyright/profiles_settings.xml 234 | .idea/encodings.xml 235 | .idea/misc.xml 236 | .idea/scopes/scope_settings.xml 237 | .idea/vcs.xml 238 | .idea/jsLibraryMappings.xml 239 | .idea/datasources.xml 240 | .idea/dataSources.ids 241 | .idea/sqlDataSources.xml 242 | .idea/dynamic.xml 243 | .idea/uiDesigner.xml 244 | 245 | # OS-specific files 246 | .DS_Store 247 | .DS_Store? 248 | ._* 249 | .Spotlight-V100 250 | .Trashes 251 | ehthumbs.db 252 | Thumbs.db 253 | 254 | # Legacy Eclipse project files 255 | .classpath 256 | .project 257 | .cproject 258 | .settings/ 259 | 260 | # Mobile Tools for Java (J2ME) 261 | .mtj.tmp/ 262 | 263 | # Package Files # 264 | *.war 265 | *.ear 266 | 267 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) 268 | hs_err_pid* 269 | 270 | ## Plugin-specific files: 271 | 272 | # mpeltonen/sbt-idea plugin 273 | .idea_modules/ 274 | 275 | # JIRA plugin 276 | atlassian-ide-plugin.xml 277 | 278 | # Mongo Explorer plugin 279 | .idea/mongoSettings.xml 280 | 281 | # Crashlytics plugin (for Android Studio and IntelliJ) 282 | com_crashlytics_export_strings.xml 283 | crashlytics.properties 284 | crashlytics-build.properties 285 | fabric.properties 286 | 287 | ### AndroidStudio Patch ### 288 | 289 | !/gradle/wrapper/gradle-wrapper.jar 290 | 291 | # End of https://www.gitignore.io/api/xcode,android,flutter,androidstudio -------------------------------------------------------------------------------- /lib/screens/main_screen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:chat_app/controllers/user_controller.dart'; 4 | import 'package:chat_app/screens/people_screen.dart'; 5 | import 'package:chat_app/widgets/ease_in_widget.dart'; 6 | import 'package:chat_app/widgets/profile_picture_widget.dart'; 7 | import 'package:chat_app/widgets/search_widget.dart'; 8 | import 'package:flutter/material.dart'; 9 | 10 | import 'chats_screen.dart'; 11 | import 'discover_screen.dart'; 12 | 13 | class MainScreen extends StatefulWidget { 14 | 15 | @override 16 | State createState() => _MainScreenState(); 17 | } 18 | 19 | class _MainScreenState extends State { 20 | ScrollController controller; 21 | double elevation = 0.0; 22 | Listscreens=[]; 23 | Listtitles=[ 24 | "Chats","People","Discover" 25 | ]; 26 | @override 27 | void initState() { 28 | super.initState(); 29 | UserController.open(); 30 | screens.add(ChatsScreen()); 31 | screens.add(PeopleScreen()); 32 | screens.add(DiscoverScreen()); 33 | controller = ScrollController(); 34 | controller.addListener(() { 35 | if (controller.offset > 0.0) { 36 | setState(() { 37 | elevation = 2.5; 38 | }); 39 | } else { 40 | setState(() { 41 | elevation = 0.0; 42 | }); 43 | } 44 | }); 45 | } 46 | int current=0; 47 | @override 48 | Widget build(BuildContext context) { 49 | print("$current"); 50 | return WillPopScope( 51 | onWillPop: (){ 52 | showDialog(context: context,builder: (context)=>AlertDialog( 53 | title: Text("Do you want to logout"), 54 | actions: [ 55 | FlatButton(onPressed: (){ 56 | Navigator.of(context).popUntil(ModalRoute.withName('/main')); 57 | }, child: Text("Cancel")), 58 | FlatButton(onPressed: (){ 59 | exit(0); 60 | }, child: Text("Ok")) 61 | ], 62 | )); 63 | }, 64 | child: Scaffold( 65 | backgroundColor: Colors.white, 66 | body: Container( 67 | color: Colors.white, 68 | padding: const EdgeInsets.symmetric(vertical: 8.0), 69 | child: Scaffold( 70 | appBar: AppBar( 71 | elevation: elevation, 72 | textTheme: TextTheme( 73 | title: TextStyle( 74 | fontSize: 26.0, 75 | fontWeight: FontWeight.w700, 76 | color: Colors.black)), 77 | automaticallyImplyLeading: false, 78 | title: Row( 79 | mainAxisAlignment: MainAxisAlignment.start, 80 | mainAxisSize: MainAxisSize.min, 81 | children: [ 82 | Padding( 83 | padding: const EdgeInsets.all(8.0), 84 | child: GestureDetector( 85 | onTap: (){ 86 | showModalBottomSheet(context: context,builder: (context)=>ListTile( 87 | leading:Icon(Icons.input), 88 | title: Text("Logout"), 89 | trailing: Icon(Icons.chevron_right), 90 | onTap: ()async{ 91 | await UserController.logOut(); 92 | Navigator.of(context).pop(); 93 | Navigator.of(context).popAndPushNamed('/'); 94 | }, 95 | )); 96 | }, 97 | child: ProfilePictureWidget( 98 | size: 40.0, 99 | ), 100 | ), 101 | ), 102 | Padding( 103 | padding: const EdgeInsets.only(left: 6.0), 104 | child: Text(titles[current]), 105 | ), 106 | ], 107 | ), 108 | actions: [ 109 | EaseInWidget( 110 | onTap: (){ 111 | Navigator.of(context).pushNamed('/camera'); 112 | }, 113 | child: Container( 114 | decoration: BoxDecoration( 115 | color: Color(0xffeeeeee), 116 | shape: BoxShape.circle, 117 | ), 118 | padding: EdgeInsets.all(10.0), 119 | child: Icon( 120 | Icons.camera_alt, 121 | size: 20.0, 122 | ), 123 | ), 124 | ), 125 | Container( 126 | width: 16.0, 127 | ), 128 | EaseInWidget( 129 | onTap: (){ 130 | Navigator.of(context).pushNamed('/search'); 131 | }, 132 | child: Container( 133 | decoration: BoxDecoration( 134 | color: Color(0xffeeeeee), 135 | shape: BoxShape.circle, 136 | ), 137 | padding: EdgeInsets.all(10.0), 138 | child: Icon( 139 | Icons.edit, 140 | size: 20.0, 141 | ), 142 | ), 143 | ), 144 | Container( 145 | width: 18.0, 146 | ), 147 | ], 148 | bottom: PreferredSize( 149 | child: Container( 150 | height: 5.0, 151 | ), 152 | preferredSize: Size(MediaQuery.of(context).size.width, 5.0)), 153 | ), 154 | body: SingleChildScrollView( 155 | controller: controller, 156 | child: Column( 157 | mainAxisSize: MainAxisSize.min, 158 | children: [ 159 | SearchWidget(), 160 | screens[current] 161 | ], 162 | ), 163 | ), 164 | ), 165 | ), 166 | bottomNavigationBar: BottomAppBar( 167 | 168 | child: Padding( 169 | padding: const EdgeInsets.symmetric(horizontal: 18.0), 170 | child: BottomNavigationBar( 171 | elevation: 0.0, 172 | iconSize: 30.0, 173 | showUnselectedLabels: false, 174 | showSelectedLabels: false, 175 | unselectedItemColor: Colors.black54, 176 | backgroundColor: Colors.white, 177 | selectedItemColor: Colors.black, 178 | currentIndex: current, 179 | onTap: (index){ 180 | setState(() { 181 | current=index; 182 | }); 183 | }, 184 | items: [ 185 | Option(title: "Chats",iconData: Icons.message), 186 | Option(title: "People",iconData: Icons.group), 187 | Option(title: "Explore",iconData: Icons.explore), 188 | ].map((item)=>BottomNavigationBarItem(icon: Icon(item.iconData),title: Text(item.title))).toList(), 189 | ), 190 | ), 191 | ), 192 | ), 193 | ); 194 | } 195 | @override 196 | void dispose() { 197 | super.dispose(); 198 | UserController.updateUser(false); 199 | } 200 | } 201 | 202 | class Option{ 203 | final String title; 204 | final IconData iconData; 205 | 206 | Option({this.title, this.iconData}); 207 | } -------------------------------------------------------------------------------- /lib/widgets/text_add_widget.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:chat_app/models/basic.dart'; 4 | import 'package:chat_app/models/text_data.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter/services.dart'; 7 | 8 | import 'color_box.dart'; 9 | 10 | class TextAddWidget extends StatefulWidget { 11 | final Function(Basic) onSelected; 12 | 13 | const TextAddWidget({Key key,@required this.onSelected}) : super(key: key); 14 | @override 15 | State createState() => _TextAddWidgetState(); 16 | } 17 | 18 | class _TextAddWidgetState extends State { 19 | List colors = [ 20 | Colors.redAccent, 21 | Color(0xffE4E4E4), 22 | Colors.greenAccent, 23 | Colors.white, 24 | Colors.black, 25 | Color(0xff222930) 26 | ]; 27 | List boxes = []; 28 | int current = 0; 29 | bool filled=true; 30 | TextAlign textAlign=TextAlign.left; 31 | Widget textWidget; 32 | @override 33 | void initState() { 34 | super.initState(); 35 | for (int i = 0; i < colors.length; i++) { 36 | boxes.add(ColorBox( 37 | color: colors[i], 38 | size: 25.0, 39 | onTap: () { 40 | onTap(i); 41 | })); 42 | } 43 | controller.addListener(() { 44 | setState(() {}); 45 | }); 46 | WidgetsBinding.instance.addPostFrameCallback((val){ 47 | focusNode.requestFocus(); 48 | }); 49 | } 50 | 51 | void onTap(int i) { 52 | current = i; 53 | setState(() { 54 | for (int i = 0; i < colors.length; i++) { 55 | if (i != current) { 56 | boxes.removeAt(i); 57 | boxes.insert( 58 | i, 59 | ColorBox( 60 | color: colors[i], 61 | size: 25.0, 62 | smallBorder: true, 63 | onTap: () { 64 | onTap(i); 65 | }, 66 | )); 67 | } else { 68 | boxes.removeAt(i); 69 | boxes.insert( 70 | i, 71 | ColorBox( 72 | color: colors[i], 73 | size: 25.0, 74 | smallBorder: false, 75 | onTap: () { 76 | onTap(i); 77 | }, 78 | )); 79 | } 80 | } 81 | }); 82 | } 83 | BoxDecoration decoration; 84 | TextStyle style; 85 | FocusNode focusNode=FocusNode(); 86 | TextEditingController controller = TextEditingController(); 87 | @override 88 | Widget build(BuildContext context) { 89 | style=TextStyle( 90 | fontSize: 24.0, 91 | fontWeight: FontWeight.w700, 92 | color: filled?textColor:colors[current], 93 | fontFamily: "B612Mono"); 94 | decoration=BoxDecoration( 95 | color: filled?colors[current]:Colors.transparent, 96 | borderRadius: BorderRadius.circular(8.0), 97 | ); 98 | textWidget= Container( 99 | padding: TextData.TEXT_PADDING, 100 | constraints: BoxConstraints( 101 | maxWidth: MediaQuery.of(context).size.width * 3 / 4, 102 | minWidth: 45.0), 103 | width: (textSize) * 21.0, 104 | decoration: decoration, 105 | child: TextField( 106 | focusNode: focusNode, 107 | controller: controller, 108 | maxLines: null, 109 | expands: false, 110 | cursorColor: textColor, 111 | textAlign: textAlign, 112 | decoration: InputDecoration( 113 | border: InputBorder.none, 114 | ), 115 | style: style, 116 | ), 117 | ); 118 | return WillPopScope( 119 | onWillPop: (){ 120 | widget.onSelected(null); 121 | }, 122 | child: SafeArea( 123 | child: Scaffold( 124 | backgroundColor: Colors.transparent, 125 | body: Stack( 126 | fit: StackFit.expand, 127 | alignment: Alignment.topRight, 128 | children: [ 129 | Center( 130 | child: 131 | textWidget, 132 | ), 133 | Padding( 134 | padding: const EdgeInsets.only(top: 8.0), 135 | child: Column( 136 | mainAxisSize: MainAxisSize.min, 137 | crossAxisAlignment: CrossAxisAlignment.end, 138 | children: [ 139 | FlatButton(onPressed: (){ 140 | popBack(); 141 | }, child: Text("Done",style: TextStyle(color: Colors.white,fontSize: 16.0),)), 142 | buildSelection(), 143 | Padding( 144 | padding: const EdgeInsets.symmetric(vertical: 12.0), 145 | child: buildTextOutline(filled), 146 | ), 147 | buildAlignmentButton(textAlign) 148 | ], 149 | ), 150 | ) 151 | ], 152 | ), 153 | bottomNavigationBar: Container( 154 | height: 60.0, 155 | alignment: Alignment.center, 156 | child: ListView.builder( 157 | itemBuilder: (context, index) => boxes[index], 158 | itemCount: boxes.length, 159 | shrinkWrap: true, 160 | scrollDirection: Axis.horizontal, 161 | ), 162 | ), 163 | ), 164 | ), 165 | ); 166 | } 167 | 168 | get textColor { 169 | var o = (((colors[current].red * 299) + 170 | (colors[current].green * 587).toInt() + 171 | ((colors[current].blue) * 114).toInt()) / 172 | 1000); 173 | return (o > 125) ? Colors.black : Colors.white; 174 | } 175 | 176 | get textSize { 177 | int i = 0; 178 | if(controller.text.length==0){ 179 | return 1; 180 | } 181 | controller.text.split("\n").forEach((val) { 182 | i = max(val.length, i); 183 | }); 184 | return i; 185 | } 186 | void popBack(){ 187 | if(controller.text==null||controller.text.length==0){ 188 | widget.onSelected(null); 189 | return; 190 | } 191 | widget.onSelected(TextData(data: controller.text, decoration: decoration, textStyle: style)); 192 | } 193 | Widget buildSelection() { 194 | return RaisedButton( 195 | onPressed: popBack, 196 | color: Colors.white, 197 | shape: CircleBorder(), 198 | child: Icon( 199 | Icons.text_fields, 200 | color: Colors.black, 201 | size: 28.0, 202 | ), 203 | padding: EdgeInsets.all(8.0), 204 | elevation: 1.0, 205 | ); 206 | } 207 | 208 | Widget buildTextOutline(bool check) { 209 | return RaisedButton( 210 | elevation: 0.0, 211 | shape: CircleBorder(), 212 | color: Colors.black54, 213 | onPressed: (){ 214 | setState(() { 215 | filled=!filled; 216 | }); 217 | }, 218 | padding: EdgeInsets.all(12.0), 219 | child: ImageIcon( 220 | AssetImage(check?"images/font_filled.png":"images/font_outline.png"), 221 | size: 20.0, 222 | color: Colors.white, 223 | ), 224 | ); 225 | } 226 | List aligns=[TextAlign.left,TextAlign.center,TextAlign.right]; 227 | int currentAlign=0; 228 | Widget buildAlignmentButton(TextAlign align){ 229 | Map icons={ 230 | TextAlign.center:Icons.format_align_center, 231 | TextAlign.left:Icons.format_align_left, 232 | TextAlign.right:Icons.format_align_right, 233 | }; 234 | return RaisedButton( 235 | elevation: 0.0, 236 | shape: CircleBorder(), 237 | color: Colors.black54, 238 | onPressed: (){ 239 | setState(() { 240 | textAlign=aligns[(++currentAlign)%3]; 241 | }); 242 | }, 243 | padding: EdgeInsets.all(12.0), 244 | child: Icon( 245 | icons[align], 246 | size: 20.0, 247 | color: Colors.white, 248 | ), 249 | ); 250 | } 251 | @override 252 | void dispose() { 253 | super.dispose(); 254 | SystemChrome.restoreSystemUIOverlays(); 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /lib/screens/chat_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:chat_app/controllers/chats_controller.dart'; 2 | import 'package:chat_app/controllers/user_controller.dart'; 3 | import 'package:chat_app/models/chat.dart'; 4 | import 'package:chat_app/models/user.dart'; 5 | import 'package:chat_app/widgets/chat_input_widget.dart'; 6 | import 'package:chat_app/widgets/chat_widget.dart'; 7 | import 'package:flutter/cupertino.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:emoji_picker/emoji_picker.dart'; 10 | import 'package:flutter/services.dart'; 11 | 12 | class ChatScreen extends StatefulWidget { 13 | final User friend; 14 | 15 | ChatScreen({ 16 | Key key, 17 | @required this.friend, 18 | }) : super(key: key); 19 | 20 | @override 21 | State createState() => _ChatScreenState(); 22 | } 23 | 24 | class _ChatScreenState extends State { 25 | ScrollController scrollController = ScrollController(); 26 | final GlobalKey _key = GlobalKey(); 27 | final GlobalKey _scaffoldKey = GlobalKey(); 28 | List chats = []; 29 | User currentUser; 30 | bool isHeader = true; 31 | 32 | @override 33 | void initState() { 34 | super.initState(); 35 | initUser(); 36 | WidgetsBinding.instance.addPostFrameCallback((val) { 37 | scrollController.addListener((){ 38 | final RenderBox renderBox=_key.currentContext.findRenderObject(); 39 | final size=renderBox.size; 40 | double height=size.height*2/3; 41 | if (scrollController.hasClients) 42 | // scrollController.animateTo(scrollController.position.maxScrollExtent, 43 | // duration: Duration(milliseconds: 100), curve: Curves.easeIn); 44 | 45 | if (scrollController.offset >= height ) { 46 | if (mounted) { 47 | setState(() { 48 | isHeader = false; 49 | }); 50 | } 51 | } else if (!isHeader) { 52 | if (mounted) { 53 | setState(() { 54 | isHeader = true; 55 | }); 56 | } 57 | } 58 | }); 59 | 60 | }); 61 | } 62 | 63 | void initUser() async { 64 | currentUser = await UserController.getUser(); 65 | if (mounted) { 66 | setState(() => 0); 67 | } 68 | } 69 | 70 | @override 71 | Widget build(BuildContext context) { 72 | return Scaffold( 73 | key: _scaffoldKey, 74 | appBar: AppBar( 75 | elevation: 1.0, 76 | automaticallyImplyLeading: false, 77 | // leading: 78 | // IconButton( 79 | // icon: Icon(Icons.arrow_back), 80 | // onPressed: () { 81 | // Navigator.of(context).pop(); 82 | // }, 83 | // color: Theme.of(context).primaryColor, 84 | // ), 85 | title: Row( 86 | mainAxisSize: MainAxisSize.min, 87 | children: [ 88 | IconButton( 89 | icon: Icon(Icons.arrow_back), 90 | onPressed: () { 91 | Navigator.of(context).pop(); 92 | }, 93 | color: Theme.of(context).primaryColor, 94 | ), 95 | isHeader?Container(width: 0.0,):Row( 96 | mainAxisSize: MainAxisSize.min, 97 | children: [ 98 | CircleAvatar( 99 | backgroundImage: NetworkImage( 100 | widget.friend.profilePicture, 101 | ), 102 | radius: 14.0, 103 | ), 104 | Padding( 105 | padding: EdgeInsets.only(left: 8.00), 106 | child: Text( 107 | widget.friend.displayName, 108 | style: TextStyle( 109 | color: Colors.black, 110 | fontSize: 18.0, 111 | fontWeight: FontWeight.normal), 112 | ), 113 | ) 114 | ], 115 | ) 116 | ], 117 | ), 118 | actions: [ 119 | IconButton( 120 | icon: Icon(Icons.call), 121 | onPressed: () {}, 122 | color: Theme.of(context).primaryColor, 123 | ), 124 | IconButton( 125 | icon: Icon(Icons.videocam), 126 | onPressed: () {}, 127 | color: Theme.of(context).primaryColor, 128 | ), 129 | PopupMenuButton( 130 | itemBuilder: (context) => [ 131 | PopupMenuItem( 132 | child: Text("Info"), 133 | value: "Info", 134 | ) 135 | ], 136 | icon: Icon( 137 | Icons.more_vert, 138 | color: Theme.of(context).primaryColor, 139 | ), 140 | ) 141 | ], 142 | ), 143 | body: currentUser == null 144 | ? Center( 145 | child: CircularProgressIndicator(), 146 | ) 147 | : Column( 148 | children: [ 149 | Flexible(child: buildChats()), 150 | ChatInputWidget( 151 | onSubmitted: (val) { 152 | if(currentUser.id==widget.friend.id){ 153 | _scaffoldKey.currentState.showSnackBar(SnackBar(content: Text("You can not send message to you"))); 154 | return; 155 | } 156 | Chat chat = Chat.fromNamed( 157 | from: currentUser, 158 | to: widget.friend, 159 | content: val, 160 | isSeen: false, 161 | publishedAt: DateTime.now()); 162 | ChatsController.sendMessage(chat); 163 | scrollController.animateTo( 164 | scrollController.position.maxScrollExtent, 165 | duration: Duration(milliseconds: 100), 166 | curve: Curves.easeIn); 167 | setState(() { 168 | chats.add(chat); 169 | }); 170 | }, 171 | ) 172 | ], 173 | ), 174 | ); 175 | } 176 | 177 | Widget buildHeader() { 178 | return Container( 179 | key: _key, 180 | padding: const EdgeInsets.symmetric(vertical: 18.0, horizontal: 12.0), 181 | child: Column( 182 | children: [ 183 | ClipRRect( 184 | borderRadius: BorderRadius.circular(125.0), 185 | child: Image.network( 186 | widget.friend.profilePicture, 187 | height: 125.0, 188 | fit: BoxFit.cover, 189 | ), 190 | ), 191 | Padding( 192 | padding: const EdgeInsets.only(top: 8.0), 193 | child: Text( 194 | widget.friend.displayName, 195 | style: TextStyle(fontSize: 20.0, fontWeight: FontWeight.w700), 196 | ), 197 | ), 198 | Padding( 199 | padding: const EdgeInsets.only(top: 4.0), 200 | child: Text( 201 | "${widget.friend.id}", 202 | style: TextStyle( 203 | fontSize: 14.0, 204 | fontWeight: FontWeight.w500, 205 | color: Colors.black54), 206 | ), 207 | ), 208 | ], 209 | ), 210 | ); 211 | } 212 | 213 | Widget buildChats() { 214 | return StreamBuilder( 215 | stream: ChatsController.listenChat(currentUser, widget.friend), 216 | builder: (context, snapshot) { 217 | if (!snapshot.hasData || snapshot.data == null) { 218 | return Center( 219 | child: CircularProgressIndicator(), 220 | ); 221 | } 222 | List chats = snapshot.data; 223 | return ListView.builder( 224 | controller: scrollController, 225 | itemBuilder: (context, index) { 226 | if (index == 0) { 227 | return buildHeader(); 228 | } 229 | return ChatWidget( 230 | chat: chats[index - 1], 231 | isReceived: currentUser.id != chats[index - 1].from.id, 232 | showUser: (index == 1) || 233 | (index >= 2 && 234 | !(chats[index - 1].from.id == 235 | chats[index - 2].from.id)), 236 | ); 237 | }, 238 | itemCount: chats.length + 1, 239 | ); 240 | }); 241 | } 242 | 243 | @override 244 | void dispose() { 245 | super.dispose(); 246 | } 247 | } 248 | -------------------------------------------------------------------------------- /lib/screens/story_create_screen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:chat_app/controllers/image_compresser.dart'; 4 | import 'package:chat_app/models/basic.dart'; 5 | import 'package:chat_app/models/image_data.dart'; 6 | import 'package:chat_app/widgets/smiley_add_widget.dart'; 7 | import 'package:chat_app/widgets/story_create_widget.dart'; 8 | import 'package:chat_app/widgets/text_add_widget.dart'; 9 | import 'package:flutter/material.dart'; 10 | 11 | class StoryCreateScreen extends StatefulWidget { 12 | final String imagePath; 13 | 14 | const StoryCreateScreen({Key key, @required this.imagePath}) 15 | : super(key: key); 16 | @override 17 | State createState() => _StoryCreateScreenState(); 18 | } 19 | 20 | class _StoryCreateScreenState extends State { 21 | List children = []; 22 | final GlobalKey key = GlobalKey(); 23 | List stackChildren = []; 24 | StoryCreateController controller; 25 | @override 26 | void initState() { 27 | super.initState(); 28 | children.add(ImageData(imagePath: widget.imagePath)); 29 | WidgetsBinding.instance.addPostFrameCallback((_) { 30 | offset = Offset(MediaQuery.of(context).size.width / 2 - 32.0, 31 | MediaQuery.of(context).size.height - 75.0); 32 | setState(() { 33 | stackChildren.add( 34 | StoryCreateWidget( 35 | children: children, 36 | deleteOffset: offset, 37 | onCompleted: (controller) { 38 | this.controller = controller; 39 | }, 40 | ), 41 | ); 42 | stackChildren.add(buildOptionsScreen()); 43 | }); 44 | }); 45 | } 46 | 47 | Offset offset; 48 | bool adding=false; 49 | @override 50 | Widget build(BuildContext context) { 51 | print("Children baby ${children.length}"); 52 | List widgets = []; 53 | widgets.addAll(stackChildren); 54 | if(!adding) 55 | widgets.add(Positioned( 56 | top: offset.dy, 57 | left: offset.dx, 58 | child: FloatingActionButton( 59 | onPressed: () {}, 60 | child: Icon( 61 | Icons.delete_forever, 62 | color: Colors.black, 63 | ), 64 | backgroundColor: Colors.white, 65 | ))); 66 | return Scaffold( 67 | key: key, 68 | backgroundColor: Colors.transparent, 69 | body: Stack( 70 | fit: StackFit.expand, 71 | children: widgets, 72 | ), 73 | ); 74 | } 75 | 76 | Widget buildButton(IconData iconData, Function onPressed) { 77 | return Padding( 78 | padding: const EdgeInsets.all(6.0), 79 | child: IconButton( 80 | onPressed: onPressed, 81 | color: Colors.black26, 82 | icon: Icon( 83 | iconData, 84 | color: Colors.white, 85 | ), 86 | ), 87 | ); 88 | } 89 | 90 | Widget buildOptionsScreen() { 91 | return Container( 92 | width: MediaQuery.of(context).size.width, 93 | height: MediaQuery.of(context).size.height, 94 | child: Column( 95 | crossAxisAlignment: CrossAxisAlignment.stretch, 96 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 97 | children: [ 98 | Row( 99 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 100 | children: [ 101 | IconButton( 102 | icon: Icon( 103 | Icons.clear, 104 | color: Colors.white, 105 | ), 106 | onPressed: () { 107 | Navigator.of(context).maybePop(); 108 | }), 109 | Row( 110 | mainAxisSize: MainAxisSize.min, 111 | children: [ 112 | buildButton(Icons.text_fields, () { 113 | setState(() { 114 | adding=true; 115 | stackChildren.removeLast(); 116 | stackChildren.add(TextAddWidget( 117 | onSelected: onSelected, 118 | )); 119 | }); 120 | }), 121 | buildButton(Icons.tag_faces, () { 122 | setState(() { 123 | adding=true; 124 | stackChildren.removeLast(); 125 | stackChildren.add(SmileyAddWidget( 126 | onSelected: onSelected, 127 | )); 128 | }); 129 | }), 130 | ], 131 | ) 132 | ], 133 | ), 134 | Padding( 135 | padding: const EdgeInsets.all(8.0), 136 | child: Row( 137 | crossAxisAlignment: CrossAxisAlignment.baseline, 138 | textBaseline: TextBaseline.alphabetic, 139 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 140 | children: [ 141 | InkWell( 142 | onTap: () {}, 143 | child: Padding( 144 | padding: const EdgeInsets.all(8.0), 145 | child: Column( 146 | mainAxisSize: MainAxisSize.min, 147 | children: [ 148 | Icon( 149 | Icons.settings, 150 | color: Colors.white, 151 | ), 152 | Padding( 153 | padding: const EdgeInsets.only(top: 8.0), 154 | child: Text( 155 | "Settings", 156 | style: TextStyle( 157 | color: Colors.white, fontSize: 13.0), 158 | ), 159 | ) 160 | ], 161 | ), 162 | ), 163 | ), 164 | RaisedButton( 165 | padding: EdgeInsets.all(16.0), 166 | shape: RoundedRectangleBorder( 167 | borderRadius: BorderRadius.circular(8.0)), 168 | onPressed: () { 169 | if (controller != null) { 170 | showLoadingDialog(); 171 | controller.createStory().then((val) { 172 | Navigator.of(context).pop(); 173 | if (val) { 174 | Navigator.of(context).popUntil(ModalRoute.withName('/main')); 175 | } 176 | else{ 177 | key.currentState.showSnackBar(SnackBar(content: Text("Something workng happened"))); 178 | } 179 | }); 180 | } 181 | }, 182 | child: Row( 183 | mainAxisSize: MainAxisSize.min, 184 | children: [ 185 | Padding( 186 | padding: const EdgeInsets.only(right: 8.0), 187 | child: Text( 188 | "Your story", 189 | style: TextStyle( 190 | fontSize: 15.0, 191 | color: Colors.black, 192 | fontWeight: FontWeight.w500), 193 | ), 194 | ), 195 | Icon(Icons.send) 196 | ], 197 | ), 198 | color: Colors.white, 199 | ), 200 | ], 201 | )) 202 | ])); 203 | } 204 | 205 | void showLoadingDialog() { 206 | showDialog( 207 | context: context, 208 | builder: (context) { 209 | return AlertDialog( 210 | content: Container( 211 | height: 75.0, 212 | child: Row( 213 | children: [ 214 | CircularProgressIndicator(), 215 | Expanded( 216 | child: Padding( 217 | padding: const EdgeInsets.all(8.0), 218 | child: Text("This may take some time"), 219 | )) 220 | ], 221 | ), 222 | ), 223 | ); 224 | }, 225 | barrierDismissible: false); 226 | } 227 | 228 | void onSelected(Basic basic) { 229 | setState(() { 230 | adding=false; 231 | if (basic != null) children.add(basic); 232 | stackChildren.clear(); 233 | stackChildren.add( 234 | StoryCreateWidget( 235 | children: children, 236 | deleteOffset: offset, 237 | onCompleted: (controller) { 238 | this.controller = controller; 239 | }, 240 | ), 241 | ); 242 | stackChildren.add(buildOptionsScreen()); 243 | }); 244 | } 245 | 246 | void pushBack() {} 247 | } 248 | --------------------------------------------------------------------------------