├── ios ├── Flutter │ ├── Debug.xcconfig │ ├── Release.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 │ ├── AppDelegate.m │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Runner.xcworkspace │ └── contents.xcworkspacedata └── Runner.xcodeproj │ ├── project.xcworkspace │ └── contents.xcworkspacedata │ ├── xcshareddata │ └── xcschemes │ │ └── Runner.xcscheme │ └── project.pbxproj ├── .gitattributes ├── screenshots └── 1.png ├── android ├── .settings │ └── org.eclipse.buildship.core.prefs ├── app │ ├── .settings │ │ └── org.eclipse.buildship.core.prefs │ ├── 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 │ │ │ │ │ └── styles.xml │ │ │ │ └── drawable │ │ │ │ │ └── launch_background.xml │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── example │ │ │ │ │ └── flutter_chat_app │ │ │ │ │ └── MainActivity.java │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── .classpath │ ├── .project │ └── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── .project ├── settings.gradle └── build.gradle ├── .metadata ├── lib ├── pages │ ├── gallary_page.dart │ ├── registration_page.dart │ ├── chat_page.dart │ └── home_page.dart └── main.dart ├── test └── widget_test.dart ├── .gitignore ├── pubspec.yaml ├── pubspec.lock └── README.md /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /screenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myvsparth/flutter_chat_app/HEAD/screenshots/1.png -------------------------------------------------------------------------------- /android/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir= 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /android/app/.settings/org.eclipse.buildship.core.prefs: -------------------------------------------------------------------------------- 1 | connection.project.dir=.. 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | 5 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myvsparth/flutter_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/myvsparth/flutter_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/myvsparth/flutter_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/myvsparth/flutter_chat_app/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myvsparth/flutter_chat_app/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myvsparth/flutter_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/myvsparth/flutter_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/myvsparth/flutter_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/myvsparth/flutter_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/myvsparth/flutter_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/myvsparth/flutter_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/myvsparth/flutter_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/myvsparth/flutter_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/myvsparth/flutter_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/myvsparth/flutter_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/myvsparth/flutter_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/myvsparth/flutter_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/myvsparth/flutter_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/myvsparth/flutter_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/myvsparth/flutter_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/myvsparth/flutter_chat_app/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myvsparth/flutter_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/myvsparth/flutter_chat_app/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /.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: e4ebcdf6f4facee5779c38a04d91d08dc58ea7a4 8 | channel: beta 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /android/app/.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/example/flutter_chat_app/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.flutter_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/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | android 4 | Project android created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.buildship.core.gradleprojectbuilder 10 | 11 | 12 | 13 | 14 | 15 | org.eclipse.buildship.core.gradleprojectnature 16 | 17 | 18 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.3.1' 9 | classpath 'com.google.gms:google-services:4.2.0' 10 | } 11 | } 12 | 13 | allprojects { 14 | repositories { 15 | google() 16 | jcenter() 17 | } 18 | } 19 | 20 | rootProject.buildDir = '../build' 21 | subprojects { 22 | project.buildDir = "${rootProject.buildDir}/${project.name}" 23 | } 24 | subprojects { 25 | project.evaluationDependsOn(':app') 26 | } 27 | 28 | task clean(type: Delete) { 29 | delete rootProject.buildDir 30 | } 31 | -------------------------------------------------------------------------------- /android/app/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | app 4 | Project app created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.buildship.core.gradleprojectbuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.buildship.core.gradleprojectnature 22 | 23 | 24 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /lib/pages/gallary_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | import 'package:photo_view/photo_view.dart'; 4 | 5 | class GalleryPage extends StatefulWidget { 6 | final String imagePath; 7 | GalleryPage({this.imagePath}); 8 | 9 | @override 10 | _GalleryPageState createState() => _GalleryPageState(); 11 | } 12 | 13 | class _GalleryPageState extends State { 14 | @override 15 | Widget build(BuildContext context) { 16 | return new Scaffold( 17 | body: Stack( 18 | children: [ 19 | Container( 20 | color: Colors.red, 21 | child: PhotoView( 22 | imageProvider: NetworkImage(widget.imagePath), 23 | ), 24 | ), 25 | Container( 26 | height: 80, 27 | child: AppBar( 28 | backgroundColor: Color.fromRGBO(0, 0, 0, 0.2), 29 | elevation: 0.0, 30 | ), 31 | ), 32 | ], 33 | ), 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_chat_app/pages/home_page.dart'; 3 | import 'package:flutter_chat_app/pages/registration_page.dart'; 4 | import 'package:shared_preferences/shared_preferences.dart'; 5 | 6 | void main() { 7 | SharedPreferences.getInstance().then((prefs) { 8 | runApp(LandingPage(prefs: prefs)); 9 | }); 10 | } 11 | 12 | class LandingPage extends StatelessWidget { 13 | final SharedPreferences prefs; 14 | LandingPage({this.prefs}); 15 | @override 16 | Widget build(BuildContext context) { 17 | return MaterialApp( 18 | theme: ThemeData( 19 | primarySwatch: Colors.blue, 20 | ), 21 | home: _decideMainPage(), 22 | ); 23 | } 24 | 25 | _decideMainPage() { 26 | if (prefs.getBool('is_verified') != null) { 27 | if (prefs.getBool('is_verified')) { 28 | return HomePage(prefs: prefs); 29 | // return RegistrationPage(prefs: prefs); 30 | } else { 31 | return RegistrationPage(prefs: prefs); 32 | } 33 | } else { 34 | return RegistrationPage(prefs: prefs); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /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:flutter_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 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_chat_app 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .packages 28 | .pub-cache/ 29 | .pub/ 30 | /build/ 31 | 32 | # Android related 33 | **/android/**/gradle-wrapper.jar 34 | **/android/.gradle 35 | **/android/captures/ 36 | **/android/gradlew 37 | **/android/gradlew.bat 38 | **/android/local.properties 39 | **/android/**/GeneratedPluginRegistrant.java 40 | 41 | # iOS/XCode related 42 | **/ios/**/*.mode1v3 43 | **/ios/**/*.mode2v3 44 | **/ios/**/*.moved-aside 45 | **/ios/**/*.pbxuser 46 | **/ios/**/*.perspectivev3 47 | **/ios/**/*sync/ 48 | **/ios/**/.sconsign.dblite 49 | **/ios/**/.tags* 50 | **/ios/**/.vagrant/ 51 | **/ios/**/DerivedData/ 52 | **/ios/**/Icon? 53 | **/ios/**/Pods/ 54 | **/ios/**/.symlinks/ 55 | **/ios/**/profile 56 | **/ios/**/xcuserdata 57 | **/ios/.generated/ 58 | **/ios/Flutter/App.framework 59 | **/ios/Flutter/Flutter.framework 60 | **/ios/Flutter/Generated.xcconfig 61 | **/ios/Flutter/app.flx 62 | **/ios/Flutter/app.zip 63 | **/ios/Flutter/flutter_assets/ 64 | **/ios/ServiceDefinitions.json 65 | **/ios/Runner/GeneratedPluginRegistrant.* 66 | 67 | # Exceptions to above rules. 68 | !**/ios/**/default.mode1v3 69 | !**/ios/**/default.mode2v3 70 | !**/ios/**/default.pbxuser 71 | !**/ios/**/default.perspectivev3 72 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 73 | 74 | android/app/google-services.json -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 13 | 20 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /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 | android { 28 | compileSdkVersion 28 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "com.example.flutter_chat_app" 37 | minSdkVersion 16 38 | targetSdkVersion 28 39 | multiDexEnabled true 40 | versionCode flutterVersionCode.toInteger() 41 | versionName flutterVersionName 42 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 43 | } 44 | 45 | buildTypes { 46 | release { 47 | // TODO: Add your own signing config for the release build. 48 | // Signing with the debug keys for now, so `flutter run --release` works. 49 | signingConfig signingConfigs.debug 50 | } 51 | } 52 | } 53 | 54 | flutter { 55 | source '../..' 56 | } 57 | 58 | dependencies { 59 | testImplementation 'junit:junit:4.12' 60 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 61 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 62 | implementation 'com.android.support:multidex:1.0.3' 63 | } 64 | apply plugin: 'com.google.gms.google-services' 65 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_chat_app 2 | description: A new Flutter project. 3 | 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 1.0.0+1 15 | 16 | environment: 17 | sdk: ">=2.1.0 <3.0.0" 18 | 19 | dependencies: 20 | flutter: 21 | sdk: flutter 22 | 23 | # The following adds the Cupertino Icons font to your application. 24 | # Use with the CupertinoIcons class for iOS style icons. 25 | cupertino_icons: ^0.1.2 26 | firebase_auth: 27 | shared_preferences: ^0.5.3+1 28 | cloud_firestore: ^0.12.7 29 | contact_picker: ^0.0.2 30 | image_picker: ^0.6.0+17 31 | firebase_storage: ^3.0.3 32 | photo_view: ^0.4.2 33 | 34 | dev_dependencies: 35 | flutter_test: 36 | sdk: flutter 37 | 38 | 39 | # For information on the generic Dart part of this file, see the 40 | # following page: https://dart.dev/tools/pub/pubspec 41 | 42 | # The following section is specific to Flutter. 43 | flutter: 44 | 45 | # The following line ensures that the Material Icons font is 46 | # included with your application, so that you can use the icons in 47 | # the material Icons class. 48 | uses-material-design: true 49 | 50 | # To add assets to your application, add an assets section, like this: 51 | # assets: 52 | # - images/a_dot_burr.jpeg 53 | # - images/a_dot_ham.jpeg 54 | 55 | # An image asset can refer to one or more resolution-specific "variants", see 56 | # https://flutter.dev/assets-and-images/#resolution-aware. 57 | 58 | # For details regarding adding assets from package dependencies, see 59 | # https://flutter.dev/assets-and-images/#from-packages 60 | 61 | # To add custom fonts to your application, add a fonts section here, 62 | # in this "flutter" section. Each entry in this list should have a 63 | # "family" key with the font family name, and a "fonts" key with a 64 | # list giving the asset and other descriptors for the font. For 65 | # example: 66 | # fonts: 67 | # - family: Schyler 68 | # fonts: 69 | # - asset: fonts/Schyler-Regular.ttf 70 | # - asset: fonts/Schyler-Italic.ttf 71 | # style: italic 72 | # - family: Trajan Pro 73 | # fonts: 74 | # - asset: fonts/TrajanPro.ttf 75 | # - asset: fonts/TrajanPro_Bold.ttf 76 | # weight: 700 77 | # 78 | # For details regarding fonts from package dependencies, 79 | # see https://flutter.dev/custom-fonts/#from-packages 80 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | after_layout: 5 | dependency: transitive 6 | description: 7 | name: after_layout 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "1.0.7+2" 11 | async: 12 | dependency: transitive 13 | description: 14 | name: async 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.3.0" 18 | boolean_selector: 19 | dependency: transitive 20 | description: 21 | name: boolean_selector 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.0.5" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.1.2" 32 | cloud_firestore: 33 | dependency: "direct main" 34 | description: 35 | name: cloud_firestore 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "0.12.9+1" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.14.11" 46 | contact_picker: 47 | dependency: "direct main" 48 | description: 49 | name: contact_picker 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "0.0.2" 53 | cupertino_icons: 54 | dependency: "direct main" 55 | description: 56 | name: cupertino_icons 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "0.1.2" 60 | firebase_auth: 61 | dependency: "direct main" 62 | description: 63 | name: firebase_auth 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "0.11.1+12" 67 | firebase_core: 68 | dependency: transitive 69 | description: 70 | name: firebase_core 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "0.4.0+9" 74 | firebase_storage: 75 | dependency: "direct main" 76 | description: 77 | name: firebase_storage 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "3.0.6" 81 | flutter: 82 | dependency: "direct main" 83 | description: flutter 84 | source: sdk 85 | version: "0.0.0" 86 | flutter_test: 87 | dependency: "direct dev" 88 | description: flutter 89 | source: sdk 90 | version: "0.0.0" 91 | image_picker: 92 | dependency: "direct main" 93 | description: 94 | name: image_picker 95 | url: "https://pub.dartlang.org" 96 | source: hosted 97 | version: "0.6.1+4" 98 | matcher: 99 | dependency: transitive 100 | description: 101 | name: matcher 102 | url: "https://pub.dartlang.org" 103 | source: hosted 104 | version: "0.12.5" 105 | meta: 106 | dependency: transitive 107 | description: 108 | name: meta 109 | url: "https://pub.dartlang.org" 110 | source: hosted 111 | version: "1.1.7" 112 | path: 113 | dependency: transitive 114 | description: 115 | name: path 116 | url: "https://pub.dartlang.org" 117 | source: hosted 118 | version: "1.6.2" 119 | pedantic: 120 | dependency: transitive 121 | description: 122 | name: pedantic 123 | url: "https://pub.dartlang.org" 124 | source: hosted 125 | version: "1.8.0+1" 126 | photo_view: 127 | dependency: "direct main" 128 | description: 129 | name: photo_view 130 | url: "https://pub.dartlang.org" 131 | source: hosted 132 | version: "0.4.2" 133 | quiver: 134 | dependency: transitive 135 | description: 136 | name: quiver 137 | url: "https://pub.dartlang.org" 138 | source: hosted 139 | version: "2.0.3" 140 | shared_preferences: 141 | dependency: "direct main" 142 | description: 143 | name: shared_preferences 144 | url: "https://pub.dartlang.org" 145 | source: hosted 146 | version: "0.5.3+4" 147 | sky_engine: 148 | dependency: transitive 149 | description: flutter 150 | source: sdk 151 | version: "0.0.99" 152 | source_span: 153 | dependency: transitive 154 | description: 155 | name: source_span 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "1.5.5" 159 | stack_trace: 160 | dependency: transitive 161 | description: 162 | name: stack_trace 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "1.9.3" 166 | stream_channel: 167 | dependency: transitive 168 | description: 169 | name: stream_channel 170 | url: "https://pub.dartlang.org" 171 | source: hosted 172 | version: "2.0.0" 173 | string_scanner: 174 | dependency: transitive 175 | description: 176 | name: string_scanner 177 | url: "https://pub.dartlang.org" 178 | source: hosted 179 | version: "1.0.4" 180 | term_glyph: 181 | dependency: transitive 182 | description: 183 | name: term_glyph 184 | url: "https://pub.dartlang.org" 185 | source: hosted 186 | version: "1.1.0" 187 | test_api: 188 | dependency: transitive 189 | description: 190 | name: test_api 191 | url: "https://pub.dartlang.org" 192 | source: hosted 193 | version: "0.2.5" 194 | typed_data: 195 | dependency: transitive 196 | description: 197 | name: typed_data 198 | url: "https://pub.dartlang.org" 199 | source: hosted 200 | version: "1.1.6" 201 | vector_math: 202 | dependency: transitive 203 | description: 204 | name: vector_math 205 | url: "https://pub.dartlang.org" 206 | source: hosted 207 | version: "2.0.8" 208 | sdks: 209 | dart: ">=2.2.2 <3.0.0" 210 | flutter: ">=1.5.9-pre.94 <2.0.0" 211 | -------------------------------------------------------------------------------- /lib/pages/registration_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:firebase_auth/firebase_auth.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:flutter_chat_app/pages/home_page.dart'; 5 | import 'package:shared_preferences/shared_preferences.dart'; 6 | import 'package:cloud_firestore/cloud_firestore.dart'; 7 | 8 | class RegistrationPage extends StatefulWidget { 9 | final SharedPreferences prefs; 10 | RegistrationPage({this.prefs}); 11 | @override 12 | _RegistrationPageState createState() => _RegistrationPageState(); 13 | } 14 | 15 | class _RegistrationPageState extends State { 16 | String phoneNo; 17 | String smsOTP; 18 | String verificationId; 19 | String errorMessage = ''; 20 | FirebaseAuth _auth = FirebaseAuth.instance; 21 | final db = Firestore.instance; 22 | 23 | @override 24 | initState() { 25 | super.initState(); 26 | } 27 | 28 | Future verifyPhone() async { 29 | final PhoneCodeSent smsOTPSent = (String verId, [int forceCodeResend]) { 30 | this.verificationId = verId; 31 | smsOTPDialog(context).then((value) {}); 32 | }; 33 | try { 34 | await _auth.verifyPhoneNumber( 35 | phoneNumber: this.phoneNo, // PHONE NUMBER TO SEND OTP 36 | codeAutoRetrievalTimeout: (String verId) { 37 | //Starts the phone number verification process for the given phone number. 38 | //Either sends an SMS with a 6 digit code to the phone number specified, or sign's the user in and [verificationCompleted] is called. 39 | this.verificationId = verId; 40 | }, 41 | codeSent: 42 | smsOTPSent, // WHEN CODE SENT THEN WE OPEN DIALOG TO ENTER OTP. 43 | timeout: const Duration(seconds: 20), 44 | verificationCompleted: (AuthCredential phoneAuthCredential) { 45 | print(phoneAuthCredential); 46 | }, 47 | verificationFailed: (AuthException e) { 48 | print('${e.message}'); 49 | }); 50 | } catch (e) { 51 | handleError(e); 52 | } 53 | } 54 | 55 | Future smsOTPDialog(BuildContext context) { 56 | return showDialog( 57 | context: context, 58 | barrierDismissible: false, 59 | builder: (BuildContext context) { 60 | return new AlertDialog( 61 | title: Text('Enter SMS Code'), 62 | content: Container( 63 | height: 85, 64 | child: Column(children: [ 65 | TextField( 66 | onChanged: (value) { 67 | this.smsOTP = value; 68 | }, 69 | ), 70 | (errorMessage != '' 71 | ? Text( 72 | errorMessage, 73 | style: TextStyle(color: Colors.red), 74 | ) 75 | : Container()) 76 | ]), 77 | ), 78 | contentPadding: EdgeInsets.all(10), 79 | actions: [ 80 | FlatButton( 81 | child: Text('Done'), 82 | onPressed: () { 83 | _auth.currentUser().then((user) async { 84 | signIn(); 85 | }); 86 | }, 87 | ) 88 | ], 89 | ); 90 | }); 91 | } 92 | 93 | signIn() async { 94 | try { 95 | final AuthCredential credential = PhoneAuthProvider.getCredential( 96 | verificationId: verificationId, 97 | smsCode: smsOTP, 98 | ); 99 | final FirebaseUser user = await _auth.signInWithCredential(credential); 100 | final FirebaseUser currentUser = await _auth.currentUser(); 101 | assert(user.uid == currentUser.uid); 102 | Navigator.of(context).pop(); 103 | DocumentReference mobileRef = db 104 | .collection("mobiles") 105 | .document(phoneNo.replaceAll(new RegExp(r'[^\w\s]+'), '')); 106 | await mobileRef.get().then((documentReference) { 107 | if (!documentReference.exists) { 108 | mobileRef.setData({}).then((documentReference) async { 109 | await db.collection("users").add({ 110 | 'name': "No Name", 111 | 'mobile': phoneNo.replaceAll(new RegExp(r'[^\w\s]+'), ''), 112 | 'profile_photo': "", 113 | }).then((documentReference) { 114 | widget.prefs.setBool('is_verified', true); 115 | widget.prefs.setString( 116 | 'mobile', 117 | phoneNo.replaceAll(new RegExp(r'[^\w\s]+'), ''), 118 | ); 119 | widget.prefs.setString('uid', documentReference.documentID); 120 | widget.prefs.setString('name', "No Name"); 121 | widget.prefs.setString('profile_photo', ""); 122 | 123 | mobileRef.setData({'uid': documentReference.documentID}).then( 124 | (documentReference) async { 125 | Navigator.of(context).pushReplacement(MaterialPageRoute( 126 | builder: (context) => HomePage(prefs: widget.prefs))); 127 | }).catchError((e) { 128 | print(e); 129 | }); 130 | }).catchError((e) { 131 | print(e); 132 | }); 133 | }); 134 | } else { 135 | widget.prefs.setBool('is_verified', true); 136 | widget.prefs.setString( 137 | 'mobile_number', 138 | phoneNo.replaceAll(new RegExp(r'[^\w\s]+'), ''), 139 | ); 140 | widget.prefs.setString('uid', documentReference["uid"]); 141 | widget.prefs.setString('name', documentReference["name"]); 142 | widget.prefs 143 | .setString('profile_photo', documentReference["profile_photo"]); 144 | 145 | Navigator.of(context).pushReplacement( 146 | MaterialPageRoute( 147 | builder: (context) => HomePage(prefs: widget.prefs), 148 | ), 149 | ); 150 | } 151 | }).catchError((e) {}); 152 | } catch (e) { 153 | handleError(e); 154 | } 155 | } 156 | 157 | handleError(PlatformException error) { 158 | switch (error.code) { 159 | case 'ERROR_INVALID_VERIFICATION_CODE': 160 | FocusScope.of(context).requestFocus(new FocusNode()); 161 | setState(() { 162 | errorMessage = 'Invalid Code'; 163 | }); 164 | Navigator.of(context).pop(); 165 | smsOTPDialog(context).then((value) {}); 166 | break; 167 | default: 168 | setState(() { 169 | errorMessage = error.message; 170 | }); 171 | 172 | break; 173 | } 174 | } 175 | 176 | @override 177 | Widget build(BuildContext context) { 178 | return Scaffold( 179 | body: Center( 180 | child: Column( 181 | mainAxisAlignment: MainAxisAlignment.center, 182 | children: [ 183 | Padding( 184 | padding: EdgeInsets.all(10), 185 | child: TextField( 186 | decoration: InputDecoration(hintText: '+910000000000'), 187 | onChanged: (value) { 188 | this.phoneNo = value; 189 | }, 190 | ), 191 | ), 192 | (errorMessage != '' 193 | ? Text( 194 | errorMessage, 195 | style: TextStyle(color: Colors.red), 196 | ) 197 | : Container()), 198 | SizedBox( 199 | height: 10, 200 | ), 201 | RaisedButton( 202 | onPressed: () { 203 | verifyPhone(); 204 | }, 205 | child: Text('Verify'), 206 | textColor: Colors.white, 207 | elevation: 7, 208 | color: Colors.blue, 209 | ) 210 | ], 211 | ), 212 | ), 213 | ); 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /lib/pages/chat_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:cloud_firestore/cloud_firestore.dart'; 2 | import 'package:firebase_storage/firebase_storage.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_chat_app/pages/gallary_page.dart'; 5 | import 'package:image_picker/image_picker.dart'; 6 | import 'package:shared_preferences/shared_preferences.dart'; 7 | 8 | class ChatPage extends StatefulWidget { 9 | final SharedPreferences prefs; 10 | final String chatId; 11 | final String title; 12 | ChatPage({this.prefs, this.chatId,this.title}); 13 | @override 14 | ChatPageState createState() { 15 | return new ChatPageState(); 16 | } 17 | } 18 | 19 | class ChatPageState extends State { 20 | final db = Firestore.instance; 21 | CollectionReference chatReference; 22 | final TextEditingController _textController = 23 | new TextEditingController(); 24 | bool _isWritting = false; 25 | 26 | @override 27 | void initState() { 28 | super.initState(); 29 | chatReference = 30 | db.collection("chats").document(widget.chatId).collection('messages'); 31 | } 32 | 33 | List generateSenderLayout(DocumentSnapshot documentSnapshot) { 34 | return [ 35 | new Expanded( 36 | child: new Column( 37 | crossAxisAlignment: CrossAxisAlignment.end, 38 | children: [ 39 | new Text(documentSnapshot.data['sender_name'], 40 | style: new TextStyle( 41 | fontSize: 14.0, 42 | color: Colors.black, 43 | fontWeight: FontWeight.bold)), 44 | new Container( 45 | margin: const EdgeInsets.only(top: 5.0), 46 | child: documentSnapshot.data['image_url'] != '' 47 | ? InkWell( 48 | child: new Container( 49 | child: Image.network( 50 | documentSnapshot.data['image_url'], 51 | fit: BoxFit.fitWidth, 52 | ), 53 | height: 150, 54 | width: 150.0, 55 | color: Color.fromRGBO(0, 0, 0, 0.2), 56 | padding: EdgeInsets.all(5), 57 | ), 58 | onTap: () { 59 | Navigator.of(context).push( 60 | MaterialPageRoute( 61 | builder: (context) => GalleryPage( 62 | imagePath: documentSnapshot.data['image_url'], 63 | ), 64 | ), 65 | ); 66 | }, 67 | ) 68 | : new Text(documentSnapshot.data['text']), 69 | ), 70 | ], 71 | ), 72 | ), 73 | new Column( 74 | crossAxisAlignment: CrossAxisAlignment.end, 75 | children: [ 76 | new Container( 77 | margin: const EdgeInsets.only(left: 8.0), 78 | child: new CircleAvatar( 79 | backgroundImage: 80 | new NetworkImage(documentSnapshot.data['profile_photo']), 81 | )), 82 | ], 83 | ), 84 | ]; 85 | } 86 | 87 | List generateReceiverLayout(DocumentSnapshot documentSnapshot) { 88 | return [ 89 | new Column( 90 | crossAxisAlignment: CrossAxisAlignment.start, 91 | children: [ 92 | new Container( 93 | margin: const EdgeInsets.only(right: 8.0), 94 | child: new CircleAvatar( 95 | backgroundImage: 96 | new NetworkImage(documentSnapshot.data['profile_photo']), 97 | )), 98 | ], 99 | ), 100 | new Expanded( 101 | child: new Column( 102 | crossAxisAlignment: CrossAxisAlignment.start, 103 | children: [ 104 | new Text(documentSnapshot.data['sender_name'], 105 | style: new TextStyle( 106 | fontSize: 14.0, 107 | color: Colors.black, 108 | fontWeight: FontWeight.bold)), 109 | new Container( 110 | margin: const EdgeInsets.only(top: 5.0), 111 | child: documentSnapshot.data['image_url'] != '' 112 | ? InkWell( 113 | child: new Container( 114 | child: Image.network( 115 | documentSnapshot.data['image_url'], 116 | fit: BoxFit.fitWidth, 117 | ), 118 | height: 150, 119 | width: 150.0, 120 | color: Color.fromRGBO(0, 0, 0, 0.2), 121 | padding: EdgeInsets.all(5), 122 | ), 123 | onTap: () { 124 | Navigator.of(context).push( 125 | MaterialPageRoute( 126 | builder: (context) => GalleryPage( 127 | imagePath: documentSnapshot.data['image_url'], 128 | ), 129 | ), 130 | ); 131 | }, 132 | ) 133 | : new Text(documentSnapshot.data['text']), 134 | ), 135 | ], 136 | ), 137 | ), 138 | ]; 139 | } 140 | 141 | generateMessages(AsyncSnapshot snapshot) { 142 | return snapshot.data.documents 143 | .map((doc) => Container( 144 | margin: const EdgeInsets.symmetric(vertical: 10.0), 145 | child: new Row( 146 | children: doc.data['sender_id'] != widget.prefs.getString('uid') 147 | ? generateReceiverLayout(doc) 148 | : generateSenderLayout(doc), 149 | ), 150 | )) 151 | .toList(); 152 | } 153 | 154 | @override 155 | Widget build(BuildContext context) { 156 | return Scaffold( 157 | appBar: AppBar( 158 | title: Text(widget.title), 159 | ), 160 | body: Container( 161 | padding: EdgeInsets.all(5), 162 | child: new Column( 163 | children: [ 164 | StreamBuilder( 165 | stream: chatReference.orderBy('time',descending: true).snapshots(), 166 | builder: (BuildContext context, 167 | AsyncSnapshot snapshot) { 168 | if (!snapshot.hasData) return new Text("No Chat"); 169 | return Expanded( 170 | child: new ListView( 171 | reverse: true, 172 | children: generateMessages(snapshot), 173 | ), 174 | ); 175 | }, 176 | ), 177 | new Divider(height: 1.0), 178 | new Container( 179 | decoration: new BoxDecoration(color: Theme.of(context).cardColor), 180 | child: _buildTextComposer(), 181 | ), 182 | new Builder(builder: (BuildContext context) { 183 | return new Container(width: 0.0, height: 0.0); 184 | }) 185 | ], 186 | ), 187 | ), 188 | ); 189 | } 190 | 191 | IconButton getDefaultSendButton() { 192 | return new IconButton( 193 | icon: new Icon(Icons.send), 194 | onPressed: _isWritting 195 | ? () => _sendText(_textController.text) 196 | : null, 197 | ); 198 | } 199 | 200 | Widget _buildTextComposer() { 201 | return new IconTheme( 202 | data: new IconThemeData( 203 | color: _isWritting 204 | ? Theme.of(context).accentColor 205 | : Theme.of(context).disabledColor, 206 | ), 207 | child: new Container( 208 | margin: const EdgeInsets.symmetric(horizontal: 8.0), 209 | child: new Row( 210 | children: [ 211 | new Container( 212 | margin: new EdgeInsets.symmetric(horizontal: 4.0), 213 | child: new IconButton( 214 | icon: new Icon( 215 | Icons.photo_camera, 216 | color: Theme.of(context).accentColor, 217 | ), 218 | onPressed: () async { 219 | var image = await ImagePicker.pickImage( 220 | source: ImageSource.gallery); 221 | int timestamp = new DateTime.now().millisecondsSinceEpoch; 222 | StorageReference storageReference = FirebaseStorage 223 | .instance 224 | .ref() 225 | .child('chats/img_' + timestamp.toString() + '.jpg'); 226 | StorageUploadTask uploadTask = 227 | storageReference.putFile(image); 228 | await uploadTask.onComplete; 229 | String fileUrl = await storageReference.getDownloadURL(); 230 | _sendImage(messageText: null, imageUrl: fileUrl); 231 | }), 232 | ), 233 | new Flexible( 234 | child: new TextField( 235 | controller: _textController, 236 | onChanged: (String messageText) { 237 | setState(() { 238 | _isWritting = messageText.length > 0; 239 | }); 240 | }, 241 | onSubmitted: _sendText, 242 | decoration: 243 | new InputDecoration.collapsed(hintText: "Send a message"), 244 | ), 245 | ), 246 | new Container( 247 | margin: const EdgeInsets.symmetric(horizontal: 4.0), 248 | child: getDefaultSendButton(), 249 | ), 250 | ], 251 | ), 252 | )); 253 | } 254 | 255 | Future _sendText(String text) async { 256 | _textController.clear(); 257 | chatReference.add({ 258 | 'text': text, 259 | 'sender_id': widget.prefs.getString('uid'), 260 | 'sender_name': widget.prefs.getString('name'), 261 | 'profile_photo': widget.prefs.getString('profile_photo'), 262 | 'image_url': '', 263 | 'time': FieldValue.serverTimestamp(), 264 | }).then((documentReference) { 265 | setState(() { 266 | _isWritting = false; 267 | }); 268 | }).catchError((e) {}); 269 | } 270 | 271 | void _sendImage({String messageText, String imageUrl}) { 272 | chatReference.add({ 273 | 'text': messageText, 274 | 'sender_id': widget.prefs.getString('uid'), 275 | 'sender_name': widget.prefs.getString('name'), 276 | 'profile_photo': widget.prefs.getString('profile_photo'), 277 | 'image_url': imageUrl, 278 | 'time': FieldValue.serverTimestamp(), 279 | }); 280 | } 281 | } 282 | -------------------------------------------------------------------------------- /lib/pages/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:firebase_auth/firebase_auth.dart'; 2 | import 'package:firebase_storage/firebase_storage.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_chat_app/pages/chat_page.dart'; 5 | import 'package:flutter_chat_app/pages/registration_page.dart'; 6 | import 'package:image_picker/image_picker.dart'; 7 | import 'package:shared_preferences/shared_preferences.dart'; 8 | import 'package:cloud_firestore/cloud_firestore.dart'; 9 | import 'package:contact_picker/contact_picker.dart'; 10 | 11 | class HomePage extends StatefulWidget { 12 | final SharedPreferences prefs; 13 | HomePage({this.prefs}); 14 | @override 15 | _HomePageState createState() => _HomePageState(); 16 | } 17 | 18 | class _HomePageState extends State { 19 | int _currentIndex = 0; 20 | String _tabTitle = "Contacts"; 21 | List _children = [Container(), Container()]; 22 | 23 | final db = Firestore.instance; 24 | final ContactPicker _contactPicker = new ContactPicker(); 25 | CollectionReference contactsReference; 26 | DocumentReference profileReference; 27 | DocumentSnapshot profileSnapshot; 28 | 29 | final GlobalKey _formStateKey = GlobalKey(); 30 | final _yourNameController = TextEditingController(); 31 | bool editName = false; 32 | @override 33 | void initState() { 34 | super.initState(); 35 | contactsReference = db 36 | .collection("users") 37 | .document(widget.prefs.getString('uid')) 38 | .collection('contacts'); 39 | profileReference = 40 | db.collection("users").document(widget.prefs.getString('uid')); 41 | 42 | profileReference.snapshots().listen((querySnapshot) { 43 | profileSnapshot = querySnapshot; 44 | widget.prefs.setString('name', profileSnapshot.data["name"]); 45 | widget.prefs 46 | .setString('profile_photo', profileSnapshot.data["profile_photo"]); 47 | 48 | setState(() { 49 | _yourNameController.text = profileSnapshot.data["name"]; 50 | }); 51 | }); 52 | } 53 | 54 | generateContactTab() { 55 | return Column( 56 | children: [ 57 | StreamBuilder( 58 | stream: contactsReference.snapshots(), 59 | builder: 60 | (BuildContext context, AsyncSnapshot snapshot) { 61 | if (!snapshot.hasData) return new Text("No Contacts"); 62 | return Expanded( 63 | child: new ListView( 64 | children: generateContactList(snapshot), 65 | ), 66 | ); 67 | }, 68 | ) 69 | ], 70 | ); 71 | } 72 | 73 | Future getProfilePicture() async { 74 | var image = await ImagePicker.pickImage(source: ImageSource.gallery); 75 | StorageReference storageReference = FirebaseStorage.instance 76 | .ref() 77 | .child('profiles/${widget.prefs.getString('uid')}'); 78 | StorageUploadTask uploadTask = storageReference.putFile(image); 79 | await uploadTask.onComplete; 80 | print('File Uploaded'); 81 | String fileUrl = await storageReference.getDownloadURL(); 82 | profileReference.updateData({'profile_photo': fileUrl}); 83 | } 84 | 85 | generateProfileTab() { 86 | return Center( 87 | child: Column( 88 | crossAxisAlignment: CrossAxisAlignment.center, 89 | mainAxisAlignment: MainAxisAlignment.center, 90 | children: [ 91 | (profileSnapshot != null 92 | ? (profileSnapshot.data['profile_photo'] != null 93 | ? InkWell( 94 | child: Container( 95 | width: 190.0, 96 | height: 190.0, 97 | decoration: BoxDecoration( 98 | shape: BoxShape.circle, 99 | image: DecorationImage( 100 | fit: BoxFit.fill, 101 | image: NetworkImage( 102 | '${profileSnapshot.data['profile_photo']}'), 103 | ), 104 | ), 105 | ), 106 | onTap: () { 107 | getProfilePicture(); 108 | }, 109 | ) 110 | : Container()) 111 | : Container()), 112 | SizedBox( 113 | height: 20, 114 | ), 115 | (!editName && profileSnapshot != null 116 | ? Row( 117 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 118 | children: [ 119 | Text('${profileSnapshot.data["name"]}'), 120 | IconButton( 121 | icon: Icon(Icons.edit), 122 | onPressed: () { 123 | setState(() { 124 | editName = true; 125 | }); 126 | }, 127 | ), 128 | ], 129 | ) 130 | : Container()), 131 | (editName 132 | ? Form( 133 | key: _formStateKey, 134 | autovalidate: true, 135 | child: Column( 136 | children: [ 137 | Padding( 138 | padding: 139 | EdgeInsets.only(left: 10, right: 10, bottom: 10), 140 | child: TextFormField( 141 | validator: (value) { 142 | if (value.isEmpty) { 143 | return 'Please Enter Name'; 144 | } 145 | if (value.trim() == "") 146 | return "Only Space is Not Valid!!!"; 147 | return null; 148 | }, 149 | controller: _yourNameController, 150 | decoration: InputDecoration( 151 | focusedBorder: new UnderlineInputBorder( 152 | borderSide: new BorderSide( 153 | width: 2, style: BorderStyle.solid)), 154 | labelText: "Your Name", 155 | icon: Icon( 156 | Icons.verified_user, 157 | ), 158 | fillColor: Colors.white, 159 | ), 160 | ), 161 | ), 162 | ], 163 | ), 164 | ) 165 | : Container()), 166 | (editName 167 | ? Row( 168 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 169 | children: [ 170 | RaisedButton( 171 | child: Text( 172 | 'UPDATE', 173 | style: TextStyle( 174 | color: Colors.white, 175 | ), 176 | ), 177 | onPressed: () { 178 | if (_formStateKey.currentState.validate()) { 179 | profileReference 180 | .updateData({'name': _yourNameController.text}); 181 | setState(() { 182 | editName = false; 183 | }); 184 | } 185 | }, 186 | color: Colors.lightBlue, 187 | ), 188 | RaisedButton( 189 | child: Text('CANCEL'), 190 | onPressed: () { 191 | setState(() { 192 | editName = false; 193 | }); 194 | }, 195 | ) 196 | ], 197 | ) 198 | : Container()) 199 | ]), 200 | ); 201 | } 202 | 203 | generateContactList(AsyncSnapshot snapshot) { 204 | return snapshot.data.documents 205 | .map( 206 | (doc) => InkWell( 207 | child: Container( 208 | decoration: BoxDecoration( 209 | border: Border( 210 | bottom: BorderSide( 211 | color: Colors.grey, 212 | ), 213 | ), 214 | ), 215 | child: ListTile( 216 | title: Text(doc["name"]), 217 | subtitle: Text(doc["mobile"]), 218 | trailing: Icon(Icons.chevron_right), 219 | ), 220 | ), 221 | onTap: () async { 222 | QuerySnapshot result = await db 223 | .collection('chats') 224 | .where('contact1', isEqualTo: widget.prefs.getString('uid')) 225 | .where('contact2', isEqualTo: doc["uid"]) 226 | .getDocuments(); 227 | List documents = result.documents; 228 | if (documents.length == 0) { 229 | result = await db 230 | .collection('chats') 231 | .where('contact2', isEqualTo: widget.prefs.getString('uid')) 232 | .where('contact1', isEqualTo: doc["uid"]) 233 | .getDocuments(); 234 | documents = result.documents; 235 | if (documents.length == 0) { 236 | await db.collection('chats').add({ 237 | 'contact1': widget.prefs.getString('uid'), 238 | 'contact2': doc["uid"] 239 | }).then((documentReference) { 240 | Navigator.of(context).push( 241 | MaterialPageRoute( 242 | builder: (context) => ChatPage( 243 | prefs: widget.prefs, 244 | chatId: documentReference.documentID, 245 | title: doc["name"], 246 | ), 247 | ), 248 | ); 249 | }).catchError((e) {}); 250 | } else { 251 | Navigator.of(context).push( 252 | MaterialPageRoute( 253 | builder: (context) => ChatPage( 254 | prefs: widget.prefs, 255 | chatId: documents[0].documentID, 256 | title: doc["name"], 257 | ), 258 | ), 259 | ); 260 | } 261 | } else { 262 | Navigator.of(context).push( 263 | MaterialPageRoute( 264 | builder: (context) => ChatPage( 265 | prefs: widget.prefs, 266 | chatId: documents[0].documentID, 267 | title: doc["name"], 268 | ), 269 | ), 270 | ); 271 | } 272 | }, 273 | ), 274 | ) 275 | .toList(); 276 | } 277 | 278 | openContacts() async { 279 | Contact contact = await _contactPicker.selectContact(); 280 | if (contact != null) { 281 | String phoneNumber = contact.phoneNumber.number 282 | .toString() 283 | .replaceAll(new RegExp(r"\s\b|\b\s"), "") 284 | .replaceAll(new RegExp(r'[^\w\s]+'), ''); 285 | if (phoneNumber.length == 10) { 286 | phoneNumber = '+91$phoneNumber'; 287 | } 288 | if (phoneNumber.length == 12) { 289 | phoneNumber = '+$phoneNumber'; 290 | } 291 | if (phoneNumber.length == 13) { 292 | DocumentReference mobileRef = db 293 | .collection("mobiles") 294 | .document(phoneNumber.replaceAll(new RegExp(r'[^\w\s]+'), '')); 295 | await mobileRef.get().then((documentReference) { 296 | if (documentReference.exists) { 297 | contactsReference.add({ 298 | 'uid': documentReference['uid'], 299 | 'name': contact.fullName, 300 | 'mobile': phoneNumber.replaceAll(new RegExp(r'[^\w\s]+'), ''), 301 | }); 302 | } else { 303 | print('User Not Registered'); 304 | } 305 | }).catchError((e) {}); 306 | } else { 307 | print('Wrong Mobile Number'); 308 | } 309 | } 310 | } 311 | 312 | void onTabTapped(int index) { 313 | setState(() { 314 | _currentIndex = index; 315 | switch (_currentIndex) { 316 | case 0: 317 | _tabTitle = "Contacts"; 318 | break; 319 | case 1: 320 | _tabTitle = "Profile"; 321 | break; 322 | } 323 | }); 324 | } 325 | 326 | @override 327 | Widget build(BuildContext context) { 328 | _children = [ 329 | generateContactTab(), 330 | generateProfileTab(), 331 | ]; 332 | return MaterialApp( 333 | home: DefaultTabController( 334 | length: 2, 335 | child: Scaffold( 336 | appBar: AppBar( 337 | title: Text(_tabTitle), 338 | actions: [ 339 | (_currentIndex == 0 340 | ? Row( 341 | children: [ 342 | IconButton( 343 | icon: Icon(Icons.add), 344 | onPressed: () { 345 | openContacts(); 346 | }, 347 | ), 348 | IconButton( 349 | icon: Icon(Icons.backspace), 350 | onPressed: () { 351 | FirebaseAuth.instance.signOut().then((response) { 352 | widget.prefs.remove('is_verified'); 353 | widget.prefs.remove('mobile_number'); 354 | widget.prefs.remove('uid'); 355 | widget.prefs.remove('name'); 356 | widget.prefs.remove('profile_photo'); 357 | Navigator.of(context).pushReplacement( 358 | MaterialPageRoute( 359 | builder: (context) => 360 | RegistrationPage(prefs: widget.prefs), 361 | ), 362 | ); 363 | }); 364 | }, 365 | ) 366 | ], 367 | ) 368 | : Container()) 369 | ], 370 | ), 371 | body: _children[_currentIndex], 372 | bottomNavigationBar: BottomNavigationBar( 373 | onTap: onTabTapped, // new 374 | currentIndex: _currentIndex, // new 375 | items: [ 376 | new BottomNavigationBarItem( 377 | icon: Icon(Icons.mail), 378 | title: Text('Contacts'), 379 | ), 380 | new BottomNavigationBarItem( 381 | icon: Icon(Icons.verified_user), 382 | title: Text('Profile'), 383 | ) 384 | ], 385 | ), 386 | ), 387 | ), 388 | ); 389 | } 390 | } 391 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 13 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 14 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 15 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 16 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 17 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 18 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 19 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 20 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 21 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXCopyFilesBuildPhase section */ 25 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 26 | isa = PBXCopyFilesBuildPhase; 27 | buildActionMask = 2147483647; 28 | dstPath = ""; 29 | dstSubfolderSpec = 10; 30 | files = ( 31 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 32 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 33 | ); 34 | name = "Embed Frameworks"; 35 | runOnlyForDeploymentPostprocessing = 0; 36 | }; 37 | /* End PBXCopyFilesBuildPhase section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 41 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 42 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 43 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 44 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 45 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 46 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 47 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 48 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 49 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 50 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 52 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 53 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 54 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 55 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | /* End PBXFileReference section */ 57 | 58 | /* Begin PBXFrameworksBuildPhase section */ 59 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 64 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | /* End PBXFrameworksBuildPhase section */ 69 | 70 | /* Begin PBXGroup section */ 71 | 9740EEB11CF90186004384FC /* Flutter */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 3B80C3931E831B6300D905FE /* App.framework */, 75 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 76 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 77 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 78 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 79 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 80 | ); 81 | name = Flutter; 82 | sourceTree = ""; 83 | }; 84 | 97C146E51CF9000F007C117D = { 85 | isa = PBXGroup; 86 | children = ( 87 | 9740EEB11CF90186004384FC /* Flutter */, 88 | 97C146F01CF9000F007C117D /* Runner */, 89 | 97C146EF1CF9000F007C117D /* Products */, 90 | CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, 91 | ); 92 | sourceTree = ""; 93 | }; 94 | 97C146EF1CF9000F007C117D /* Products */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 97C146EE1CF9000F007C117D /* Runner.app */, 98 | ); 99 | name = Products; 100 | sourceTree = ""; 101 | }; 102 | 97C146F01CF9000F007C117D /* Runner */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 106 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 107 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 108 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 109 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 110 | 97C147021CF9000F007C117D /* Info.plist */, 111 | 97C146F11CF9000F007C117D /* Supporting Files */, 112 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 113 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 114 | ); 115 | path = Runner; 116 | sourceTree = ""; 117 | }; 118 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 97C146F21CF9000F007C117D /* main.m */, 122 | ); 123 | name = "Supporting Files"; 124 | sourceTree = ""; 125 | }; 126 | /* End PBXGroup section */ 127 | 128 | /* Begin PBXNativeTarget section */ 129 | 97C146ED1CF9000F007C117D /* Runner */ = { 130 | isa = PBXNativeTarget; 131 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 132 | buildPhases = ( 133 | 9740EEB61CF901F6004384FC /* Run Script */, 134 | 97C146EA1CF9000F007C117D /* Sources */, 135 | 97C146EB1CF9000F007C117D /* Frameworks */, 136 | 97C146EC1CF9000F007C117D /* Resources */, 137 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 138 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 139 | ); 140 | buildRules = ( 141 | ); 142 | dependencies = ( 143 | ); 144 | name = Runner; 145 | productName = Runner; 146 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 147 | productType = "com.apple.product-type.application"; 148 | }; 149 | /* End PBXNativeTarget section */ 150 | 151 | /* Begin PBXProject section */ 152 | 97C146E61CF9000F007C117D /* Project object */ = { 153 | isa = PBXProject; 154 | attributes = { 155 | LastUpgradeCheck = 1020; 156 | ORGANIZATIONNAME = "The Chromium Authors"; 157 | TargetAttributes = { 158 | 97C146ED1CF9000F007C117D = { 159 | CreatedOnToolsVersion = 7.3.1; 160 | }; 161 | }; 162 | }; 163 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 164 | compatibilityVersion = "Xcode 3.2"; 165 | developmentRegion = en; 166 | hasScannedForEncodings = 0; 167 | knownRegions = ( 168 | en, 169 | Base, 170 | ); 171 | mainGroup = 97C146E51CF9000F007C117D; 172 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 173 | projectDirPath = ""; 174 | projectRoot = ""; 175 | targets = ( 176 | 97C146ED1CF9000F007C117D /* Runner */, 177 | ); 178 | }; 179 | /* End PBXProject section */ 180 | 181 | /* Begin PBXResourcesBuildPhase section */ 182 | 97C146EC1CF9000F007C117D /* Resources */ = { 183 | isa = PBXResourcesBuildPhase; 184 | buildActionMask = 2147483647; 185 | files = ( 186 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 187 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 188 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 189 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 190 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 191 | ); 192 | runOnlyForDeploymentPostprocessing = 0; 193 | }; 194 | /* End PBXResourcesBuildPhase section */ 195 | 196 | /* Begin PBXShellScriptBuildPhase section */ 197 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 198 | isa = PBXShellScriptBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | ); 202 | inputPaths = ( 203 | ); 204 | name = "Thin Binary"; 205 | outputPaths = ( 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | shellPath = /bin/sh; 209 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; 210 | }; 211 | 9740EEB61CF901F6004384FC /* Run Script */ = { 212 | isa = PBXShellScriptBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | ); 216 | inputPaths = ( 217 | ); 218 | name = "Run Script"; 219 | outputPaths = ( 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | shellPath = /bin/sh; 223 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 224 | }; 225 | /* End PBXShellScriptBuildPhase section */ 226 | 227 | /* Begin PBXSourcesBuildPhase section */ 228 | 97C146EA1CF9000F007C117D /* Sources */ = { 229 | isa = PBXSourcesBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 233 | 97C146F31CF9000F007C117D /* main.m in Sources */, 234 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 235 | ); 236 | runOnlyForDeploymentPostprocessing = 0; 237 | }; 238 | /* End PBXSourcesBuildPhase section */ 239 | 240 | /* Begin PBXVariantGroup section */ 241 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 242 | isa = PBXVariantGroup; 243 | children = ( 244 | 97C146FB1CF9000F007C117D /* Base */, 245 | ); 246 | name = Main.storyboard; 247 | sourceTree = ""; 248 | }; 249 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 250 | isa = PBXVariantGroup; 251 | children = ( 252 | 97C147001CF9000F007C117D /* Base */, 253 | ); 254 | name = LaunchScreen.storyboard; 255 | sourceTree = ""; 256 | }; 257 | /* End PBXVariantGroup section */ 258 | 259 | /* Begin XCBuildConfiguration section */ 260 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 261 | isa = XCBuildConfiguration; 262 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 263 | buildSettings = { 264 | ALWAYS_SEARCH_USER_PATHS = NO; 265 | CLANG_ANALYZER_NONNULL = YES; 266 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 267 | CLANG_CXX_LIBRARY = "libc++"; 268 | CLANG_ENABLE_MODULES = YES; 269 | CLANG_ENABLE_OBJC_ARC = YES; 270 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 271 | CLANG_WARN_BOOL_CONVERSION = YES; 272 | CLANG_WARN_COMMA = YES; 273 | CLANG_WARN_CONSTANT_CONVERSION = YES; 274 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 275 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 276 | CLANG_WARN_EMPTY_BODY = YES; 277 | CLANG_WARN_ENUM_CONVERSION = YES; 278 | CLANG_WARN_INFINITE_RECURSION = YES; 279 | CLANG_WARN_INT_CONVERSION = YES; 280 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 281 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 282 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 283 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 284 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 285 | CLANG_WARN_STRICT_PROTOTYPES = YES; 286 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 287 | CLANG_WARN_UNREACHABLE_CODE = YES; 288 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 289 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 290 | COPY_PHASE_STRIP = NO; 291 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 292 | ENABLE_NS_ASSERTIONS = NO; 293 | ENABLE_STRICT_OBJC_MSGSEND = YES; 294 | GCC_C_LANGUAGE_STANDARD = gnu99; 295 | GCC_NO_COMMON_BLOCKS = YES; 296 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 297 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 298 | GCC_WARN_UNDECLARED_SELECTOR = YES; 299 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 300 | GCC_WARN_UNUSED_FUNCTION = YES; 301 | GCC_WARN_UNUSED_VARIABLE = YES; 302 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 303 | MTL_ENABLE_DEBUG_INFO = NO; 304 | SDKROOT = iphoneos; 305 | TARGETED_DEVICE_FAMILY = "1,2"; 306 | VALIDATE_PRODUCT = YES; 307 | }; 308 | name = Profile; 309 | }; 310 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 311 | isa = XCBuildConfiguration; 312 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 313 | buildSettings = { 314 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 315 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 316 | ENABLE_BITCODE = NO; 317 | FRAMEWORK_SEARCH_PATHS = ( 318 | "$(inherited)", 319 | "$(PROJECT_DIR)/Flutter", 320 | ); 321 | INFOPLIST_FILE = Runner/Info.plist; 322 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 323 | LIBRARY_SEARCH_PATHS = ( 324 | "$(inherited)", 325 | "$(PROJECT_DIR)/Flutter", 326 | ); 327 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterChatApp; 328 | PRODUCT_NAME = "$(TARGET_NAME)"; 329 | VERSIONING_SYSTEM = "apple-generic"; 330 | }; 331 | name = Profile; 332 | }; 333 | 97C147031CF9000F007C117D /* Debug */ = { 334 | isa = XCBuildConfiguration; 335 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 336 | buildSettings = { 337 | ALWAYS_SEARCH_USER_PATHS = NO; 338 | CLANG_ANALYZER_NONNULL = YES; 339 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 340 | CLANG_CXX_LIBRARY = "libc++"; 341 | CLANG_ENABLE_MODULES = YES; 342 | CLANG_ENABLE_OBJC_ARC = YES; 343 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 344 | CLANG_WARN_BOOL_CONVERSION = YES; 345 | CLANG_WARN_COMMA = YES; 346 | CLANG_WARN_CONSTANT_CONVERSION = YES; 347 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 348 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 349 | CLANG_WARN_EMPTY_BODY = YES; 350 | CLANG_WARN_ENUM_CONVERSION = YES; 351 | CLANG_WARN_INFINITE_RECURSION = YES; 352 | CLANG_WARN_INT_CONVERSION = YES; 353 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 354 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 355 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 356 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 357 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 358 | CLANG_WARN_STRICT_PROTOTYPES = YES; 359 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 360 | CLANG_WARN_UNREACHABLE_CODE = YES; 361 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 362 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 363 | COPY_PHASE_STRIP = NO; 364 | DEBUG_INFORMATION_FORMAT = dwarf; 365 | ENABLE_STRICT_OBJC_MSGSEND = YES; 366 | ENABLE_TESTABILITY = YES; 367 | GCC_C_LANGUAGE_STANDARD = gnu99; 368 | GCC_DYNAMIC_NO_PIC = NO; 369 | GCC_NO_COMMON_BLOCKS = YES; 370 | GCC_OPTIMIZATION_LEVEL = 0; 371 | GCC_PREPROCESSOR_DEFINITIONS = ( 372 | "DEBUG=1", 373 | "$(inherited)", 374 | ); 375 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 376 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 377 | GCC_WARN_UNDECLARED_SELECTOR = YES; 378 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 379 | GCC_WARN_UNUSED_FUNCTION = YES; 380 | GCC_WARN_UNUSED_VARIABLE = YES; 381 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 382 | MTL_ENABLE_DEBUG_INFO = YES; 383 | ONLY_ACTIVE_ARCH = YES; 384 | SDKROOT = iphoneos; 385 | TARGETED_DEVICE_FAMILY = "1,2"; 386 | }; 387 | name = Debug; 388 | }; 389 | 97C147041CF9000F007C117D /* Release */ = { 390 | isa = XCBuildConfiguration; 391 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 392 | buildSettings = { 393 | ALWAYS_SEARCH_USER_PATHS = NO; 394 | CLANG_ANALYZER_NONNULL = YES; 395 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 396 | CLANG_CXX_LIBRARY = "libc++"; 397 | CLANG_ENABLE_MODULES = YES; 398 | CLANG_ENABLE_OBJC_ARC = YES; 399 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 400 | CLANG_WARN_BOOL_CONVERSION = YES; 401 | CLANG_WARN_COMMA = YES; 402 | CLANG_WARN_CONSTANT_CONVERSION = YES; 403 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 404 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 405 | CLANG_WARN_EMPTY_BODY = YES; 406 | CLANG_WARN_ENUM_CONVERSION = YES; 407 | CLANG_WARN_INFINITE_RECURSION = YES; 408 | CLANG_WARN_INT_CONVERSION = YES; 409 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 410 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 411 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 412 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 413 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 414 | CLANG_WARN_STRICT_PROTOTYPES = YES; 415 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 416 | CLANG_WARN_UNREACHABLE_CODE = YES; 417 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 418 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 419 | COPY_PHASE_STRIP = NO; 420 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 421 | ENABLE_NS_ASSERTIONS = NO; 422 | ENABLE_STRICT_OBJC_MSGSEND = YES; 423 | GCC_C_LANGUAGE_STANDARD = gnu99; 424 | GCC_NO_COMMON_BLOCKS = YES; 425 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 426 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 427 | GCC_WARN_UNDECLARED_SELECTOR = YES; 428 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 429 | GCC_WARN_UNUSED_FUNCTION = YES; 430 | GCC_WARN_UNUSED_VARIABLE = YES; 431 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 432 | MTL_ENABLE_DEBUG_INFO = NO; 433 | SDKROOT = iphoneos; 434 | TARGETED_DEVICE_FAMILY = "1,2"; 435 | VALIDATE_PRODUCT = YES; 436 | }; 437 | name = Release; 438 | }; 439 | 97C147061CF9000F007C117D /* Debug */ = { 440 | isa = XCBuildConfiguration; 441 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 442 | buildSettings = { 443 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 444 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 445 | ENABLE_BITCODE = NO; 446 | FRAMEWORK_SEARCH_PATHS = ( 447 | "$(inherited)", 448 | "$(PROJECT_DIR)/Flutter", 449 | ); 450 | INFOPLIST_FILE = Runner/Info.plist; 451 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 452 | LIBRARY_SEARCH_PATHS = ( 453 | "$(inherited)", 454 | "$(PROJECT_DIR)/Flutter", 455 | ); 456 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterChatApp; 457 | PRODUCT_NAME = "$(TARGET_NAME)"; 458 | VERSIONING_SYSTEM = "apple-generic"; 459 | }; 460 | name = Debug; 461 | }; 462 | 97C147071CF9000F007C117D /* Release */ = { 463 | isa = XCBuildConfiguration; 464 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 465 | buildSettings = { 466 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 467 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 468 | ENABLE_BITCODE = NO; 469 | FRAMEWORK_SEARCH_PATHS = ( 470 | "$(inherited)", 471 | "$(PROJECT_DIR)/Flutter", 472 | ); 473 | INFOPLIST_FILE = Runner/Info.plist; 474 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 475 | LIBRARY_SEARCH_PATHS = ( 476 | "$(inherited)", 477 | "$(PROJECT_DIR)/Flutter", 478 | ); 479 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterChatApp; 480 | PRODUCT_NAME = "$(TARGET_NAME)"; 481 | VERSIONING_SYSTEM = "apple-generic"; 482 | }; 483 | name = Release; 484 | }; 485 | /* End XCBuildConfiguration section */ 486 | 487 | /* Begin XCConfigurationList section */ 488 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 489 | isa = XCConfigurationList; 490 | buildConfigurations = ( 491 | 97C147031CF9000F007C117D /* Debug */, 492 | 97C147041CF9000F007C117D /* Release */, 493 | 249021D3217E4FDB00AE95B9 /* Profile */, 494 | ); 495 | defaultConfigurationIsVisible = 0; 496 | defaultConfigurationName = Release; 497 | }; 498 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 499 | isa = XCConfigurationList; 500 | buildConfigurations = ( 501 | 97C147061CF9000F007C117D /* Debug */, 502 | 97C147071CF9000F007C117D /* Release */, 503 | 249021D4217E4FDB00AE95B9 /* Profile */, 504 | ); 505 | defaultConfigurationIsVisible = 0; 506 | defaultConfigurationName = Release; 507 | }; 508 | /* End XCConfigurationList section */ 509 | }; 510 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 511 | } 512 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CHAT APP IN FLUTTER USING GOOGLE FIREBASE 2 | Chat App in Flutter using Google Firebase 3 | 4 | # Introduction: 5 | In this article we will learn how to create chat app in flutter using Google Firebase as backend. This article consists of number of articles in which you will learn 1. OTP Authentication in Flutter 2. Chat App Data Structure In Firebase Firestore 3. Pagination In Flutter Using Firebase Cloud Firestore 4. Upload Image File To Firebase Storage Using Flutter. I have divided chat app series in multiple articles, you will learn lots of stuff regarding flutter in this flutter chat app series. So let’s begin our app. 6 | 7 | # Output: 8 | ![Chat App in Flutte using Google Firebase](https://raw.githubusercontent.com/myvsparth/flutter_chat_app/master/screenshots/1.png) 9 | 10 | # Plugin Required: 11 | firebase_auth: // for firebase otp authentication 12 | shared_preferences: ^0.5.3+1 // for storing user credentials persistence 13 | cloud_firestore: ^0.12.7 // to access firebase real time database 14 | contact_picker: ^0.0.2 // to add friends from contact list 15 | image_picker: ^0.6.0+17 // to select image from device 16 | firebase_storage: ^3.0.3 // to send image to user for that we need to store image on server 17 | photo_view: ^0.4.2 // to view sent and received image in expanded view 18 | 19 | # Programming Steps: 20 | 1. First and basic step to create new application in flutter. If you are a beginner in flutter then you can check my blog Create a first app in Flutter. I have created an app named as “flutter_chat_app” 21 | 22 | 2. Open the pubspec.yaml file in your project and add the following dependencies into it. 23 | ``` 24 | dependencies: 25 | flutter: 26 | sdk: flutter 27 | cupertino_icons: ^0.1.2 28 | firebase_auth: 29 | shared_preferences: ^0.5.3+1 30 | cloud_firestore: ^0.12.7 31 | contact_picker: ^0.0.2 32 | image_picker: ^0.6.0+17 33 | firebase_storage: ^3.0.3 34 | photo_view: ^0.4.2 35 | ``` 36 | 37 | 3. Now we need to setup firebase project to provide authentication and storage feature. I have placed important implementation below but you can study full article OTP Authentication in Flutter , Chat App Data Structure In Firebase Firestore. Following is the OTP Authentication (registration_page.dart) Programming Implementation. 38 | ``` 39 | import 'package:flutter/material.dart'; 40 | import 'package:firebase_auth/firebase_auth.dart'; 41 | import 'package:flutter/services.dart'; 42 | import 'package:flutter_chat_app/pages/home_page.dart'; 43 | import 'package:shared_preferences/shared_preferences.dart'; 44 | import 'package:cloud_firestore/cloud_firestore.dart'; 45 | 46 | class RegistrationPage extends StatefulWidget { 47 | final SharedPreferences prefs; 48 | RegistrationPage({this.prefs}); 49 | @override 50 | _RegistrationPageState createState() => _RegistrationPageState(); 51 | } 52 | 53 | class _RegistrationPageState extends State { 54 | String phoneNo; 55 | String smsOTP; 56 | String verificationId; 57 | String errorMessage = ''; 58 | FirebaseAuth _auth = FirebaseAuth.instance; 59 | final db = Firestore.instance; 60 | 61 | @override 62 | initState() { 63 | super.initState(); 64 | } 65 | 66 | Future verifyPhone() async { 67 | final PhoneCodeSent smsOTPSent = (String verId, [int forceCodeResend]) { 68 | this.verificationId = verId; 69 | smsOTPDialog(context).then((value) {}); 70 | }; 71 | try { 72 | await _auth.verifyPhoneNumber( 73 | phoneNumber: this.phoneNo, // PHONE NUMBER TO SEND OTP 74 | codeAutoRetrievalTimeout: (String verId) { 75 | //Starts the phone number verification process for the given phone number. 76 | //Either sends an SMS with a 6 digit code to the phone number specified, or sign's the user in and [verificationCompleted] is called. 77 | this.verificationId = verId; 78 | }, 79 | codeSent: 80 | smsOTPSent, // WHEN CODE SENT THEN WE OPEN DIALOG TO ENTER OTP. 81 | timeout: const Duration(seconds: 20), 82 | verificationCompleted: (AuthCredential phoneAuthCredential) { 83 | print(phoneAuthCredential); 84 | }, 85 | verificationFailed: (AuthException e) { 86 | print('${e.message}'); 87 | }); 88 | } catch (e) { 89 | handleError(e); 90 | } 91 | } 92 | 93 | Future smsOTPDialog(BuildContext context) { 94 | return showDialog( 95 | context: context, 96 | barrierDismissible: false, 97 | builder: (BuildContext context) { 98 | return new AlertDialog( 99 | title: Text('Enter SMS Code'), 100 | content: Container( 101 | height: 85, 102 | child: Column(children: [ 103 | TextField( 104 | onChanged: (value) { 105 | this.smsOTP = value; 106 | }, 107 | ), 108 | (errorMessage != '' 109 | ? Text( 110 | errorMessage, 111 | style: TextStyle(color: Colors.red), 112 | ) 113 | : Container()) 114 | ]), 115 | ), 116 | contentPadding: EdgeInsets.all(10), 117 | actions: [ 118 | FlatButton( 119 | child: Text('Done'), 120 | onPressed: () { 121 | _auth.currentUser().then((user) async { 122 | signIn(); 123 | }); 124 | }, 125 | ) 126 | ], 127 | ); 128 | }); 129 | } 130 | 131 | signIn() async { 132 | try { 133 | final AuthCredential credential = PhoneAuthProvider.getCredential( 134 | verificationId: verificationId, 135 | smsCode: smsOTP, 136 | ); 137 | final FirebaseUser user = await _auth.signInWithCredential(credential); 138 | final FirebaseUser currentUser = await _auth.currentUser(); 139 | assert(user.uid == currentUser.uid); 140 | Navigator.of(context).pop(); 141 | DocumentReference mobileRef = db 142 | .collection("mobiles") 143 | .document(phoneNo.replaceAll(new RegExp(r'[^\w\s]+'), '')); 144 | await mobileRef.get().then((documentReference) { 145 | if (!documentReference.exists) { 146 | mobileRef.setData({}).then((documentReference) async { 147 | await db.collection("users").add({ 148 | 'name': "No Name", 149 | 'mobile': phoneNo.replaceAll(new RegExp(r'[^\w\s]+'), ''), 150 | 'profile_photo': "", 151 | }).then((documentReference) { 152 | widget.prefs.setBool('is_verified', true); 153 | widget.prefs.setString( 154 | 'mobile', 155 | phoneNo.replaceAll(new RegExp(r'[^\w\s]+'), ''), 156 | ); 157 | widget.prefs.setString('uid', documentReference.documentID); 158 | widget.prefs.setString('name', "No Name"); 159 | widget.prefs.setString('profile_photo', ""); 160 | 161 | mobileRef.setData({'uid': documentReference.documentID}).then( 162 | (documentReference) async { 163 | Navigator.of(context).pushReplacement(MaterialPageRoute( 164 | builder: (context) => HomePage(prefs: widget.prefs))); 165 | }).catchError((e) { 166 | print(e); 167 | }); 168 | }).catchError((e) { 169 | print(e); 170 | }); 171 | }); 172 | } else { 173 | widget.prefs.setBool('is_verified', true); 174 | widget.prefs.setString( 175 | 'mobile_number', 176 | phoneNo.replaceAll(new RegExp(r'[^\w\s]+'), ''), 177 | ); 178 | widget.prefs.setString('uid', documentReference["uid"]); 179 | widget.prefs.setString('name', documentReference["name"]); 180 | widget.prefs 181 | .setString('profile_photo', documentReference["profile_photo"]); 182 | 183 | Navigator.of(context).pushReplacement( 184 | MaterialPageRoute( 185 | builder: (context) => HomePage(prefs: widget.prefs), 186 | ), 187 | ); 188 | } 189 | }).catchError((e) {}); 190 | } catch (e) { 191 | handleError(e); 192 | } 193 | } 194 | 195 | handleError(PlatformException error) { 196 | switch (error.code) { 197 | case 'ERROR_INVALID_VERIFICATION_CODE': 198 | FocusScope.of(context).requestFocus(new FocusNode()); 199 | setState(() { 200 | errorMessage = 'Invalid Code'; 201 | }); 202 | Navigator.of(context).pop(); 203 | smsOTPDialog(context).then((value) {}); 204 | break; 205 | default: 206 | setState(() { 207 | errorMessage = error.message; 208 | }); 209 | 210 | break; 211 | } 212 | } 213 | 214 | @override 215 | Widget build(BuildContext context) { 216 | return Scaffold( 217 | body: Center( 218 | child: Column( 219 | mainAxisAlignment: MainAxisAlignment.center, 220 | children: [ 221 | Padding( 222 | padding: EdgeInsets.all(10), 223 | child: TextField( 224 | decoration: InputDecoration(hintText: '+910000000000'), 225 | onChanged: (value) { 226 | this.phoneNo = value; 227 | }, 228 | ), 229 | ), 230 | (errorMessage != '' 231 | ? Text( 232 | errorMessage, 233 | style: TextStyle(color: Colors.red), 234 | ) 235 | : Container()), 236 | SizedBox( 237 | height: 10, 238 | ), 239 | RaisedButton( 240 | onPressed: () { 241 | verifyPhone(); 242 | }, 243 | child: Text('Verify'), 244 | textColor: Colors.white, 245 | elevation: 7, 246 | color: Colors.blue, 247 | ) 248 | ], 249 | ), 250 | ), 251 | ); 252 | } 253 | } 254 | ``` 255 | 256 | 4. Now, we will implement add friend from contact list. Following is the programming implementation for access contacts from the device and add them as a friend to chat. 257 | ``` 258 | openContacts() async { 259 | Contact contact = await _contactPicker.selectContact(); 260 | if (contact != null) { 261 | String phoneNumber = contact.phoneNumber.number 262 | .toString() 263 | .replaceAll(new RegExp(r"\s\b|\b\s"), "") 264 | .replaceAll(new RegExp(r'[^\w\s]+'), ''); 265 | if (phoneNumber.length == 10) { 266 | phoneNumber = '+91$phoneNumber'; 267 | } 268 | if (phoneNumber.length == 12) { 269 | phoneNumber = '+$phoneNumber'; 270 | } 271 | if (phoneNumber.length == 13) { 272 | DocumentReference mobileRef = db 273 | .collection("mobiles") 274 | .document(phoneNumber.replaceAll(new RegExp(r'[^\w\s]+'), '')); 275 | await mobileRef.get().then((documentReference) { 276 | if (documentReference.exists) { 277 | contactsReference.add({ 278 | 'uid': documentReference['uid'], 279 | 'name': contact.fullName, 280 | 'mobile': phoneNumber.replaceAll(new RegExp(r'[^\w\s]+'), ''), 281 | }); 282 | } else { 283 | print('User Not Registered'); 284 | } 285 | }).catchError((e) {}); 286 | } else { 287 | print('Wrong Mobile Number'); 288 | } 289 | } 290 | } 291 | ``` 292 | 293 | 5. Now, we will implement chat screen in which usee will send text and image message to friend and vice versa. Following is the programming implementation for that. chat_page.dart. Pagination and image upload both are covered in this page. For full article reference please see Pagination In Flutter Using Firebase Cloud Firestore, Upload Image File To Firebase Storage Using Flutter. 294 | ``` 295 | import 'package:cloud_firestore/cloud_firestore.dart'; 296 | import 'package:firebase_storage/firebase_storage.dart'; 297 | import 'package:flutter/material.dart'; 298 | import 'package:flutter_chat_app/pages/gallary_page.dart'; 299 | import 'package:image_picker/image_picker.dart'; 300 | import 'package:shared_preferences/shared_preferences.dart'; 301 | 302 | class ChatPage extends StatefulWidget { 303 | final SharedPreferences prefs; 304 | final String chatId; 305 | final String title; 306 | ChatPage({this.prefs, this.chatId,this.title}); 307 | @override 308 | ChatPageState createState() { 309 | return new ChatPageState(); 310 | } 311 | } 312 | 313 | class ChatPageState extends State { 314 | final db = Firestore.instance; 315 | CollectionReference chatReference; 316 | final TextEditingController _textController = 317 | new TextEditingController(); 318 | bool _isWritting = false; 319 | 320 | @override 321 | void initState() { 322 | super.initState(); 323 | chatReference = 324 | db.collection("chats").document(widget.chatId).collection('messages'); 325 | } 326 | 327 | List generateSenderLayout(DocumentSnapshot documentSnapshot) { 328 | return [ 329 | new Expanded( 330 | child: new Column( 331 | crossAxisAlignment: CrossAxisAlignment.end, 332 | children: [ 333 | new Text(documentSnapshot.data['sender_name'], 334 | style: new TextStyle( 335 | fontSize: 14.0, 336 | color: Colors.black, 337 | fontWeight: FontWeight.bold)), 338 | new Container( 339 | margin: const EdgeInsets.only(top: 5.0), 340 | child: documentSnapshot.data['image_url'] != '' 341 | ? InkWell( 342 | child: new Container( 343 | child: Image.network( 344 | documentSnapshot.data['image_url'], 345 | fit: BoxFit.fitWidth, 346 | ), 347 | height: 150, 348 | width: 150.0, 349 | color: Color.fromRGBO(0, 0, 0, 0.2), 350 | padding: EdgeInsets.all(5), 351 | ), 352 | onTap: () { 353 | Navigator.of(context).push( 354 | MaterialPageRoute( 355 | builder: (context) => GalleryPage( 356 | imagePath: documentSnapshot.data['image_url'], 357 | ), 358 | ), 359 | ); 360 | }, 361 | ) 362 | : new Text(documentSnapshot.data['text']), 363 | ), 364 | ], 365 | ), 366 | ), 367 | new Column( 368 | crossAxisAlignment: CrossAxisAlignment.end, 369 | children: [ 370 | new Container( 371 | margin: const EdgeInsets.only(left: 8.0), 372 | child: new CircleAvatar( 373 | backgroundImage: 374 | new NetworkImage(documentSnapshot.data['profile_photo']), 375 | )), 376 | ], 377 | ), 378 | ]; 379 | } 380 | 381 | List generateReceiverLayout(DocumentSnapshot documentSnapshot) { 382 | return [ 383 | new Column( 384 | crossAxisAlignment: CrossAxisAlignment.start, 385 | children: [ 386 | new Container( 387 | margin: const EdgeInsets.only(right: 8.0), 388 | child: new CircleAvatar( 389 | backgroundImage: 390 | new NetworkImage(documentSnapshot.data['profile_photo']), 391 | )), 392 | ], 393 | ), 394 | new Expanded( 395 | child: new Column( 396 | crossAxisAlignment: CrossAxisAlignment.start, 397 | children: [ 398 | new Text(documentSnapshot.data['sender_name'], 399 | style: new TextStyle( 400 | fontSize: 14.0, 401 | color: Colors.black, 402 | fontWeight: FontWeight.bold)), 403 | new Container( 404 | margin: const EdgeInsets.only(top: 5.0), 405 | child: documentSnapshot.data['image_url'] != '' 406 | ? InkWell( 407 | child: new Container( 408 | child: Image.network( 409 | documentSnapshot.data['image_url'], 410 | fit: BoxFit.fitWidth, 411 | ), 412 | height: 150, 413 | width: 150.0, 414 | color: Color.fromRGBO(0, 0, 0, 0.2), 415 | padding: EdgeInsets.all(5), 416 | ), 417 | onTap: () { 418 | Navigator.of(context).push( 419 | MaterialPageRoute( 420 | builder: (context) => GalleryPage( 421 | imagePath: documentSnapshot.data['image_url'], 422 | ), 423 | ), 424 | ); 425 | }, 426 | ) 427 | : new Text(documentSnapshot.data['text']), 428 | ), 429 | ], 430 | ), 431 | ), 432 | ]; 433 | } 434 | 435 | generateMessages(AsyncSnapshot snapshot) { 436 | return snapshot.data.documents 437 | .map((doc) => Container( 438 | margin: const EdgeInsets.symmetric(vertical: 10.0), 439 | child: new Row( 440 | children: doc.data['sender_id'] != widget.prefs.getString('uid') 441 | ? generateReceiverLayout(doc) 442 | : generateSenderLayout(doc), 443 | ), 444 | )) 445 | .toList(); 446 | } 447 | 448 | @override 449 | Widget build(BuildContext context) { 450 | return Scaffold( 451 | appBar: AppBar( 452 | title: Text(widget.title), 453 | ), 454 | body: Container( 455 | padding: EdgeInsets.all(5), 456 | child: new Column( 457 | children: [ 458 | StreamBuilder( 459 | stream: chatReference.orderBy('time',descending: true).snapshots(), 460 | builder: (BuildContext context, 461 | AsyncSnapshot snapshot) { 462 | if (!snapshot.hasData) return new Text("No Chat"); 463 | return Expanded( 464 | child: new ListView( 465 | reverse: true, 466 | children: generateMessages(snapshot), 467 | ), 468 | ); 469 | }, 470 | ), 471 | new Divider(height: 1.0), 472 | new Container( 473 | decoration: new BoxDecoration(color: Theme.of(context).cardColor), 474 | child: _buildTextComposer(), 475 | ), 476 | new Builder(builder: (BuildContext context) { 477 | return new Container(width: 0.0, height: 0.0); 478 | }) 479 | ], 480 | ), 481 | ), 482 | ); 483 | } 484 | 485 | IconButton getDefaultSendButton() { 486 | return new IconButton( 487 | icon: new Icon(Icons.send), 488 | onPressed: _isWritting 489 | ? () => _sendText(_textController.text) 490 | : null, 491 | ); 492 | } 493 | 494 | Widget _buildTextComposer() { 495 | return new IconTheme( 496 | data: new IconThemeData( 497 | color: _isWritting 498 | ? Theme.of(context).accentColor 499 | : Theme.of(context).disabledColor, 500 | ), 501 | child: new Container( 502 | margin: const EdgeInsets.symmetric(horizontal: 8.0), 503 | child: new Row( 504 | children: [ 505 | new Container( 506 | margin: new EdgeInsets.symmetric(horizontal: 4.0), 507 | child: new IconButton( 508 | icon: new Icon( 509 | Icons.photo_camera, 510 | color: Theme.of(context).accentColor, 511 | ), 512 | onPressed: () async { 513 | var image = await ImagePicker.pickImage( 514 | source: ImageSource.gallery); 515 | int timestamp = new DateTime.now().millisecondsSinceEpoch; 516 | StorageReference storageReference = FirebaseStorage 517 | .instance 518 | .ref() 519 | .child('chats/img_' + timestamp.toString() + '.jpg'); 520 | StorageUploadTask uploadTask = 521 | storageReference.putFile(image); 522 | await uploadTask.onComplete; 523 | String fileUrl = await storageReference.getDownloadURL(); 524 | _sendImage(messageText: null, imageUrl: fileUrl); 525 | }), 526 | ), 527 | new Flexible( 528 | child: new TextField( 529 | controller: _textController, 530 | onChanged: (String messageText) { 531 | setState(() { 532 | _isWritting = messageText.length > 0; 533 | }); 534 | }, 535 | onSubmitted: _sendText, 536 | decoration: 537 | new InputDecoration.collapsed(hintText: "Send a message"), 538 | ), 539 | ), 540 | new Container( 541 | margin: const EdgeInsets.symmetric(horizontal: 4.0), 542 | child: getDefaultSendButton(), 543 | ), 544 | ], 545 | ), 546 | )); 547 | } 548 | 549 | Future _sendText(String text) async { 550 | _textController.clear(); 551 | chatReference.add({ 552 | 'text': text, 553 | 'sender_id': widget.prefs.getString('uid'), 554 | 'sender_name': widget.prefs.getString('name'), 555 | 'profile_photo': widget.prefs.getString('profile_photo'), 556 | 'image_url': '', 557 | 'time': FieldValue.serverTimestamp(), 558 | }).then((documentReference) { 559 | setState(() { 560 | _isWritting = false; 561 | }); 562 | }).catchError((e) {}); 563 | } 564 | 565 | void _sendImage({String messageText, String imageUrl}) { 566 | chatReference.add({ 567 | 'text': messageText, 568 | 'sender_id': widget.prefs.getString('uid'), 569 | 'sender_name': widget.prefs.getString('name'), 570 | 'profile_photo': widget.prefs.getString('profile_photo'), 571 | 'image_url': imageUrl, 572 | 'time': FieldValue.serverTimestamp(), 573 | }); 574 | } 575 | } 576 | ``` 577 | 578 | 6. Great you are done with chat app in flutter using google firebase firestore. Please download our source code attached and run the code on device or emulator. 579 | 580 | ## NOTE: 581 | PLEASE CHECK OUT GIT REPO FOR FULL SOURCE CODE. YOU NEED TO ADD YOUR google-services.json FILE IN ANDROID => APP FOLDER. 582 | 583 | ## Possible Errors: 584 | 1. flutter barcode scan Failed to notify project evaluation listener. > java.lang.AbstractMethodError (no error message) 585 | 586 | 2. Android dependency 'androidx.core:core' has different version for the compile (1.0.0) and runtime (1.0.1) classpath. You should manually set the same version via DependencyResolution 587 | 588 | 3. import androidx.annotation.NonNull; 589 | 590 | ## Solution: 591 | 1 & 2. In android/build.grader change the version 592 | classpath 'com.android.tools.build:gradle:3.3.1' 593 | 594 | 3. Put 595 | android.useAndroidX=true 596 | android.enableJetifier=true 597 | In android/gradle.properties file 598 | 599 | Git: https://github.com/myvsparth/flutter_chat_app 600 | 601 | ## Related Tags: Flutter, Chat App, Dart, Android, iOS, Chat App Source Code, Chat App Demo --------------------------------------------------------------------------------