Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions lib/chat_page.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';

class ChatPage extends StatefulWidget {
final String otherUserId;
final String otherUserName;

const ChatPage(
{super.key, required this.otherUserId, required this.otherUserName});

@override
State<ChatPage> createState() => _ChatPageState();
}

class _ChatPageState extends State<ChatPage> {
final TextEditingController _controller = TextEditingController();
final currentUser = FirebaseAuth.instance.currentUser;

Stream<QuerySnapshot> getMessagesStream() {
return FirebaseFirestore.instance
.collection('messages')
.where('chatId',
isEqualTo: _getChatId(currentUser!.uid, widget.otherUserId))
.orderBy('timestamp')
.snapshots();
}

String _getChatId(String uid1, String uid2) {
return uid1.hashCode <= uid2.hashCode ? '$uid1\_$uid2' : '$uid2\_$uid1';
}

void sendMessage() async {
final text = _controller.text.trim();
if (text.isEmpty) return;

final chatId = _getChatId(currentUser!.uid, widget.otherUserId);

await FirebaseFirestore.instance.collection('messages').add({
'chatId': chatId,
'senderId': currentUser!.uid,
'receiverId': widget.otherUserId,
'text': text,
'timestamp': DateTime.now().toUtc().toIso8601String(),
});

_controller.clear();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text("Chat with ${widget.otherUserName}")),
body: Column(
children: [
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: getMessagesStream(),
builder: (context, snapshot) {
if (!snapshot.hasData)
return Center(child: CircularProgressIndicator());

final messages = snapshot.data!.docs;

return ListView(
children: messages.map((doc) {
final data = doc.data() as Map<String, dynamic>;
final isMine = data['senderId'] == currentUser!.uid;
return ListTile(
title: Align(
alignment: isMine
? Alignment.centerRight
: Alignment.centerLeft,
child: Container(
padding: EdgeInsets.all(10),
color: isMine ? Colors.blue[100] : Colors.grey[300],
child: Text(data['text']),
),
),
);
}).toList(),
);
},
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: Row(
children: [
Expanded(child: TextField(controller: _controller)),
IconButton(onPressed: sendMessage, icon: Icon(Icons.send)),
],
),
),
],
),
);
}
}
96 changes: 50 additions & 46 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,64 +7,68 @@ void main() {
}

class Root extends StatelessWidget {
const Root({ super.key });
const Root({super.key});

@override
Widget build(BuildContext ctx) {
return MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: App()
);
}
return MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: App());
}
}

class App extends StatefulWidget {
const App({ super.key });
@override
State<App> createState() => AppState();
const App({super.key});

@override
State<App> createState() => AppState();
}

class AppState extends State<App> {
int pageIdx = 2;
List<Widget Function()> pageBuilders = [
() => Center(child: Text("Settings Placeholder")),
() => ProfileView(Profile("My Profile", DateTime.now(), "My Bio", ["My1", "My2", "My3"])),
int pageIdx = 2;
List<Widget Function()> pageBuilders = [
() => Center(child: Text("Settings Placeholder")),
() => ProfileView(
Profile("My Profile", DateTime.now(), "My Bio", ["My1", "My2", "My3"])),
//() => ProfileView(Profile("Example Profile", DateTime.now(), "Example Bio", ["Ex1", "Ex2", "Ex3", "Ex4"])),
() => ExplorePage([
Profile("Example Profile #2", DateTime.now(), "Example Bio #2", ["Interest #1", "Interest #2", "Interest #3", "Interest #4"]),
Profile("Example Profile #1", DateTime.now(), "Example Bio", ["Ex1", "Ex2", "Ex3", "Ex4"]),
]),
() => MatchesPage([
Profile("Match 1", DateTime.now(), "Example Bio", ["I1", "I2"]),
Profile("Match 2", DateTime.now(), "Example Bio", ["I1", "I2"]),
Profile("Match 3", DateTime.now(), "Example Bio", ["I1", "I2"]),
])
Profile("Example Profile #2", DateTime.now(), "Example Bio #2",
["Interest #1", "Interest #2", "Interest #3", "Interest #4"]),
Profile("Example Profile #1", DateTime.now(), "Example Bio",
["Ex1", "Ex2", "Ex3", "Ex4"]),
]),
() => MatchesPage([
Profile("Match 1", DateTime.now(), "Example Bio", ["I1", "I2"]),
Profile("Match 2", DateTime.now(), "Example Bio", ["I1", "I2"]),
Profile("Match 3", DateTime.now(), "Example Bio", ["I1", "I2"]),
])
];

@override
Widget build(BuildContext context) {
return Scaffold(
Widget build(BuildContext context) {
return Scaffold(
body: pageBuilders[pageIdx](),
bottomNavigationBar: BottomNavigationBar(
bottomNavigationBar: BottomNavigationBar(
// for unknown reasons the navbar becomes (mostly) invisible when in "shifting" mode
type: BottomNavigationBarType.fixed,
currentIndex: pageIdx,
onTap: (int idx) {
setState(() {
pageIdx = idx;
});
},
items: [
BottomNavigationBarItem(icon: Icon(Icons.settings), label: "Settings"),
currentIndex: pageIdx,
onTap: (int idx) {
setState(() {
pageIdx = idx;
});
},
items: [
BottomNavigationBarItem(
icon: Icon(Icons.settings), label: "Settings"),
BottomNavigationBarItem(icon: Icon(Icons.person), label: "Profile"),
BottomNavigationBarItem(icon: Icon(Icons.star), label: "Explore"),
BottomNavigationBarItem(icon: Icon(Icons.heart_broken), label: "Matches"),
],
),
);
}
BottomNavigationBarItem(icon: Icon(Icons.star), label: "Explore"),
BottomNavigationBarItem(
icon: Icon(Icons.heart_broken), label: "Matches"),
],
),
);
}
}


75 changes: 38 additions & 37 deletions lib/matches.dart
Original file line number Diff line number Diff line change
@@ -1,41 +1,42 @@

import 'package:flutter/material.dart';
import 'profile.dart';


/*class Match {
Profile profile;
//Profile? profile; // We may not have their profile from the server yet
}*/

import 'profile.dart'; // Assuming Profile is already defined elsewhere
import 'chat_page.dart'; // <- We will create this next

class MatchesPage extends StatelessWidget {

final List<Profile> profiles;
MatchesPage(this.profiles, { super.key });

@override
Widget build(BuildContext context) {

Widget profileEntryBuilder(Profile profile) {
return ListTile(
title: Text(profile.name),
subtitle: Text("[latest message...]"),
leading: SizedBox.fromSize(
size: Size(40.0, 40.0),
child: Placeholder()
),
trailing: Icon(Icons.arrow_right)
);
}

return ListView.separated(
itemCount: profiles.length,
itemBuilder: (context, i) => profileEntryBuilder(profiles[i]),
separatorBuilder: (context, i) => Divider(color: Colors.blueGrey[700], thickness: 2, indent: 10, endIndent: 10)
);
}
final List<Profile> profiles;
const MatchesPage(this.profiles, {super.key});

@override
Widget build(BuildContext context) {
Widget profileEntryBuilder(Profile profile) {
return ListTile(
title: Text(profile.name),
subtitle: Text("[latest message...]"),
leading: SizedBox.fromSize(
size: const Size(40.0, 40.0),
child: const Placeholder(),
),
trailing: const Icon(Icons.arrow_right),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ChatPage(profile: profile),
),
);
},
);
}

return ListView.separated(
itemCount: profiles.length,
itemBuilder: (context, i) => profileEntryBuilder(profiles[i]),
separatorBuilder: (context, i) => Divider(
color: Colors.blueGrey[700],
thickness: 2,
indent: 10,
endIndent: 10,
),
);
}
}



31 changes: 31 additions & 0 deletions lib/message.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class Message {
final String senderId;
final String receiverId;
final String text;
final DateTime timestamp;

Message({
required this.senderId,
required this.receiverId,
required this.text,
required this.timestamp,
});

Map<String, dynamic> toMap() {
return {
'senderId': senderId,
'receiverId': receiverId,
'text': text,
'timestamp': timestamp.toIso8601String(),
};
}

factory Message.fromMap(Map<String, dynamic> map) {
return Message(
senderId: map['senderId'],
receiverId: map['receiverId'],
text: map['text'],
timestamp: DateTime.parse(map['timestamp']),
);
}
}
2 changes: 2 additions & 0 deletions macos/Flutter/GeneratedPluginRegistrant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
import FlutterMacOS
import Foundation

import cloud_firestore
import firebase_auth
import firebase_core

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FLTFirebaseFirestorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseFirestorePlugin"))
FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin"))
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
}
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ dependencies:
firebase_auth: ^5.5.1
logger: ^2.5.0
firebase_core: ^3.12.1
cloud_firestore: ^5.6.7

dev_dependencies:
flutter_test:
Expand Down
3 changes: 3 additions & 0 deletions windows/flutter/generated_plugin_registrant.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@

#include "generated_plugin_registrant.h"

#include <cloud_firestore/cloud_firestore_plugin_c_api.h>
#include <firebase_auth/firebase_auth_plugin_c_api.h>
#include <firebase_core/firebase_core_plugin_c_api.h>

void RegisterPlugins(flutter::PluginRegistry* registry) {
CloudFirestorePluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("CloudFirestorePluginCApi"));
FirebaseAuthPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi"));
FirebaseCorePluginCApiRegisterWithRegistrar(
Expand Down
1 change: 1 addition & 0 deletions windows/flutter/generated_plugins.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#

list(APPEND FLUTTER_PLUGIN_LIST
cloud_firestore
firebase_auth
firebase_core
)
Expand Down