🆕 Design-Driven Firebase in Flutter: Building Custom In-App Messaging with SwiftUI & Compose
In-app messaging plays a critical role in how users interact with modern apps — from promoting offers to driving engagement and retention.
Published on: April 11, 2025 · ⏱️ 10 min read
Android Ain’t Easy: Crafting Custom FIAM with Compose
Setting Up Jetpack Compose: The First Brick in the Wall 🧱
Before diving into the custom renderer, we first needed to enable Jetpack Compose in our Flutter project’s native Android layer. This meant updating the build.gradle file with the proper buildFeatures, setting the kotlinCompilerExtensionVersion, and pulling in all the right dependencies.
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.4.8"
}
Then we added the essential Compose libraries:
// Compose BOM
implementation platform("androidx.compose:compose-bom:2025.02.00")
// Core UI
implementation "androidx.compose.ui:ui"
implementation "androidx.compose.ui:ui-tooling-preview"
implementation "androidx.compose.material3:material3"
// Runtime
implementation "androidx.activity:activity-compose:1.10.1"
// For debugging previews
debugImplementation "androidx.compose.ui:ui-tooling"
🧨 Why version 1.4.8?
Because if you go any higher while using Kotlin 1.8.22 (which Flutter ships with), Compose will politely scream at you. We initially tried newer versions, only to be met with this charming message:
This version of the Compose Compiler requires Kotlin version 1.9.23 but you are using 1.8.22 which is not known to be compatible…
📌 Check the Compose-Kotlin compatibility table for more details.
Then it hits you with the legendary line:
"…or suppressKotlinVersionCompatibilityCheck but don’t say I didn’t warn you!"
Yeah. Compose isn’t messing around 😅.
So we locked it down to 1.4.8, which plays nicely with the Kotlin version Flutter currently ships.
Replacing the Default: Building Our Custom In-App Renderer
Firebase tells us: implement FirebaseInAppMessagingDisplay and register it. That’s all. No examples, no docs for Compose. Just vibes.
FirebaseInAppMessaging.getInstance().setMessageDisplayComponent(NologyInAppMessageDisplay(context))
Let's walk through the custom implementation we built — clean, robust, and fully integrated with your Compose UI.
🧠 The Heart of the Custom FIAM: NologyInAppMessageDisplay
class NologyInAppMessageDisplay(private val context: Context) : FirebaseInAppMessagingDisplay {
override fun displayMessage(
inAppMessage: InAppMessage,
callbacks: FirebaseInAppMessagingDisplayCallbacks
) {
// Implementation logic
}
}
✅ What This Class Does:
- Confirms message type: Only supports
ModalMessage. - Loads remote image manually (unlike iOS).
- Extracts button styles and parses
appDatafor optional buttons. - Wraps the Composable inside a
DialogFragment. - Handles actions and calls
messageClicked()/messageDismissed().
🔍 Platform Differences: iOS vs Android
📸 Image Handling
-
iOS: Uses
imageRawDatadirectly, no network call required. -
Android: Must manually download image from
imageUrl.
🔗 Deep Linking
- iOS: Firebase automatically opens links.
-
Android: Must manually launch an
Intent.ACTION_VIEWwith the URL.
🧪 How to Test Custom In-App Messages
Firebase caches campaigns for up to 24 hours — but test mode lets you bypass that using a device’s FID (Firebase Installation ID).
Step 1: Get the FID
On Android:
Look for this in Logcat:
I/FIAM.Headless: Starting InAppMessaging runtime with Installation ID <YOUR_ID>
On iOS:
Go to Xcode → Product > Scheme > Edit Scheme…
Add argument: -FIRDebugEnabled
Then find in logs:
[Firebase/InAppMessaging] Starting InAppMessaging runtime with Firebase Installation ID <YOUR_ID>
Step 2: Send a Test Message
- Go to Firebase → In-App Messaging → New Campaign
- Select Modal layout (⚠️ Only one supported by custom renderer)
- Add custom data:
{
"secondaryTitle": "SKIP",
"secondaryTextColor": "#000000",
"secondaryBackgroundColor": "#C9C9C9"
}
- Click Test on your device
- Paste the FID
- Reopen app — message should appear instantly 💥
🔗 About Deep Links?
Setting up proper deep link support is a big deal — and deserves its own article.
I’ll cover everything in a future piece:
- App links & Universal Links
- Domain verification
- JSON files for each platform
- Mapping paths into Flutter navigation
If that sounds helpful — drop a comment and I’ll prioritize it!
✅ Wrapping Up
Building custom layouts for Firebase In-App Messaging isn’t just about styling — it’s about creating immersive, native, analytics-driven experiences that match your app’s personality and performance goals.
From deep link support to analytics tracking, we stayed close to Firebase’s native protocols while still delivering a UI that feels ours.
🙋♂️ Who’s Behind This?
I’m Salah Nahed, a Flutter & mobile craftsman passionate about building beautiful cross-platform experiences.
- 📦 Creator of Bond
- 💼 Available for consulting & freelancing
- 🧠 Follow me for more dev stories
Let’s connect:
Thanks for reading — and see you in the next deep dive ✌️
Top comments (0)