CaoIM: Self-Hosted Chat on a Single Flutter Codebase
CaoIM is an instant messaging suite I built for people who want to deploy chat themselves. Server side is Java, client side is Flutter. Android, iOS, Windows, macOS and Web share the same client code.
What the client looks like
On desktop it opens as three columns. Conversations on the left, chat in the middle, and when viewing a group the profile is on the right. On mobile, there are three bottom tabs: Messages, Contacts, Me. The browser shows the same interface, not a separate web version.
Supported message types include text, emoji, images, camera capture, voice, files, and short video. Messages can be quoted, forwarded, recalled, and favorited. Conversations support pinning, do-not-disturb, and unread counts. When you stay in a chat, that conversation stops accumulating unread numbers.
In contacts, you can handle friend requests, edit nicknames, block users, search users, or scan QR codes to add friends or join groups. Group info includes name, announcement, admins, muting, join verification, and group nickname. Member avatars are combined together.
In the top right of a private chat you can start voice or video, currently only one-on-one. There's a ringtone; video supports local preview and switching camera. Group calls are not implemented. The example only sets public STUN; LAN usually works, but on public networks you often need to add TURN yourself.
Dark mode is also supported. When the app is in the background, new message notifications can pop up.
Server components
The repositories are split so each can be viewed separately:
im-server: friends, groups, history messages, read status, offline sync, WebSocket, and one-on-one call signaling. Port 8080app-server: registration, login, profiles, file upload. Port 8081im-admin: operations backend, Vue 3. Can enable/disable users, dissolve groups, search messages, manage sensitive words and permissionspush-server: optional. Sends system notifications when the other party is offlineim-sdk-flutter: long connection, local database, conversations and signaling, no UIflutter-demo: the client described above
Text goes over the long connection. Images and files upload to app-server first, and the message only stores the URL. Storage can be a local directory or S3-compatible object storage.
After login you get two tokens. token is used to access app-server, imToken is used to connect to im-server. The backend calls the message service's admin interface through an internal token, without modifying business tables directly.
If the other party is online, messages push immediately; if offline, they wait until the user comes online to fetch. Read receipts return to the sender; in groups, you see the number of readers.
How to run
Requirements: JDK 17, Maven 3.6, MySQL 5.7 (utf8mb4). Flutter 3, Dart 3.11 or above. The admin page needs Node.js 18.
mysql -u root -p < im-server/sql/schema.sql
mysql -u root -p < app-server/sql/schema.sql
cd im-server && mvn clean install -DskipTests
cd im-server/im-boot && mvn spring-boot:run
cd app-server && mvn spring-boot:run
cd flutter-demo && flutter pub get && flutter run
In Windows PowerShell, don't use &&; run them in separate directories.
Default admin account is admin / admin123, page at http://localhost:5173. Before going live, change this password and also replace the JWT secret. For phone debugging, set the server address to your computer's LAN IP; localhost on a phone points to the phone itself.
Voice-to-text, group calls, payments, and mini programs are not in the current code. One-on-one call media doesn't go through the server; the server only forwards signaling.
Code is in the cao-im organization on GitHub and Gitee:
Other repositories can be reached from the table in the README. It's free for learning and personal use, no need to contact me. Commercial use is also free, but you need to obtain authorization first.