Note
Update: Push notifications are finally implemented! 🥳 Get notified instantly when someone reaches out through your website.
A simple real-time messaging app built with Flutter, Firebase (Auth, Cloud Firestore) and Supabase (Edge Functions, Database) - designed for a single purpose: let people reach you directly from your personal website.
No need to share social media links. No need for third-party contact forms.
Just embed Talkest on your website, and anyone with a Google account can start a conversation with you instantly.
Why does this exist?
I built Talkest because I wanted a simple "get in touch" solution for my portfolio website. Instead of redirecting visitors to social media, they can just chat with me right there, powered by a Flutter Web widget embedded directly into the page.
- Real-time messaging — Powered by Cloud Firestore with live message streaming
- Google Sign-In — One-tap authentication, no extra account needed
- Light & Dark theme — With system theme detection and manual toggle
- QR Code profile — Each user gets a personal QR code for quick contact sharing
- QR Scanner — Scan someone's QR code to start a chat instantly
- Start chat by email — Find and message any registered user by their email address
- Editable display name — Customize how your name appears to others
- Embeddable chat widget — Deploy the Flutter Web build and embed it on any website via iframe
- Native mobile app — Install the Android app to monitor and reply to incoming messages on the go
- Push Notifications — High-priority alerts with heads-up display, custom sounds, and auto-dismissal when chat is read.
- Deep Linking — Clicking a notification takes you directly to the relevant chat room.
| Platform | Status | Link |
|---|---|---|
| Android | ✅ Available | Github release |
| Web | ✅ Available | khip01.github.io/talkest |
| Embedded mode | ✅ Available | Used on my portfolio |
| iOS | ❌ Not yet | No Mac device available for development |
| Login | Chat List | Messaging |
![]() | ![]() | ![]() |
| Embed mode — Landing page on portfolio website |
|---|
![]() |
| Embed mode — Chat view on portfolio website |
|---|
![]() |
| Category | Technology |
|---|---|
| Framework | Flutter (Dart) |
| Backend (Primary) | Firebase (Auth, Cloud Firestore) |
| Backend (Functions) | Supabase (Edge Functions, Database) |
| Push Notifications | Firebase Cloud Messaging (FCM v1) |
| Authentication | Google Sign-In |
| State Management | BLoC + Provider |
| Routing | GoRouter |
| Deployment | GitHub Pages (Web), APK (Android) |
- Flutter SDK
- Firebase project with Authentication and Cloud Firestore enabled
- Google OAuth 2.0 Client ID (for Flutter Web only)
Collections and documents are created automatically when the app runs for the first time — no manual setup needed. Below is the database structure for reference:
├── app_users (collection) │ └── {uid} (document) │ ├── name: string │ ├── displayName: string │ ├── email: string │ ├── photoUrl: string │ ├── provider: string │ ├── createdAt: timestamp │ ├── updatedAt: timestamp │ └── lastLoginAt: timestamp │ └── chats (collection) └── {chatId} (document) ├── type: string ("direct") ├── participants: array [uid1, uid2] ├── createdAt: timestamp ├── updatedAt: timestamp ├── unreadCount: map { uid1: number, uid2: number } ├── lastMessage: map │ ├── id: string │ ├── senderId: string │ ├── text: string │ ├── type: string │ ├── createdAt: timestamp │ └── isDeleted: boolean │ └── 📁 messages (subcollection) └── {messageId} (document) ├── id: string ├── chatId: string ├── senderId: string ├── type: string ├── text: string ├── createdAt: timestamp ├── editedAt: timestamp (optional) ├── isDeleted: boolean ├── replyToId: string (optional) ├── replyToSenderId: string (optional) ├── replyToSenderName: string (optional) └── replyToText: string (optional) Security rules are defined in firestore.rules.
Important
The included rules are stricter than the default test-mode rules. They enforce that:
- Users can only write to their own profile
- Only chat participants can read/write chats and messages
- Messages can only be created (no editing or deleting from client)
Review and adjust the rules in firestore.rules to fit your needs before deploying.
This project requires a composite index for querying chats. The index configuration is defined in firestore.indexes.json.
| Collection | Fields | Query Scope |
|---|---|---|
chats | participants (Array) + updatedAt (Descending) | Collection |
Tip
If you skip deploying indexes, Firestore will show an error with a direct link to create the required index when the app first runs a query that needs it.
Deploy both rules and indexes at once:
firebase deploy --only firestore --project YOUR_PROJECT_IDOr deploy them individually if needed:
firebase deploy --only firestore:rules --project YOUR_PROJECT_ID firebase deploy --only firestore:indexes --project YOUR_PROJECT_IDTip
If you skip this step, Firestore will show an error with a direct link to create the required index when the app first runs a query that needs it.
-
Via Firebase Console (Recommended):
- Open Firebase Console
- Select your project → Project Settings
- Add a Web app (if not already added)
- The Client ID will be auto-generated in Google Cloud Console
-
Manual Setup:
- Go to Google Cloud Console
- Navigate to APIs & Services → Credentials
- Create OAuth client ID → Select Web application
- Configure:
- Authorized JavaScript origins:
http://localhosthttp://localhost:5000https://your-domain.firebaseapp.com(production)
- Authorized redirect URIs:
https://your-domain.firebaseapp.com/__/auth/handler
- Authorized JavaScript origins:
- Copy the generated Client ID
The profiles table in Supabase is used to sync FCM tokens for notification delivery:
create table profiles ( id uuid references auth.users on delete cascade, email text unique, fcm_token text, updated_at timestamp with time zone, primary key (id) );Talkest uses Supabase Edge Functions to trigger FCM v1.
- Service Account: Place your Firebase Service Account JSON in the Edge Function environment.
- Environment Variables: Set up the following in your Supabase project:
FIREBASE_SERVICE_ACCOUNT: Your JSON key.
- Deployment:
supabase functions deploy send-notification
Web (Development):
flutter run -d chrome \ --dart-define=GOOGLE_WEB_CLIENT_ID=YOUR_CLIENT_ID.apps.googleusercontent.com \ --dart-define=SUPABASE_ANON_KEY=YOUR_SUPABASE_ANON_KEYMobile (Android/iOS):
flutter run -d <device-id> \ --dart-define=SUPABASE_ANON_KEY=YOUR_SUPABASE_KEYWeb (Release)
flutter build web --release \ --dart-define=GOOGLE_WEB_CLIENT_ID=YOUR_WEB_ID.apps.googleusercontent.com \ --dart-define=SUPABASE_ANON_KEY=YOUR_SUPABASE_KEYImportant
Web builds require the Google Web Client ID for authentication.
Mobile (Release)
flutter build --release \ --dart-define=SUPABASE_ANON_KEY=YOUR_SUPABASE_KEYImportant
Mobile builds now require the SUPABASE_ANON_KEY to sync FCM tokens and send notifications.
Talkest supports an embedded chat widget mode, designed to be loaded inside an <iframe> on any website. This allows visitors to chat with a specific user directly from your page.
URL format:
https://your-talkest-deployment.com/?embed=1&targetUid=FIREBASE_USER_UID Example iframe usage:
<iframe src="https://khip01.github.io/talkest/?embed=1&targetUid=YOUR_FIREBASE_UID" width="400" height="600" style="border: none; border-radius: 12px;" allow="clipboard-read; clipboard-write" ></iframe>Parameters:
| Parameter | Required | Description |
|---|---|---|
embed | Yes | Set to 1 to activate embedded mode |
targetUid | Yes | The Firebase UID of the user to chat with |
Tip
You can find your Firebase UID in the Firebase Console → Authentication → Users tab.
In embed mode, the app will:
- Show a landing page with a sign-in prompt for unauthenticated visitors
- Automatically open a direct chat with the target user after sign-in
- Hide navigation elements like the FAB and profile access for a clean widget experience
Made with 🤍 by Khip01




