What's in the box
ZinklyChat is a standalone Flutter app — chat, groups, calls, stories, VIP and a marketplace-style shop — built to talk to a ZinklySocial-compatible backend over REST and WebSockets. The distribution zip ships clean: no live server URL, no real Firebase project, no signing key. You provide those.
Stack
Flutter, Riverpod, GoRouter, Dio, Firebase Cloud Messaging.
Backend
Any ZinklySocial-compatible REST + WebSocket API (see API reference).
Ownership
No Zinkly credentials are baked in — everything below is yours to fill in.
Requirements
Flutter SDK
Flutter 3.x / Dart ≥3.2. Run flutter doctor to confirm your toolchain.
Android Studio
For the Android SDK, an emulator, and building the .aab/.apk.
Xcode (macOS only)
Required to build and sign the iOS app. An Apple Developer account is needed to publish.
A Firebase project
Free tier is enough — used only for push notifications (FCM).
Installation
- Unzip the projectExtract
ZinklyChat.zipanywhere on your machine. - Install dependencies
cd ZinklyChat flutter pub get - Configure your backend URL and Firebase — see Backend URL & app info and Firebase & notifications below. The app runs without Firebase configured, but pushes won't work until you do.
- Run it
flutter run
The zip has no .git history and isn't tied to any Zinkly account — treat it as your own project from the moment you unzip it.
Backend URL & app info
Everything the app needs to know about your install lives in one file: lib/constants/api_constants.dart
class ApiConstants {
static const String baseUrl = 'https://YourZinklyMediaURL/api/v1';
static const String wsHost = 'YourZinklyMediaURL';
static const int wsPort = 443;
static const String reverbKey = 'YourReverbAppKey';
// ...
}
| Constant | Used for |
|---|---|
baseUrl | All REST API calls (auth, conversations, messages, calls, VIP…) |
wsHost | WebSocket connection for realtime messages, typing, presence |
webBaseUrl | Derived automatically from wsHost — used only by the in-app VIP checkout WebView |
reverbKey | Your backend's Reverb/Pusher-protocol broadcasting app key — see below |
Your backend runs Laravel Reverb (a self-hosted, Pusher-protocol-compatible WebSocket server), which has its own app key —
found in Laravel's config/broadcasting.php under reverb.key, or your BROADCAST_REVERB_APP_KEY/PUSHER_APP_KEY
env var. Put that value in reverbKey above. Without it, the realtime connection won't authenticate — the app still works over
plain REST, but new messages/typing/presence won't update live until the user manually refreshes.
App identity
Also in the same file, the AppConstants class:
class AppConstants {
static const String appName = 'ZinklyChat';
static const String appScheme = 'zinklymessenger'; // deep-link / URL scheme
}
Update appName if you're renaming the app. If you change appScheme, also update the matching scheme in
android/app/src/main/AndroidManifest.xml and ios/Runner/Info.plist so deep links keep working.
Firebase setup
ZinklyChat ships with placeholder Firebase config files so the project still builds out of the box. Replace them with your own before you ship, or pushes silently won't arrive (the app catches the failure and keeps working otherwise).
- Create a Firebase projectGo to console.firebase.google.com → Add project.
- Register your Android appPackage name:
zinkly.chat(or whatever you set it to — see Branding). Downloadgoogle-services.jsonand replace:
android/app/google-services.json - Register your iOS appBundle ID:
com.zinkly.zinklyMessenger(or your own). DownloadGoogleService-Info.plistand replace:
ios/Runner/GoogleService-Info.plist - Or automate both at once
This detects your Firebase project and writes both platform config files for you.dart pub global activate flutterfire_cli flutterfire configure - Enable Cloud MessagingIn the Firebase console, under Project settings → Cloud Messaging, make sure the API is enabled. No extra server key setup is needed on the client side.
How it's wired in code
- lib/main.dart calls
Firebase.initializeApp()at startup, wrapped in a try/catch — a missing/placeholder config won't crash the app. - lib/services/fcm_service.dart requests notification permission, gets the device token, and listens for foreground/background/tapped messages.
- The device token is sent to your backend via
POST {baseUrl}/device-tokens(see API reference) — your backend is what actually sends the pushes through Firebase's server SDK when a new message/call/notification happens.
Your backend also needs its own Firebase server credentials (a service-account key) to send pushes — that's separate from the client config above and is configured on the ZinklyMedia/backend side, not in this Flutter app.
API reference
Every endpoint the app calls is declared in lib/constants/api_constants.dart as a relative
path appended to baseUrl. Your backend must implement these routes for the matching feature to work.
Core groups
| Area | Examples |
|---|---|
| Auth | /auth/login, /auth/register, /auth/logout |
| Conversations & messages | /conversations, /conversations/{id}/messages |
| Groups | /groups, /conversations/{id}/members |
| Calls | /calls, /conversations/{id}/call/token |
| Stories | /stories, /stories/{id}/view |
| Social | /users/{username}/follow, /blocked-users |
| VIP / payments | /vip/packages, /vip/{id}/paypal/create |
| Push | /device-tokens |
| Support | /support/tickets, /support/categories |
Realtime events (new messages, typing, presence) arrive over the WebSocket at wsHost, authenticated via
authEndpoint (/api/v1/broadcasting/auth) — a Laravel Echo/Pusher-protocol style broadcast auth handshake.
The full, authoritative list of ~70 endpoints is in the ApiConstants class itself — open
lib/constants/api_constants.dart and read top to bottom; every route the app can call is right there in one place.
Branding & icons
App icon
Drop your artwork into assets/icons/app_icon.png, then run:
flutter pub run flutter_launcher_icons
Splash screen
Replace assets/icons/splash_logo.png, then run:
dart run flutter_native_splash:create
Display name
AppConstants.appName, plus android:label in AndroidManifest.xml
and CFBundleDisplayName in Info.plist.
Bundle / package ID
applicationId in android/app/build.gradle.kts and
PRODUCT_BUNDLE_IDENTIFIER in the Xcode project.
Updating ZinklyChat
This distribution is a plain zip, not a git remote, so "updating" means dropping a newer ZinklyChat release on top of your customized copy without losing your own settings.
- Before updatingNote down (or diff) the files you customized: api_constants.dart, both Firebase config files, key.properties, app icon/splash assets, and any bundle-ID changes.
- Put the project under version controlIf you haven't already,
git initand commit your customized copy first — this makes re-applying your changes after an update a normalgit diff/merge instead of manual copy-paste. - Drop in the new releaseCopy the new zip's files over yours, except the files you customized in step 1.
- Re-run setup
(the second command regenerates theflutter pub get dart run build_runner build --delete-conflicting-outputs*.g.dart/*.freezed.dartfiles if any models changed). - Re-testLaunch the app and confirm your backend URL and push notifications still work.
Building for release
Android
- Generate an upload keystore (once — keep this file and its password forever, every future update must be signed with it):
Put it at android/app/keystore/upload-keystore.jks.keytool -genkey -v -keystore upload-keystore.jks \ -keyalg RSA -keysize 2048 -validity 10000 -alias upload - Point the build at itCopy android/key.properties.example to android/key.properties and fill in the real
storePassword,keyPassword,keyAlias,storeFile. - Build the App Bundle (what Google Play wants):
Output: build/app/outputs/bundle/release/app-release.aabflutter build appbundle --release
iOS
- Open the workspace
open ios/Runner.xcworkspace - Set your teamIn Xcode → Runner target → Signing & Capabilities, select your Apple Developer Team. No team is pre-configured in the zip.
- Build & archive
Output: build/ios/ipa/*.ipa, ready to upload via Transporter or Xcode Organizer.flutter build ipa --release
Publishing to the stores
Once you have a signed .aab and/or .ipa from the previous section, here's the store-side checklist.
Google Play Store
- Create a developer accountOne-time $25 fee at play.google.com/console.
- Create the app listingPlay Console → Create app — name, default language, app/game, free/paid.
- Fill in store presenceScreenshots, feature graphic, short/full description, app icon, privacy policy URL, content rating questionnaire, target audience, data safety form.
- Upload your buildProduction (or an internal/closed testing track first) → Create new release → upload the
.aabfrom build/app/outputs/bundle/release/. - Submit for reviewGoogle's review typically takes anywhere from a few hours to a few days for a first submission.
Test on the internal testing track first — it skips full review and lets you sanity-check the signed build on a real device before going public.
Apple App Store
- Enroll in the Apple Developer Program$99/year at developer.apple.com/programs.
- Register the appApp Store Connect → My Apps → + — bundle ID must match
PRODUCT_BUNDLE_IDENTIFIERin Xcode. - Upload the buildUse Xcode Organizer or the standalone Transporter app to upload the
.ipafrom build/ios/ipa/. - Fill in the listingScreenshots (per device size), description, keywords, privacy policy URL, App Privacy details (what data the app collects), age rating.
- TestFlight first (recommended)Once uploaded, the build is available to TestFlight testers immediately — no review needed for internal testers.
- Submit for reviewApple's review is typically 24–48 hours. Make sure the review notes include a working test account if login is required.
Apple reviewers will test push notifications, calls, and account creation end-to-end — make sure your backend URL in api_constants.dart points at a live, reachable server before submitting, not a placeholder or local address.
Troubleshooting
| Symptom | Likely cause |
|---|---|
| App builds but can't log in / load chats | baseUrl/wsHost in api_constants.dart still points at a placeholder or unreachable host |
| Push notifications never arrive | Placeholder Firebase config files still in place, or your backend lacks its own Firebase server credentials |
| Android release build fails to sign | android/key.properties missing or pointing at the wrong keystore path |
| Play Store rejects the upload | Uploaded a debug-signed .apk instead of a release-signed .aab |
| Xcode build fails to sign | No Apple Developer Team selected under Signing & Capabilities |