chat_app_flutter
A Flutter package that embeds a real-time chat UI into any existing app. Connects to the chat-app Spring Boot backend over WebSocket/STOMP.
This package is one half of an open-source chat system.
- Flutter package (this) — drop-in chat UI for your Flutter app
- Spring Boot backend — github.com/kamilsahin/chat-app — REST + WebSocket/STOMP server with MongoDB, JWT auth, FCM push notifications, and an Internal API for room management
Both projects are open-source and designed to work together. You can self-host the backend and plug this package into any Flutter app.
Features
Messaging
- Real-time messaging via WebSocket (STOMP over SockJS)
- Own messages right-aligned, others left-aligned
- Deleted message placeholder ("Mesaj silindi")
- Pinned message support
Room List
- Last message preview and timestamp
- Unread count badge (green for normal, grey for muted)
- Pull-to-refresh
- Rooms sorted by latest activity
Reply
- Reply to any message with a preview bar above the input field
- Replied message shown as a quoted block inside the bubble
- Tap the quoted block to scroll to and highlight the original message
Emoji Reactions
- Long-press a bubble to open emoji picker (6 emojis: 👍 ❤️ 😂 😮 😢 🔥)
- Already-selected emoji highlighted with a green ring
- Reactions shown as tappable chips at the bottom-right of the bubble
- Own reaction highlighted with a green border on the chip
Photo Sharing
- Send photos from gallery or camera via the image button in the input bar
- Images displayed as inline thumbnails inside the bubble
- Tap to open full-screen viewer with pinch-to-zoom (
InteractiveViewer)
Message Actions (long-press)
- Copy — copies text content to clipboard
- Reply — opens the reply bar pre-filled with the original message
- Edit — opens an edit dialog (own text messages only); broadcasts update to all participants
- Delete — soft-deletes with confirmation; replaced by "Bu mesaj silindi" placeholder for everyone
Mute / Unmute
- Long-press a room tile to open the mute sheet
- Duration options: 1 hour, 8 hours, 1 week, permanent
- Muted rooms show a bell-off icon next to the name
- Optimistic local state update — no re-fetch needed
Unread Count
- Badge cleared automatically when entering or leaving a room
- Own sent messages never increment the unread counter
- Messages received while a room is open do not increment the counter
Push Notifications (FCM)
- Opt-in: pass an
fcmTokeninChatConfigand the package registers it automatically - Your app handles Firebase initialization and token retrieval (
firebase_messaging); the package has no Firebase dependency - Backend sends FCM push when a new message arrives (text or photo)
- Skips notification for the sender and for muted members (respects mute duration)
- Stale/unregistered tokens are cleaned up automatically
- Enable on the backend: set
chat.notifications.enabled=trueandchat.notifications.fcm.credentials-fileto your service-account JSON path
Other
- Auto-scroll to latest message on send
- Typing indicators in room app bar
- JWT-based auth — your app issues the token, this package just uses it
- Easily embedded as a path or git dependency
Getting started
1. Backend
Clone and run the chat-app Spring Boot backend. Copy .env.example to .env and set your values:
CHAT_JWT_SECRET=SecretKeyToGenJWTs
CHAT_INTERNAL_SECRET=dev-internal-secret
Start with Docker Compose:
docker-compose up
The server runs on http://localhost:8081 by default.
2. Create a room
Rooms are managed via the Internal API (backend-to-backend). Create a direct room with:
curl -X POST http://localhost:8081/internal/rooms \
-H "Content-Type: application/json" \
-H "X-Internal-Secret: dev-internal-secret" \
-d '{
"type": "DIRECT",
"memberIds": ["user1", "user2"],
"name": "Test Room"
}'
3. Generate a test JWT
Go to jwt.io and fill in:
| Field | Value |
|---|---|
| Algorithm | HS256 |
| Payload | { "sub": "user1" } |
| Secret | dev-secret-key-for-chat-app-minimum-32chars |
Copy the encoded token from the left panel. Generate a second token with "sub": "user2" for the other user.
The
subclaim becomes the user's ID inside the chat system.
4. Add the dependency
# pubspec.yaml
dependencies:
chat_app_flutter:
path: ../chat-app-flutter # or use a git: reference
Usage
Wrap the chat UI with ChatApp.initialize() once after login, passing the server URL and the JWT token issued by your app:
import 'package:chat_app_flutter/chat_app_flutter.dart';
// After login — wrap the subtree that needs chat
Widget chatWidget = ChatApp.initialize(
config: ChatConfig(
serverUrl: 'http://10.0.2.2:8081', // use your server URL
jwtToken: yourJwtToken,
),
child: const RoomListScreen(),
);
Navigate to RoomScreen for a specific room:
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => UncontrolledProviderScope(
container: ProviderScope.containerOf(context),
child: RoomScreen(roomId: roomId),
),
),
);
UncontrolledProviderScopeis required so the new route inherits the nested provider scope created byChatApp.initialize().
Android emulator
When testing on an Android emulator, use 10.0.2.2 instead of localhost to
reach your host machine:
serverUrl: 'http://10.0.2.2:8081'
Example app
See the example/ directory for a full working demo with a login screen that
accepts a server URL and JWT token.
cd example
flutter run
Architecture
| Layer | Technology |
|---|---|
| State management | Riverpod (nested ProviderScope) |
| HTTP client | Dio |
| WebSocket | STOMP over SockJS (stomp_dart_client) |
| Backend | Spring Boot 3 + MongoDB |