├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ └── feature_request.md
└── workflows
│ └── dart.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── LICENSE.txt
├── README.md
├── _config.yml
├── chat_sdk.iml
├── cloudFunction
└── index.js
├── example
└── flutter_chat.dart
├── images
├── icon_logout.png
├── icon_music.png
├── icon_nature.png
└── img_not_available.jpeg
├── lib
├── Models
│ └── UserModel.dart
├── chatDB.dart
├── chatData.dart
├── chatWidget.dart
├── constants.dart
├── hexColor.dart
└── screens
│ ├── chat.dart
│ ├── dashboard_screen.dart
│ ├── login_screen.dart
│ └── zoomImage.dart
├── pubspec.lock
└── pubspec.yaml
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: #[ankesh-kumar]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: ["https://paypal.me/ankeshkumar01"]
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/dart.yml:
--------------------------------------------------------------------------------
1 | name: Dart CI
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 |
8 | runs-on: ubuntu-latest
9 |
10 | container:
11 | image: google/dart:latest
12 |
13 | steps:
14 | - uses: actions/checkout@v1
15 | - name: Install dependencies
16 | run: pub get
17 | - name: Run tests
18 | run: pub run test
19 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .flutter-plugins
2 | .flutter-plugins-dependencies
3 | .packages
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## version 2.0.2 dependencies update and code improvement
2 | ## version 2.0.1 flutter web demo added
3 | ## version 2.0.0 Flutter Web Support added
4 | ## version 1.1.2 dependencies updated
5 | ## version 1.1.1 dependencies updated
6 | ## version 1.1.0 Chat with added friends only (privacy)
7 | ## version 1.0.0 dependencies updated
8 | ## version 0.1.4 user online/offline status
9 | ## version 0.1.3 widget seprated
10 | ## version 0.1.2 example added
11 | ## version 0.1.1 add screen widgets
12 | ## version 0.1.0 flutter Chat android and iOS
13 |
14 |
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Ankesh Kumar
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Ankesh Kumar
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # flutter_chat
2 | A Chat Helper for create chat application in Flutter using Firebase as backend services.
3 |
4 |
5 | # Checkout Android app Demo
6 | [
](https://play.google.com/store/apps/details?id=com.smartmobilevilla.just_chat)
7 |
8 | # Checkout Web Demo
9 | [Just Chat Web Demo](http://justchat.smartmobilevilla.com)
10 |
11 | # Support Development
12 | If you found this project helpful or you learned something from the source code and want to thank me, consider buying me a cup of ☕️
13 |
14 | [PayPal](https://paypal.me/ankeshkumar01)
15 |
16 | [
](https://www.buymeacoffee.com/ankeshkumar)
17 |
18 | ## Features:
19 | 1. 1-1 chat.
20 | 2. Chat with only added friends(Privacy). New
21 | 3. Share Pic with Gallery/Camera
22 | 4. User online status
23 | 5. Flutter web supported
24 |
25 | ## Next Future Scope
26 |
27 | 1. Notification
28 | 2. Group Chat
29 | 3. User acceptance on chat request
30 | 4. share location on chat
31 |
32 |
33 |
34 | ## Screenshots:
35 |
36 | 
37 | 
38 | 
39 |
40 | ## Getting Started
41 | * Add this to your package's pubspec.yaml file:
42 | dependencies:
[flutter_chat](https://pub.dev/packages/flutter_chat)
43 |
44 | * Add [firebase](https://firebase.google.com/) in your android and ios project.
45 |
46 | * Security Rules for Storage:
47 |
48 | rules_version = '2';
49 | service firebase.storage {
50 | match /b/{bucket}/o {
51 | match /{allPaths=**} {
52 | allow read, write: if request.auth != null;
53 | }
54 | }
55 | }
56 |
57 | * Security Rules for Cloud fireStore:
58 |
59 | service cloud.firestore {
60 | match /databases/{database}/documents {
61 | match /{document=**} {
62 | allow read, write: if request.auth != null;
63 | }
64 | }
65 | }
66 |
67 | * You can modify the security rules as your need.
68 |
69 | * Deploy "Cloud Function" on firebase. (provided on cloudFunction folder, used for show user online/offline status).
70 |
71 | * Create a Stateful widget class and call the method in body (example can be found in Github repo),
72 | within initState():
73 | -> ChatData.init("app name",context);
74 | and in body of Widget build:
75 | -> ChatData.widgetWelcomeScreen(context)
76 |
77 |
78 | ## Flutter Web
79 |
80 | * If want to run Flutter-Chat project, on web
81 | Go to App, Firebase->Settings and then add new app, web
82 | Follow the instructions,
83 | Put the firebaseConfig script on index.html in web folder,
84 |
92 |
93 | Enjoy Fluttering
94 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-cayman
--------------------------------------------------------------------------------
/chat_sdk.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/cloudFunction/index.js:
--------------------------------------------------------------------------------
1 | const functions = require('firebase-functions');
2 | const admin = require('firebase-admin');
3 | admin.initializeApp(functions.config().firebase);
4 |
5 | const firestore = functions.firestore;
6 |
7 | exports.onUserStatusChange = functions.database
8 | .ref('/status/{id}')
9 | .onUpdate(event => {
10 |
11 | var db = admin.firestore();
12 |
13 |
14 | //const usersRef = firestore.document('/users/' + event.params.userId);
15 | const usersRef = db.collection("users");
16 | var snapShot = event.data;
17 |
18 | return event.data.ref.once('value')
19 | .then(statusSnap => snapShot.val())
20 | .then(status => {
21 | if (status === 'offline'){
22 | usersRef
23 | .doc(event.params.id)
24 | .set({
25 | isOnline: false,
26 | last_active: Date.now()
27 | }, {merge: true});
28 | }
29 | })
30 | });
--------------------------------------------------------------------------------
/example/flutter_chat.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter_chat/chatData.dart';
2 | import 'package:flutter/material.dart';
3 | import 'package:flutter_chat/chatWidget.dart';
4 |
5 | class WelcomeScreen extends StatefulWidget {
6 | static const String id = "welcome_screen";
7 | @override
8 | _WelcomeScreenState createState() => _WelcomeScreenState();
9 | }
10 |
11 | class _WelcomeScreenState extends State {
12 | @override
13 | void initState() {
14 | super.initState();
15 | ChatData.init("Just Chat",context);
16 | }
17 |
18 | @override
19 | Widget build(BuildContext context) {
20 | return Scaffold(
21 | appBar: ChatWidget.getAppBar(),
22 | backgroundColor: Colors.white,
23 | body: ChatWidget.widgetWelcomeScreen(context));
24 | }
25 | }
--------------------------------------------------------------------------------
/images/icon_logout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ankesh-kumar/Flutter-chat/45b50b3a71470239c4f0e620c54e6191c969ba17/images/icon_logout.png
--------------------------------------------------------------------------------
/images/icon_music.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ankesh-kumar/Flutter-chat/45b50b3a71470239c4f0e620c54e6191c969ba17/images/icon_music.png
--------------------------------------------------------------------------------
/images/icon_nature.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ankesh-kumar/Flutter-chat/45b50b3a71470239c4f0e620c54e6191c969ba17/images/icon_nature.png
--------------------------------------------------------------------------------
/images/img_not_available.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ankesh-kumar/Flutter-chat/45b50b3a71470239c4f0e620c54e6191c969ba17/images/img_not_available.jpeg
--------------------------------------------------------------------------------
/lib/Models/UserModel.dart:
--------------------------------------------------------------------------------
1 | class UserModel {
2 | final String photoUrl;
3 | final String nickname;
4 | final String id;
5 |
6 | @override
7 | String toString() =>
8 | 'UserModel{photoUrl: $photoUrl, nickname: $nickname, id: $id}';
9 |
10 | UserModel({this.id, this.nickname, this.photoUrl});
11 | }
12 |
--------------------------------------------------------------------------------
/lib/chatDB.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:math';
3 |
4 | import 'package:cloud_firestore/cloud_firestore.dart';
5 | import 'package:firebase_auth/firebase_auth.dart';
6 | import 'package:firebase_database/firebase_database.dart';
7 | import 'package:flutter/cupertino.dart';
8 | import 'package:google_sign_in/google_sign_in.dart';
9 |
10 | class ChatDBFireStore {
11 | static Future isUserSignedIn() async {
12 | // GoogleSignInAccount? user = _currentUser;
13 |
14 | return false;
15 | }
16 |
17 | static String getDocName() {
18 | String dbUser = "users";
19 | return dbUser;
20 | }
21 |
22 | void setUserLastSeen(User logInUser) async {
23 | if (logInUser != null)
24 | await FirebaseFirestore.instance
25 | .collection(getDocName())
26 | .doc(logInUser.uid)
27 | .set({
28 | 'lastActive': DateTime.now().millisecondsSinceEpoch,
29 | 'online': 'online'
30 | }, SetOptions(merge: true));
31 | }
32 |
33 | void setChatLastSeen(
34 | var currentUserId, var stCollection, var chatId, bool isChat) async {
35 | //if (logInUser != null) {
36 | await FirebaseFirestore.instance.collection(stCollection).doc(chatId).set(
37 | {currentUserId: isChat ? true : DateTime.now().millisecondsSinceEpoch},
38 | SetOptions(merge: true));
39 | //}
40 | }
41 |
42 | static Future checkUserExists(User logInUser) async {
43 | //print('abcdearlier');
44 | QuerySnapshot result;
45 | try {
46 | result = await FirebaseFirestore.instance
47 | .collection(getDocName())
48 | .where('userId', isEqualTo: logInUser.uid)
49 | .get();
50 | } catch (e) {
51 | //print('ex ' + e.toString());
52 | }
53 |
54 | //print('abcdeafter');
55 | final List documents = result.docs;
56 | //print('abcdeafterfinal');
57 | if (documents.length == 0) {
58 | // Update data to server if new user
59 | await saveNewUser(logInUser);
60 | }
61 | }
62 |
63 | static saveNewUser(User logInUser) {
64 | //print('UserId ' + logInUser.uid);
65 | //print('UserID' + logInUser.displayName);
66 | //print('UserID' + logInUser.photoURL);
67 | //print('UserID' + logInUser.email);
68 |
69 | List friendList = [];
70 |
71 | FirebaseFirestore.instance.collection(getDocName()).doc(logInUser.uid).set({
72 | 'nickname': logInUser.displayName,
73 | 'photoUrl': logInUser.photoURL,
74 | 'userId': logInUser.uid,
75 | 'email': logInUser.email,
76 | 'friends': friendList,
77 | 'createdAt': DateTime.now().millisecondsSinceEpoch.toString(),
78 | 'chattingWith': null,
79 | 'online': null
80 | });
81 | }
82 |
83 | static streamChatData() {
84 | FirebaseFirestore.instance
85 | .collection(ChatDBFireStore.getDocName())
86 | .snapshots();
87 | }
88 |
89 | static Future makeUserOnline(User logInUser) async {
90 | await ChatDBFireStore().setUserLastSeen(logInUser);
91 |
92 | FirebaseDatabase.instance
93 | .reference()
94 | .child("/status/" + logInUser.uid)
95 | .onDisconnect()
96 | .set("offline");
97 | FirebaseDatabase.instance
98 | .reference()
99 | .child("/status/" + logInUser.uid)
100 | .set("online");
101 | }
102 |
103 | Stream streamChatDataList(var stCollection, var groupChatId) {
104 | return FirebaseFirestore.instance
105 | .collection(stCollection)
106 | .doc(groupChatId)
107 | .collection(groupChatId)
108 | .orderBy('timestamp', descending: true)
109 | .limit(50)
110 | .snapshots();
111 | }
112 |
113 | Future> getFriendList(var userId) async {
114 | List friendList;
115 |
116 | FirebaseFirestore.instance
117 | .collection('users')
118 | .doc(userId)
119 | .get()
120 | .then((DocumentSnapshot documentSnapshot) {
121 | if (documentSnapshot.exists) {
122 | friendList = documentSnapshot.get('friends');
123 | return friendList;
124 | } else {
125 | return null;
126 | //print('Document does not exist on the database');
127 | }
128 | });
129 |
130 | return friendList;
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/lib/chatData.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 | import 'package:firebase_auth/firebase_auth.dart';
4 | import 'package:firebase_core/firebase_core.dart';
5 | import 'package:flutter/cupertino.dart';
6 | import 'package:flutter/material.dart';
7 | import 'package:fluttertoast/fluttertoast.dart';
8 | import 'package:google_sign_in/google_sign_in.dart';
9 | import 'chatDB.dart';
10 | import 'chatWidget.dart';
11 | import 'constants.dart';
12 | import 'screens/dashboard_screen.dart';
13 | import 'screens/login_screen.dart';
14 |
15 | class ChatData {
16 | static String appName = "Just Chat ";
17 |
18 | static Future openDialog(BuildContext context) async {
19 | switch (await showDialog(
20 | context: context,
21 | builder: (BuildContext context) {
22 | return SimpleDialog(
23 | contentPadding:
24 | EdgeInsets.only(left: 0.0, right: 0.0, top: 0.0, bottom: 0.0),
25 | children: [
26 | Container(
27 | color: themeColor,
28 | margin: EdgeInsets.all(0.0),
29 | padding: EdgeInsets.only(bottom: 10.0, top: 10.0),
30 | height: 100.0,
31 | child: Column(
32 | children: [
33 | Container(
34 | child: Icon(
35 | Icons.exit_to_app,
36 | size: 30.0,
37 | color: Colors.white,
38 | ),
39 | margin: EdgeInsets.only(bottom: 10.0),
40 | ),
41 | ChatWidget.widgetShowText(
42 | 'Are you sure to exit app?', '', ''),
43 | ],
44 | ),
45 | ),
46 | SimpleDialogOption(
47 | onPressed: () {
48 | Navigator.pop(context, 0);
49 | },
50 | child: Row(
51 | children: [
52 | Container(
53 | child: Icon(
54 | Icons.cancel,
55 | color: Colors.white70,
56 | ),
57 | margin: EdgeInsets.only(right: 10.0),
58 | ),
59 | ChatWidget.widgetShowText('Cancel', '', ''),
60 | ],
61 | ),
62 | ),
63 | SimpleDialogOption(
64 | onPressed: () {
65 | Navigator.pop(context, 1);
66 | },
67 | child: Row(
68 | children: [
69 | Container(
70 | child: Icon(
71 | Icons.check_circle,
72 | color: Colors.white70,
73 | ),
74 | margin: EdgeInsets.only(right: 10.0),
75 | ),
76 | ChatWidget.widgetShowText('Yes', '', ''),
77 | ],
78 | ),
79 | ),
80 | ],
81 | );
82 | })) {
83 | case 0:
84 | break;
85 | case 1:
86 | exit(0);
87 | break;
88 | }
89 | }
90 |
91 | static Future handleSignOut(BuildContext context) async {
92 | await Firebase.initializeApp();
93 | final GoogleSignIn googleSignIn = GoogleSignIn();
94 |
95 | await FirebaseAuth.instance.signOut();
96 | await googleSignIn.disconnect();
97 | await googleSignIn.signOut();
98 |
99 | Navigator.pushReplacement(
100 | context, MaterialPageRoute(builder: (context) => LoginScreen()));
101 | }
102 |
103 | static Future authUsersGoogle(BuildContext context) async {
104 | await Firebase.initializeApp();
105 | final GoogleSignIn googleSignIn = GoogleSignIn();
106 | final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
107 |
108 | GoogleSignInAccount googleUser = await googleSignIn.signIn();
109 | GoogleSignInAuthentication googleAuth = await googleUser.authentication;
110 |
111 | final AuthCredential credential = GoogleAuthProvider.credential(
112 | accessToken: googleAuth.accessToken,
113 | idToken: googleAuth.idToken,
114 | );
115 |
116 | final UserCredential logInUser =
117 | await firebaseAuth.signInWithCredential(credential);
118 |
119 | if (logInUser != null) {
120 | // Check is already sign up
121 | await ChatDBFireStore.checkUserExists(firebaseAuth.currentUser);
122 |
123 | final User logInUser =
124 | (await firebaseAuth.signInWithCredential(credential)).user;
125 |
126 | /**
127 | * Make user online
128 | */
129 | await ChatDBFireStore.makeUserOnline(logInUser);
130 |
131 | Navigator.pushReplacement(
132 | context,
133 | MaterialPageRoute(
134 | builder: (context) =>
135 | DashboardScreen(currentUserId: logInUser.uid)));
136 | return true;
137 | } else {
138 | return false;
139 | }
140 | }
141 |
142 | static Future checkUserLoggedin(BuildContext context) async {
143 | User user = FirebaseAuth.instance.currentUser;
144 | if (user != null)
145 | return true;
146 | else
147 | return false;
148 | }
149 |
150 | static String getGroupChatID(String logInUserId, String peerId) {
151 | if (logInUserId.hashCode <= peerId.hashCode) {
152 | return '$logInUserId-$peerId';
153 | } else {
154 | return '$peerId-$logInUserId';
155 | }
156 | }
157 |
158 | static Future isSignedIn() async {
159 | final GoogleSignIn googleSignIn = GoogleSignIn();
160 | bool isLoggedIn = await googleSignIn.isSignedIn();
161 | return isLoggedIn;
162 | }
163 |
164 | static void authUser(BuildContext context) async {
165 | bool isValidUser = await ChatData.authUsersGoogle(context);
166 | //print('isValid' + isValidUser.toString());
167 | if (isValidUser) {
168 | //if (await ChatData.isSignedIn()) {
169 | ////print('sign in signin');
170 | //ChatData.checkUserLogin(context);
171 |
172 | } else {
173 | //print('sign in fail');
174 | Fluttertoast.showToast(msg: "Sign in fail");
175 | }
176 | }
177 |
178 | static init(String applicationName, BuildContext context) {
179 | appName = applicationName;
180 | //startTime(context);
181 | checkUserLogin(context);
182 | }
183 |
184 | static checkUserLogin(BuildContext context) async {
185 | await Firebase.initializeApp();
186 | final GoogleSignIn googleSignIn = GoogleSignIn();
187 | final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
188 |
189 | if (await isSignedIn() == true) {
190 | GoogleSignInAccount googleUser = await googleSignIn.signIn();
191 | GoogleSignInAuthentication googleAuth = await googleUser.authentication;
192 |
193 | final AuthCredential credential = GoogleAuthProvider.credential(
194 | accessToken: googleAuth.accessToken,
195 | idToken: googleAuth.idToken,
196 | );
197 |
198 | final User logInUser =
199 | (await firebaseAuth.signInWithCredential(credential)).user;
200 |
201 | /**
202 | * Make user online
203 | */
204 | await ChatDBFireStore.makeUserOnline(logInUser);
205 |
206 | // Navigator.pushReplacement(
207 | // context,
208 | // MaterialPageRoute(
209 | // builder: (context) =>
210 | // DashboardScreen(currentUserId: logInUser.uid)));
211 |
212 | Navigator.pushReplacement(
213 | context,
214 | MaterialPageRoute(
215 | builder: (context) =>
216 | DashboardScreen(currentUserId: logInUser.uid)));
217 | } else {
218 | //return ChatData.widgetLoginScreen(context);
219 | Navigator.pushReplacement(
220 | context, MaterialPageRoute(builder: (context) => LoginScreen()));
221 | }
222 | }
223 |
224 | static startTime(BuildContext context) async {
225 | var _duration = new Duration(seconds: 2);
226 | return new Timer(_duration, checkUserLogin(context));
227 | }
228 |
229 | static bool isLastMessageLeft(var listMessage, String id, int index) {
230 | if ((index > 0 &&
231 | listMessage != null &&
232 | listMessage[index - 1].get('idFrom') == id) ||
233 | index == 0) {
234 | return true;
235 | } else {
236 | return false;
237 | }
238 | }
239 |
240 | static bool isLastMessageRight(var listMessage, String id, int index) {
241 | if ((index > 0 &&
242 | listMessage != null &&
243 | listMessage[index - 1].get('idFrom') != id) ||
244 | index == 0) {
245 | return true;
246 | } else {
247 | return false;
248 | }
249 | }
250 | }
251 |
--------------------------------------------------------------------------------
/lib/chatWidget.dart:
--------------------------------------------------------------------------------
1 | import 'package:cached_network_image/cached_network_image.dart';
2 | import 'package:cloud_firestore/cloud_firestore.dart';
3 | import 'package:flutter/foundation.dart';
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_linkify/flutter_linkify.dart';
6 | import 'package:intl/intl.dart';
7 | import 'package:photo_view/photo_view.dart';
8 | import 'package:url_launcher/url_launcher.dart';
9 | import 'chatDB.dart';
10 | import 'chatData.dart';
11 | import 'constants.dart';
12 | import 'screens/chat.dart';
13 | import 'screens/zoomImage.dart';
14 |
15 | class ChatWidget {
16 | static getFriendList() {
17 | List data = [
18 | "h7127ZCne0OzSRwNXtktMPei0CH3",
19 | "pdj9dlfEQMTUD6s954qiPeplEog2"
20 | ];
21 | return data;
22 | }
23 |
24 | //static Future userListStack(String currentUserId, BuildContext context) async{
25 | //
26 | // //List friendList=await getFriendList();
27 | // List friendList=await getFriendList();
28 | //
29 | // return
30 | // }
31 |
32 | static Widget userListbuildItem(
33 | BuildContext context, String currentUserId, DocumentSnapshot document) {
34 | //print('adgbasdg_userbuildItem');
35 |
36 | //print(currentUserId);
37 | if (document.get('userId') == currentUserId) {
38 | return Container();
39 | } else {
40 | return Container(
41 | child: ElevatedButton(
42 | child: Row(
43 | children: [
44 | Material(
45 | child: document.get('photoUrl') != null
46 | ? widgetShowImages(document.get('photoUrl'), 50)
47 | : Icon(
48 | Icons.account_circle,
49 | size: 50.0,
50 | color: colorPrimaryDark,
51 | ),
52 | borderRadius: BorderRadius.all(Radius.circular(25.0)),
53 | clipBehavior: Clip.hardEdge,
54 | ),
55 | Flexible(
56 | child: Container(
57 | child: Column(
58 | children: [
59 | Container(
60 | child: Text(
61 | 'Nickname: ${document.get('nickname')}',
62 | style: TextStyle(color: primaryColor),
63 | ),
64 | alignment: Alignment.centerLeft,
65 | margin: EdgeInsets.fromLTRB(10.0, 0.0, 0.0, 5.0),
66 | ),
67 | ],
68 | ),
69 | margin: EdgeInsets.only(left: 20.0),
70 | ),
71 | ),
72 | ConstrainedBox(
73 | constraints: new BoxConstraints(
74 | minHeight: 10.0,
75 | minWidth: 10.0,
76 | maxHeight: 30.0,
77 | maxWidth: 30.0,
78 | ),
79 | child: new DecoratedBox(
80 | decoration: new BoxDecoration(
81 | color: document.get('online') == 'online'
82 | ? Colors.greenAccent
83 | : Colors.transparent),
84 | ),
85 | ),
86 | ],
87 | ),
88 | onPressed: () {
89 | Navigator.push(
90 | context,
91 | MaterialPageRoute(
92 | builder: (context) => Chat(
93 | currentUserId: currentUserId,
94 | peerId: document.id,
95 | peerName: document.get('nickname'),
96 | peerAvatar: document.get('photoUrl'),
97 | )));
98 | },
99 | style: ElevatedButton.styleFrom(
100 | primary: viewBg,
101 | onPrimary: viewBg,
102 | padding: EdgeInsets.fromLTRB(25.0, 10.0, 25.0, 10.0))),
103 | margin: EdgeInsets.only(bottom: 10.0, left: 5.0, right: 5.0),
104 | );
105 | }
106 | }
107 |
108 | static Widget widgetLoginScreen(BuildContext context) {
109 | return Padding(
110 | padding: EdgeInsets.symmetric(horizontal: 24.0),
111 | child: Column(
112 | mainAxisAlignment: MainAxisAlignment.center,
113 | crossAxisAlignment: CrossAxisAlignment.stretch,
114 | children: [
115 | Container(
116 | child: Row(
117 | crossAxisAlignment: CrossAxisAlignment.center,
118 | mainAxisAlignment: MainAxisAlignment.center,
119 | children: [
120 | Container(
121 | child: Icon(
122 | Icons.message,
123 | color: Colors.greenAccent,
124 | ),
125 | height: 25.0,
126 | ),
127 | Text(
128 | ChatData.appName,
129 | style: TextStyle(
130 | fontSize: 25.0,
131 | fontWeight: FontWeight.w900,
132 | ),
133 | ),
134 | ],
135 | ),
136 | ),
137 | SizedBox(
138 | height: 48.0,
139 | ),
140 | Center(
141 | child: ElevatedButton(
142 | onPressed: () {
143 | ChatData.authUser(context);
144 | },
145 | child: Text(
146 | 'SIGN IN WITH GOOGLE',
147 | style: TextStyle(fontSize: 16.0, color: Colors.white),
148 | ),
149 | style: ElevatedButton.styleFrom(
150 | primary: Color(0xffdd4b39),
151 | onPrimary: Color(0xffff7f7f),
152 | padding: EdgeInsets.fromLTRB(30.0, 15.0, 30.0, 15.0))),
153 | ),
154 | ],
155 | ),
156 | );
157 | }
158 |
159 | static Widget getAppBar() {
160 | return AppBar(
161 | leading: null,
162 | title: Text(ChatData.appName),
163 | backgroundColor: themeColor,
164 | );
165 | }
166 |
167 | static Widget widgetWelcomeScreen(BuildContext context) {
168 | return Center(
169 | child: Container(
170 | child: Text(
171 | ChatData.appName,
172 | style: TextStyle(fontSize: 28),
173 | )),
174 | );
175 | }
176 |
177 | static Widget widgetFullPhoto(BuildContext context, String url) {
178 | return Container(child: PhotoView(imageProvider: NetworkImage(url)));
179 | }
180 |
181 | static Widget widgetChatBuildItem(BuildContext context, var listMessage,
182 | String id, int index, DocumentSnapshot document, String peerAvatar) {
183 | if (document.get('idFrom') == id) {
184 | return Row(
185 | children: [
186 | document.get('type') == 0
187 | ? chatText(document.get('content'), id, listMessage, index, true)
188 | : chatImage(context, id, listMessage, document.get('content'),
189 | index, true)
190 | ],
191 | mainAxisAlignment: MainAxisAlignment.end,
192 | );
193 | } else {
194 | return Container(
195 | child: Column(
196 | children: [
197 | Row(
198 | children: [
199 | ChatData.isLastMessageLeft(listMessage, id, index)
200 | ? Material(
201 | child: widgetShowImages(peerAvatar, 35),
202 | borderRadius: BorderRadius.all(
203 | Radius.circular(18.0),
204 | ),
205 | clipBehavior: Clip.hardEdge,
206 | )
207 | : Container(width: 35.0),
208 | document.get('type') == 0
209 | ? chatText(
210 | document.get('content'), id, listMessage, index, false)
211 | : chatImage(context, id, listMessage,
212 | document.get('content'), index, false)
213 | ],
214 | ),
215 |
216 | // Time
217 | ChatData.isLastMessageLeft(listMessage, id, index)
218 | ? Container(
219 | child: Text(
220 | DateFormat('dd MMM kk:mm').format(
221 | DateTime.fromMillisecondsSinceEpoch(
222 | int.parse(document.get('timestamp')))),
223 | style: TextStyle(
224 | color: greyColor,
225 | fontSize: 12.0,
226 | fontStyle: FontStyle.italic),
227 | ),
228 | margin: EdgeInsets.only(left: 50.0, top: 5.0, bottom: 5.0),
229 | )
230 | : Container()
231 | ],
232 | crossAxisAlignment: CrossAxisAlignment.start,
233 | ),
234 | margin: EdgeInsets.only(bottom: 10.0),
235 | );
236 | }
237 | }
238 |
239 | static Widget widgetChatBuildListMessage(groupChatId, listMessage,
240 | currentUserId, peerAvatar, listScrollController, var stCollection) {
241 | Stream _streamChatData;
242 | _streamChatData =
243 | ChatDBFireStore().streamChatDataList(stCollection, groupChatId);
244 |
245 | return Flexible(
246 | child: groupChatId == ''
247 | ? Center(
248 | child: CircularProgressIndicator(
249 | valueColor: AlwaysStoppedAnimation(themeColor)))
250 | : StreamBuilder(
251 | stream: _streamChatData,
252 | builder: (context, snapshot) {
253 | if (!snapshot.hasData) {
254 | return Center(
255 | child: CircularProgressIndicator(
256 | valueColor:
257 | AlwaysStoppedAnimation(themeColor)));
258 | } else {
259 | listMessage = snapshot.data.docs;
260 | return ListView.builder(
261 | padding: EdgeInsets.all(10.0),
262 | itemBuilder: (context, index) =>
263 | ChatWidget.widgetChatBuildItem(
264 | context,
265 | listMessage,
266 | currentUserId,
267 | index,
268 | snapshot.data.docs[index],
269 | peerAvatar),
270 | itemCount: snapshot.data.docs.length,
271 | reverse: true,
272 | controller: listScrollController,
273 | );
274 | }
275 | },
276 | ),
277 | );
278 | }
279 |
280 | static Widget chatText(String chatContent, String id, var listMessage,
281 | int index, bool logUserMsg) {
282 | return Container(
283 | child: Linkify(
284 | onOpen: (link) async {
285 | if (await canLaunch(link.url)) {
286 | await launch(link.url);
287 | } else {
288 | throw 'Could not launch $link';
289 | }
290 | },
291 | text: chatContent,
292 | style: TextStyle(color: logUserMsg ? primaryColor : Colors.white),
293 | linkStyle: TextStyle(color: Colors.blueGrey),
294 | ),
295 | // Text(
296 | // chatContent,
297 | // style: TextStyle(color: logUserMsg ? primaryColor : Colors.white),
298 | // ),
299 | padding: EdgeInsets.fromLTRB(15.0, 10.0, 15.0, 10.0),
300 | width: kIsWeb ? 400 : 200.0,
301 | decoration: BoxDecoration(
302 | color: logUserMsg ? greyColor2 : primaryColor,
303 | borderRadius: BorderRadius.circular(8.0)),
304 | margin: logUserMsg
305 | ? EdgeInsets.only(
306 | bottom: ChatData.isLastMessageRight(listMessage, id, index)
307 | ? 20.0
308 | : 10.0,
309 | right: 10.0)
310 | : EdgeInsets.only(left: 10.0),
311 | );
312 | }
313 |
314 | static Widget chatImage(BuildContext context, String id, var listMessage,
315 | String chatContent, int index, bool logUserMsg) {
316 | return Container(
317 | child: ElevatedButton(
318 | child: Material(
319 | child: kIsWeb
320 | ? widgetShowImages(chatContent, 250)
321 | : widgetShowImages(chatContent, 100),
322 | borderRadius: BorderRadius.all(Radius.circular(10.0)),
323 | clipBehavior: Clip.hardEdge,
324 | ),
325 | onPressed: () {
326 | Navigator.push(
327 | context,
328 | MaterialPageRoute(
329 | builder: (context) => ZoomImage(url: chatContent)));
330 | },
331 | style: ElevatedButton.styleFrom(padding: EdgeInsets.all(10.0))),
332 | margin: logUserMsg
333 | ? EdgeInsets.only(
334 | bottom: ChatData.isLastMessageRight(listMessage, id, index)
335 | ? 20.0
336 | : 10.0,
337 | right: 10.0)
338 | : EdgeInsets.only(left: 10.0),
339 | );
340 | }
341 |
342 | // Show Images from network
343 | static Widget widgetShowImages(String imageUrl, double imageSize) {
344 | return CachedNetworkImage(
345 | imageUrl: imageUrl,
346 | imageBuilder: (context, imageProvider) => Container(
347 | decoration: BoxDecoration(
348 | image: DecorationImage(
349 | image: imageProvider,
350 | fit: BoxFit.cover,
351 | //colorFilter:ColorFilter.mode(Colors.red, BlendMode.colorBurn)
352 | ),
353 | ),
354 | ),
355 | height: imageSize,
356 | width: imageSize,
357 | placeholder: (context, url) => CircularProgressIndicator(),
358 | errorWidget: (context, url, error) => Icon(Icons.error),
359 | );
360 | }
361 |
362 | static Widget widgetShowText(
363 | String text, dynamic textSize, dynamic textColor) {
364 | return Text(
365 | '$text',
366 | style: TextStyle(
367 | color: (textColor == '') ? Colors.white70 : textColor,
368 | fontSize: textSize == '' ? 14.0 : textSize),
369 | );
370 | }
371 | }
372 |
--------------------------------------------------------------------------------
/lib/constants.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import 'dart:ui';
3 | import 'hexColor.dart';
4 |
5 | final themeColor = HexColor('#ad1a7f');
6 | final colorPrimaryDark = HexColor('#6d0094');
7 | final colorAccent = HexColor('#ff2068');
8 | final colorAccentSecondary = HexColor('#6d0094');
9 | final viewBg = HexColor('#f8f8f8');
10 | final lblValue = HexColor('#222222');
11 |
12 | final themeColor2 = Color(0xff203152);
13 | final primaryColor = Color(0xff203152);
14 | final greyColor = Color(0xffaeaeae);
15 | final greyColor2 = Color(0xffE8E8E8);
16 |
--------------------------------------------------------------------------------
/lib/hexColor.dart:
--------------------------------------------------------------------------------
1 | import 'dart:ui';
2 |
3 | class HexColor extends Color {
4 | static int _getColorFromHex(String hexColor) {
5 | hexColor = hexColor.toUpperCase().replaceAll("#", "");
6 | if (hexColor.length == 6) {
7 | hexColor = "FF" + hexColor;
8 | }
9 | return int.parse(hexColor, radix: 16);
10 | }
11 |
12 | HexColor(final String hexColor) : super(_getColorFromHex(hexColor));
13 | }
14 |
--------------------------------------------------------------------------------
/lib/screens/chat.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 | import 'package:cloud_firestore/cloud_firestore.dart';
4 | import 'package:firebase_storage/firebase_storage.dart' as firebase_storage;
5 | import 'package:flutter/foundation.dart';
6 | import 'package:flutter/material.dart';
7 | import 'package:flutter_native_image/flutter_native_image.dart';
8 | import 'package:image_picker/image_picker.dart';
9 | import '../chatWidget.dart';
10 | import '../constants.dart';
11 | import 'package:fluttertoast/fluttertoast.dart';
12 |
13 | class Chat extends StatelessWidget {
14 | final String peerId;
15 | final String peerAvatar;
16 | final String peerName;
17 | final String currentUserId;
18 | static const String id = "chat";
19 |
20 | Chat(
21 | {Key key,
22 | @required this.currentUserId,
23 | @required this.peerId,
24 | @required this.peerAvatar,
25 | @required this.peerName})
26 | : super(key: key);
27 |
28 | @override
29 | Widget build(BuildContext context) {
30 | return new Scaffold(
31 | backgroundColor: Colors.white,
32 | appBar: AppBar(
33 | leading: null,
34 | title: Text(peerName),
35 | backgroundColor: themeColor,
36 | ),
37 | body: new _ChatScreen(
38 | currentUserId: currentUserId,
39 | peerId: peerId,
40 | peerAvatar: peerAvatar,
41 | ),
42 | );
43 | }
44 | }
45 |
46 | class _ChatScreen extends StatefulWidget {
47 | final String peerId;
48 | final String peerAvatar;
49 | final String currentUserId;
50 |
51 | _ChatScreen(
52 | {Key key,
53 | @required this.peerId,
54 | @required this.peerAvatar,
55 | @required this.currentUserId})
56 | : super(key: key);
57 |
58 | @override
59 | State createState() =>
60 | new _ChatScreenState(peerId: peerId, peerAvatar: peerAvatar);
61 | }
62 |
63 | class _ChatScreenState extends State<_ChatScreen> {
64 | _ChatScreenState({Key key, @required this.peerId, @required this.peerAvatar});
65 |
66 | String peerId;
67 | String peerAvatar;
68 | String id;
69 |
70 | var listMessage;
71 | String groupChatId;
72 |
73 | File imageFile;
74 | bool isLoading;
75 | bool isShowSticker;
76 | String imageUrl;
77 | var stCollection = 'messages';
78 |
79 | final TextEditingController textEditingController =
80 | new TextEditingController();
81 | final ScrollController listScrollController = new ScrollController();
82 | final FocusNode focusNode = new FocusNode();
83 |
84 | @override
85 | void initState() {
86 | super.initState();
87 | focusNode.addListener(onFocusChange);
88 |
89 | groupChatId = '';
90 |
91 | isLoading = false;
92 | isShowSticker = false;
93 | imageUrl = '';
94 |
95 | readLocal();
96 | }
97 |
98 | void onFocusChange() {
99 | if (focusNode.hasFocus) {
100 | // Hide sticker when keyboard appear
101 | setState(() {
102 | isShowSticker = false;
103 | });
104 | }
105 | }
106 |
107 | readLocal() async {
108 | id = widget.currentUserId ?? '';
109 | if (id.hashCode <= peerId.hashCode) {
110 | groupChatId = '$id-$peerId';
111 | } else {
112 | groupChatId = '$peerId-$id';
113 | }
114 |
115 | FirebaseFirestore.instance
116 | .collection('users')
117 | .doc(id)
118 | .update({'chattingWith': peerId});
119 |
120 | setState(() {});
121 | }
122 |
123 | Future getImage(int index) async {
124 | PickedFile selectedFile;
125 |
126 | if (kIsWeb) {
127 | //selectedFile=await ImagePicker.platform.pickImage(source: ImageSource.gallery);
128 | selectedFile = await ImagePicker().getImage(source: ImageSource.gallery);
129 | } else {
130 | if (index == 0)
131 | selectedFile =
132 | await ImagePicker().getImage(source: ImageSource.gallery);
133 | else
134 | selectedFile = await ImagePicker().getImage(source: ImageSource.camera);
135 |
136 | //imageFile =File(selectedFile.path);
137 |
138 | }
139 |
140 | if (selectedFile != null) {
141 | setState(() {
142 | //orFile=selectedFile;
143 | isLoading = true;
144 | });
145 | uploadFile(selectedFile);
146 | }
147 | }
148 |
149 | Future uploadFile(PickedFile orFile) async {
150 | String fileName = DateTime.now().millisecondsSinceEpoch.toString();
151 | firebase_storage.Reference reference =
152 | firebase_storage.FirebaseStorage.instance.ref().child(fileName);
153 |
154 | File compressedFile;
155 | if (!kIsWeb)
156 | compressedFile = await FlutterNativeImage.compressImage(orFile.path,
157 | quality: 80, percentage: 90);
158 |
159 | try {
160 | firebase_storage.UploadTask uploadTask;
161 |
162 | final metadata = firebase_storage.SettableMetadata(
163 | contentType: 'image/jpeg',
164 | customMetadata: {'picked-file-path': orFile.path});
165 |
166 | if (kIsWeb)
167 | uploadTask = reference.putData(await orFile.readAsBytes(), metadata);
168 | else
169 | uploadTask = reference.putFile(compressedFile);
170 |
171 | firebase_storage.TaskSnapshot storageTaskSnapshot = await uploadTask;
172 |
173 | storageTaskSnapshot.ref.getDownloadURL().then((downloadUrl) {
174 | imageUrl = downloadUrl;
175 | setState(() {
176 | isLoading = false;
177 | onSendMessage(imageUrl, 1);
178 | });
179 | }, onError: (err) {
180 | setState(() {
181 | isLoading = false;
182 | });
183 | });
184 | } catch (e) {
185 | print(e.toString());
186 | }
187 | }
188 |
189 | void onSendMessage(String content, int type) {
190 | // type: 0 = text, 1 = image, 2 = sticker
191 | if (content.trim() != '') {
192 | textEditingController.clear();
193 |
194 | var documentReference = FirebaseFirestore.instance
195 | .collection('messages')
196 | .doc(groupChatId)
197 | .collection(groupChatId)
198 | .doc(DateTime.now().millisecondsSinceEpoch.toString());
199 |
200 | FirebaseFirestore.instance.runTransaction((transaction) async {
201 | await transaction.set(
202 | documentReference,
203 | {
204 | 'idFrom': id,
205 | 'idTo': peerId,
206 | 'timestamp': DateTime.now().millisecondsSinceEpoch.toString(),
207 | 'content': content,
208 | 'type': type
209 | },
210 | );
211 | });
212 | listScrollController.animateTo(0.0,
213 | duration: Duration(milliseconds: 300), curve: Curves.easeOut);
214 | } else {
215 | Fluttertoast.showToast(msg: 'Nothing to send');
216 | }
217 | }
218 |
219 | Future onBackPress() {
220 | Navigator.pop(context);
221 | return Future.value(false);
222 | }
223 |
224 | @override
225 | Widget build(BuildContext context) {
226 | return WillPopScope(
227 | child: Stack(
228 | children: [
229 | Column(
230 | children: [
231 | // List of messages
232 | ChatWidget.widgetChatBuildListMessage(
233 | groupChatId,
234 | listMessage,
235 | widget.currentUserId,
236 | peerAvatar,
237 | listScrollController,
238 | stCollection),
239 |
240 | // Input content
241 | buildInput(),
242 | ],
243 | ),
244 |
245 | // Loading
246 | buildLoading()
247 | ],
248 | ),
249 | onWillPop: onBackPress,
250 | );
251 | }
252 |
253 | Widget buildLoading() {
254 | return Positioned(
255 | child: isLoading
256 | ? Container(
257 | child: Center(
258 | child: CircularProgressIndicator(
259 | valueColor: AlwaysStoppedAnimation(themeColor)),
260 | ),
261 | color: Colors.white.withOpacity(0.8),
262 | )
263 | : Container(),
264 | );
265 | }
266 |
267 | Widget buildInput() {
268 | return Container(
269 | child: Row(
270 | children: [
271 | // Button send image
272 | Material(
273 | child: new Container(
274 | margin: new EdgeInsets.symmetric(horizontal: 1.0),
275 | child: new IconButton(
276 | icon: new Icon(Icons.image),
277 | onPressed: () => getImage(0),
278 | color: primaryColor,
279 | ),
280 | ),
281 | color: Colors.white,
282 | ),
283 | Visibility(
284 | visible: !kIsWeb,
285 | child: Material(
286 | child: new Container(
287 | margin: new EdgeInsets.symmetric(horizontal: 1.0),
288 | child: new IconButton(
289 | icon: new Icon(Icons.camera_alt),
290 | onPressed: () => getImage(1),
291 | color: primaryColor,
292 | ),
293 | ),
294 | color: Colors.white,
295 | ),
296 | ),
297 | // Edit text
298 | Flexible(
299 | child: Container(
300 | child: TextField(
301 | style: TextStyle(color: primaryColor, fontSize: 15.0),
302 | controller: textEditingController,
303 | decoration: InputDecoration.collapsed(
304 | hintText: 'Type your message...',
305 | hintStyle: TextStyle(color: greyColor),
306 | ),
307 | focusNode: focusNode,
308 | ),
309 | ),
310 | ),
311 |
312 | // Button send message
313 | Material(
314 | child: new Container(
315 | margin: new EdgeInsets.symmetric(horizontal: 8.0),
316 | child: new IconButton(
317 | icon: new Icon(Icons.send),
318 | onPressed: () => onSendMessage(textEditingController.text, 0),
319 | color: primaryColor,
320 | ),
321 | ),
322 | color: Colors.white,
323 | ),
324 | ],
325 | ),
326 | width: double.infinity,
327 | height: 50.0,
328 | decoration: new BoxDecoration(
329 | border:
330 | new Border(top: new BorderSide(color: greyColor2, width: 0.5)),
331 | color: Colors.white),
332 | );
333 | }
334 | }
335 |
--------------------------------------------------------------------------------
/lib/screens/dashboard_screen.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'package:cloud_firestore/cloud_firestore.dart';
3 | import 'package:flutter/cupertino.dart';
4 | import 'package:flutter/material.dart';
5 | import '../chatDB.dart';
6 | import '../chatData.dart';
7 | import '../chatWidget.dart';
8 | import '../constants.dart';
9 | import 'chat.dart';
10 | import 'package:fluttertoast/fluttertoast.dart';
11 |
12 | List friendList = [];
13 |
14 | class DashboardScreen extends StatefulWidget {
15 | static const String id = "dashboard_screen";
16 | final String currentUserId;
17 |
18 | DashboardScreen({Key key, @required this.currentUserId}) : super(key: key);
19 |
20 | @override
21 | _DashboardScreenState createState() => _DashboardScreenState();
22 | }
23 |
24 | class _DashboardScreenState extends State {
25 | _DashboardScreenState({Key key});
26 | List unreadSubscriptions = [];
27 | List controllers = [];
28 |
29 | bool isLoading = false;
30 | bool addNewFriend = false;
31 | List choices = const [
32 | const Choice(title: 'Settings', icon: Icons.settings),
33 | const Choice(title: 'Log out', icon: Icons.exit_to_app),
34 | ];
35 |
36 | final friendController = TextEditingController();
37 |
38 | Stream _streamFriendList;
39 |
40 | @override
41 | void initState() {
42 | super.initState();
43 | }
44 |
45 | Future> getFriendList(bool onLoad) async {
46 | await FirebaseFirestore.instance
47 | .collection('users')
48 | .doc(widget.currentUserId)
49 | .get()
50 | .then((DocumentSnapshot documentSnapshot) {
51 | if (documentSnapshot.exists) {
52 | //print('Document data: ${documentSnapshot.data()}');
53 | //setState(() {
54 | friendList = documentSnapshot.get('friends');
55 | return friendList;
56 | //});
57 | } else {
58 | return friendList;
59 | }
60 | });
61 | return friendList;
62 | }
63 |
64 | void goExit() async {
65 | await showDialog(
66 | context: context,
67 | builder: (context) {
68 | return AlertDialog(
69 | title: Text('Confirmation'),
70 | content: Text('Do you want to logout?'),
71 | actions: [
72 | new TextButton(
73 | onPressed: () {
74 | Navigator.of(context, rootNavigator: true).pop(
75 | false); // dismisses only the dialog and returns false
76 | },
77 | child: Text('No'),
78 | ),
79 | TextButton(
80 | onPressed: () {
81 | ChatData.handleSignOut(context);
82 | },
83 | child: Text('Yes'),
84 | ),
85 | ],
86 | );
87 | });
88 | }
89 |
90 | @override
91 | Widget build(BuildContext context) {
92 | return DefaultTabController(
93 | length: 2,
94 | child: Scaffold(
95 | backgroundColor: Colors.white,
96 | body: WillPopScope(
97 | child: showFriendList(widget.currentUserId),
98 | onWillPop: onBackPress,
99 | ),
100 | ),
101 | );
102 | }
103 |
104 | Future onBackPress() {
105 | ChatData.openDialog(context);
106 | return Future.value(false);
107 | }
108 |
109 | Widget showAddFriend() {
110 | return Container(
111 | padding: EdgeInsets.all(10.0),
112 | child: TextButton(
113 | child: Text('Add New Friend'),
114 | onPressed: _showAddFriendDialog,
115 | ),
116 | );
117 | }
118 |
119 | _showAddFriendDialog() async {
120 | await showDialog(
121 | context: context,
122 | builder: (context) {
123 | return _SystemPadding(
124 | child: new AlertDialog(
125 | contentPadding: const EdgeInsets.all(16.0),
126 | content: new Row(
127 | children: [
128 | new Expanded(
129 | child: new TextField(
130 | autofocus: true,
131 | controller: friendController,
132 | decoration: new InputDecoration(
133 | labelText: 'user Email',
134 | hintText: 'smartmobilevilla@gmail.com'),
135 | ),
136 | )
137 | ],
138 | ),
139 | actions: [
140 | new TextButton(
141 | child: const Text('CANCEL'),
142 | onPressed: () {
143 | Navigator.pop(context);
144 | }),
145 | new TextButton(
146 | child: const Text('Add'),
147 | onPressed: () {
148 | Navigator.pop(context);
149 | if (friendController.text != '') _addNewFriend();
150 | })
151 | ],
152 | ),
153 | );
154 | });
155 | }
156 |
157 | void _addNewFriend() async {
158 | friendList = await getFriendList(true);
159 |
160 | FirebaseFirestore.instance
161 | .collection('users')
162 | .where('email', isEqualTo: friendController.text)
163 | .get()
164 | .then((value) {
165 | if (value.docs.length > 0) {
166 | value.docs.forEach((result) {
167 | bool alreadyExist = false;
168 |
169 | for (var fr in friendList) {
170 | if (fr == result.data()['userId']) alreadyExist = true;
171 | }
172 | if (alreadyExist == true) {
173 | showToast("already friend", true);
174 | } else {
175 | friendList.add(result.data()['userId']);
176 | FirebaseFirestore.instance
177 | .collection('users')
178 | .doc(widget.currentUserId)
179 | .update({"friends": friendList}).whenComplete(() {
180 | // Navigator.pop(context);
181 | setState(() {
182 | getFriendList(true);
183 | });
184 | // getFriendList(true);
185 | });
186 | }
187 | friendController.text = "";
188 | });
189 | } else {
190 | showToast("No user found with this email.", true);
191 | Navigator.pop(context);
192 | }
193 | });
194 | }
195 |
196 | showToast(var text, bool error) {
197 | // if (error == false){
198 | //
199 | // }
200 |
201 | Fluttertoast.showToast(
202 | msg: text,
203 | toastLength: Toast.LENGTH_SHORT,
204 | gravity: ToastGravity.CENTER,
205 | timeInSecForIosWeb: 1,
206 | backgroundColor: error ? Colors.red : Colors.blue,
207 | textColor: Colors.white,
208 | fontSize: 16.0);
209 | }
210 |
211 | Widget showFriendList(var currentUserId) {
212 | return Stack(
213 | children: [
214 | // List
215 | Container(
216 | width: double.infinity,
217 | child: Column(
218 | crossAxisAlignment: CrossAxisAlignment.end,
219 | children: [
220 | showAddFriend(),
221 | ],
222 | ),
223 | ),
224 |
225 | FutureBuilder>(
226 | future: getFriendList(true),
227 | builder: (context, snapshot) {
228 | if (snapshot.hasData) {
229 | List newFrList = snapshot.data;
230 | if (newFrList.length > 0) {
231 | return widgetFriendList(currentUserId, newFrList);
232 | } else {
233 | return Column(
234 | crossAxisAlignment: CrossAxisAlignment.center,
235 | mainAxisAlignment: MainAxisAlignment.center,
236 | children: [
237 | Center(
238 | child: Text(
239 | 'No Friend in your list\nAdd New Friend to start chat',
240 | style: TextStyle(fontSize: 16.0),
241 | )),
242 | ],
243 | );
244 | }
245 | } else {
246 | return Center(
247 | child: Text('Loading FriendList'),
248 | );
249 | }
250 | }),
251 | ],
252 | );
253 | }
254 |
255 | Widget widgetFriendList(var currentUserId, List friendLists) {
256 | return Container(
257 | margin: EdgeInsets.fromLTRB(0, 35, 0, 0),
258 | child: StreamBuilder(
259 | stream: streamFriendList(),
260 | builder: (context, snapshot) {
261 | if (!snapshot.hasData) {
262 | return Center(
263 | child: CircularProgressIndicator(
264 | valueColor: AlwaysStoppedAnimation(themeColor),
265 | ),
266 | );
267 | }
268 | if (snapshot.connectionState == ConnectionState.waiting) {
269 | return Text("Loading");
270 | }
271 |
272 | return new ListView(
273 | children: snapshot.data.docs.map((DocumentSnapshot document) {
274 | return (document.get('userId') == currentUserId)
275 | ? SizedBox(
276 | height: 2,
277 | )
278 | : new ListTile(
279 | leading: Material(
280 | child: document.get('photoUrl') != null
281 | ? ChatWidget.widgetShowImages(
282 | document.get('photoUrl'), 50)
283 | : Icon(
284 | Icons.account_circle,
285 | size: 50.0,
286 | color: colorPrimaryDark,
287 | ),
288 | borderRadius: BorderRadius.all(Radius.circular(25.0)),
289 | clipBehavior: Clip.hardEdge,
290 | ),
291 | title: new Text(document.get('nickname')),
292 | subtitle: new Text(document.get('nickname')),
293 | trailing: Wrap(
294 | children: [
295 | Container(
296 | height: 40,
297 | width: 40,
298 | child: Column(
299 | crossAxisAlignment: CrossAxisAlignment.center,
300 | mainAxisAlignment: MainAxisAlignment.center,
301 | children: [
302 | SizedBox(
303 | height: 10,
304 | width: 10,
305 | child: new DecoratedBox(
306 | decoration: new BoxDecoration(
307 | color: document.get('online') == 'online'
308 | ? Colors.greenAccent
309 | : Colors.red),
310 | ),
311 | ),
312 | ],
313 | ),
314 | ),
315 | SizedBox(
316 | width: 5,
317 | ),
318 | StreamBuilder(
319 | stream: getUnread(document.get('userId')),
320 | builder: (context,
321 | AsyncSnapshot unreadData) {
322 | int unreadMsg =
323 | 0; //unreadData.data.snapshot.docs.length -1;
324 | if (unreadData.hasData &&
325 | unreadData.data.snapshot.docs.isNotEmpty) {
326 | for (int i = 0;
327 | i < unreadData.data.snapshot.docs.length;
328 | i++) {
329 | try {
330 | if (int.parse(unreadData.data.lastSeen
331 | .toString()) <
332 | int.parse(unreadData.data.snapshot
333 | .docs[i]['timestamp']
334 | .toString()) &&
335 | unreadData
336 | .data.snapshot.docs[i]['idFrom']
337 | .toString() !=
338 | currentUserId.toString())
339 | unreadMsg = unreadMsg + 1;
340 | } catch (ex) {
341 | print('exception' + ex.toString());
342 | }
343 | }
344 | }
345 | return unreadMsg > 0
346 | ? Container(
347 | height: 40,
348 | width: 40,
349 | child: Column(
350 | crossAxisAlignment:
351 | CrossAxisAlignment.center,
352 | mainAxisAlignment:
353 | MainAxisAlignment.center,
354 | children: [
355 | Text(unreadMsg.toString(),
356 | style: TextStyle(
357 | fontSize: 16,
358 | color: Colors.black,
359 | fontWeight:
360 | FontWeight.normal)),
361 | ],
362 | ),
363 | //: Container(width: 0, height: 0),
364 |
365 | decoration: BoxDecoration(
366 | border: Border.all(width: 2),
367 | shape: BoxShape.circle,
368 | color: Colors.amber,
369 | ),
370 | )
371 | : SizedBox(
372 | height: 10,
373 | );
374 | }),
375 | ],
376 | ),
377 | onTap: () {
378 | Navigator.push(
379 | context,
380 | MaterialPageRoute(
381 | builder: (context) => Chat(
382 | currentUserId: widget.currentUserId,
383 | peerId: document.get('userId'),
384 | peerName: document.get('nickname'),
385 | peerAvatar: document.get('photoUrl'),
386 | )));
387 | },
388 | );
389 | }).toList());
390 | },
391 | ),
392 | );
393 | }
394 |
395 | Stream getUnread(var peerId) {
396 | var id = widget.currentUserId ?? '';
397 | var groupChatId = ChatData.getGroupChatID(id, peerId);
398 |
399 | //ChatDBFireStore().setChatLastSeen(widget.currentUserId,'messages',groupChatId);
400 | //ChatDBFireStore().setChatLastSeen(peerId,'messages',groupChatId);
401 |
402 | try {
403 | print('unreadData ' + groupChatId);
404 | var controller = StreamController.broadcast();
405 |
406 | unreadSubscriptions.add(FirebaseFirestore.instance
407 | .collection('messages')
408 | .doc(groupChatId)
409 | .snapshots()
410 | .listen((doc) {
411 | if (doc[id] != null) {
412 | unreadSubscriptions.add(FirebaseFirestore.instance
413 | .collection('messages')
414 | .doc(groupChatId)
415 | .collection(groupChatId)
416 | .snapshots()
417 | .listen((snapshot) {
418 | controller.add(MessageData(snapshot: snapshot, lastSeen: doc[id]));
419 | }));
420 | }
421 | }));
422 | controllers.add(controller);
423 | return controller.stream;
424 | } catch (e) {
425 | print('unreadExcept' + e.toString());
426 | }
427 | return null;
428 | }
429 |
430 | Stream streamFriendList() {
431 | return FirebaseFirestore.instance
432 | .collection(ChatDBFireStore.getDocName())
433 | .where('userId', whereIn: friendList)
434 | .snapshots();
435 |
436 | // friendList = documentSnapshot.data()['friends'];
437 | }
438 | }
439 |
440 | class MessageData {
441 | int lastSeen;
442 | QuerySnapshot snapshot;
443 | MessageData({@required this.snapshot, @required this.lastSeen});
444 | }
445 |
446 | class _SystemPadding extends StatelessWidget {
447 | final Widget child;
448 |
449 | _SystemPadding({Key key, this.child}) : super(key: key);
450 |
451 | @override
452 | Widget build(BuildContext context) {
453 | var mediaQuery = MediaQuery.of(context);
454 | return new AnimatedContainer(
455 | padding: mediaQuery.viewInsets / 2,
456 | duration: const Duration(milliseconds: 300),
457 | child: child);
458 | }
459 | }
460 |
461 | class Choice {
462 | const Choice({this.title, this.icon});
463 |
464 | final String title;
465 | final IconData icon;
466 | }
467 |
--------------------------------------------------------------------------------
/lib/screens/login_screen.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import '../chatWidget.dart';
3 |
4 | class LoginScreen extends StatefulWidget {
5 | static const String id = "login_screen";
6 |
7 | @override
8 | _LoginScreenState createState() => _LoginScreenState();
9 | }
10 |
11 | class _LoginScreenState extends State {
12 | bool isLoggedIn = false;
13 |
14 | @override
15 | void initState() {
16 | super.initState();
17 | }
18 |
19 | @override
20 | Widget build(BuildContext context) {
21 | return Scaffold(
22 | backgroundColor: Colors.white,
23 | body: ChatWidget.widgetLoginScreen(context));
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/lib/screens/zoomImage.dart:
--------------------------------------------------------------------------------
1 | import 'package:flutter/material.dart';
2 | import '../chatWidget.dart';
3 | import '../constants.dart';
4 |
5 | class ZoomImage extends StatelessWidget {
6 | final String url;
7 | static const String id = "ZoomImage";
8 |
9 | ZoomImage({Key key, @required this.url}) : super(key: key);
10 |
11 | @override
12 | Widget build(BuildContext context) {
13 | return new Scaffold(
14 | backgroundColor: Colors.white,
15 | appBar: new AppBar(
16 | title: new Text(
17 | 'FULL PHOTO',
18 | style: TextStyle(color: primaryColor, fontWeight: FontWeight.bold),
19 | ),
20 | centerTitle: true,
21 | ),
22 | body: new ZoomImageScreen(url: url),
23 | );
24 | }
25 | }
26 |
27 | class ZoomImageScreen extends StatefulWidget {
28 | final String url;
29 |
30 | ZoomImageScreen({Key key, @required this.url}) : super(key: key);
31 |
32 | @override
33 | State createState() => new ZoomImageScreenState(url: url);
34 | }
35 |
36 | class ZoomImageScreenState extends State {
37 | final String url;
38 |
39 | ZoomImageScreenState({Key key, @required this.url});
40 |
41 | @override
42 | void initState() {
43 | super.initState();
44 | }
45 |
46 | @override
47 | Widget build(BuildContext context) {
48 | return ChatWidget.widgetFullPhoto(context, url);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | archive:
5 | dependency: transitive
6 | description:
7 | name: archive
8 | url: "https://pub.dartlang.org"
9 | source: hosted
10 | version: "2.0.13"
11 | args:
12 | dependency: transitive
13 | description:
14 | name: args
15 | url: "https://pub.dartlang.org"
16 | source: hosted
17 | version: "1.6.0"
18 | async:
19 | dependency: transitive
20 | description:
21 | name: async
22 | url: "https://pub.dartlang.org"
23 | source: hosted
24 | version: "2.5.0"
25 | boolean_selector:
26 | dependency: transitive
27 | description:
28 | name: boolean_selector
29 | url: "https://pub.dartlang.org"
30 | source: hosted
31 | version: "2.1.0"
32 | cached_network_image:
33 | dependency: "direct main"
34 | description:
35 | name: cached_network_image
36 | url: "https://pub.dartlang.org"
37 | source: hosted
38 | version: "2.5.1"
39 | characters:
40 | dependency: transitive
41 | description:
42 | name: characters
43 | url: "https://pub.dartlang.org"
44 | source: hosted
45 | version: "1.1.0"
46 | charcode:
47 | dependency: transitive
48 | description:
49 | name: charcode
50 | url: "https://pub.dartlang.org"
51 | source: hosted
52 | version: "1.2.0"
53 | clock:
54 | dependency: transitive
55 | description:
56 | name: clock
57 | url: "https://pub.dartlang.org"
58 | source: hosted
59 | version: "1.1.0"
60 | cloud_firestore:
61 | dependency: "direct main"
62 | description:
63 | name: cloud_firestore
64 | url: "https://pub.dartlang.org"
65 | source: hosted
66 | version: "2.2.2"
67 | cloud_firestore_platform_interface:
68 | dependency: transitive
69 | description:
70 | name: cloud_firestore_platform_interface
71 | url: "https://pub.dartlang.org"
72 | source: hosted
73 | version: "5.1.2"
74 | cloud_firestore_web:
75 | dependency: transitive
76 | description:
77 | name: cloud_firestore_web
78 | url: "https://pub.dartlang.org"
79 | source: hosted
80 | version: "2.1.2"
81 | collection:
82 | dependency: transitive
83 | description:
84 | name: collection
85 | url: "https://pub.dartlang.org"
86 | source: hosted
87 | version: "1.15.0"
88 | convert:
89 | dependency: transitive
90 | description:
91 | name: convert
92 | url: "https://pub.dartlang.org"
93 | source: hosted
94 | version: "2.1.1"
95 | crypto:
96 | dependency: transitive
97 | description:
98 | name: crypto
99 | url: "https://pub.dartlang.org"
100 | source: hosted
101 | version: "2.1.5"
102 | cupertino_icons:
103 | dependency: "direct main"
104 | description:
105 | name: cupertino_icons
106 | url: "https://pub.dartlang.org"
107 | source: hosted
108 | version: "1.0.0"
109 | fake_async:
110 | dependency: transitive
111 | description:
112 | name: fake_async
113 | url: "https://pub.dartlang.org"
114 | source: hosted
115 | version: "1.2.0"
116 | ffi:
117 | dependency: transitive
118 | description:
119 | name: ffi
120 | url: "https://pub.dartlang.org"
121 | source: hosted
122 | version: "1.0.0"
123 | file:
124 | dependency: transitive
125 | description:
126 | name: file
127 | url: "https://pub.dartlang.org"
128 | source: hosted
129 | version: "6.1.0"
130 | firebase_auth:
131 | dependency: "direct main"
132 | description:
133 | name: firebase_auth
134 | url: "https://pub.dartlang.org"
135 | source: hosted
136 | version: "1.1.3"
137 | firebase_auth_platform_interface:
138 | dependency: transitive
139 | description:
140 | name: firebase_auth_platform_interface
141 | url: "https://pub.dartlang.org"
142 | source: hosted
143 | version: "4.2.1"
144 | firebase_auth_web:
145 | dependency: transitive
146 | description:
147 | name: firebase_auth_web
148 | url: "https://pub.dartlang.org"
149 | source: hosted
150 | version: "1.1.1"
151 | firebase_core:
152 | dependency: "direct main"
153 | description:
154 | name: firebase_core
155 | url: "https://pub.dartlang.org"
156 | source: hosted
157 | version: "1.3.0"
158 | firebase_core_platform_interface:
159 | dependency: transitive
160 | description:
161 | name: firebase_core_platform_interface
162 | url: "https://pub.dartlang.org"
163 | source: hosted
164 | version: "4.0.1"
165 | firebase_core_web:
166 | dependency: transitive
167 | description:
168 | name: firebase_core_web
169 | url: "https://pub.dartlang.org"
170 | source: hosted
171 | version: "1.1.0"
172 | firebase_database:
173 | dependency: "direct main"
174 | description:
175 | name: firebase_database
176 | url: "https://pub.dartlang.org"
177 | source: hosted
178 | version: "6.1.2"
179 | firebase_messaging:
180 | dependency: "direct main"
181 | description:
182 | name: firebase_messaging
183 | url: "https://pub.dartlang.org"
184 | source: hosted
185 | version: "10.0.2"
186 | firebase_messaging_platform_interface:
187 | dependency: transitive
188 | description:
189 | name: firebase_messaging_platform_interface
190 | url: "https://pub.dartlang.org"
191 | source: hosted
192 | version: "3.0.2"
193 | firebase_messaging_web:
194 | dependency: transitive
195 | description:
196 | name: firebase_messaging_web
197 | url: "https://pub.dartlang.org"
198 | source: hosted
199 | version: "2.0.2"
200 | firebase_storage:
201 | dependency: "direct main"
202 | description:
203 | name: firebase_storage
204 | url: "https://pub.dartlang.org"
205 | source: hosted
206 | version: "8.0.5"
207 | firebase_storage_platform_interface:
208 | dependency: transitive
209 | description:
210 | name: firebase_storage_platform_interface
211 | url: "https://pub.dartlang.org"
212 | source: hosted
213 | version: "2.0.3"
214 | firebase_storage_web:
215 | dependency: transitive
216 | description:
217 | name: firebase_storage_web
218 | url: "https://pub.dartlang.org"
219 | source: hosted
220 | version: "1.0.5"
221 | flutter:
222 | dependency: "direct main"
223 | description: flutter
224 | source: sdk
225 | version: "0.0.0"
226 | flutter_blurhash:
227 | dependency: transitive
228 | description:
229 | name: flutter_blurhash
230 | url: "https://pub.dartlang.org"
231 | source: hosted
232 | version: "0.5.0"
233 | flutter_cache_manager:
234 | dependency: transitive
235 | description:
236 | name: flutter_cache_manager
237 | url: "https://pub.dartlang.org"
238 | source: hosted
239 | version: "2.1.2"
240 | flutter_linkify:
241 | dependency: "direct main"
242 | description:
243 | name: flutter_linkify
244 | url: "https://pub.dartlang.org"
245 | source: hosted
246 | version: "4.1.0"
247 | flutter_local_notifications:
248 | dependency: "direct main"
249 | description:
250 | name: flutter_local_notifications
251 | url: "https://pub.dartlang.org"
252 | source: hosted
253 | version: "5.0.0+4"
254 | flutter_local_notifications_platform_interface:
255 | dependency: transitive
256 | description:
257 | name: flutter_local_notifications_platform_interface
258 | url: "https://pub.dartlang.org"
259 | source: hosted
260 | version: "3.0.0"
261 | flutter_native_image:
262 | dependency: "direct main"
263 | description:
264 | name: flutter_native_image
265 | url: "https://pub.dartlang.org"
266 | source: hosted
267 | version: "0.0.5+2"
268 | flutter_plugin_android_lifecycle:
269 | dependency: transitive
270 | description:
271 | name: flutter_plugin_android_lifecycle
272 | url: "https://pub.dartlang.org"
273 | source: hosted
274 | version: "2.0.1"
275 | flutter_test:
276 | dependency: "direct dev"
277 | description: flutter
278 | source: sdk
279 | version: "0.0.0"
280 | flutter_web_plugins:
281 | dependency: transitive
282 | description: flutter
283 | source: sdk
284 | version: "0.0.0"
285 | fluttertoast:
286 | dependency: "direct main"
287 | description:
288 | name: fluttertoast
289 | url: "https://pub.dartlang.org"
290 | source: hosted
291 | version: "7.1.8"
292 | google_sign_in:
293 | dependency: "direct main"
294 | description:
295 | name: google_sign_in
296 | url: "https://pub.dartlang.org"
297 | source: hosted
298 | version: "5.0.2"
299 | google_sign_in_platform_interface:
300 | dependency: transitive
301 | description:
302 | name: google_sign_in_platform_interface
303 | url: "https://pub.dartlang.org"
304 | source: hosted
305 | version: "2.0.1"
306 | google_sign_in_web:
307 | dependency: transitive
308 | description:
309 | name: google_sign_in_web
310 | url: "https://pub.dartlang.org"
311 | source: hosted
312 | version: "0.10.0"
313 | http:
314 | dependency: "direct main"
315 | description:
316 | name: http
317 | url: "https://pub.dartlang.org"
318 | source: hosted
319 | version: "0.13.3"
320 | http_parser:
321 | dependency: transitive
322 | description:
323 | name: http_parser
324 | url: "https://pub.dartlang.org"
325 | source: hosted
326 | version: "4.0.0"
327 | image:
328 | dependency: transitive
329 | description:
330 | name: image
331 | url: "https://pub.dartlang.org"
332 | source: hosted
333 | version: "2.1.19"
334 | image_picker:
335 | dependency: "direct main"
336 | description:
337 | name: image_picker
338 | url: "https://pub.dartlang.org"
339 | source: hosted
340 | version: "0.7.4"
341 | image_picker_for_web:
342 | dependency: "direct main"
343 | description:
344 | name: image_picker_for_web
345 | url: "https://pub.dartlang.org"
346 | source: hosted
347 | version: "2.0.0"
348 | image_picker_platform_interface:
349 | dependency: transitive
350 | description:
351 | name: image_picker_platform_interface
352 | url: "https://pub.dartlang.org"
353 | source: hosted
354 | version: "2.1.0"
355 | intl:
356 | dependency: "direct main"
357 | description:
358 | name: intl
359 | url: "https://pub.dartlang.org"
360 | source: hosted
361 | version: "0.17.0"
362 | js:
363 | dependency: transitive
364 | description:
365 | name: js
366 | url: "https://pub.dartlang.org"
367 | source: hosted
368 | version: "0.6.3"
369 | linkify:
370 | dependency: transitive
371 | description:
372 | name: linkify
373 | url: "https://pub.dartlang.org"
374 | source: hosted
375 | version: "3.0.0"
376 | matcher:
377 | dependency: transitive
378 | description:
379 | name: matcher
380 | url: "https://pub.dartlang.org"
381 | source: hosted
382 | version: "0.12.10"
383 | meta:
384 | dependency: transitive
385 | description:
386 | name: meta
387 | url: "https://pub.dartlang.org"
388 | source: hosted
389 | version: "1.3.0"
390 | octo_image:
391 | dependency: transitive
392 | description:
393 | name: octo_image
394 | url: "https://pub.dartlang.org"
395 | source: hosted
396 | version: "0.3.0"
397 | path:
398 | dependency: "direct main"
399 | description:
400 | name: path
401 | url: "https://pub.dartlang.org"
402 | source: hosted
403 | version: "1.8.0"
404 | path_provider:
405 | dependency: transitive
406 | description:
407 | name: path_provider
408 | url: "https://pub.dartlang.org"
409 | source: hosted
410 | version: "2.0.1"
411 | path_provider_linux:
412 | dependency: transitive
413 | description:
414 | name: path_provider_linux
415 | url: "https://pub.dartlang.org"
416 | source: hosted
417 | version: "2.0.0"
418 | path_provider_macos:
419 | dependency: transitive
420 | description:
421 | name: path_provider_macos
422 | url: "https://pub.dartlang.org"
423 | source: hosted
424 | version: "2.0.0"
425 | path_provider_platform_interface:
426 | dependency: transitive
427 | description:
428 | name: path_provider_platform_interface
429 | url: "https://pub.dartlang.org"
430 | source: hosted
431 | version: "2.0.1"
432 | path_provider_windows:
433 | dependency: transitive
434 | description:
435 | name: path_provider_windows
436 | url: "https://pub.dartlang.org"
437 | source: hosted
438 | version: "2.0.1"
439 | pedantic:
440 | dependency: transitive
441 | description:
442 | name: pedantic
443 | url: "https://pub.dartlang.org"
444 | source: hosted
445 | version: "1.11.0"
446 | petitparser:
447 | dependency: transitive
448 | description:
449 | name: petitparser
450 | url: "https://pub.dartlang.org"
451 | source: hosted
452 | version: "3.1.0"
453 | photo_view:
454 | dependency: "direct main"
455 | description:
456 | name: photo_view
457 | url: "https://pub.dartlang.org"
458 | source: hosted
459 | version: "0.11.1"
460 | platform:
461 | dependency: transitive
462 | description:
463 | name: platform
464 | url: "https://pub.dartlang.org"
465 | source: hosted
466 | version: "3.0.0"
467 | plugin_platform_interface:
468 | dependency: transitive
469 | description:
470 | name: plugin_platform_interface
471 | url: "https://pub.dartlang.org"
472 | source: hosted
473 | version: "2.0.0"
474 | process:
475 | dependency: transitive
476 | description:
477 | name: process
478 | url: "https://pub.dartlang.org"
479 | source: hosted
480 | version: "4.2.1"
481 | quiver:
482 | dependency: transitive
483 | description:
484 | name: quiver
485 | url: "https://pub.dartlang.org"
486 | source: hosted
487 | version: "3.0.1"
488 | rxdart:
489 | dependency: transitive
490 | description:
491 | name: rxdart
492 | url: "https://pub.dartlang.org"
493 | source: hosted
494 | version: "0.24.1"
495 | shared_preferences:
496 | dependency: "direct main"
497 | description:
498 | name: shared_preferences
499 | url: "https://pub.dartlang.org"
500 | source: hosted
501 | version: "2.0.5"
502 | shared_preferences_linux:
503 | dependency: transitive
504 | description:
505 | name: shared_preferences_linux
506 | url: "https://pub.dartlang.org"
507 | source: hosted
508 | version: "2.0.0"
509 | shared_preferences_macos:
510 | dependency: transitive
511 | description:
512 | name: shared_preferences_macos
513 | url: "https://pub.dartlang.org"
514 | source: hosted
515 | version: "2.0.0"
516 | shared_preferences_platform_interface:
517 | dependency: transitive
518 | description:
519 | name: shared_preferences_platform_interface
520 | url: "https://pub.dartlang.org"
521 | source: hosted
522 | version: "2.0.0"
523 | shared_preferences_web:
524 | dependency: transitive
525 | description:
526 | name: shared_preferences_web
527 | url: "https://pub.dartlang.org"
528 | source: hosted
529 | version: "2.0.0"
530 | shared_preferences_windows:
531 | dependency: transitive
532 | description:
533 | name: shared_preferences_windows
534 | url: "https://pub.dartlang.org"
535 | source: hosted
536 | version: "2.0.0"
537 | sky_engine:
538 | dependency: transitive
539 | description: flutter
540 | source: sdk
541 | version: "0.0.99"
542 | source_span:
543 | dependency: transitive
544 | description:
545 | name: source_span
546 | url: "https://pub.dartlang.org"
547 | source: hosted
548 | version: "1.8.0"
549 | sqflite:
550 | dependency: transitive
551 | description:
552 | name: sqflite
553 | url: "https://pub.dartlang.org"
554 | source: hosted
555 | version: "2.0.0+3"
556 | sqflite_common:
557 | dependency: transitive
558 | description:
559 | name: sqflite_common
560 | url: "https://pub.dartlang.org"
561 | source: hosted
562 | version: "2.0.0+2"
563 | stack_trace:
564 | dependency: transitive
565 | description:
566 | name: stack_trace
567 | url: "https://pub.dartlang.org"
568 | source: hosted
569 | version: "1.10.0"
570 | stream_channel:
571 | dependency: transitive
572 | description:
573 | name: stream_channel
574 | url: "https://pub.dartlang.org"
575 | source: hosted
576 | version: "2.1.0"
577 | string_scanner:
578 | dependency: transitive
579 | description:
580 | name: string_scanner
581 | url: "https://pub.dartlang.org"
582 | source: hosted
583 | version: "1.1.0"
584 | synchronized:
585 | dependency: transitive
586 | description:
587 | name: synchronized
588 | url: "https://pub.dartlang.org"
589 | source: hosted
590 | version: "3.0.0"
591 | term_glyph:
592 | dependency: transitive
593 | description:
594 | name: term_glyph
595 | url: "https://pub.dartlang.org"
596 | source: hosted
597 | version: "1.2.0"
598 | test_api:
599 | dependency: transitive
600 | description:
601 | name: test_api
602 | url: "https://pub.dartlang.org"
603 | source: hosted
604 | version: "0.2.19"
605 | timezone:
606 | dependency: transitive
607 | description:
608 | name: timezone
609 | url: "https://pub.dartlang.org"
610 | source: hosted
611 | version: "0.7.0"
612 | typed_data:
613 | dependency: transitive
614 | description:
615 | name: typed_data
616 | url: "https://pub.dartlang.org"
617 | source: hosted
618 | version: "1.3.0"
619 | url_launcher:
620 | dependency: "direct main"
621 | description:
622 | name: url_launcher
623 | url: "https://pub.dartlang.org"
624 | source: hosted
625 | version: "6.0.6"
626 | url_launcher_linux:
627 | dependency: transitive
628 | description:
629 | name: url_launcher_linux
630 | url: "https://pub.dartlang.org"
631 | source: hosted
632 | version: "2.0.0"
633 | url_launcher_macos:
634 | dependency: transitive
635 | description:
636 | name: url_launcher_macos
637 | url: "https://pub.dartlang.org"
638 | source: hosted
639 | version: "2.0.0"
640 | url_launcher_platform_interface:
641 | dependency: transitive
642 | description:
643 | name: url_launcher_platform_interface
644 | url: "https://pub.dartlang.org"
645 | source: hosted
646 | version: "2.0.3"
647 | url_launcher_web:
648 | dependency: transitive
649 | description:
650 | name: url_launcher_web
651 | url: "https://pub.dartlang.org"
652 | source: hosted
653 | version: "2.0.1"
654 | url_launcher_windows:
655 | dependency: transitive
656 | description:
657 | name: url_launcher_windows
658 | url: "https://pub.dartlang.org"
659 | source: hosted
660 | version: "2.0.0"
661 | uuid:
662 | dependency: transitive
663 | description:
664 | name: uuid
665 | url: "https://pub.dartlang.org"
666 | source: hosted
667 | version: "2.2.2"
668 | vector_math:
669 | dependency: transitive
670 | description:
671 | name: vector_math
672 | url: "https://pub.dartlang.org"
673 | source: hosted
674 | version: "2.1.0"
675 | win32:
676 | dependency: transitive
677 | description:
678 | name: win32
679 | url: "https://pub.dartlang.org"
680 | source: hosted
681 | version: "2.0.5"
682 | xdg_directories:
683 | dependency: transitive
684 | description:
685 | name: xdg_directories
686 | url: "https://pub.dartlang.org"
687 | source: hosted
688 | version: "0.2.0"
689 | xml:
690 | dependency: transitive
691 | description:
692 | name: xml
693 | url: "https://pub.dartlang.org"
694 | source: hosted
695 | version: "4.5.1"
696 | sdks:
697 | dart: ">=2.12.0 <3.0.0"
698 | flutter: ">=2.0.0"
699 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: flutter_chat
2 | description: Messaging app for Flutter android , iOS and flutter-web using firebase as backend services.
3 |
4 | version: 2.0.2
5 | homepage: https://github.com/ankesh-kumar/Flutter-chat-sdk
6 |
7 | environment:
8 | sdk: ">=2.7.0 <3.0.0"
9 |
10 | dependencies:
11 | flutter:
12 | sdk: flutter
13 |
14 |
15 | # The following adds the Cupertino Icons font to your application.
16 | # Use with the CupertinoIcons class for iOS style icons.
17 | cupertino_icons: ^1.0.0
18 | shared_preferences: ^2.0.3
19 | firebase_core: ^1.0.1
20 | firebase_auth: ^1.0.1
21 | firebase_storage: ^8.0.0
22 | cloud_firestore: ^2.2.2
23 | fluttertoast: ^7.1.8
24 | photo_view: ^0.11.1
25 | google_sign_in: ^5.0.0
26 | flutter_native_image: ^0.0.4
27 | cached_network_image: ^2.5.1
28 | intl: ^0.17.0
29 | firebase_database: ^6.0.0
30 | image_picker: ^0.7.2+1
31 | image_picker_for_web: ^2.0.0
32 | path: ^1.8.0
33 | firebase_messaging: ^10.0.0
34 | flutter_local_notifications: ^5.0.0+2
35 | url_launcher: ^6.0.2
36 | flutter_linkify: ^4.0.2
37 | http: ^0.13.0
38 |
39 | dev_dependencies:
40 | flutter_test:
41 | sdk: flutter
42 |
43 | # For information on the generic Dart part of this file, see the
44 | # following page: https://dart.dev/tools/pub/pubspec
45 |
46 | # The following section is specific to Flutter.
47 | flutter:
48 |
49 | # The following line ensures that the Material Icons font is
50 | # included with your application, so that you can use the icons in
51 | # the material Icons class.
52 | uses-material-design: true
53 |
54 |
55 | assets:
56 | - images/
57 |
58 | # To add assets to your application, add an assets section, like this:
59 | # assets:
60 | # - images/a_dot_burr.jpeg
61 | # - images/a_dot_ham.jpeg
62 |
63 | # An image asset can refer to one or more resolution-specific "variants", see
64 | # https://flutter.dev/assets-and-images/#resolution-aware.
65 |
66 | # For details regarding adding assets from package dependencies, see
67 | # https://flutter.dev/assets-and-images/#from-packages
68 |
69 | # To add custom fonts to your application, add a fonts section here,
70 | # in this "flutter" section. Each entry in this list should have a
71 | # "family" key with the font family name, and a "fonts" key with a
72 | # list giving the asset and other descriptors for the font. For
73 | # example:
74 | # fonts:
75 | # - family: Schyler
76 | # fonts:
77 | # - asset: fonts/Schyler-Regular.ttf
78 | # - asset: fonts/Schyler-Italic.ttf
79 | # style: italic
80 | # - family: Trajan Pro
81 | # fonts:
82 | # - asset: fonts/TrajanPro.ttf
83 | # - asset: fonts/TrajanPro_Bold.ttf
84 | # weight: 700
85 | #
86 | # For details regarding fonts from package dependencies,
87 | # see https://flutter.dev/custom-fonts/#from-packages
88 |
--------------------------------------------------------------------------------