Recent Conversations Page, Conversations Page implemented'

This commit is contained in:
2025-02-16 18:37:42 +00:00
parent 7e33872886
commit 1a74d16d85
17 changed files with 707 additions and 150 deletions
+1 -1
View File
@@ -2,6 +2,6 @@
# A chat app built using Flutter for Both iOS and Android. # A chat app built using Flutter for Both iOS and Android.
Backend is being covered by Appwrite. It is opensource and can be selfhosted, which is the main reason for it being used a backend. Backend is being covered by Appwrite. It is opensource and can be selfhosted, which is the main reason for it being used as a backend.
+12 -8
View File
@@ -1,10 +1,14 @@
//const endPoint = 'http://10.15.45.11/v1'; //const endPoint = 'http://10.15.45.11/v1';
//const endPoint = 'https://api.theonasanyas.com/v1'; //const endPoint = 'https://api.theonasanyas.com/v1';
const endPoint = 'https://cloud.appwrite.io/v1'; class Constants {
const projectId = '678bdc7c00203eff8af5'; static Constants instance = Constants();
const selfSigned = false;
const databaseId = '678c8659000a7d6b5bff'; var endPoint = 'https://cloud.appwrite.io/v1';
const userCollectionId = '678c865e002fe1cddd06'; var projectId = '678bdc7c00203eff8af5';
const chatsCollectionId = '678c866c0038822276ad'; var selfSigned = false;
const convCollectionId = '678c8667001944dee794'; var databaseId = '678c8659000a7d6b5bff';
const imageStorageBucket = '678d2162002a6770aaad'; var userCollectionId = '678c865e002fe1cddd06';
var chatsCollectionId = '678c866c0038822276ad';
var convCollectionId = '678c8667001944dee794';
var imageStorageBucket = '678d2162002a6770aaad';
}
+66 -15
View File
@@ -4,6 +4,7 @@ import 'package:appwrite/models.dart' as model;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
import 'package:messaging_app/components/constants.dart'; import 'package:messaging_app/components/constants.dart';
import 'package:messaging_app/models/chats.dart';
import 'package:messaging_app/models/conversations.dart'; import 'package:messaging_app/models/conversations.dart';
import 'package:messaging_app/models/users.dart'; import 'package:messaging_app/models/users.dart';
import 'package:messaging_app/service/navigation_service.dart'; import 'package:messaging_app/service/navigation_service.dart';
@@ -25,9 +26,8 @@ class MessagingProviders extends ChangeNotifier {
late final Storage storage; late final Storage storage;
// Auth Status // Auth Status
late AuthStatus status; AuthStatus? status;
late model.User? user; late model.User? user;
late model.Session session;
static MessagingProviders instance = MessagingProviders(); static MessagingProviders instance = MessagingProviders();
@@ -37,7 +37,10 @@ class MessagingProviders extends ChangeNotifier {
} }
_init() { _init() {
client.setEndpoint(endPoint).setProject(projectId).setSelfSigned(); client
.setEndpoint(Constants.instance.endPoint)
.setProject(Constants.instance.projectId)
.setSelfSigned();
account = Account(client); account = Account(client);
database = Databases(client); database = Databases(client);
storage = Storage(client); storage = Storage(client);
@@ -48,6 +51,7 @@ class MessagingProviders extends ChangeNotifier {
user = await account.get(); user = await account.get();
if (user != null) { if (user != null) {
status = AuthStatus.authenticated; status = AuthStatus.authenticated;
await updateLastSeen();
_autoLogin(); _autoLogin();
} }
} catch (e) { } catch (e) {
@@ -69,13 +73,14 @@ class MessagingProviders extends ChangeNotifier {
try { try {
status = AuthStatus.authenticating; status = AuthStatus.authenticating;
session = await account.createEmailPasswordSession( await account.createEmailPasswordSession(
email: _email, password: _password); email: _email, password: _password);
user = await account.get(); user = await account.get();
status = AuthStatus.authenticated; status = AuthStatus.authenticated;
SnackBarService.instance.showSnackBarSuccess("Welcome! ${user!.name}"); SnackBarService.instance.showSnackBarSuccess("Welcome! ${user!.name}");
await updateLastSeen();
NavigationService.instance.navigateToReplacement("home"); NavigationService.instance.navigateToReplacement("home");
} on AppwriteException catch (e) { } on AppwriteException catch (e) {
SnackBarService.instance.showSnackBarError(e.message!); SnackBarService.instance.showSnackBarError(e.message!);
@@ -109,7 +114,7 @@ class MessagingProviders extends ChangeNotifier {
} }
Future<void> createUser(String _email, String _password, String _name, Future<void> createUser(String _email, String _password, String _name,
Future<void> onSuccess(String _uid)) async { Future<void> Function(String _uid) onSuccess) async {
notifyListeners(); notifyListeners();
try { try {
user = await account.create( user = await account.create(
@@ -118,6 +123,7 @@ 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();
NavigationService.instance.goBack(); NavigationService.instance.goBack();
NavigationService.instance.navigateToReplacement("home"); NavigationService.instance.navigateToReplacement("home");
} on AppwriteException catch (e) { } on AppwriteException catch (e) {
@@ -136,8 +142,8 @@ class MessagingProviders extends ChangeNotifier {
notifyListeners(); notifyListeners();
try { try {
await database.createDocument( await database.createDocument(
databaseId: databaseId, databaseId: Constants.instance.databaseId,
collectionId: userCollectionId, collectionId: Constants.instance.userCollectionId,
documentId: _documentId, documentId: _documentId,
data: { data: {
"name": _name, "name": _name,
@@ -166,18 +172,51 @@ class MessagingProviders extends ChangeNotifier {
notifyListeners(); notifyListeners();
} }
Future<void> updateLastSeen() async {
await database.updateDocument(
databaseId: Constants.instance.databaseId,
collectionId: Constants.instance.userCollectionId,
documentId: user!.$id,
data: {"lastSeen": DateTime.now().toUtc().toIso8601String()},
);
}
Stream<Users> getUser(String userId) { Stream<Users> getUser(String userId) {
var _userData = database.getDocument( var _userData = database.getDocument(
databaseId: databaseId, databaseId: Constants.instance.databaseId,
collectionId: userCollectionId, collectionId: Constants.instance.userCollectionId,
documentId: userId, documentId: userId,
); );
return _userData.asStream().map((_snapshot) { return _userData.asStream().map((_document) {
return Users.fromData(_snapshot); return Users.fromData(_document);
}); });
} }
Stream<List<Users>> getAllUsers(String _searchText) {
List<Users> _returnValue = [];
if (_searchText.isNotEmpty) {
var _usersData = database.listDocuments(
databaseId: Constants.instance.databaseId,
collectionId: Constants.instance.userCollectionId,
queries: [
Query.contains("name", _searchText),
],
);
return _usersData.asStream().map(
(_documentList) {
for (var _document in _documentList.documents) {
_returnValue.add(Users.fromData(_document));
}
return _returnValue;
},
);
} else {
return Stream.empty();
}
}
Stream<List<Conversations>> getUserConversation(String userId) { Stream<List<Conversations>> getUserConversation(String userId) {
// //
List<dynamic> _chatId = []; List<dynamic> _chatId = [];
@@ -185,8 +224,8 @@ class MessagingProviders extends ChangeNotifier {
List<Conversations> _returnValue = []; List<Conversations> _returnValue = [];
var _conversationData = database.getDocument( var _conversationData = database.getDocument(
databaseId: databaseId, databaseId: Constants.instance.databaseId,
collectionId: convCollectionId, collectionId: Constants.instance.convCollectionId,
documentId: userId.trim(), documentId: userId.trim(),
); );
@@ -203,12 +242,24 @@ class MessagingProviders extends ChangeNotifier {
); );
} }
Stream<Chats> getChat(String _chatId) {
var _chat = database.getDocument(
databaseId: Constants.instance.databaseId,
collectionId: Constants.instance.chatsCollectionId,
documentId: _chatId,
);
return _chat.asStream().map((_document) {
return Chats.fromData(_document);
});
}
uploadUserImage(XFile? file, String _uid) async { uploadUserImage(XFile? file, String _uid) async {
notifyListeners(); notifyListeners();
try { try {
File _file = File(file!.path); File _file = File(file!.path);
var _message = await storage.createFile( var _message = await storage.createFile(
bucketId: imageStorageBucket, bucketId: Constants.instance.imageStorageBucket,
fileId: ID.unique(), fileId: ID.unique(),
file: InputFile.fromPath(path: _file.path, filename: file.name), file: InputFile.fromPath(path: _file.path, filename: file.name),
permissions: [ permissions: [
@@ -217,7 +268,7 @@ class MessagingProviders extends ChangeNotifier {
], ],
); );
return "$endPoint/storage/buckets/${_message.bucketId}/files/${_message.$id}/view?project=$projectId"; return "${Constants.instance.endPoint}/storage/buckets/${_message.bucketId}/files/${_message.$id}/view?project=${Constants.instance.projectId}";
} on AppwriteException catch (e) { } on AppwriteException catch (e) {
SnackBarService.instance.showSnackBarError(e.message!); SnackBarService.instance.showSnackBarError(e.message!);
// //
+38
View File
@@ -0,0 +1,38 @@
import 'package:appwrite/models.dart';
import 'package:messaging_app/models/messages.dart';
class Chats {
String ownerId;
List<String> members;
String? type;
List<Messages>? messages;
Chats({
required this.ownerId,
required this.members,
required this.type,
required this.messages,
});
factory Chats.fromData(Document _document) {
List<Messages> _outMessages = [];
List<String> _outMembers = [];
List<dynamic> _messages = _document.data['messages'];
for (var _message in _messages) {
_outMessages.add(Messages.fromData(_message));
}
List<dynamic> _members = _document.data['members'];
for (var _member in _members) {
_outMembers.add(_member);
}
return Chats(
ownerId: _document.data['ownerId'],
members: _outMembers,
type: _document.data['type'],
messages: _outMessages,
);
}
}
+2 -2
View File
@@ -9,10 +9,10 @@ class Conversations {
required this.details, required this.details,
}); });
factory Conversations.fromData(String _chatId, String details) { factory Conversations.fromData(String _chatId, String _details) {
return Conversations( return Conversations(
chatId: _chatId, chatId: _chatId,
details: Details.fromData(details), details: Details.fromData(_details),
); );
} }
} }
+8 -9
View File
@@ -3,8 +3,8 @@ import 'dart:convert';
class Details { class Details {
String name; String name;
String image; String image;
int? unseenCount; int unseenCount;
String lastMessage; String? lastMessage;
DateTime? timeStamp; DateTime? timeStamp;
Details({ Details({
@@ -15,14 +15,13 @@ class Details {
required this.unseenCount, required this.unseenCount,
}); });
factory Details.fromData(String _data) { factory Details.fromData(String _details) {
Map _decodedData = json.decode(_data);
return Details( return Details(
name: _decodedData['name'], name: json.decode(_details)['name'],
image: _decodedData['image'], image: json.decode(_details)['image'],
lastMessage: _decodedData['lastMessage'], lastMessage: json.decode(_details)['lastMessage'],
timeStamp: DateTime.parse(_decodedData['timeStamp'] ?? ""), timeStamp: DateTime.parse(json.decode(_details)['timeStamp'] ?? ""),
unseenCount: _decodedData['unseenCount'], unseenCount: json.decode(_details)['unseenCount'],
); );
} }
} }
+33
View File
@@ -0,0 +1,33 @@
import 'dart:convert';
enum MessageType {
Text,
Image,
}
class Messages {
String message;
String senderId;
MessageType type;
DateTime timeStamp;
Messages({
required this.message,
required this.senderId,
required this.type,
required this.timeStamp,
});
factory Messages.fromData(String _message) {
var _messageType = json.decode(_message)['type'] == "text"
? MessageType.Text
: MessageType.Image;
return Messages(
message: json.decode(_message)['message'],
senderId: json.decode(_message)['senderId'],
type: _messageType,
timeStamp: DateTime.parse(json.decode(_message)['timeStamp'] ?? ""),
);
}
}
+8 -6
View File
@@ -1,25 +1,27 @@
import 'package:appwrite/models.dart'; import 'package:appwrite/models.dart';
class Users { class Users {
String id;
String name; String name;
String email; String email;
String image; String image;
DateTime lastSeen; DateTime lastSeen;
Users({ Users({
required this.id,
required this.name, required this.name,
required this.email, required this.email,
required this.image, required this.image,
required this.lastSeen, required this.lastSeen,
}); });
factory Users.fromData(Document _snapshot) { factory Users.fromData(Document _document) {
return Users( return Users(
name: _snapshot.data["name"], id: _document.data['\$id'],
email: _snapshot.data["email"], name: _document.data['name'],
image: _snapshot.data["image"], email: _document.data['email'],
lastSeen: DateTime.parse(_snapshot.data["lastSeen"]), image: _document.data['image'],
lastSeen: DateTime.parse(_document.data['lastSeen']),
); );
} }
} }
+234
View File
@@ -0,0 +1,234 @@
import 'package:flutter/material.dart';
import 'package:messaging_app/components/messaging_provider.dart';
import 'package:messaging_app/models/chats.dart';
import 'package:messaging_app/models/messages.dart';
import 'package:provider/provider.dart';
import 'package:timeago/timeago.dart' as timeago;
class ConversationsPage extends StatefulWidget {
ConversationsPage({
super.key,
required this.conversationId,
// required this.receiverId,
required this.chatImage,
required this.chatName,
});
String conversationId;
// String receiverId;
String chatName;
String chatImage;
@override
State<StatefulWidget> createState() {
return _ConversationsPageState();
}
}
class _ConversationsPageState extends State<ConversationsPage> {
//
late double _deviceHeight;
late double _deviceWidth;
late MessagingProviders _auth;
@override
Widget build(BuildContext context) {
//
_deviceHeight = MediaQuery.of(context).size.height;
_deviceWidth = MediaQuery.of(context).size.width;
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
title: Text(widget.chatName),
titleTextStyle: TextStyle(fontSize: 16),
),
body: ChangeNotifierProvider<MessagingProviders>.value(
value: MessagingProviders.instance,
child: _conversationPageUI(),
),
);
}
Widget _conversationPageUI() {
return Builder(builder: (_context) {
_auth = Provider.of<MessagingProviders>(_context);
return Stack(
clipBehavior: Clip.none,
children: [
_messageListView(),
_messageInputField(_context),
],
);
});
}
Widget _messageListView() {
return Container(
height: _deviceHeight * 0.75,
width: _deviceWidth,
child: StreamBuilder<Chats>(
stream: MessagingProviders.instance.getChat(widget.conversationId),
builder: (_context, _snapshot) {
return _snapshot.hasData
? ListView.builder(
padding: EdgeInsets.symmetric(
horizontal: 10,
vertical: 10,
),
itemCount: _snapshot.data!.messages!.length,
itemBuilder: (BuildContext _context, int _index) {
var _message = _snapshot.data!.messages![_index];
return _messageView(_message);
},
)
: Text("");
}),
);
}
Widget _messageView(Messages _message) {
bool _isOwnMessage = false;
if (_message.senderId == _auth.user!.$id) {
_isOwnMessage = true;
}
return Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment:
_isOwnMessage ? MainAxisAlignment.end : MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
_isOwnMessage ? Container() : _imageViewBubble(),
_textMessageBubble(_isOwnMessage, _message),
],
),
);
}
Widget _textMessageBubble(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),
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: [
Text(_message.message),
Text(
timeago.format(_message.timeStamp),
style: TextStyle(
color: Colors.white70,
),
),
],
),
);
}
Widget _imageViewBubble() {
return Container(
height: _deviceHeight * 0.05,
width: _deviceHeight * 0.05,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(500),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(
widget.chatImage,
),
),
),
);
}
Widget _messageInputField(BuildContext _context) {
return Align(
alignment: Alignment.bottomCenter,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
children: [
_messageText(),
_imageUpload(),
_sendMessage(_context),
],
),
);
}
Widget _messageText() {
return Container(
padding: EdgeInsets.only(left: 10, right: 10),
decoration: BoxDecoration(
color: Color.fromRGBO(43, 43, 43, 1),
borderRadius: BorderRadius.circular(30)
),
margin: EdgeInsets.symmetric(
horizontal: _deviceWidth * 0.04,
vertical: _deviceHeight * 0.02
),
width: _deviceWidth * 0.70,
child: TextFormField(
validator: (_input) {
if (_input == null) {
return "";
}
},
onChanged: (_input) {
//
},
onSaved: (_input) {
//
},
cursorColor: Colors.white,
decoration: InputDecoration(
border: InputBorder.none,
hintText: "type a message",
),
autocorrect: false,
),
);
}
Widget _sendMessage(BuildContext context) {
return Container(
height: _deviceHeight * 0.045,
width: _deviceHeight * 0.045,
child: IconButton(
onPressed: () {},
icon: Icon(
Icons.send,
color: Colors.white,
),
),
);
}
Widget _imageUpload() {
return SizedBox(
height: _deviceHeight * 0.045,
width: _deviceHeight * 0.045,
child: FloatingActionButton(onPressed: () {},
backgroundColor: Colors.blue,
child: Icon(Icons.camera_enhance),),
);
}
}
+11 -9
View File
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:messaging_app/pages/profile_page.dart'; import 'package:messaging_app/pages/profile_page.dart';
import 'package:messaging_app/pages/recent_converstations.dart'; import 'package:messaging_app/pages/recent_converstations_page.dart';
import 'package:messaging_app/pages/search_page.dart';
class HomePage extends StatefulWidget { class HomePage extends StatefulWidget {
const HomePage({super.key}); const HomePage({super.key});
@@ -14,10 +15,11 @@ class HomePage extends StatefulWidget {
class _HomePageState extends State<HomePage> class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin { with SingleTickerProviderStateMixin {
//TickerProviderStateMixin
// // // //
late double _deviceHeight; late double _deviceHeight;
late double _deviceWidth; late double _deviceWidth;
late TabController _tabController; late final TabController _tabController;
@override @override
void initState() { void initState() {
@@ -42,10 +44,10 @@ 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, // backgroundColor: Theme.of(context).scaffoldBackgroundColor,
title: Text("Slick Messenger"), title: Text("the messenger"),
titleTextStyle: TextStyle(fontSize: 16), titleTextStyle: TextStyle(fontSize: 16),
bottom: TabBar( bottom: TabBar(
unselectedLabelColor: Colors.grey, unselectedLabelColor: Colors.grey,
@@ -56,19 +58,19 @@ class _HomePageState extends State<HomePage>
Tab( Tab(
icon: Icon( icon: Icon(
Icons.people_outline, Icons.people_outline,
size: 25, size: 30,
), ),
), ),
Tab( Tab(
icon: Icon( icon: Icon(
Icons.chat_bubble_outline, Icons.chat_bubble_outline,
size: 25, size: 30,
), ),
), ),
Tab( Tab(
icon: Icon( icon: Icon(
Icons.person_outline, Icons.person_outline,
size: 25, size: 30,
), ),
), ),
], ],
@@ -82,7 +84,7 @@ class _HomePageState extends State<HomePage>
return TabBarView( return TabBarView(
controller: _tabController, controller: _tabController,
children: [ children: [
Placeholder(), SearchPage( deviceHeight: _deviceHeight , deviceWidth: _deviceWidth,),
RecentConverstationsPage(_deviceHeight, _deviceWidth), RecentConverstationsPage(_deviceHeight, _deviceWidth),
ProfilePage(_deviceHeight, _deviceWidth), ProfilePage(_deviceHeight, _deviceWidth),
], ],
+1 -1
View File
@@ -176,7 +176,7 @@ class _LoginPageState extends State<LoginPage> {
}, },
color: Colors.blue, color: Colors.blue,
child: Text( child: Text(
'Login'.toUpperCase(), 'LOGIN',
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
+3
View File
@@ -2,8 +2,10 @@ 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';
import 'package:messaging_app/models/users.dart'; import 'package:messaging_app/models/users.dart';
import 'package:messaging_app/service/snackbar_service.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
// ignore: must_be_immutable
class ProfilePage extends StatelessWidget { class ProfilePage extends StatelessWidget {
// //
@@ -27,6 +29,7 @@ class ProfilePage extends StatelessWidget {
Widget _profilePageUI() { Widget _profilePageUI() {
return Builder(builder: (BuildContext _context) { return Builder(builder: (BuildContext _context) {
SnackBarService.instance.buildContext = _context;
_auth = Provider.of<MessagingProviders>(_context); _auth = Provider.of<MessagingProviders>(_context);
return StreamBuilder<Users>( return StreamBuilder<Users>(
stream: MessagingProviders.instance.getUser(_auth.user!.$id), stream: MessagingProviders.instance.getUser(_auth.user!.$id),
-95
View File
@@ -1,95 +0,0 @@
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:messaging_app/components/messaging_provider.dart';
import 'package:messaging_app/models/conversations.dart';
import 'package:provider/provider.dart';
import 'package:timeago/timeago.dart' as timeago;
class RecentConverstationsPage extends StatelessWidget {
//
final double _deviceHeight;
final double _deviceWidth;
late MessagingProviders _auth;
RecentConverstationsPage(this._deviceHeight, this._deviceWidth);
@override
Widget build(BuildContext context) {
return Container(
height: _deviceHeight,
width: _deviceWidth,
child: ChangeNotifierProvider<MessagingProviders>.value(
value: MessagingProviders.instance,
child: _conversationView(),
),
);
}
Widget _conversationView() {
return Builder(builder: (_context) {
_auth = Provider.of<MessagingProviders>(_context);
return Container(
height: _deviceHeight,
width: _deviceWidth,
child: StreamBuilder<List<Conversations>>(
stream: MessagingProviders.instance
.getUserConversation(_auth.user!.$id),
builder: (_, _snapshot) {
return _snapshot.hasData
? ListView.builder(
itemCount: _snapshot.data!.length,
itemBuilder: (_context, _index) {
var _details = _snapshot.data![_index].details;
return ListTile(
onTap: () {
//
},
title: Text(_details.name),
subtitle: Text(_details.lastMessage),
leading: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(_details.image),
),
),
),
trailing: _listTrailingWidget(_details.timeStamp),
);
},
)
: SpinKitWanderingCubes(
color: Colors.blue,
size: 50.0,
);
}),
);
});
}
Widget _listTrailingWidget(DateTime? _date) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
timeago.format(_date!),
style: TextStyle(fontSize: 15),
),
Container(
height: 12,
width: 12,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(100),
),
)
],
);
}
}
+120
View File
@@ -0,0 +1,120 @@
import 'package:flutter/material.dart';
import 'package:flutter_spinkit/flutter_spinkit.dart';
import 'package:messaging_app/components/messaging_provider.dart';
import 'package:messaging_app/models/conversations.dart';
import 'package:messaging_app/pages/conversations_page.dart';
import 'package:messaging_app/service/navigation_service.dart';
import 'package:messaging_app/service/snackbar_service.dart';
import 'package:provider/provider.dart';
import 'package:timeago/timeago.dart' as timeago;
// ignore: must_be_immutable
class RecentConverstationsPage extends StatelessWidget {
//
final double _deviceHeight;
final double _deviceWidth;
late MessagingProviders _auth;
RecentConverstationsPage(this._deviceHeight, this._deviceWidth);
@override
Widget build(BuildContext context) {
return Container(
height: _deviceHeight,
width: _deviceWidth,
child: ChangeNotifierProvider<MessagingProviders>.value(
value: MessagingProviders.instance,
child: _conversationView(),
),
);
}
Widget _conversationView() {
return Builder(builder: (_context) {
SnackBarService.instance.buildContext = _context;
_auth = Provider.of<MessagingProviders>(_context);
return SizedBox(
height: _deviceHeight,
width: _deviceWidth,
child: StreamBuilder<List<Conversations>>(
stream: MessagingProviders.instance
.getUserConversation(_auth.user!.$id),
builder: (_, _snapshot) {
if (_snapshot.hasData) {
return ListView.builder(
itemCount: _snapshot.data!.length,
itemBuilder: (_context, _index) {
var _details = _snapshot.data![_index].details;
var _chatId = _snapshot.data![_index].chatId;
return ListTile(
onTap: () {
//
NavigationService.instance.navigateToRoute(
MaterialPageRoute(
builder: (BuildContext _context) {
return ConversationsPage(
conversationId: _chatId,
chatName: _details.name,
chatImage: _details.image,
);
},
),
);
},
title: Text(_details.name),
subtitle: Text(_details.lastMessage!),
leading: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(_details.image),
),
),
),
trailing: _listTrailingWidget(_details.timeStamp),
);
},
);
}
if (_snapshot.hasError) {
return Align(
child: Text(
"no active conversation",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 20,
),
),
);
} else {
return SpinKitWanderingCubes(
color: Colors.blue,
size: 50.0,
);
}
}),
);
});
}
Widget _listTrailingWidget(DateTime? _date) {
return Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
"last message",
style: TextStyle(fontSize: 15),
),
Text(
timeago.format(_date!),
style: TextStyle(fontSize: 15),
)
],
);
}
}
+2 -2
View File
@@ -226,7 +226,7 @@ class _RegisterationPageState extends State<RegisterationPage> {
onPressed: () { onPressed: () {
if (_image == null) { if (_image == null) {
SnackBarService.instance SnackBarService.instance
.showSnackBarError("Please Select Image"); .showSnackBarError("Please Select an Image");
} }
if (_formKey.currentState!.validate() && _image != null) { if (_formKey.currentState!.validate() && _image != null) {
// //
@@ -247,7 +247,7 @@ class _RegisterationPageState extends State<RegisterationPage> {
}, },
color: Colors.blue, color: Colors.blue,
child: Text( child: Text(
'Register'.toUpperCase(), "REGISTER",
style: TextStyle( style: TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,
+168
View File
@@ -0,0 +1,168 @@
import 'package:flutter/material.dart';
import 'package:messaging_app/components/messaging_provider.dart';
import 'package:messaging_app/models/users.dart';
import 'package:provider/provider.dart';
import 'package:timeago/timeago.dart' as timeago;
class SearchPage extends StatefulWidget {
SearchPage({
super.key,
required this.deviceHeight,
required this.deviceWidth,
});
double deviceHeight;
double deviceWidth;
late MessagingProviders _auth;
@override
State<SearchPage> createState() => _SearchPageState();
}
class _SearchPageState extends State<SearchPage> {
late String _searchText;
late List<Users> _usersData;
@override
void initState() {
super.initState();
_searchText = '';
_usersData = [];
}
@override
Widget build(BuildContext context) {
return SizedBox(
height: widget.deviceHeight,
width: widget.deviceWidth,
child: ChangeNotifierProvider.value(
value: MessagingProviders.instance,
child: _searchPaheUI(),
),
);
}
Widget _searchPaheUI() {
return Builder(builder: (_context) {
widget._auth = Provider.of<MessagingProviders>(_context);
return Column(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_searchPageUIField(),
_searchPageUIList(),
],
);
});
}
Widget _searchPageUIField() {
return Container(
height: widget.deviceHeight * 0.08,
width: widget.deviceWidth,
padding: EdgeInsets.symmetric(vertical: widget.deviceHeight * 0.02),
child: TextField(
autocorrect: false,
style: TextStyle(
color: Colors.white,
),
onChanged: (_input) {
setState(() {
_searchText = _input;
});
},
decoration: InputDecoration(
prefixIcon: Icon(Icons.search),
labelText: "search",
labelStyle: TextStyle(color: Colors.white),
border: OutlineInputBorder(borderSide: BorderSide.none),
),
),
);
}
Widget _searchPageUIList() {
return StreamBuilder<List<Users>>(
stream: MessagingProviders.instance.getAllUsers(_searchText.trim()),
builder: (_, _snapshot) {
if (_snapshot.hasData) {
_usersData = _snapshot.data!
.where((_data) => _data.id != widget._auth.user!.$id)
.toList();
}
return _snapshot.hasData
? SizedBox(
height: widget.deviceHeight * 0.70,
child: ListView.builder(
itemCount: _usersData.length,
itemBuilder: (_, _index) {
var _userData = _usersData[_index];
var _isUserActive = _userData.lastSeen.isBefore(
DateTime.now().toUtc().subtract(
Duration(
minutes: 1,
),
),
);
return ListTile(
title: Text(_userData.name),
leading: Container(
width: 50,
height: 50,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100),
image: DecorationImage(
fit: BoxFit.cover,
image: NetworkImage(_userData.image),
),
),
),
trailing: Column(
mainAxisAlignment:
MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.end,
children: [
_isUserActive
? Text(
"last seen",
style: TextStyle(
fontSize: 15,
),
)
: Text(
"active",
style: TextStyle(
fontSize: 15,
),
),
_isUserActive
? Text(
timeago.format(
_userData.lastSeen,
),
style: TextStyle(fontSize: 15),
)
: Container(
height: 10,
width: 10,
decoration: BoxDecoration(
color: Colors.blue,
borderRadius:
BorderRadius.circular(100),
),
)
],
),
);
}),
)
: Container()
// : SpinKitWanderingCubes(
// color: Colors.blue,
// size: 50.0,
// );
;
});
}
}
-2
View File
@@ -6,8 +6,6 @@ class SnackBarService {
BuildContext? _buildContext; BuildContext? _buildContext;
static SnackBarService instance = SnackBarService(); static SnackBarService instance = SnackBarService();
SnackBarService() {}
set buildContext(BuildContext _context) { set buildContext(BuildContext _context) {
_buildContext = _context; _buildContext = _context;
} }