Messaging
Implemented Chat Screens, Profile Pages, Search Pages WIP: User profile, new chat button Intended: Groups, Calls, Voice Messages Properly deleting chats, offline storage of messages
This commit is contained in:
@@ -1,14 +1,17 @@
|
|||||||
//const endPoint = 'http://10.15.45.11/v1';
|
|
||||||
//const endPoint = 'https://api.theonasanyas.com/v1';
|
|
||||||
class Constants {
|
class Constants {
|
||||||
static Constants instance = Constants();
|
static Constants instance = Constants();
|
||||||
|
|
||||||
|
var endPoint = 'https://api.theonasanyas.com/v1';
|
||||||
|
var projectId = '67833160000cb0801fdb';
|
||||||
|
|
||||||
|
// var endPoint = 'https://cloud.appwrite.io/v1';
|
||||||
|
// var projectId = '678bdc7c00203eff8af5';
|
||||||
|
|
||||||
var endPoint = 'https://cloud.appwrite.io/v1';
|
|
||||||
var projectId = '678bdc7c00203eff8af5';
|
|
||||||
var selfSigned = false;
|
var selfSigned = false;
|
||||||
var databaseId = '678c8659000a7d6b5bff';
|
var databaseId = '678c8659000a7d6b5bff';
|
||||||
var userCollectionId = '678c865e002fe1cddd06';
|
var userCollectionId = '678c865e002fe1cddd06';
|
||||||
var chatsCollectionId = '678c866c0038822276ad';
|
var chatsCollectionId = '678c866c0038822276ad';
|
||||||
var convCollectionId = '678c8667001944dee794';
|
var convCollectionId = '678c8667001944dee794';
|
||||||
|
var conversationLinkId = '67b949c9001234c0d7b1';
|
||||||
var imageStorageBucket = '678d2162002a6770aaad';
|
var imageStorageBucket = '678d2162002a6770aaad';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'package:appwrite/appwrite.dart';
|
import 'package:appwrite/appwrite.dart';
|
||||||
import 'package:appwrite/models.dart' as model;
|
import 'package:appwrite/models.dart' as model;
|
||||||
@@ -31,6 +32,7 @@ class MessagingProviders extends ChangeNotifier {
|
|||||||
|
|
||||||
static MessagingProviders instance = MessagingProviders();
|
static MessagingProviders instance = MessagingProviders();
|
||||||
|
|
||||||
|
|
||||||
MessagingProviders() {
|
MessagingProviders() {
|
||||||
_init();
|
_init();
|
||||||
_loadUser();
|
_loadUser();
|
||||||
@@ -99,12 +101,13 @@ class MessagingProviders extends ChangeNotifier {
|
|||||||
|
|
||||||
void logout() async {
|
void logout() async {
|
||||||
try {
|
try {
|
||||||
|
await updateLastSeen();
|
||||||
await account.deleteSession(sessionId: 'current');
|
await account.deleteSession(sessionId: 'current');
|
||||||
user = null;
|
user = null;
|
||||||
status = AuthStatus.notAuthenticated;
|
status = AuthStatus.notAuthenticated;
|
||||||
// await onSuccess();
|
// await onSuccess();
|
||||||
await NavigationService.instance.navigateToReplacement("login");
|
await NavigationService.instance.navigateToReplacement("login");
|
||||||
SnackBarService.instance.showSnackBarError("Logged out Successfully");
|
SnackBarService.instance.showSnackBarError("logged out successfully");
|
||||||
} on AppwriteException catch (e) {
|
} on AppwriteException catch (e) {
|
||||||
SnackBarService.instance.showSnackBarError(e.message!);
|
SnackBarService.instance.showSnackBarError(e.message!);
|
||||||
} on Exception catch (e) {
|
} on Exception catch (e) {
|
||||||
@@ -122,8 +125,8 @@ class MessagingProviders extends ChangeNotifier {
|
|||||||
|
|
||||||
status = AuthStatus.authenticated;
|
status = AuthStatus.authenticated;
|
||||||
await onSuccess(user!.$id);
|
await onSuccess(user!.$id);
|
||||||
SnackBarService.instance.showSnackBarSuccess("Welcome! ${user!.name}");
|
SnackBarService.instance.showSnackBarSuccess("welcome! ${user!.name}");
|
||||||
await updateLastSeen();
|
// await updateLastSeen();
|
||||||
NavigationService.instance.goBack();
|
NavigationService.instance.goBack();
|
||||||
NavigationService.instance.navigateToReplacement("home");
|
NavigationService.instance.navigateToReplacement("home");
|
||||||
} on AppwriteException catch (e) {
|
} on AppwriteException catch (e) {
|
||||||
@@ -181,7 +184,8 @@ class MessagingProviders extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<Users> getUser(String userId) {
|
Stream<User> getUser(String userId) {
|
||||||
|
//
|
||||||
var _userData = database.getDocument(
|
var _userData = database.getDocument(
|
||||||
databaseId: Constants.instance.databaseId,
|
databaseId: Constants.instance.databaseId,
|
||||||
collectionId: Constants.instance.userCollectionId,
|
collectionId: Constants.instance.userCollectionId,
|
||||||
@@ -189,12 +193,14 @@ class MessagingProviders extends ChangeNotifier {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return _userData.asStream().map((_document) {
|
return _userData.asStream().map((_document) {
|
||||||
return Users.fromData(_document);
|
return User.fromData(_document);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<List<Users>> getAllUsers(String _searchText) {
|
Stream<List<User>> getAllUsers(String _searchText) async* {
|
||||||
List<Users> _returnValue = [];
|
//
|
||||||
|
// while (true) {
|
||||||
|
List<User> _returnValue = [];
|
||||||
if (_searchText.isNotEmpty) {
|
if (_searchText.isNotEmpty) {
|
||||||
var _usersData = database.listDocuments(
|
var _usersData = database.listDocuments(
|
||||||
databaseId: Constants.instance.databaseId,
|
databaseId: Constants.instance.databaseId,
|
||||||
@@ -204,23 +210,27 @@ class MessagingProviders extends ChangeNotifier {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
return _usersData.asStream().map(
|
yield* _usersData.asStream().map(
|
||||||
(_documentList) {
|
(_documentList) {
|
||||||
for (var _document in _documentList.documents) {
|
for (var _document in _documentList.documents) {
|
||||||
_returnValue.add(Users.fromData(_document));
|
_returnValue.add(User.fromData(_document));
|
||||||
}
|
}
|
||||||
|
_returnValue.sort((a,b) => b.lastSeen.compareTo(a.lastSeen));
|
||||||
return _returnValue;
|
return _returnValue;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Stream.empty();
|
yield* Stream.empty();
|
||||||
}
|
}
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<List<Conversations>> getUserConversation(String userId) {
|
Stream<List<Conversations>> getUserConversation(String userId) async* {
|
||||||
//
|
//
|
||||||
|
// while (true) {
|
||||||
List<dynamic> _chatId = [];
|
List<dynamic> _chatId = [];
|
||||||
List<dynamic> _details = [];
|
List<dynamic> _details = [];
|
||||||
|
List<dynamic> _lastModified = [];
|
||||||
List<Conversations> _returnValue = [];
|
List<Conversations> _returnValue = [];
|
||||||
|
|
||||||
var _conversationData = database.getDocument(
|
var _conversationData = database.getDocument(
|
||||||
@@ -229,32 +239,117 @@ class MessagingProviders extends ChangeNotifier {
|
|||||||
documentId: userId.trim(),
|
documentId: userId.trim(),
|
||||||
);
|
);
|
||||||
|
|
||||||
return _conversationData.asStream().map(
|
yield* _conversationData.asStream().map(
|
||||||
(_snapshot) {
|
(_snapshot) {
|
||||||
_chatId = _snapshot.data['chatId'];
|
_chatId = _snapshot.data['chatId'];
|
||||||
_details = _snapshot.data['details'];
|
_details = _snapshot.data['details'];
|
||||||
|
_lastModified = _snapshot.data['lastModified'];
|
||||||
|
|
||||||
for (int i = 0; i < _chatId.length; i++) {
|
for (int i = 0; i < _chatId.length; i++) {
|
||||||
_returnValue.add(Conversations.fromData(_chatId[i], _details[i]));
|
_returnValue.add(Conversations.fromData(
|
||||||
|
_chatId[i], _details[i], _lastModified[i]));
|
||||||
}
|
}
|
||||||
|
_returnValue.sort((a, b) => b.lastModified!.compareTo(a.lastModified!));
|
||||||
return _returnValue;
|
return _returnValue;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream<Chats> getChat(String _chatId) {
|
|
||||||
|
// Stream<List<Conversations>> getUserConversations(String userId) async* {
|
||||||
|
// //
|
||||||
|
// // while (true) {
|
||||||
|
// List<dynamic> _chatId = [];
|
||||||
|
// List<dynamic> _details = [];
|
||||||
|
// List<dynamic> _lastModified = [];
|
||||||
|
// List<Conversations> _returnValue = [];
|
||||||
|
|
||||||
|
// var _conversationData = await database.getDocument(
|
||||||
|
// databaseId: Constants.instance.databaseId,
|
||||||
|
// collectionId: Constants.instance.convCollectionId,
|
||||||
|
// documentId: userId.trim(),
|
||||||
|
// );
|
||||||
|
|
||||||
|
// yield _conversationData.toMap map(
|
||||||
|
// (_snapshot) {
|
||||||
|
// _chatId = _snapshot.data['chatId'];
|
||||||
|
// _details = _snapshot.data['details'];
|
||||||
|
// _lastModified = _snapshot.data['lastModified'];
|
||||||
|
|
||||||
|
// for (int i = 0; i < _chatId.length; i++) {
|
||||||
|
// _returnValue.add(Conversations.fromData(
|
||||||
|
// _chatId[i], _details[i], _lastModified[i]));
|
||||||
|
// }
|
||||||
|
// _returnValue.sort((a, b) => b.lastModified!.compareTo(a.lastModified!));
|
||||||
|
// return _returnValue;
|
||||||
|
// },
|
||||||
|
// );
|
||||||
|
// // }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Stream<Chats> getMessage(String _chatId) async* {
|
||||||
|
//
|
||||||
|
// while (true) {
|
||||||
var _chat = database.getDocument(
|
var _chat = database.getDocument(
|
||||||
databaseId: Constants.instance.databaseId,
|
databaseId: Constants.instance.databaseId,
|
||||||
collectionId: Constants.instance.chatsCollectionId,
|
collectionId: Constants.instance.chatsCollectionId,
|
||||||
documentId: _chatId,
|
documentId: _chatId,
|
||||||
);
|
);
|
||||||
|
|
||||||
return _chat.asStream().map((_document) {
|
yield* _chat.asStream().map(
|
||||||
return Chats.fromData(_document);
|
(_document) {
|
||||||
});
|
return Chats.fromDocument(_document);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
uploadUserImage(XFile? file, String _uid) async {
|
Future<String> getConversationLink(var _linkId) async {
|
||||||
|
//
|
||||||
|
try {
|
||||||
|
var _conversationLinkDetail = await database.getDocument(
|
||||||
|
databaseId: Constants.instance.databaseId,
|
||||||
|
collectionId: Constants.instance.conversationLinkId,
|
||||||
|
documentId: _linkId,
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
return _conversationLinkDetail.data['chatId'];
|
||||||
|
} on AppwriteException catch (e) {
|
||||||
|
notifyListeners();
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> newMesaage(Map<String, dynamic> _chat) async {
|
||||||
|
//
|
||||||
|
var chatId = ID.unique();
|
||||||
|
await database.createDocument(
|
||||||
|
databaseId: Constants.instance.databaseId,
|
||||||
|
collectionId: Constants.instance.chatsCollectionId,
|
||||||
|
documentId: chatId,
|
||||||
|
data: _chat,
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
return chatId;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> sendMessage(
|
||||||
|
String chatId, Map<String, dynamic> _messages) async {
|
||||||
|
//
|
||||||
|
await database.updateDocument(
|
||||||
|
databaseId: Constants.instance.databaseId,
|
||||||
|
collectionId: Constants.instance.chatsCollectionId,
|
||||||
|
documentId: chatId,
|
||||||
|
data: _messages,
|
||||||
|
);
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<String> uploadUserImage(XFile? file, String _uid) async {
|
||||||
|
//
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
try {
|
try {
|
||||||
File _file = File(file!.path);
|
File _file = File(file!.path);
|
||||||
@@ -279,5 +374,6 @@ class MessagingProviders extends ChangeNotifier {
|
|||||||
//
|
//
|
||||||
}
|
}
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,9 @@ class MessagingRoutes {
|
|||||||
//
|
//
|
||||||
static MessagingRoutes instance = MessagingRoutes();
|
static MessagingRoutes instance = MessagingRoutes();
|
||||||
|
|
||||||
Map<String, WidgetBuilder> get messagingRoutes {
|
Map<String, WidgetBuilder> get messagingRoutes => {
|
||||||
return {
|
"login": (BuildContext _context) => LoginPage(),
|
||||||
"login": (BuildContext _context) => LoginPage(),
|
"register": (BuildContext _context) => RegisterationPage(),
|
||||||
"register": (BuildContext _context) => RegisterationPage(),
|
"home": (BuildContext _context) => HomePage(),
|
||||||
"home": (BuildContext _context) => HomePage(),
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ void main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MessagingApp extends StatelessWidget {
|
class MessagingApp extends StatelessWidget {
|
||||||
|
|
||||||
const MessagingApp({super.key});
|
const MessagingApp({super.key});
|
||||||
|
|
||||||
// This widget is the root of your application.
|
// This widget is the root of your application.
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
import 'package:appwrite/appwrite.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
runApp(const MyApp());
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyApp extends StatelessWidget {
|
||||||
|
const MyApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return MaterialApp(
|
||||||
|
title: 'FlAppwrite Realtime Demo',
|
||||||
|
theme: ThemeData(
|
||||||
|
primarySwatch: Colors.blue,
|
||||||
|
),
|
||||||
|
home: const HomePage(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HomePage extends StatefulWidget {
|
||||||
|
const HomePage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<HomePage> createState() => _HomePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _HomePageState extends State<HomePage> {
|
||||||
|
List<Map<String, dynamic>> items = [];
|
||||||
|
final TextEditingController _nameController = TextEditingController();
|
||||||
|
RealtimeSubscription? subscription;
|
||||||
|
late final Client client;
|
||||||
|
final database = '67db1d75002a1826f6f9'; // your database id
|
||||||
|
final itemsCollection = '67db1e36001ffd2719b7'; // your collection id
|
||||||
|
late final Databases databases;
|
||||||
|
|
||||||
|
@override
|
||||||
|
initState() {
|
||||||
|
super.initState();
|
||||||
|
|
||||||
|
client = Client()
|
||||||
|
.setEndpoint('https://api.theonasanyas.com/v1')
|
||||||
|
.setProject('67833160000cb0801fdb'); // your project id
|
||||||
|
databases = Databases(client);
|
||||||
|
loadItems();
|
||||||
|
subscribe();
|
||||||
|
}
|
||||||
|
|
||||||
|
loadItems() async {
|
||||||
|
try {
|
||||||
|
final res = await databases.listDocuments(
|
||||||
|
databaseId: database,
|
||||||
|
collectionId: itemsCollection,
|
||||||
|
);
|
||||||
|
setState(() {
|
||||||
|
items = List<Map<String, dynamic>>.from(res.documents.map(
|
||||||
|
(e) => e.data,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
} on AppwriteException catch (e) {
|
||||||
|
print(e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void subscribe() {
|
||||||
|
final realtime = Realtime(client);
|
||||||
|
|
||||||
|
subscription = realtime.subscribe([
|
||||||
|
'documents' // subscribe to all documents in every database and collection
|
||||||
|
]);
|
||||||
|
|
||||||
|
// listen to changes
|
||||||
|
subscription!.stream.listen((data) {
|
||||||
|
// data will consist of `events` and a `payload`
|
||||||
|
final event = data.events.first;
|
||||||
|
if (data.payload.isNotEmpty) {
|
||||||
|
if (event.endsWith('.create')) {
|
||||||
|
var item = data.payload;
|
||||||
|
items.add(item);
|
||||||
|
setState(() {});
|
||||||
|
} else if (event.endsWith('.delete')) {
|
||||||
|
var item = data.payload;
|
||||||
|
items.removeWhere((it) => it['\$id'] == item['\$id']);
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
dispose() {
|
||||||
|
subscription?.close();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('FlAppwrite Realtime Demo'),
|
||||||
|
),
|
||||||
|
body: ListView(children: [
|
||||||
|
...items.map((item) => ListTile(
|
||||||
|
title: Text(item['name']),
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
onPressed: () async {
|
||||||
|
await databases.deleteDocument(
|
||||||
|
databaseId: database,
|
||||||
|
collectionId: itemsCollection,
|
||||||
|
documentId: item['\$id'],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
]),
|
||||||
|
floatingActionButton: FloatingActionButton(
|
||||||
|
child: const Icon(Icons.add),
|
||||||
|
onPressed: () {
|
||||||
|
// dialog to add new item
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('Add new item'),
|
||||||
|
content: TextField(
|
||||||
|
controller: _nameController,
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
child: const Text('Cancel'),
|
||||||
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
child: const Text('Add'),
|
||||||
|
onPressed: () {
|
||||||
|
// add new item
|
||||||
|
final name = _nameController.text;
|
||||||
|
if (name.isNotEmpty) {
|
||||||
|
_nameController.clear();
|
||||||
|
_addItem(name);
|
||||||
|
}
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _addItem(String name) async {
|
||||||
|
try {
|
||||||
|
await databases.createDocument(
|
||||||
|
databaseId: database,
|
||||||
|
collectionId: itemsCollection,
|
||||||
|
documentId: ID.unique(),
|
||||||
|
data: {'name': name},
|
||||||
|
);
|
||||||
|
} on AppwriteException catch (e) {
|
||||||
|
print(e.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
-13
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:appwrite/models.dart';
|
import 'package:appwrite/models.dart';
|
||||||
import 'package:messaging_app/models/messages.dart';
|
import 'package:messaging_app/models/messages.dart';
|
||||||
|
|
||||||
@@ -5,7 +7,7 @@ class Chats {
|
|||||||
String ownerId;
|
String ownerId;
|
||||||
List<String> members;
|
List<String> members;
|
||||||
String? type;
|
String? type;
|
||||||
List<Messages>? messages;
|
List<List<Messages>>? messages;
|
||||||
|
|
||||||
Chats({
|
Chats({
|
||||||
required this.ownerId,
|
required this.ownerId,
|
||||||
@@ -14,25 +16,29 @@ class Chats {
|
|||||||
required this.messages,
|
required this.messages,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Chats.fromData(Document _document) {
|
factory Chats.fromDocument(Document _document) {
|
||||||
List<Messages> _outMessages = [];
|
//
|
||||||
List<String> _outMembers = [];
|
//
|
||||||
List<dynamic> _messages = _document.data['messages'];
|
List<List<Messages>> _messagesList = [];
|
||||||
|
List _members = _document.data['members'];
|
||||||
|
List _messages = _document.data['messages'];
|
||||||
|
|
||||||
for (var _message in _messages) {
|
for (var i = 0; i < _members.length; i++) {
|
||||||
_outMessages.add(Messages.fromData(_message));
|
List _msg = [];
|
||||||
}
|
_msg = json.decode(_messages[i])[_members[i]];
|
||||||
|
|
||||||
List<dynamic> _members = _document.data['members'];
|
_messagesList.add(_msg.map((_temp) {
|
||||||
for (var _member in _members) {
|
return Messages.fromMap(_temp);
|
||||||
_outMembers.add(_member);
|
}).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
return Chats(
|
return Chats(
|
||||||
ownerId: _document.data['ownerId'],
|
ownerId: _document.data['ownerId'],
|
||||||
members: _outMembers,
|
members: _members.map((_member) {
|
||||||
|
return _member.toString();
|
||||||
|
}).toList(),
|
||||||
type: _document.data['type'],
|
type: _document.data['type'],
|
||||||
messages: _outMessages,
|
messages: _messagesList,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,18 @@ import 'package:messaging_app/models/details.dart';
|
|||||||
class Conversations {
|
class Conversations {
|
||||||
String chatId;
|
String chatId;
|
||||||
Details details;
|
Details details;
|
||||||
|
DateTime? lastModified;
|
||||||
|
|
||||||
Conversations({
|
Conversations({
|
||||||
required this.chatId,
|
required this.chatId,
|
||||||
required this.details,
|
required this.details,
|
||||||
|
this.lastModified,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Conversations.fromData(String _chatId, String _details) {
|
factory Conversations.fromData(
|
||||||
return Conversations(
|
String _chatId, String _details, String _lastModified) =>
|
||||||
chatId: _chatId,
|
Conversations(
|
||||||
details: Details.fromData(_details),
|
chatId: _chatId,
|
||||||
);
|
details: Details.fromData(_details),
|
||||||
}
|
lastModified: DateTime.parse(_lastModified));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ class Details {
|
|||||||
int unseenCount;
|
int unseenCount;
|
||||||
String? lastMessage;
|
String? lastMessage;
|
||||||
DateTime? timeStamp;
|
DateTime? timeStamp;
|
||||||
|
String? type;
|
||||||
|
|
||||||
Details({
|
Details({
|
||||||
required this.name,
|
required this.name,
|
||||||
@@ -13,6 +14,7 @@ class Details {
|
|||||||
required this.lastMessage,
|
required this.lastMessage,
|
||||||
required this.timeStamp,
|
required this.timeStamp,
|
||||||
required this.unseenCount,
|
required this.unseenCount,
|
||||||
|
required this.type
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Details.fromData(String _details) {
|
factory Details.fromData(String _details) {
|
||||||
@@ -22,6 +24,7 @@ class Details {
|
|||||||
lastMessage: json.decode(_details)['lastMessage'],
|
lastMessage: json.decode(_details)['lastMessage'],
|
||||||
timeStamp: DateTime.parse(json.decode(_details)['timeStamp'] ?? ""),
|
timeStamp: DateTime.parse(json.decode(_details)['timeStamp'] ?? ""),
|
||||||
unseenCount: json.decode(_details)['unseenCount'],
|
unseenCount: json.decode(_details)['unseenCount'],
|
||||||
|
type: json.decode(_details)['type'],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-18
@@ -1,15 +1,10 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
enum MessageType {
|
|
||||||
Text,
|
|
||||||
Image,
|
|
||||||
}
|
|
||||||
|
|
||||||
class Messages {
|
class Messages {
|
||||||
String message;
|
String message;
|
||||||
String senderId;
|
String senderId;
|
||||||
MessageType type;
|
String type;
|
||||||
DateTime timeStamp;
|
String timeStamp;
|
||||||
|
|
||||||
Messages({
|
Messages({
|
||||||
required this.message,
|
required this.message,
|
||||||
@@ -18,16 +13,25 @@ class Messages {
|
|||||||
required this.timeStamp,
|
required this.timeStamp,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Messages.fromData(String _message) {
|
factory Messages.fromString(String _message) => Messages(
|
||||||
var _messageType = json.decode(_message)['type'] == "text"
|
message: json.decode(_message)['message'],
|
||||||
? MessageType.Text
|
senderId: json.decode(_message)['senderId'],
|
||||||
: MessageType.Image;
|
type: json.decode(_message)['type'],
|
||||||
|
timeStamp: json.decode(_message)['timeStamp'] ?? "",
|
||||||
|
);
|
||||||
|
|
||||||
return Messages(
|
factory Messages.fromMap(Map _message) => Messages(
|
||||||
message: json.decode(_message)['message'],
|
message: _message['message'],
|
||||||
senderId: json.decode(_message)['senderId'],
|
senderId: _message['senderId'],
|
||||||
type: _messageType,
|
type: _message['type'],
|
||||||
timeStamp: DateTime.parse(json.decode(_message)['timeStamp'] ?? ""),
|
timeStamp: _message['timeStamp'] ?? "",
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"message": message,
|
||||||
|
"senderId": senderId,
|
||||||
|
"type": type,
|
||||||
|
"timeStamp": timeStamp,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import 'package:appwrite/models.dart';
|
import 'package:appwrite/models.dart';
|
||||||
|
|
||||||
class Users {
|
class User {
|
||||||
String id;
|
String id;
|
||||||
String name;
|
String name;
|
||||||
String email;
|
String email;
|
||||||
String image;
|
String image;
|
||||||
DateTime lastSeen;
|
DateTime lastSeen;
|
||||||
|
|
||||||
Users({
|
User({
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.email,
|
required this.email,
|
||||||
@@ -15,8 +15,8 @@ class Users {
|
|||||||
required this.lastSeen,
|
required this.lastSeen,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory Users.fromData(Document _document) {
|
factory User.fromData(Document _document) {
|
||||||
return Users(
|
return User(
|
||||||
id: _document.data['\$id'],
|
id: _document.data['\$id'],
|
||||||
name: _document.data['name'],
|
name: _document.data['name'],
|
||||||
email: _document.data['email'],
|
email: _document.data['email'],
|
||||||
|
|||||||
@@ -1,20 +1,26 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||||
|
import 'package:image_picker/image_picker.dart';
|
||||||
import 'package:messaging_app/components/messaging_provider.dart';
|
import 'package:messaging_app/components/messaging_provider.dart';
|
||||||
import 'package:messaging_app/models/chats.dart';
|
import 'package:messaging_app/models/chats.dart';
|
||||||
import 'package:messaging_app/models/messages.dart';
|
import 'package:messaging_app/models/messages.dart';
|
||||||
|
import 'package:messaging_app/service/media_service.dart';
|
||||||
|
import 'package:messaging_app/service/snackbar_service.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:timeago/timeago.dart' as timeago;
|
import 'package:timeago/timeago.dart' as timeago;
|
||||||
|
|
||||||
class ConversationsPage extends StatefulWidget {
|
class ConversationsPage extends StatefulWidget {
|
||||||
ConversationsPage({
|
ConversationsPage({
|
||||||
super.key,
|
super.key,
|
||||||
required this.conversationId,
|
required this.chatId,
|
||||||
// required this.receiverId,
|
// required this.receiverId,
|
||||||
required this.chatImage,
|
required this.chatImage,
|
||||||
required this.chatName,
|
required this.chatName,
|
||||||
});
|
});
|
||||||
|
|
||||||
String conversationId;
|
String chatId;
|
||||||
// String receiverId;
|
// String receiverId;
|
||||||
String chatName;
|
String chatName;
|
||||||
String chatImage;
|
String chatImage;
|
||||||
@@ -30,7 +36,24 @@ class _ConversationsPageState extends State<ConversationsPage> {
|
|||||||
|
|
||||||
late double _deviceHeight;
|
late double _deviceHeight;
|
||||||
late double _deviceWidth;
|
late double _deviceWidth;
|
||||||
|
|
||||||
|
final ScrollController _scrollController;
|
||||||
|
final GlobalKey<FormState> _formKey;
|
||||||
late MessagingProviders _auth;
|
late MessagingProviders _auth;
|
||||||
|
List<List<Messages>> _messagesArray;
|
||||||
|
List<Messages> _messages;
|
||||||
|
List<String> _members;
|
||||||
|
int _memberPosition;
|
||||||
|
String _messageText;
|
||||||
|
|
||||||
|
_ConversationsPageState()
|
||||||
|
: _messageText = '',
|
||||||
|
_formKey = GlobalKey<FormState>(),
|
||||||
|
_scrollController = ScrollController(),
|
||||||
|
_messagesArray = [],
|
||||||
|
_messages = [],
|
||||||
|
_members = [],
|
||||||
|
_memberPosition = 0;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -52,45 +75,71 @@ class _ConversationsPageState extends State<ConversationsPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _conversationPageUI() {
|
Widget _conversationPageUI() {
|
||||||
return Builder(builder: (_context) {
|
return Builder(
|
||||||
_auth = Provider.of<MessagingProviders>(_context);
|
builder: (_context) {
|
||||||
return Stack(
|
_auth = Provider.of<MessagingProviders>(_context);
|
||||||
clipBehavior: Clip.none,
|
SnackBarService.instance.buildContext = _context;
|
||||||
children: [
|
return Stack(
|
||||||
_messageListView(),
|
clipBehavior: Clip.none,
|
||||||
_messageInputField(_context),
|
children: [
|
||||||
],
|
_messageListView(),
|
||||||
);
|
_messageInputField(_context),
|
||||||
});
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _messageListView() {
|
Widget _messageListView() {
|
||||||
return Container(
|
return Container(
|
||||||
height: _deviceHeight * 0.75,
|
height: _deviceHeight * 0.80,
|
||||||
width: _deviceWidth,
|
width: _deviceWidth,
|
||||||
child: StreamBuilder<Chats>(
|
child: StreamBuilder<Chats>(
|
||||||
stream: MessagingProviders.instance.getChat(widget.conversationId),
|
stream: MessagingProviders.instance.getMessage(widget.chatId),
|
||||||
builder: (_context, _snapshot) {
|
builder: (_context, _snapshot) {
|
||||||
return _snapshot.hasData
|
if (_snapshot.hasData) {
|
||||||
? ListView.builder(
|
_members = _snapshot.data!.members;
|
||||||
padding: EdgeInsets.symmetric(
|
_memberPosition = _members.indexOf(_auth.user!.$id);
|
||||||
horizontal: 10,
|
_messages = _snapshot.data!.messages![_memberPosition];
|
||||||
vertical: 10,
|
_messagesArray = _snapshot.data!.messages!;
|
||||||
),
|
}
|
||||||
itemCount: _snapshot.data!.messages!.length,
|
if (_snapshot.hasData) {
|
||||||
itemBuilder: (BuildContext _context, int _index) {
|
if (_messages.isEmpty) {
|
||||||
var _message = _snapshot.data!.messages![_index];
|
return Align(
|
||||||
return _messageView(_message);
|
child: Text("No Chat"),
|
||||||
},
|
);
|
||||||
)
|
} else {
|
||||||
: Text("");
|
return ListView.builder(
|
||||||
}),
|
controller: _scrollController,
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 10,
|
||||||
|
vertical: 10,
|
||||||
|
),
|
||||||
|
itemCount: _messages.length,
|
||||||
|
itemBuilder: (BuildContext _context, int _index) {
|
||||||
|
_messages[_index];
|
||||||
|
return _messageView(_messages, _index);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (_snapshot.hasError) {
|
||||||
|
return Align(
|
||||||
|
child: Text("No Chat"),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return SpinKitWanderingCubes(
|
||||||
|
color: Colors.blue,
|
||||||
|
size: 50.0,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _messageView(Messages _message) {
|
Widget _messageView(List<Messages> _messages, int _index) {
|
||||||
bool _isOwnMessage = false;
|
bool _isOwnMessage = false;
|
||||||
if (_message.senderId == _auth.user!.$id) {
|
if (_messages[_index].senderId == _auth.user!.$id) {
|
||||||
_isOwnMessage = true;
|
_isOwnMessage = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,8 +151,15 @@ class _ConversationsPageState extends State<ConversationsPage> {
|
|||||||
_isOwnMessage ? MainAxisAlignment.end : MainAxisAlignment.start,
|
_isOwnMessage ? MainAxisAlignment.end : MainAxisAlignment.start,
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: [
|
children: [
|
||||||
|
deleteButton(_isOwnMessage, _messages, _index),
|
||||||
_isOwnMessage ? Container() : _imageViewBubble(),
|
_isOwnMessage ? Container() : _imageViewBubble(),
|
||||||
_textMessageBubble(_isOwnMessage, _message),
|
SizedBox(
|
||||||
|
width: _deviceWidth * 0.01,
|
||||||
|
),
|
||||||
|
_messages[_index].type == "text"
|
||||||
|
? _textMessageBubble(_isOwnMessage, _messages[_index])
|
||||||
|
: _imageMessageBubble(_isOwnMessage, _messages[_index]),
|
||||||
|
deleteButton(!_isOwnMessage, _messages, _index),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -114,9 +170,9 @@ class _ConversationsPageState extends State<ConversationsPage> {
|
|||||||
? [Colors.blue, Color.fromRGBO(42, 117, 188, 1)]
|
? [Colors.blue, Color.fromRGBO(42, 117, 188, 1)]
|
||||||
: [Color.fromRGBO(49, 69, 69, 1), Color.fromRGBO(43, 43, 43, 1)];
|
: [Color.fromRGBO(49, 69, 69, 1), Color.fromRGBO(43, 43, 43, 1)];
|
||||||
return Container(
|
return Container(
|
||||||
height: _deviceHeight * 0.10,
|
// height: _deviceHeight * 0.10,
|
||||||
width: _deviceWidth * 0.75,
|
width: _deviceWidth * 0.60,
|
||||||
padding: EdgeInsets.symmetric(horizontal: 10),
|
padding: EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(15),
|
borderRadius: BorderRadius.circular(15),
|
||||||
gradient: LinearGradient(
|
gradient: LinearGradient(
|
||||||
@@ -131,12 +187,57 @@ class _ConversationsPageState extends State<ConversationsPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(_message.message),
|
Text(_message.message),
|
||||||
|
SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
Text(
|
Text(
|
||||||
timeago.format(_message.timeStamp),
|
timeago.format(DateTime.parse(_message.timeStamp)),
|
||||||
style: TextStyle(
|
style: TextStyle(color: Colors.white70, fontSize: 10),
|
||||||
color: Colors.white70,
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _imageMessageBubble(bool _isOwnMessage, Messages _message) {
|
||||||
|
List<Color> _colorScheme = _isOwnMessage
|
||||||
|
? [Colors.blue, Color.fromRGBO(42, 117, 188, 1)]
|
||||||
|
: [Color.fromRGBO(49, 69, 69, 1), Color.fromRGBO(43, 43, 43, 1)];
|
||||||
|
return Container(
|
||||||
|
// height: _deviceHeight * 0.10,
|
||||||
|
// width: _deviceWidth * 0.75,
|
||||||
|
padding: EdgeInsets.symmetric(horizontal: 10, vertical: 10),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
gradient: LinearGradient(
|
||||||
|
colors: _colorScheme,
|
||||||
|
stops: [0.25, 0.85],
|
||||||
|
begin: Alignment.bottomLeft,
|
||||||
|
end: Alignment.bottomRight),
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Container(
|
||||||
|
height: _deviceHeight * 0.30,
|
||||||
|
width: _deviceWidth * 0.40,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
image: DecorationImage(
|
||||||
|
image: NetworkImage(_message.message),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
SizedBox(
|
||||||
|
height: 10,
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
timeago.format(DateTime.parse(_message.timeStamp)),
|
||||||
|
style: TextStyle(color: Colors.white70, fontSize: 10),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -161,42 +262,49 @@ class _ConversationsPageState extends State<ConversationsPage> {
|
|||||||
Widget _messageInputField(BuildContext _context) {
|
Widget _messageInputField(BuildContext _context) {
|
||||||
return Align(
|
return Align(
|
||||||
alignment: Alignment.bottomCenter,
|
alignment: Alignment.bottomCenter,
|
||||||
child: Row(
|
child: Form(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
key: _formKey,
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
_messageText(),
|
mainAxisSize: MainAxisSize.max,
|
||||||
_imageUpload(),
|
children: [
|
||||||
_sendMessage(_context),
|
_messageInput(),
|
||||||
],
|
_sendImage(),
|
||||||
|
_sendText(_context),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _messageText() {
|
Widget _messageInput() {
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.only(left: 10, right: 10),
|
padding: EdgeInsets.only(left: 10, right: 10),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Color.fromRGBO(43, 43, 43, 1),
|
color: Color.fromRGBO(43, 43, 43, 1),
|
||||||
borderRadius: BorderRadius.circular(30)
|
borderRadius: BorderRadius.circular(30)),
|
||||||
),
|
|
||||||
margin: EdgeInsets.symmetric(
|
margin: EdgeInsets.symmetric(
|
||||||
horizontal: _deviceWidth * 0.04,
|
horizontal: _deviceWidth * 0.04,
|
||||||
vertical: _deviceHeight * 0.02
|
vertical: _deviceHeight * 0.02,
|
||||||
),
|
),
|
||||||
width: _deviceWidth * 0.70,
|
width: _deviceWidth * 0.70,
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
|
maxLines: null,
|
||||||
validator: (_input) {
|
validator: (_input) {
|
||||||
if (_input == null) {
|
if (_input!.isEmpty) {
|
||||||
return "";
|
SnackBarService.instance
|
||||||
|
.showSnackBarError("please enter a message");
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
},
|
},
|
||||||
onChanged: (_input) {
|
onChanged: (_input) {
|
||||||
//
|
_formKey.currentState?.save();
|
||||||
},
|
},
|
||||||
onSaved: (_input) {
|
onSaved: (_input) {
|
||||||
//
|
setState(() {
|
||||||
|
_messageText = _input!;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
cursorColor: Colors.white,
|
cursorColor: Colors.white,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
@@ -208,12 +316,22 @@ class _ConversationsPageState extends State<ConversationsPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _sendMessage(BuildContext context) {
|
Widget _sendText(BuildContext _context) {
|
||||||
return Container(
|
return Container(
|
||||||
height: _deviceHeight * 0.045,
|
height: _deviceHeight * 0.045,
|
||||||
width: _deviceHeight * 0.045,
|
width: _deviceHeight * 0.045,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
onPressed: () {},
|
onPressed: () async {
|
||||||
|
if (_formKey.currentState!.validate()) {
|
||||||
|
if (_messageText.isNotEmpty) {
|
||||||
|
//
|
||||||
|
await sendMessage('text', _messageText);
|
||||||
|
|
||||||
|
_formKey.currentState?.reset();
|
||||||
|
FocusScope.of(_context).unfocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
Icons.send,
|
Icons.send,
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
@@ -222,13 +340,84 @@ class _ConversationsPageState extends State<ConversationsPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _imageUpload() {
|
Widget _sendImage() {
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: _deviceHeight * 0.045,
|
height: _deviceHeight * 0.045,
|
||||||
width: _deviceHeight * 0.045,
|
width: _deviceHeight * 0.045,
|
||||||
child: FloatingActionButton(onPressed: () {},
|
child: FloatingActionButton(
|
||||||
backgroundColor: Colors.blue,
|
onPressed: () async {
|
||||||
child: Icon(Icons.camera_enhance),),
|
XFile? _imageFile = await MediaService.instance.getImageFromLibrary();
|
||||||
|
if (_imageFile != null) {
|
||||||
|
var _url = await MessagingProviders.instance
|
||||||
|
.uploadUserImage(_imageFile, '_uid');
|
||||||
|
|
||||||
|
await sendMessage('image', _url);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
backgroundColor: Colors.blue,
|
||||||
|
child: Icon(
|
||||||
|
Icons.camera_enhance,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget deleteButton(
|
||||||
|
bool _isOwnMessage, List<Messages> _messages, int _index) {
|
||||||
|
return !_isOwnMessage
|
||||||
|
? SizedBox()
|
||||||
|
: Row(
|
||||||
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.delete,
|
||||||
|
size: 15,
|
||||||
|
),
|
||||||
|
onPressed: () async {
|
||||||
|
List outMessages = [];
|
||||||
|
_messages.removeAt(_index);
|
||||||
|
_messagesArray[_memberPosition] = _messages;
|
||||||
|
for (var i = 0; i < _messagesArray.length; i++) {
|
||||||
|
outMessages.add(
|
||||||
|
json.encode(
|
||||||
|
{_members[i]: _messagesArray[i]},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _auth.sendMessage(
|
||||||
|
widget.chatId,
|
||||||
|
{"messages": outMessages},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> sendMessage(var type, var _message) async {
|
||||||
|
List outMessages = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < _members.length; i++) {
|
||||||
|
_messagesArray[i].add(
|
||||||
|
Messages(
|
||||||
|
message: _message,
|
||||||
|
senderId: _auth.user!.$id,
|
||||||
|
type: type,
|
||||||
|
timeStamp: DateTime.now().toUtc().toString(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
outMessages.add(
|
||||||
|
json.encode(
|
||||||
|
{_members[i]: _messagesArray[i]},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _auth.sendMessage(
|
||||||
|
widget.chatId,
|
||||||
|
{"messages": outMessages},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-37
@@ -21,11 +21,32 @@ class _HomePageState extends State<HomePage>
|
|||||||
late double _deviceWidth;
|
late double _deviceWidth;
|
||||||
late final TabController _tabController;
|
late final TabController _tabController;
|
||||||
|
|
||||||
|
final List<Tab> _iconTabs = [
|
||||||
|
Tab(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.people_outline,
|
||||||
|
size: 30,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Tab(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.chat_bubble_outline,
|
||||||
|
size: 30,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Tab(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.person_outline,
|
||||||
|
size: 30,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_tabController = TabController(
|
_tabController = TabController(
|
||||||
length: 3,
|
length: _iconTabs.length,
|
||||||
vsync: this,
|
vsync: this,
|
||||||
initialIndex: 1,
|
initialIndex: 1,
|
||||||
);
|
);
|
||||||
@@ -44,49 +65,45 @@ class _HomePageState extends State<HomePage>
|
|||||||
_deviceWidth = MediaQuery.of(context).size.width;
|
_deviceWidth = MediaQuery.of(context).size.width;
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
// backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
// backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
// backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
automaticallyImplyLeading: false,
|
||||||
title: Text("the messenger"),
|
// backgroundColor: Theme.of(context).scaffoldBackgroundColor,
|
||||||
titleTextStyle: TextStyle(fontSize: 16),
|
title: Text("the messenger"),
|
||||||
bottom: TabBar(
|
titleTextStyle: TextStyle(fontSize: 16),
|
||||||
unselectedLabelColor: Colors.grey,
|
bottom: TabBar(
|
||||||
indicatorColor: Colors.blue,
|
unselectedLabelColor: Colors.grey,
|
||||||
labelColor: Colors.blue,
|
indicatorColor: Colors.blue,
|
||||||
controller: _tabController,
|
labelColor: Colors.blue,
|
||||||
tabs: [
|
controller: _tabController,
|
||||||
Tab(
|
tabs: _iconTabs,
|
||||||
icon: Icon(
|
),
|
||||||
Icons.people_outline,
|
|
||||||
size: 30,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Tab(
|
|
||||||
icon: Icon(
|
|
||||||
Icons.chat_bubble_outline,
|
|
||||||
size: 30,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Tab(
|
|
||||||
icon: Icon(
|
|
||||||
Icons.person_outline,
|
|
||||||
size: 30,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
body: _tabBarPages(),
|
||||||
body: _tabBarPages(),
|
floatingActionButton: _tabController.index == 1
|
||||||
);
|
? Container()
|
||||||
|
: FloatingActionButton(
|
||||||
|
child: Icon(Icons.add),
|
||||||
|
onPressed: () {},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _tabBarPages() {
|
Widget _tabBarPages() {
|
||||||
return TabBarView(
|
return TabBarView(
|
||||||
controller: _tabController,
|
controller: _tabController,
|
||||||
children: [
|
children: [
|
||||||
SearchPage( deviceHeight: _deviceHeight , deviceWidth: _deviceWidth,),
|
SearchPage(
|
||||||
RecentConverstationsPage(_deviceHeight, _deviceWidth),
|
deviceHeight: _deviceHeight,
|
||||||
ProfilePage(_deviceHeight, _deviceWidth),
|
deviceWidth: _deviceWidth,
|
||||||
|
),
|
||||||
|
RecentConverstationsPage(
|
||||||
|
_deviceHeight,
|
||||||
|
_deviceWidth,
|
||||||
|
),
|
||||||
|
ProfilePage(
|
||||||
|
_deviceHeight,
|
||||||
|
_deviceWidth,
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,9 @@ import 'package:messaging_app/service/snackbar_service.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
class LoginPage extends StatefulWidget {
|
class LoginPage extends StatefulWidget {
|
||||||
const LoginPage({super.key,});
|
const LoginPage({
|
||||||
|
super.key,
|
||||||
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatefulWidget> createState() {
|
State<StatefulWidget> createState() {
|
||||||
@@ -78,11 +80,11 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'Welcome Back!',
|
'welcome back!',
|
||||||
style: TextStyle(fontSize: 35, fontWeight: FontWeight.w700),
|
style: TextStyle(fontSize: 35, fontWeight: FontWeight.w700),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Please Login to your Account',
|
'please login to your account',
|
||||||
style: TextStyle(fontSize: 25, fontWeight: FontWeight.w200),
|
style: TextStyle(fontSize: 25, fontWeight: FontWeight.w200),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -114,13 +116,15 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
Widget _emailTextField() {
|
Widget _emailTextField() {
|
||||||
return TextFormField(
|
return TextFormField(
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
validator: (_input) {
|
validator: (_input) {
|
||||||
return _input!.isNotEmpty && _input.contains("@")
|
return _input!.isNotEmpty && _input.contains("@")
|
||||||
? null
|
? null
|
||||||
: "Please enter a valid Email";
|
: "please enter a valid email";
|
||||||
},
|
},
|
||||||
onSaved: (_input) {
|
onSaved: (_input) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -129,7 +133,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
},
|
},
|
||||||
cursorColor: Colors.white,
|
cursorColor: Colors.white,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "Email Address",
|
hintText: "email address",
|
||||||
focusedBorder: UnderlineInputBorder(
|
focusedBorder: UnderlineInputBorder(
|
||||||
borderSide: BorderSide(color: Colors.white))),
|
borderSide: BorderSide(color: Colors.white))),
|
||||||
);
|
);
|
||||||
@@ -138,11 +142,13 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
Widget _passwordTextField() {
|
Widget _passwordTextField() {
|
||||||
return TextFormField(
|
return TextFormField(
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
validator: (_input) {
|
validator: (_input) {
|
||||||
return _input!.isNotEmpty ? null : "Please enter a Password";
|
return _input!.isNotEmpty ? null : "please enter a aassword";
|
||||||
},
|
},
|
||||||
onSaved: (_input) {
|
onSaved: (_input) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -152,7 +158,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
obscureText: true,
|
obscureText: true,
|
||||||
cursorColor: Colors.white,
|
cursorColor: Colors.white,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "Password",
|
hintText: "password",
|
||||||
focusedBorder: UnderlineInputBorder(
|
focusedBorder: UnderlineInputBorder(
|
||||||
borderSide: BorderSide(color: Colors.white))),
|
borderSide: BorderSide(color: Colors.white))),
|
||||||
);
|
);
|
||||||
@@ -176,7 +182,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
},
|
},
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
child: Text(
|
child: Text(
|
||||||
'LOGIN',
|
'login',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
@@ -195,7 +201,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
height: _deviceHeight * 0.06,
|
height: _deviceHeight * 0.06,
|
||||||
width: _deviceWidth,
|
width: _deviceWidth,
|
||||||
child: Text(
|
child: Text(
|
||||||
'REGISTER',
|
'register',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class ProfilePage extends StatelessWidget {
|
|||||||
return Builder(builder: (BuildContext _context) {
|
return Builder(builder: (BuildContext _context) {
|
||||||
SnackBarService.instance.buildContext = _context;
|
SnackBarService.instance.buildContext = _context;
|
||||||
_auth = Provider.of<MessagingProviders>(_context);
|
_auth = Provider.of<MessagingProviders>(_context);
|
||||||
return StreamBuilder<Users>(
|
return StreamBuilder<User>(
|
||||||
stream: MessagingProviders.instance.getUser(_auth.user!.$id),
|
stream: MessagingProviders.instance.getUser(_auth.user!.$id),
|
||||||
builder: (_, _snapshot) {
|
builder: (_, _snapshot) {
|
||||||
var _userData = _snapshot.data;
|
var _userData = _snapshot.data;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
import 'package:flutter_spinkit/flutter_spinkit.dart';
|
||||||
import 'package:messaging_app/components/messaging_provider.dart';
|
import 'package:messaging_app/components/messaging_provider.dart';
|
||||||
@@ -9,37 +11,56 @@ import 'package:provider/provider.dart';
|
|||||||
import 'package:timeago/timeago.dart' as timeago;
|
import 'package:timeago/timeago.dart' as timeago;
|
||||||
|
|
||||||
// ignore: must_be_immutable
|
// ignore: must_be_immutable
|
||||||
class RecentConverstationsPage extends StatelessWidget {
|
class RecentConverstationsPage extends StatefulWidget {
|
||||||
//
|
//
|
||||||
final double _deviceHeight;
|
final double _deviceHeight;
|
||||||
final double _deviceWidth;
|
final double _deviceWidth;
|
||||||
|
|
||||||
|
const RecentConverstationsPage(this._deviceHeight, this._deviceWidth,
|
||||||
|
{super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RecentConverstationsPage> createState() =>
|
||||||
|
_RecentConverstationsPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RecentConverstationsPageState extends State<RecentConverstationsPage> {
|
||||||
late MessagingProviders _auth;
|
late MessagingProviders _auth;
|
||||||
|
|
||||||
RecentConverstationsPage(this._deviceHeight, this._deviceWidth);
|
final myController = StreamController<List<Conversations>>.broadcast();
|
||||||
|
Stream<List<Conversations>> get messageStream => myController.stream;
|
||||||
|
|
||||||
|
void messager() {
|
||||||
|
MessagingProviders.instance.getUserConversation(_auth.user!.$id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void dispose() {
|
||||||
|
myController.close();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return SizedBox(
|
||||||
height: _deviceHeight,
|
height: widget._deviceHeight,
|
||||||
width: _deviceWidth,
|
width: widget._deviceWidth,
|
||||||
child: ChangeNotifierProvider<MessagingProviders>.value(
|
child: ChangeNotifierProvider<MessagingProviders>.value(
|
||||||
value: MessagingProviders.instance,
|
value: MessagingProviders.instance,
|
||||||
child: _conversationView(),
|
child: _conversationView(context),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _conversationView() {
|
Widget _conversationView(BuildContext _context) {
|
||||||
return Builder(builder: (_context) {
|
return Builder(builder: (BuildContext _context) {
|
||||||
SnackBarService.instance.buildContext = _context;
|
SnackBarService.instance.buildContext = _context;
|
||||||
_auth = Provider.of<MessagingProviders>(_context);
|
_auth = Provider.of<MessagingProviders>(_context);
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: _deviceHeight,
|
height: widget._deviceHeight,
|
||||||
width: _deviceWidth,
|
width: widget._deviceWidth,
|
||||||
child: StreamBuilder<List<Conversations>>(
|
child: StreamBuilder<List<Conversations>>(
|
||||||
stream: MessagingProviders.instance
|
stream: MessagingProviders.instance
|
||||||
.getUserConversation(_auth.user!.$id),
|
.getUserConversation(_auth.user!.$id),
|
||||||
builder: (_, _snapshot) {
|
builder: (_context, _snapshot) {
|
||||||
if (_snapshot.hasData) {
|
if (_snapshot.hasData) {
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
itemCount: _snapshot.data!.length,
|
itemCount: _snapshot.data!.length,
|
||||||
@@ -53,7 +74,7 @@ class RecentConverstationsPage extends StatelessWidget {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (BuildContext _context) {
|
builder: (BuildContext _context) {
|
||||||
return ConversationsPage(
|
return ConversationsPage(
|
||||||
conversationId: _chatId,
|
chatId: _chatId,
|
||||||
chatName: _details.name,
|
chatName: _details.name,
|
||||||
chatImage: _details.image,
|
chatImage: _details.image,
|
||||||
);
|
);
|
||||||
@@ -62,7 +83,11 @@ class RecentConverstationsPage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
title: Text(_details.name),
|
title: Text(_details.name),
|
||||||
subtitle: Text(_details.lastMessage!),
|
subtitle: Text(
|
||||||
|
_details.type == 'text'
|
||||||
|
? _details.lastMessage!
|
||||||
|
: 'attachment: image',
|
||||||
|
),
|
||||||
leading: Container(
|
leading: Container(
|
||||||
width: 50,
|
width: 50,
|
||||||
height: 50,
|
height: 50,
|
||||||
@@ -78,8 +103,7 @@ class RecentConverstationsPage extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
} else if (_snapshot.hasError) {
|
||||||
if (_snapshot.hasError) {
|
|
||||||
return Align(
|
return Align(
|
||||||
child: Text(
|
child: Text(
|
||||||
"no active conversation",
|
"no active conversation",
|
||||||
|
|||||||
@@ -26,12 +26,10 @@ class _RegisterationPageState extends State<RegisterationPage> {
|
|||||||
String? _email;
|
String? _email;
|
||||||
String? _password;
|
String? _password;
|
||||||
|
|
||||||
late GlobalKey<FormState> _formKey;
|
GlobalKey<FormState> _formKey;
|
||||||
late MessagingProviders _auth;
|
late MessagingProviders _auth;
|
||||||
|
|
||||||
_RegisterationPageState() {
|
_RegisterationPageState() : _formKey = GlobalKey<FormState>();
|
||||||
_formKey = GlobalKey<FormState>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -56,20 +54,22 @@ class _RegisterationPageState extends State<RegisterationPage> {
|
|||||||
return Builder(builder: (BuildContext _context) {
|
return Builder(builder: (BuildContext _context) {
|
||||||
SnackBarService.instance.buildContext = _context;
|
SnackBarService.instance.buildContext = _context;
|
||||||
_auth = Provider.of<MessagingProviders>(_context);
|
_auth = Provider.of<MessagingProviders>(_context);
|
||||||
return Container(
|
return SingleChildScrollView(
|
||||||
// color: Colors.deepOrange,
|
child: Container(
|
||||||
height: _deviceHeight * 0.75,
|
// color: Colors.deepOrange,
|
||||||
padding: EdgeInsets.symmetric(horizontal: _deviceWidth * 0.10),
|
height: _deviceHeight * 0.75,
|
||||||
child: Column(
|
padding: EdgeInsets.symmetric(horizontal: _deviceWidth * 0.10),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
child: Column(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
_headingWidget(),
|
children: [
|
||||||
_inputForm(),
|
_headingWidget(),
|
||||||
_registerButton(),
|
_inputForm(),
|
||||||
_backToLoginPage(),
|
_registerButton(),
|
||||||
],
|
_backToLoginPage(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -83,11 +83,11 @@ class _RegisterationPageState extends State<RegisterationPage> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(
|
||||||
'SignUp to Enjoy!',
|
'signup to enjoy!',
|
||||||
style: TextStyle(fontSize: 35, fontWeight: FontWeight.w700),
|
style: TextStyle(fontSize: 35, fontWeight: FontWeight.w700),
|
||||||
),
|
),
|
||||||
Text(
|
Text(
|
||||||
'Please enter your details',
|
'please enter your details',
|
||||||
style: TextStyle(fontSize: 25, fontWeight: FontWeight.w200),
|
style: TextStyle(fontSize: 25, fontWeight: FontWeight.w200),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -148,11 +148,13 @@ class _RegisterationPageState extends State<RegisterationPage> {
|
|||||||
Widget _nameTextField() {
|
Widget _nameTextField() {
|
||||||
return TextFormField(
|
return TextFormField(
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
validator: (_input) {
|
validator: (_input) {
|
||||||
return _input!.isNotEmpty ? null : "Please enter a valid Name";
|
return _input!.isNotEmpty ? null : "please enter a valid name";
|
||||||
},
|
},
|
||||||
onSaved: (_input) {
|
onSaved: (_input) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -161,7 +163,7 @@ class _RegisterationPageState extends State<RegisterationPage> {
|
|||||||
},
|
},
|
||||||
cursorColor: Colors.white,
|
cursorColor: Colors.white,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "Name",
|
hintText: "name",
|
||||||
focusedBorder: UnderlineInputBorder(
|
focusedBorder: UnderlineInputBorder(
|
||||||
borderSide: BorderSide(color: Colors.white))),
|
borderSide: BorderSide(color: Colors.white))),
|
||||||
);
|
);
|
||||||
@@ -170,13 +172,15 @@ class _RegisterationPageState extends State<RegisterationPage> {
|
|||||||
Widget _emailTextField() {
|
Widget _emailTextField() {
|
||||||
return TextFormField(
|
return TextFormField(
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
validator: (_input) {
|
validator: (_input) {
|
||||||
return _input!.isNotEmpty && _input.contains("@")
|
return _input!.isNotEmpty && _input.contains("@")
|
||||||
? null
|
? null
|
||||||
: "Please enter a valid Email";
|
: "please enter a valid email";
|
||||||
},
|
},
|
||||||
onSaved: (_input) {
|
onSaved: (_input) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -185,7 +189,7 @@ class _RegisterationPageState extends State<RegisterationPage> {
|
|||||||
},
|
},
|
||||||
cursorColor: Colors.white,
|
cursorColor: Colors.white,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "Email",
|
hintText: "email",
|
||||||
focusedBorder: UnderlineInputBorder(
|
focusedBorder: UnderlineInputBorder(
|
||||||
borderSide: BorderSide(color: Colors.white))),
|
borderSide: BorderSide(color: Colors.white))),
|
||||||
);
|
);
|
||||||
@@ -194,11 +198,13 @@ class _RegisterationPageState extends State<RegisterationPage> {
|
|||||||
Widget _passwordTextField() {
|
Widget _passwordTextField() {
|
||||||
return TextFormField(
|
return TextFormField(
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
|
keyboardType: TextInputType.emailAddress,
|
||||||
|
textInputAction: TextInputAction.next,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
),
|
),
|
||||||
validator: (_input) {
|
validator: (_input) {
|
||||||
return _input!.isNotEmpty ? null : "Please enter a Password";
|
return _input!.isNotEmpty ? null : "please enter a password";
|
||||||
},
|
},
|
||||||
onSaved: (_input) {
|
onSaved: (_input) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -208,7 +214,7 @@ class _RegisterationPageState extends State<RegisterationPage> {
|
|||||||
obscureText: true,
|
obscureText: true,
|
||||||
cursorColor: Colors.white,
|
cursorColor: Colors.white,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: "Password",
|
hintText: "password",
|
||||||
focusedBorder: UnderlineInputBorder(
|
focusedBorder: UnderlineInputBorder(
|
||||||
borderSide: BorderSide(color: Colors.white))),
|
borderSide: BorderSide(color: Colors.white))),
|
||||||
);
|
);
|
||||||
@@ -226,7 +232,7 @@ class _RegisterationPageState extends State<RegisterationPage> {
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
if (_image == null) {
|
if (_image == null) {
|
||||||
SnackBarService.instance
|
SnackBarService.instance
|
||||||
.showSnackBarError("Please Select an Image");
|
.showSnackBarError("please select an image");
|
||||||
}
|
}
|
||||||
if (_formKey.currentState!.validate() && _image != null) {
|
if (_formKey.currentState!.validate() && _image != null) {
|
||||||
//
|
//
|
||||||
@@ -247,7 +253,7 @@ class _RegisterationPageState extends State<RegisterationPage> {
|
|||||||
},
|
},
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
child: Text(
|
child: Text(
|
||||||
"REGISTER",
|
"register",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontWeight: FontWeight.w700,
|
fontWeight: FontWeight.w700,
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:messaging_app/components/messaging_provider.dart';
|
import 'package:messaging_app/components/messaging_provider.dart';
|
||||||
import 'package:messaging_app/models/users.dart';
|
import 'package:messaging_app/models/users.dart';
|
||||||
|
import 'package:messaging_app/pages/conversations_page.dart';
|
||||||
|
import 'package:messaging_app/service/navigation_service.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:timeago/timeago.dart' as timeago;
|
import 'package:timeago/timeago.dart' as timeago;
|
||||||
|
|
||||||
@@ -21,7 +25,8 @@ class SearchPage extends StatefulWidget {
|
|||||||
|
|
||||||
class _SearchPageState extends State<SearchPage> {
|
class _SearchPageState extends State<SearchPage> {
|
||||||
late String _searchText;
|
late String _searchText;
|
||||||
late List<Users> _usersData;
|
late List<User> _usersData;
|
||||||
|
//
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -31,12 +36,14 @@ class _SearchPageState extends State<SearchPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SizedBox(
|
return SingleChildScrollView(
|
||||||
height: widget.deviceHeight,
|
child: SizedBox(
|
||||||
width: widget.deviceWidth,
|
height: widget.deviceHeight,
|
||||||
child: ChangeNotifierProvider.value(
|
width: widget.deviceWidth,
|
||||||
value: MessagingProviders.instance,
|
child: ChangeNotifierProvider.value(
|
||||||
child: _searchPaheUI(),
|
value: MessagingProviders.instance,
|
||||||
|
child: _searchPaheUI(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -82,7 +89,7 @@ class _SearchPageState extends State<SearchPage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Widget _searchPageUIList() {
|
Widget _searchPageUIList() {
|
||||||
return StreamBuilder<List<Users>>(
|
return StreamBuilder<List<User>>(
|
||||||
stream: MessagingProviders.instance.getAllUsers(_searchText.trim()),
|
stream: MessagingProviders.instance.getAllUsers(_searchText.trim()),
|
||||||
builder: (_, _snapshot) {
|
builder: (_, _snapshot) {
|
||||||
if (_snapshot.hasData) {
|
if (_snapshot.hasData) {
|
||||||
@@ -105,6 +112,53 @@ class _SearchPageState extends State<SearchPage> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
return ListTile(
|
return ListTile(
|
||||||
|
enabled: true,
|
||||||
|
onTap: () async {
|
||||||
|
String _chatId;
|
||||||
|
String _linkId =
|
||||||
|
'${widget._auth.user!.$id}${_userData.id}';
|
||||||
|
|
||||||
|
_chatId = await MessagingProviders.instance
|
||||||
|
.getConversationLink(
|
||||||
|
_linkId.substring(0, 36));
|
||||||
|
|
||||||
|
if (_chatId.isEmpty) {
|
||||||
|
List<dynamic> _members = [
|
||||||
|
widget._auth.user!.$id,
|
||||||
|
_userData.id
|
||||||
|
];
|
||||||
|
|
||||||
|
List<String> messagesTemp = [];
|
||||||
|
messagesTemp = [
|
||||||
|
json.encode({
|
||||||
|
widget._auth.user!.$id: [],
|
||||||
|
}),
|
||||||
|
json.encode({
|
||||||
|
_userData.id: [],
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
Map<String, dynamic> _chat = {
|
||||||
|
"ownerId": widget._auth.user!.$id,
|
||||||
|
"members": _members,
|
||||||
|
"messages": messagesTemp
|
||||||
|
};
|
||||||
|
_chatId = await MessagingProviders.instance
|
||||||
|
.newMesaage(_chat);
|
||||||
|
}
|
||||||
|
|
||||||
|
NavigationService.instance.navigateToRoute(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (BuildContext _context) {
|
||||||
|
return ConversationsPage(
|
||||||
|
chatId: _chatId,
|
||||||
|
chatName: _userData.name,
|
||||||
|
chatImage: _userData.image,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
title: Text(_userData.name),
|
title: Text(_userData.name),
|
||||||
leading: Container(
|
leading: Container(
|
||||||
width: 50,
|
width: 50,
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ dependencies:
|
|||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
appwrite: 13.1.1
|
appwrite: 13.0.0
|
||||||
flutter_spinkit: ^5.2.1
|
flutter_spinkit: ^5.2.1
|
||||||
provider: ^6.1.2
|
provider: ^6.1.2
|
||||||
image_picker: ^1.1.2
|
image_picker: ^1.1.2
|
||||||
|
|||||||
Reference in New Issue
Block a user