Design-Driven Firebase in Flutter: Building Custom In-App Messaging with SwiftUI & Compose

# flutter# mobile# tutorial# ui
Design-Driven Firebase in Flutter: Building Custom In-App Messaging with SwiftUI & ComposeSalah Nahed

🆕 Design-Driven Firebase in Flutter: Building Custom In-App Messaging with SwiftUI &...

🆕 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"
}
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

🧨 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…
Enter fullscreen mode Exit fullscreen mode

📌 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))
Enter fullscreen mode Exit fullscreen mode

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
    }
}
Enter fullscreen mode Exit fullscreen mode

What This Class Does:

  • Confirms message type: Only supports ModalMessage.
  • Loads remote image manually (unlike iOS).
  • Extracts button styles and parses appData for optional buttons.
  • Wraps the Composable inside a DialogFragment.
  • Handles actions and calls messageClicked() / messageDismissed().

🔍 Platform Differences: iOS vs Android

📸 Image Handling

  • iOS: Uses imageRawData directly, 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_VIEW with 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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Step 2: Send a Test Message

  1. Go to Firebase → In-App Messaging → New Campaign
  2. Select Modal layout (⚠️ Only one supported by custom renderer)
  3. Add custom data:
{
  "secondaryTitle": "SKIP",
  "secondaryTextColor": "#000000",
  "secondaryBackgroundColor": "#C9C9C9"
}
Enter fullscreen mode Exit fullscreen mode
  1. Click Test on your device
  2. Paste the FID
  3. 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 ✌️