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:
2025-03-26 13:41:56 +00:00
parent 1a74d16d85
commit 1ba9e981e1
18 changed files with 812 additions and 236 deletions
+7 -4
View File
@@ -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://cloud.appwrite.io/v1'; var endPoint = 'https://api.theonasanyas.com/v1';
var projectId = '678bdc7c00203eff8af5'; var projectId = '67833160000cb0801fdb';
// 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';
} }
+115 -19
View File
@@ -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 "";
} }
} }
+1 -3
View File
@@ -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(),
}; };
}
} }
+1
View File
@@ -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.
+167
View File
@@ -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
View File
@@ -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,
); );
} }
} }
+6 -4
View File
@@ -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) =>
Conversations(
chatId: _chatId, chatId: _chatId,
details: Details.fromData(_details), details: Details.fromData(_details),
); lastModified: DateTime.parse(_lastModified));
}
} }
+3
View File
@@ -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'],
); );
} }
} }
+20 -16
View File
@@ -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"
? MessageType.Text
: MessageType.Image;
return Messages(
message: json.decode(_message)['message'], message: json.decode(_message)['message'],
senderId: json.decode(_message)['senderId'], senderId: json.decode(_message)['senderId'],
type: _messageType, type: json.decode(_message)['type'],
timeStamp: DateTime.parse(json.decode(_message)['timeStamp'] ?? ""), timeStamp: json.decode(_message)['timeStamp'] ?? "",
); );
}
factory Messages.fromMap(Map _message) => Messages(
message: _message['message'],
senderId: _message['senderId'],
type: _message['type'],
timeStamp: _message['timeStamp'] ?? "",
);
Map<String, dynamic> toJson() => {
"message": message,
"senderId": senderId,
"type": type,
"timeStamp": timeStamp,
};
} }
+4 -4
View File
@@ -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'],
+230 -41
View File
@@ -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,8 +75,10 @@ class _ConversationsPageState extends State<ConversationsPage> {
} }
Widget _conversationPageUI() { Widget _conversationPageUI() {
return Builder(builder: (_context) { return Builder(
builder: (_context) {
_auth = Provider.of<MessagingProviders>(_context); _auth = Provider.of<MessagingProviders>(_context);
SnackBarService.instance.buildContext = _context;
return Stack( return Stack(
clipBehavior: Clip.none, clipBehavior: Clip.none,
children: [ children: [
@@ -61,36 +86,60 @@ class _ConversationsPageState extends State<ConversationsPage> {
_messageInputField(_context), _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;
_memberPosition = _members.indexOf(_auth.user!.$id);
_messages = _snapshot.data!.messages![_memberPosition];
_messagesArray = _snapshot.data!.messages!;
}
if (_snapshot.hasData) {
if (_messages.isEmpty) {
return Align(
child: Text("No Chat"),
);
} else {
return ListView.builder(
controller: _scrollController,
padding: EdgeInsets.symmetric( padding: EdgeInsets.symmetric(
horizontal: 10, horizontal: 10,
vertical: 10, vertical: 10,
), ),
itemCount: _snapshot.data!.messages!.length, itemCount: _messages.length,
itemBuilder: (BuildContext _context, int _index) { itemBuilder: (BuildContext _context, int _index) {
var _message = _snapshot.data!.messages![_index]; _messages[_index];
return _messageView(_message); return _messageView(_messages, _index);
}, },
) );
: Text(""); }
}), } 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,11 +187,56 @@ class _ConversationsPageState extends State<ConversationsPage> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text(_message.message), Text(_message.message),
Text( SizedBox(
timeago.format(_message.timeStamp), height: 10,
style: TextStyle(
color: Colors.white70,
), ),
Text(
timeago.format(DateTime.parse(_message.timeStamp)),
style: TextStyle(color: Colors.white70, fontSize: 10),
),
],
),
);
}
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: Form(
key: _formKey,
child: Row( child: Row(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround, mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
children: [ children: [
_messageText(), _messageInput(),
_imageUpload(), _sendImage(),
_sendMessage(_context), _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(
onPressed: () async {
XFile? _imageFile = await MediaService.instance.getImageFromLibrary();
if (_imageFile != null) {
var _url = await MessagingProviders.instance
.uploadUserImage(_imageFile, '_uid');
await sendMessage('image', _url);
}
},
backgroundColor: Colors.blue, backgroundColor: Colors.blue,
child: Icon(Icons.camera_enhance),), 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},
); );
} }
} }
+42 -25
View File
@@ -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,
); );
@@ -46,6 +67,7 @@ class _HomePageState extends State<HomePage>
return Scaffold( return Scaffold(
// backgroundColor: Theme.of(context).scaffoldBackgroundColor, // backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: AppBar( appBar: AppBar(
automaticallyImplyLeading: false,
// backgroundColor: Theme.of(context).scaffoldBackgroundColor, // backgroundColor: Theme.of(context).scaffoldBackgroundColor,
title: Text("the messenger"), title: Text("the messenger"),
titleTextStyle: TextStyle(fontSize: 16), titleTextStyle: TextStyle(fontSize: 16),
@@ -54,39 +76,34 @@ class _HomePageState extends State<HomePage>
indicatorColor: Colors.blue, indicatorColor: Colors.blue,
labelColor: Colors.blue, labelColor: Colors.blue,
controller: _tabController, controller: _tabController,
tabs: [ tabs: _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,
),
),
],
), ),
), ),
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,
),
], ],
); );
} }
+15 -9
View File
@@ -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,
+1 -1
View File
@@ -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;
+39 -15
View File
@@ -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",
+21 -15
View File
@@ -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,7 +54,8 @@ 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(
child: Container(
// color: Colors.deepOrange, // color: Colors.deepOrange,
height: _deviceHeight * 0.75, height: _deviceHeight * 0.75,
padding: EdgeInsets.symmetric(horizontal: _deviceWidth * 0.10), padding: EdgeInsets.symmetric(horizontal: _deviceWidth * 0.10),
@@ -71,6 +70,7 @@ class _RegisterationPageState extends State<RegisterationPage> {
_backToLoginPage(), _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,
+57 -3
View File
@@ -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,13 +36,15 @@ class _SearchPageState extends State<SearchPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SizedBox( return SingleChildScrollView(
child: SizedBox(
height: widget.deviceHeight, height: widget.deviceHeight,
width: widget.deviceWidth, width: widget.deviceWidth,
child: ChangeNotifierProvider.value( child: ChangeNotifierProvider.value(
value: MessagingProviders.instance, value: MessagingProviders.instance,
child: _searchPaheUI(), 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
View File
@@ -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