Customizing Inbox

This tutorial explains how to customize the appearance and behavior of the Inbox in your Android application.

Overview

The AepInbox composable provides extensive customization options to match your app's design system. You can customize:

All customizations are applied using the InboxUIStyle builder and AepUIStyle when rendering the AepInbox composable.

Customizing Inbox Container Style

The InboxUIStyle class provides a builder pattern to customize the inbox container appearance and custom views.

Heading Style

Customize the heading text appearance using AepTextStyle:

data-slots=heading, code
data-repeat=1
data-languages=Kotlin

Kotlin

import com.adobe.marketing.mobile.aepcomposeui.style.AepTextStyle
import com.adobe.marketing.mobile.aepcomposeui.style.InboxUIStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.sp

val headingStyle = AepTextStyle(
    modifier = Modifier.fillMaxWidth().padding(16.dp),
    textStyle = TextStyle(
        color = MaterialTheme.colorScheme.onSurface,
        fontWeight = FontWeight.Bold,
        fontSize = 24.sp,
        textAlign = TextAlign.Start
    )
)

val inboxStyle = InboxUIStyle.Builder()
    .headingStyle(headingStyle)
    .build()

Vertical Layout (LazyColumn) Style

For vertical inbox layouts, customize the LazyColumn using AepLazyColumnStyle:

data-slots=heading, code
data-repeat=1
data-languages=Kotlin

Kotlin

import com.adobe.marketing.mobile.aepcomposeui.style.AepLazyColumnStyle
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.ui.Alignment

val lazyColumnStyle = AepLazyColumnStyle(
    modifier = Modifier
        .fillMaxSize()
        .background(Color.LightGray),
    contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
    verticalArrangement = Arrangement.spacedBy(12.dp),
    horizontalAlignment = Alignment.CenterHorizontally
)

val inboxStyle = InboxUIStyle.Builder()
    .lazyColumnStyle(lazyColumnStyle)
    .build()

Horizontal Layout (LazyRow) Style

For horizontal inbox layouts, customize the LazyRow using AepLazyRowStyle:

data-slots=heading, code
data-repeat=1
data-languages=Kotlin

Kotlin

import com.adobe.marketing.mobile.aepcomposeui.style.AepLazyRowStyle

val lazyRowStyle = AepLazyRowStyle(
    modifier = Modifier
        .height(250.dp)
        .background(Color.DarkGray),
    contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
    horizontalArrangement = Arrangement.spacedBy(12.dp),
    verticalAlignment = Alignment.CenterVertically
)

val inboxStyle = InboxUIStyle.Builder()
    .lazyRowStyle(lazyRowStyle)
    .build()

Custom Views

The Inbox provides hooks to replace the default loading, error, and empty state views with your own custom composables.

Custom Loading View

Replace the default loading indicator with your own custom view:

data-slots=heading, code
data-repeat=1
data-languages=Kotlin

Kotlin

val inboxStyle = InboxUIStyle.Builder()
    .loadingView {
        Box(
            modifier = Modifier.fillMaxSize(),
            contentAlignment = Alignment.Center
        ) {
            Column(
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.spacedBy(16.dp)
            ) {
                CircularProgressIndicator(
                    modifier = Modifier.size(48.dp),
                    color = MaterialTheme.colorScheme.primary
                )
                Text(
                    text = "Loading your inbox...",
                    style = MaterialTheme.typography.bodyLarge,
                    color = MaterialTheme.colorScheme.onSurfaceVariant
                )
            }
        }
    }
    .build()

Custom Error View

Replace the default error view with your own custom view:

data-slots=heading, code
data-repeat=1
data-languages=Kotlin

Kotlin

val inboxStyle = InboxUIStyle.Builder()
    .errorView {
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(32.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Icon(
                imageVector = Icons.Default.Warning,
                contentDescription = "Error",
                modifier = Modifier.size(64.dp),
                tint = MaterialTheme.colorScheme.error
            )
            Spacer(modifier = Modifier.height(16.dp))
            Text(
                text = "Unable to load inbox",
                style = MaterialTheme.typography.titleLarge,
                color = MaterialTheme.colorScheme.onSurface
            )
            Spacer(modifier = Modifier.height(8.dp))
            Text(
                text = "Please check your connection and try again",
                style = MaterialTheme.typography.bodyMedium,
                color = MaterialTheme.colorScheme.onSurfaceVariant,
                textAlign = TextAlign.Center
            )
            Spacer(modifier = Modifier.height(24.dp))
            Button(onClick = { viewModel.refresh() }) {
                Text("Retry")
            }
        }
    }
    .build()

Empty State Style

Customize the appearance of the empty state message and image:

data-slots=heading, code
data-repeat=1
data-languages=Kotlin

Kotlin

val emptyMessageStyle = AepTextStyle(
    modifier = Modifier.padding(16.dp),
    textStyle = TextStyle(
        fontSize = 18.sp,
        color = Color.Gray,
        textAlign = TextAlign.Center
    )
)

val emptyImageStyle = AepImageStyle(
    modifier = Modifier.size(120.dp)
)

val inboxStyle = InboxUIStyle.Builder()
    .emptyMessageStyle(emptyMessageStyle)
    .emptyImageStyle(emptyImageStyle)
    .build()

Unread Indicator Customization

Customize how unread messages are visually distinguished in the inbox.

Unread Icon Style

Customize the unread indicator icon:

data-slots=heading, code
data-repeat=1
data-languages=Kotlin

Kotlin

import com.adobe.marketing.mobile.aepcomposeui.style.AepImageStyle

val unreadIconStyle = AepImageStyle(
    modifier = Modifier
        .padding(8.dp)
        .size(24.dp)
)

val inboxStyle = InboxUIStyle.Builder()
    .unreadIconStyle(unreadIconStyle)
    .build()

Unread Icon Alignment

Set the position of the unread indicator icon on the card:

data-slots=heading, code
data-repeat=1
data-languages=Kotlin

Kotlin

import androidx.compose.ui.Alignment

val inboxStyle = InboxUIStyle.Builder()
    .unreadIconAlignment(Alignment.TopEnd) // TopStart, TopEnd, BottomStart, BottomEnd
    .build()

Unread Background Color

Set a custom background color for unread cards that adapts to light and dark themes:

data-slots=heading, code
data-repeat=1
data-languages=Kotlin

Kotlin

import com.adobe.marketing.mobile.aepcomposeui.uimodels.AepColor
import androidx.compose.ui.graphics.Color

val unreadBgColor = AepColor(
    light = Color(0xFFE3F2FD), // Light blue for light theme
    dark = Color(0xFF1E3A5F)   // Dark blue for dark theme
)

val inboxStyle = InboxUIStyle.Builder()
    .unreadBgColor(unreadBgColor)
    .build()

For detailed information on all available style classes and their properties, see InboxUIStyle.

Customizing Content Cards

Individual content cards within the Inbox can be customized using AepUIStyle. This allows you to style different card types (SmallImage, LargeImage, ImageOnly) independently.

Applying Content Card Styles

data-slots=heading, code
data-repeat=1
data-languages=Kotlin

Kotlin

import com.adobe.marketing.mobile.aepcomposeui.style.AepCardStyle
import com.adobe.marketing.mobile.aepcomposeui.style.AepUIStyle
import com.adobe.marketing.mobile.aepcomposeui.style.SmallImageUIStyle
import com.adobe.marketing.mobile.aepcomposeui.style.LargeImageUIStyle
import com.adobe.marketing.mobile.aepcomposeui.style.ImageOnlyUIStyle

// Define card style
val cardStyle = AepCardStyle(
    modifier = Modifier
        .fillMaxWidth()
        .padding(horizontal = 16.dp, vertical = 8.dp)
)

// Create styles for each card type
val smallImageCardStyle = SmallImageUIStyle.Builder()
    .cardStyle(cardStyle)
    .titleAepTextStyle(AepTextStyle(
        textStyle = TextStyle(
            fontWeight = FontWeight.SemiBold,
            fontSize = 16.sp
        )
    ))
    .bodyAepTextStyle(AepTextStyle(
        textStyle = TextStyle(
            fontSize = 14.sp,
            color = Color.Gray
        )
    ))
    .build()

val largeImageCardStyle = LargeImageUIStyle.Builder()
    .cardStyle(cardStyle)
    .build()

val imageOnlyCardStyle = ImageOnlyUIStyle.Builder()
    .cardStyle(cardStyle)
    .build()

// Combine into AepUIStyle
val cardUIStyle = AepUIStyle(
    smallImageUIStyle = smallImageCardStyle,
    largeImageUIStyle = largeImageCardStyle,
    imageOnlyUIStyle = imageOnlyCardStyle
)

// Apply to AepInbox
AepInbox(
    uiState = inboxUIState,
    inboxStyle = inboxStyle,
    itemsStyle = cardUIStyle,
    observer = viewModel.observer
)

For detailed information on customizing content cards, see Customizing Content Card Templates.

Best Practices

1. Test Light and Dark Modes

Always test your customizations in both light and dark mode. Use AepColor for theme-aware colors:

data-slots=heading, code
data-repeat=1
data-languages=Kotlin

Kotlin

val unreadBgColor = AepColor(
    light = Color(0xFFE3F2FD), // Light theme color
    dark = Color(0xFF1E3A5F)   // Dark theme color
)

2. Use Material Theme Colors

Leverage Material Theme for consistent styling that adapts to your app's theme:

data-slots=heading, code
data-repeat=1
data-languages=Kotlin

Kotlin

val headingStyle = AepTextStyle(
    textStyle = TextStyle(
        color = MaterialTheme.colorScheme.onSurface,
        fontSize = MaterialTheme.typography.titleLarge.fontSize
    )
)

3. Keep Custom Views Lightweight

Avoid complex or heavy composables in custom loading, error, and empty views to maintain smooth performance.

4. Maintain Consistency

Keep your inbox customizations consistent with your app's overall design system by defining reusable style constants:

data-slots=heading, code
data-repeat=1
data-languages=Kotlin

Kotlin

object InboxDesignSystem {
    val cardPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
    val cardSpacing = 12.dp
    val headingFontSize = 22.sp
}

5. Remember Style Objects

Use remember for style objects that don't change to avoid unnecessary recomposition:

data-slots=heading, code
data-repeat=1
data-languages=Kotlin

Kotlin

@Composable
fun InboxScreen(viewModel: InboxViewModel) {
    val inboxUIState by viewModel.inboxUIState.collectAsStateWithLifecycle()

    val inboxStyle = remember {
        InboxUIStyle.Builder()
            .headingStyle(headingStyle)
            .lazyColumnStyle(lazyColumnStyle)
            .build()
    }

    AepInbox(
        uiState = inboxUIState,
        inboxStyle = inboxStyle,
        observer = viewModel.observer
    )
}

6. Ensure Accessibility

Follow Android accessibility guidelines when customizing:

7. Consider Screen Sizes

Design your inbox customizations to work across different screen sizes. Use responsive modifiers and avoid fixed widths:

data-slots=heading, code
data-repeat=1
data-languages=Kotlin

Kotlin

val cardStyle = AepCardStyle(
    modifier = Modifier
        .fillMaxWidth()  // Adapts to screen width
        .padding(horizontal = 16.dp, vertical = 8.dp)
)