Pages Implementation

Implemented Profile and Recent Conversation skeleton pages
This commit is contained in:
2025-02-07 20:13:07 +00:00
parent 7504f341d2
commit 7e33872886
15 changed files with 448 additions and 63 deletions
+3 -3
View File
@@ -1,7 +1,7 @@
# messaging_app
## Messaging App - messaging_app
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 using is a backend.
Backend is being covered by Appwrite. It is opensource and can be selfhosted, which is the main reason for it being used a backend.
+1
View File
@@ -1,3 +1,4 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
- provider: true
+74 -20
View File
@@ -1,10 +1,11 @@
import 'dart:io';
import 'package:appwrite/appwrite.dart';
import 'package:appwrite/models.dart' as models;
import 'package:appwrite/models.dart' as model;
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:messaging_app/components/constants.dart';
import 'package:messaging_app/models/conversations.dart';
import 'package:messaging_app/models/users.dart';
import 'package:messaging_app/service/navigation_service.dart';
import 'package:messaging_app/service/snackbar_service.dart';
@@ -24,29 +25,31 @@ class MessagingProviders extends ChangeNotifier {
late final Storage storage;
// Auth Status
AuthStatus? status;
models.User? user;
models.Session? session;
late AuthStatus status;
late model.User? user;
late model.Session session;
static MessagingProviders instance = MessagingProviders();
MessagingProviders() {
init();
loadUser();
_init();
_loadUser();
}
init() {
_init() {
client.setEndpoint(endPoint).setProject(projectId).setSelfSigned();
account = Account(client);
database = Databases(client);
storage = Storage(client);
}
loadUser() async {
_loadUser() async {
try {
user = await account.get();
status = AuthStatus.authenticated;
if (user != null) {
status = AuthStatus.authenticated;
_autoLogin();
}
} catch (e) {
status = AuthStatus.notAuthenticated;
} finally {
@@ -54,7 +57,11 @@ class MessagingProviders extends ChangeNotifier {
}
}
void loginWithEmailAndPassword(String _email, String _password) async {
void _autoLogin() {
NavigationService.instance.navigateToReplacement("home");
}
void login(String _email, String _password) async {
//
notifyListeners();
try {
@@ -65,17 +72,11 @@ class MessagingProviders extends ChangeNotifier {
session = await account.createEmailPasswordSession(
email: _email, password: _password);
user = await account.get();
status = AuthStatus.authenticated;
var value = await database.getDocument(
databaseId: databaseId,
collectionId: userCollectionId,
documentId: session!.userId,
);
SnackBarService.instance
.showSnackBarSuccess("Welcome! ${value.data['name']}");
SnackBarService.instance.showSnackBarSuccess("Welcome! ${user!.name}");
NavigationService.instance.navigateToReplacement("home");
account.deleteSession(sessionId: 'current');
} on AppwriteException catch (e) {
SnackBarService.instance.showSnackBarError(e.message!);
status = AuthStatus.error;
@@ -91,6 +92,22 @@ class MessagingProviders extends ChangeNotifier {
}
}
void logout() async {
try {
await account.deleteSession(sessionId: 'current');
user = null;
status = AuthStatus.notAuthenticated;
// await onSuccess();
await NavigationService.instance.navigateToReplacement("login");
SnackBarService.instance.showSnackBarError("Logged out Successfully");
} on AppwriteException catch (e) {
SnackBarService.instance.showSnackBarError(e.message!);
} on Exception catch (e) {
SnackBarService.instance.showSnackBarError(e.toString());
}
notifyListeners();
}
Future<void> createUser(String _email, String _password, String _name,
Future<void> onSuccess(String _uid)) async {
notifyListeners();
@@ -149,6 +166,43 @@ class MessagingProviders extends ChangeNotifier {
notifyListeners();
}
Stream<Users> getUser(String userId) {
var _userData = database.getDocument(
databaseId: databaseId,
collectionId: userCollectionId,
documentId: userId,
);
return _userData.asStream().map((_snapshot) {
return Users.fromData(_snapshot);
});
}
Stream<List<Conversations>> getUserConversation(String userId) {
//
List<dynamic> _chatId = [];
List<dynamic> _details = [];
List<Conversations> _returnValue = [];
var _conversationData = database.getDocument(
databaseId: databaseId,
collectionId: convCollectionId,
documentId: userId.trim(),
);
return _conversationData.asStream().map(
(_snapshot) {
_chatId = _snapshot.data['chatId'];
_details = _snapshot.data['details'];
for (int i = 0; i < _chatId.length; i++) {
_returnValue.add(Conversations.fromData(_chatId[i], _details[i]));
}
return _returnValue;
},
);
}
uploadUserImage(XFile? file, String _uid) async {
notifyListeners();
try {
+1 -1
View File
@@ -22,7 +22,7 @@ class MessagingApp extends StatelessWidget {
theme: ThemeData(
brightness: Brightness.dark,
scaffoldBackgroundColor: Colors.black,
primaryColor: Color.fromRGBO(42, 117, 188, 1.0),
primaryColor: Color.fromRGBO(42, 117, 188, 1.0),
colorScheme: ColorScheme.dark(),
useMaterial3: true,
),
View File
+18
View File
@@ -0,0 +1,18 @@
import 'package:messaging_app/models/details.dart';
class Conversations {
String chatId;
Details details;
Conversations({
required this.chatId,
required this.details,
});
factory Conversations.fromData(String _chatId, String details) {
return Conversations(
chatId: _chatId,
details: Details.fromData(details),
);
}
}
+28
View File
@@ -0,0 +1,28 @@
import 'dart:convert';
class Details {
String name;
String image;
int? unseenCount;
String lastMessage;
DateTime? timeStamp;
Details({
required this.name,
required this.image,
required this.lastMessage,
required this.timeStamp,
required this.unseenCount,
});
factory Details.fromData(String _data) {
Map _decodedData = json.decode(_data);
return Details(
name: _decodedData['name'],
image: _decodedData['image'],
lastMessage: _decodedData['lastMessage'],
timeStamp: DateTime.parse(_decodedData['timeStamp'] ?? ""),
unseenCount: _decodedData['unseenCount'],
);
}
}
+25
View File
@@ -0,0 +1,25 @@
import 'package:appwrite/models.dart';
class Users {
String name;
String email;
String image;
DateTime lastSeen;
Users({
required this.name,
required this.email,
required this.image,
required this.lastSeen,
});
factory Users.fromData(Document _snapshot) {
return Users(
name: _snapshot.data["name"],
email: _snapshot.data["email"],
image: _snapshot.data["image"],
lastSeen: DateTime.parse(_snapshot.data["lastSeen"]),
);
}
}
+68 -3
View File
@@ -1,4 +1,6 @@
import 'package:flutter/material.dart';
import 'package:messaging_app/pages/profile_page.dart';
import 'package:messaging_app/pages/recent_converstations.dart';
class HomePage extends StatefulWidget {
const HomePage({super.key});
@@ -10,17 +12,80 @@ class HomePage extends StatefulWidget {
}
}
class _HomePageState extends State<HomePage> {
class _HomePageState extends State<HomePage>
with SingleTickerProviderStateMixin {
// //
late double _deviceHeight;
late double _deviceWidth;
late TabController _tabController;
@override
void initState() {
super.initState();
_tabController = TabController(
length: 3,
vsync: this,
initialIndex: 1,
);
}
@override
void dispose() {
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
//
_deviceHeight = MediaQuery.of(context).size.height;
_deviceWidth = MediaQuery.of(context).size.width;
return Scaffold(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
appBar: AppBar(
backgroundColor: Theme.of(context).scaffoldBackgroundColor,
title: Text("Messaging App"),
titleTextStyle: TextStyle(fontSize: 15),
title: Text("Slick Messenger"),
titleTextStyle: TextStyle(fontSize: 16),
bottom: TabBar(
unselectedLabelColor: Colors.grey,
indicatorColor: Colors.blue,
labelColor: Colors.blue,
controller: _tabController,
tabs: [
Tab(
icon: Icon(
Icons.people_outline,
size: 25,
),
),
Tab(
icon: Icon(
Icons.chat_bubble_outline,
size: 25,
),
),
Tab(
icon: Icon(
Icons.person_outline,
size: 25,
),
),
],
),
),
body: _tabBarPages(),
);
}
Widget _tabBarPages() {
return TabBarView(
controller: _tabController,
children: [
Placeholder(),
RecentConverstationsPage(_deviceHeight, _deviceWidth),
ProfilePage(_deviceHeight, _deviceWidth),
],
);
}
}
+3 -4
View File
@@ -5,7 +5,7 @@ import 'package:messaging_app/service/snackbar_service.dart';
import 'package:provider/provider.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key});
const LoginPage({super.key,});
@override
State<StatefulWidget> createState() {
@@ -41,7 +41,6 @@ class _LoginPageState extends State<LoginPage> {
value: MessagingProviders.instance,
child: _loginPageUI(),
),
//_loginPageUI(),
),
);
}
@@ -172,12 +171,12 @@ class _LoginPageState extends State<LoginPage> {
onPressed: () {
if (_formKey.currentState!.validate()) {
//Login User
_auth.loginWithEmailAndPassword(_email!, _password!);
_auth.login(_email!, _password!);
}
},
color: Colors.blue,
child: Text(
'Login',
'Login'.toUpperCase(),
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
+124
View File
@@ -0,0 +1,124 @@
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/users.dart';
import 'package:provider/provider.dart';
class ProfilePage extends StatelessWidget {
//
final double _deviceHeight;
final double _deviceWidth;
late MessagingProviders _auth;
ProfilePage(this._deviceHeight, this._deviceWidth, {super.key});
@override
Widget build(BuildContext context) {
return Container(
height: _deviceHeight,
width: _deviceWidth,
child: ChangeNotifierProvider<MessagingProviders>.value(
value: MessagingProviders.instance,
child: _profilePageUI(),
),
);
}
Widget _profilePageUI() {
return Builder(builder: (BuildContext _context) {
_auth = Provider.of<MessagingProviders>(_context);
return StreamBuilder<Users>(
stream: MessagingProviders.instance.getUser(_auth.user!.$id),
builder: (_, _snapshot) {
var _userData = _snapshot.data;
return _snapshot.hasData
? Align(
child: SizedBox(
height: _deviceHeight * 0.50,
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_userImageWidget(_userData!.image),
_userNameWidget(_userData.name),
_userEmailWidget(_userData.email),
_logoutButton(),
],
),
),
)
: SpinKitWanderingCubes(
color: Colors.blue,
size: 50.0,
);
});
});
}
Widget _userImageWidget(String _image) {
double _imageRadius = _deviceHeight * 0.20;
return Container(
height: _imageRadius,
width: _imageRadius,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(_imageRadius),
image: DecorationImage(
fit: BoxFit.fill,
image: NetworkImage(_image),
),
),
);
}
Widget _userNameWidget(String _name) {
return Container(
height: _deviceHeight * 0.05,
width: _deviceWidth,
child: Text(
_name,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white,
fontSize: 30,
),
),
);
}
Widget _userEmailWidget(String _email) {
return Container(
height: _deviceHeight * 0.03,
width: _deviceWidth,
child: Text(
_email,
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.white24,
fontSize: 15,
),
),
);
}
Widget _logoutButton() {
return Container(
height: _deviceHeight * 0.06,
width: _deviceWidth * 0.80,
child: MaterialButton(
onPressed: () {
_auth.logout();
},
color: Colors.red,
child: Text(
"Logout".toUpperCase(),
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
),
),
),
);
}
}
+95
View File
@@ -0,0 +1,95 @@
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),
),
)
],
);
}
}
+4 -5
View File
@@ -224,7 +224,6 @@ class _RegisterationPageState extends State<RegisterationPage> {
width: _deviceWidth,
child: MaterialButton(
onPressed: () {
String name = _name!;
if (_image == null) {
SnackBarService.instance
.showSnackBarError("Please Select Image");
@@ -234,12 +233,12 @@ class _RegisterationPageState extends State<RegisterationPage> {
_auth.createUser(
_email!,
_password!,
name,
_name!,
(String _uid) async {
var _url = await _auth.uploadUserImage(_image, _uid);
_auth.createUserInDB(_uid, _email!, name, _url);
_auth.createUserInDB(_uid, _email!, _name!, _url);
if (_auth.status == AuthStatus.authenticated) {
_auth.loginWithEmailAndPassword(_email!, _password!);
_auth.login(_email!, _password!);
}
//
},
@@ -248,7 +247,7 @@ class _RegisterationPageState extends State<RegisterationPage> {
},
color: Colors.blue,
child: Text(
'Register',
'Register'.toUpperCase(),
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
+1 -1
View File
@@ -23,7 +23,7 @@ class NavigationService {
}
// Manually Call Function
goBack() {
void goBack() {
return navigatorKey!.currentState!.pop();
}
}
+3 -26
View File
@@ -1,54 +1,31 @@
name: messaging_app
description: "A new Flutter project."
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: ^3.6.0
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
appwrite: 13.1.1
timeago: ^3.7.0
flutter_spinkit: ^5.2.1
provider: ^6.1.2
image_picker: ^1.1.2
timeago: ^3.7.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^5.0.0
flutter:
uses-material-design: true
# To add assets to your application, add an assets section, like this: