Fiori Object Cell¶
FioriObjectCell is a list item view that can represent a business object.
Overview¶
The FioriObjectCell is a structured UI component designed to present detailed information in a visually organized and interactive manner. It consists of multiple blocks, allowing for the modular placement of various UI elements that can be configured and rearranged as needed.
Layout Structure
The FioriObjectCelllayout consists of the following main sections:
- Block 1 - The primary information block contains essential details and dynamic elements.
- Right Accessory - A supplementary information block, typically located on the right side of the Cell.
- Block 2 - The secondary information block is located below Block 1.
![]() |
|---|
| Fiori Object Cell Overview |
Example¶
![]() |
|---|
| Fiori Object Cell on a Tablet |
Block 1¶
Available elements in Block 1:
![]() |
|---|
| Fiori Object Cell Block 1 |
Block 2¶
Available elements in Block 2:
![]() |
|---|
| Fiori Object Cell Block 2 |
Right Accessory¶
Available elements in Right Accessory:
![]() |
|---|
| Fiori Object Cell Right Accessory |
Development¶
FioriObjectCell is implemented entirely using Jetpack Compose, including a Composable FioriHorizonTheme. To understand the fundamentals of Jetpack Compose, refer to the Android Developer's documentation.
The Fiori Object Cell provides two UI components for application developers to use
FioriObjectCell– a UI component to display a single Fiori Object Cell.FioriObjectCellList– a UI component to display a series of Fiori Object Cell in a horizontally scrollable list.
Usage in an Application¶
This section describes how to use the Fiori Object Cell components in an application.
![]() |
|---|
| Fiori Object Cell on a Phone |
How to Create an Fiori Object Cell¶
FioriObjectCell is a Composable function that takes below parameters:
modifier– aModifierthat is used to define the height and width of the cell.cellData– aFioriObjectCellDatathat specifies the data to be displayed in the cell.cellType– aCellTypethat specifies the cell type. Users can chooseOBJECTorCONTACT.onClick– alambdathat specifies thelamdafunction that can be called when click on cellonLongPress– alambdathat specifies thelamdafunction that can be called when long press on cellonSwipeToStart– alambdathat specifies thelamdafunction that can be called when swipe to Start, this function will not work whencellSwipeableinFioriObjectCellDatanot set to trueonSwipeToStartIcon– aPaintericon define for the icon displayed when swipe to StartonSwipeToEnd– alambdathat specifies thelamdafunction that can be called when swipe to End, this function will not work whencellSwipeableinFioriObjectCellDatanot set trueonSwipeToEndIcon– aPaintericon define for the icon displayed when swipe to Endprogress– alambdafor the progress bar. This will displayed whendisplayProgressset to true inFioriObjectCellUiStatecolors– aFioriObjectCellColorsdefine the color use in celltextStyles– aFioriObjectCellTextStylesdefine thetextStylesin cellstyles– aFioriObjectCellStylesdefine the styles such like padding in cell
@Composable
fun FioriObjectCell(
modifier: Modifier = Modifier,
cellData: FioriObjectCellData,
onClick: (() -> Unit)? = null,
onLongPress: (() -> Unit)? = null,
onSwipeToStart: (() -> Unit)? = null,
onSwipeToStartIcon: Painter = PainterBuilder.build(
icon = FioriIcon(resId = R.drawable.ic_sap_icon_delete_filled)
),
onSwipeToEnd: (() -> Unit)? = null,
onSwipeToEndIcon: Painter = PainterBuilder.build(
icon = FioriIcon(resId = R.drawable.ic_sap_icon_add)
),
progress: (@Composable () -> Unit)? = {
CircularProgressIndicator(
modifier = Modifier.size(FioriObjectCellDefaults.styles().actionButtonSize()),
color = FioriObjectCellDefaults.colors().progressIndicatorColor()
)
},
colors: FioriObjectCellColors = FioriObjectCellDefaults.colors(),
textStyles: FioriObjectCellTextStyles = FioriObjectCellDefaults.textStyles(),
styles: FioriObjectCellStyles = FioriObjectCellDefaults.styles(),
)
FioriObjectCellData is like below
data class FioriObjectCellData(
override val headline: String = "",
val annotatedHeadline: @RawValue MutableState<AnnotatedString>? = null,
override val avatar: FioriAvatarConstruct? = null,
var iconStack: List<IconStackElement>? = null,
override val subheadline: String? = null,
val annotatedSubheadline: @RawValue MutableState<AnnotatedString>? = null,
override val footnote: String? = null,
val annotatedFootnote: @RawValue MutableState<AnnotatedString>? = null,
val description: String? = null,
val tags: List<FioriTagData> = listOf(),
val avatarStack: FioriAvatarConstruct? = null,
var status: FioriObjectCellStatusData? = null,
var subStatus: FioriObjectCellStatusData? = null,
val attributeIcons: AttributeIcons? = null,
val action: Action? = null,
override val cellClickable: Boolean = true,
override val cellSwipeable: Boolean = false,
var displayReadIndicator: MutableState<Boolean> = mutableStateOf(true),
var displayProcess: MutableState<Boolean> = mutableStateOf(false),
override var displaySelectBox: MutableState<Boolean> = mutableStateOf(false),
override var contentDescription: String = "",
override val enabled: Boolean = true,
override var isSelected: MutableState<Boolean> = mutableStateOf(false),
override var progressData: @RawValue ObjectCellProgressData = ObjectCellProgressData(),
)
The FioriObjectCellData object is now created using a Builder class which provides a fluent interface for object construction. This Builder class encapsulates the construction logic of FioriObjectCellData and allows for cleaner and more readable code.
A typical usage of the builder would look like this:
val fioriObjectCellData = FioriObjectCellData.Builder()
.setHeadline("Headline")
.setAvatar(someAvatarConstruct)
.setIconStack(someIconStackList)
.setCellClickable(true)
.build()
Each setter method in the Builder class corresponds to a property of FioriObjectCellData.
The build() method is called at the end to finalize the creation of FioriObjectCellData. The build() method ensures all required properties are set and then returns an instance of FioriObjectCellData.
Note: Some properties, such as headline and annotatedHeadline, cannot be set simultaneously. An IllegalArgumentException will be thrown if an attempt is made to do so.
Fields¶
Headline¶
The headline is the main area for text content. The headline is the only mandatory content for Fiori Object Cell. The title can be specified by:
headline = ""
in FioriObjectCellData
Avatar¶
The avatar provides a visual representation of the object , can be specified by :
avatar = FioriAvatarConstruct(
hasBadge = hasBadge,
hasBorder = hasBorder,
avatarList = listOf(
FioriAvatarData(
image = FioriImage(url = ""),
), FioriAvatarData(
image = FioriImage(url = ""),
)
),
size = 18.dp,
avatarBadge = FioriIcon(
resId = R.drawable.ic_circle_unread
),
type = FioriAvatarType.DOUBLE
)
FioriAvatarConstruct is used to construct the Avatar area.
@Parcelize
data class FioriAvatarConstruct(
val type: FioriAvatarType = FioriAvatarType.SINGLE,
var avatarList: List<FioriAvatarData>,
val hasBadge: Boolean = false,
val avatarBadge: FioriIcon? = null,
val groupDisplayNumber: Int = 0,
val size: Dp = 40.dp,
val hasBorder: Boolean = false,
val borderColor: Color? = null,
val backgroundColor: Color? = null,
val shape: FioriAvatarShape = FioriAvatarShape.ROUNDEDCORNER,
val labelType: FioriAvatarLabelType = FioriAvatarLabelType.TRAILINGLABEL,
val labelOptionLayoutType: FioriAvatarLabelLayoutType? = FioriAvatarLabelLayoutType.FULL,
val labelValue: String? = null,
val avatarGroupSemantic: String? = null
) : Parcelable
type– aFioriAvatarhave three type. SINGLE,DOUBLE,GROUP. SINGLE and DOUBLE is used to display in the Avatar area. Group is used to display in Avatar stack area.avatarList– aListcontainsFioriAvatarDataobjectshasBadge– aBooleanis used to display the badge or notavatarBadge– aFioriIconis used to define the badgegroupDisplayNumber– aIntis used to display the number when use the group type.size– aDpis used to define the Avatar area size whenAvatarTypeis Single and Double.hasBorder– aBooleanis used to display the border of Avatar.borderColor– aColoris used to define the color of border.backgroundColor– aColoris used to display the background of Avatar.shape– aFioriAvatarShapeis used to support two type.ROUNDEDCORNER and CIRCLE.labelType– aFioriAvatarLabelTypeis used to support the label in the group type of avatar. It support at leading, trail, below, above the avatar group.labelOptionLayoutType– aFioriAvatarLabelLayoutTypeis used to identify if label displayed in full or truncate.labelValue– aStringto display in avatar group.avatarGroupSemantic– aStringfor google accessibility.
![]()
Icon Stack¶
The iconStack is the area for icon and text display. The text will always displayed at the top of iconStack. Two iconStack elements can be displayed in the stack.
The iconStack can be specified as follows:
iconStack = listOf(
IconStackElement(text = "Display"),
IconStackElement(
icon = FioriIcon(
resId = R.drawable.ic_directions_24px,
contentDescription = "checked"
)
)
),
Subheadline¶
The subheadline is under the headline and provides additional information.The subheadline can be specified by:
subheadline = ""
Footnote¶
The footnote is under the subheadline and provides further information.The footnote can be specified by:
footnote = ""
Description¶
The description is under the headline, subheadline, footnote, tags, and status, substatus, attribute icons, and provides further information. The description can be specified as follows:
description = ""
Tags¶
Tags may be used to indicate categories, types, or statuses. Tags are displayed next to the footnote if space is available. The tags can be specified by :
tags= listOf(
FioriTagData(text = "gray"),
FioriTagData(text = "blue"),
FioriTagData(text = "red"),
)
Avatar Stack¶
The avatar stack is a FioriAvatarConstruct that displays a row of FioriAvatar
at the bottom of the cell. FioriAvatarType should set to the Group. The Avatar stack can be specified by :
avatarStack = FioriAvatarConstruct(
hasBadge = false,
type = FioriAvatarType.GROUP,
avatarList = listOf(avatar2, avatar3, avatar4, avatar5),
size = 16.dp,
groupDisplayNumber = 4,
hasBorder = false
)
Status¶
Up to two statuses(status and substatus) can be displayed, stacked vertically, to show attributes of the object. Status could be customized as either text or an image.
statusData = FioriStatusInfoLabelDataInOC(
items = listOf(
FioriLabelItemData(
label = "Success",
iconType = FioriLabelIconType.ICON,
icon = FioriIcon(
resId = R.drawable.ic_sap_icon_message_warning,
contentDescription = "Success",
),
color = FioriSemanticColors.POSITIVE,
)
)
)
Attribute Icons¶
A set of icons can be displayed horizontally under the statuses. These icons can represent both persistent and variable information about the object. It is recommended to have at most four attribute icons.
attribute = AttributeIcons(
iconsList = listOf(
FioriIcon(
resId = R.drawable.ic_chip_checked,
contentDescription = "checked"
),
FioriIcon(
resId = R.drawable.ic_chip_checked,
contentDescription = "checked"
)
)
Action¶
Action can be displayed on the right of cell. It is suggest to only have one action in Fiori object cell.
iconAction =
Action(
icon = FioriIcon(
resId = R.drawable.ic_email_black_24dp,
contentDescription = "email",
)
)
ObjectCellProgressData¶
ObjectCellProgressData of cellData can be used to control the progress state of Object cell operation triggered by the action icon. Object cell progress data is define as
data class ObjectCellProgressData(
var progress: MutableState<Float>? = null,
var progressType: MutableState<ProgressType>? = null,
var isIndeterminate: MutableState<Boolean>? = null,
var progressDescription: MutableState<String>? = null,
var isInProgress: MutableState<Boolean>? = null,
)
progress is a value between 0 and 1 that denotes 0% progress 10 100%
progressType can take ProgressType.LINEAR or ProgressType.CIRCULAR based on what type of progress indicator is required
isInDeterminate this value can be used to make progress type determinate or indeterminate
isInProgress is used to enable or disable progress based on any user defined operation
progressDescription provides a talk back text for the progress indicator when it is in progress.
Progress¶
User can define the progress by using lambda. Below is the default Progress bar. Progress display or not decide by displayProcess in FioriObjectCellUiState
{
CircularProgressIndicator(
modifier = Modifier.size(FioriObjectCellDefaults.styles().actionButtonSize()),
color = FioriObjectCellDefaults.colors().progressIndicatorColor(),
)
},
User can define in-place progress by setting progressData value of CellData parameter for of the FioriObjectCell using the rememberObjectCellProgressData method in the following way
data.progressData = rememberObjectCellProgressData(ObjectCellProgressData(
progressDescription = remember{ mutableStateOf("In Progress")
}))
The value of the progress can be used by setting them as given in the example in the onClick handler in the User Action code.
data.progressData.progress.value = .4 //Change the progress to 40%
data.progressData.isInProgress.value = true //Can be used true or false to enable or disable progress
````
### User Action
#### Simple Tap/Click to Enable/Disable Read Mode
Upon clicking or tapping an Fiori Object Cell, the entire cell will display a ripple effect and then the details about the clicked Fiori Object Cell will open up. Returning to the list of Fiori Object Cells, the clicked Fiori Object Cell will be marked as Read, i.e.the unread object indicator will disappear. User can customize the `onClick` function. Below is the default implementation.
#### Long Press to Enable/Disable Selection Mode
An Fiori Object Cell can enter and exit Selection mode using `displaySelectBox` in `FioriObjectCellData`. The Fiori Object Cell gains a checkbox in Selection mode. Users can also customize the `onLongPress`.
The following sample illustrates how to enable Selection mode in `FioriObjectCellList`
```kotlin
var enableSelectBox by remember { mutableStateOf(false) }
FioriObjectCellList(
cellList = cellList,
onClick = { p1, _ ->
cellList[p1].displayReadIndicator.value = false
},
onLongPress = { _, _ ->
enableSelectBox = !enableSelectBox
cellViewModel.updateObjectCellDisplaySelectBoxInList(enableSelectBox)
},
cellType = CellType.OBJECT,
)

Enabling Actions on Swipe¶
cellSwipeable can set to true to enable the swipe action of Fiori Object Cell.
onSwipeToStart,onSwipeToStartIcon,onSwipeToEnd,onSwipeToEndIcon are used to define the swipe lambda and swipe icon.
App developers can define their own actions in response to the swipe gesture in two directions. In the demo app provided by Fiori UI, a delete action is defined for swiping from right to left, and a mock add action is defined for swiping from left to right.
Swipe can defined either in FioriObjectCell or FioriObjectCellList. Below is the example in FioriObjectCellList.
Swipe can have two behavior when Release the swipe. Release.EXTEND and Release.Reset . These two behavior can defined in styles.
FioriObjectCellList(
objectCellList = objectList,
enableSelectBox = enableSelectBox,
onClick = {
if (it.isRead == false) {
it.isRead = true
}
currentNews.newsData = it.stateData
navController.navigate(Screen.ObjectCellDetail.route)
},
onSwipeToEnd = { p1, p2 ->
p2.isRead = !p2.isRead
},
onSwipeToStart = { p1, p2 ->
objectList.remove(p2)
Toast.makeText(
context,
" The index ${p1!!} item is deleted from list ",
Toast.LENGTH_SHORT
)
.show()
},
styles = FioriObjectCellDefaults.styles(
headlineDisplayLineMaxNumber = headlineNumber,
subheadlineDisplayLineMaxNumber = subheadlineNumber,
footnoteDisplayLineMaxNumber = footnoteNumber,
swipeToEnd = SwipeRelease.RESET,
swipeToStart = SwipeRelease.EXTEND
),
)


Contact Cell¶
Fiori Object Cells can defined as a special type of Contact Cell. Contact Cells can have multiple actions. Users can define the Fiori Contact Cell using FioriContactCell or FioriContactCellList:
FioriContactCell(
cellData = FioriContactCellData(
headline = "Headline",
subheadline = "Developer",
footnote = "Earth",
avatar = FioriAvatarConstruct(
avatarList = listOf(
FioriAvatarData(
text = "A",
shape = FioriAvatarShape.ROUNDEDCORNER
)
),
),
contactActions = listOf(
Action(
icon = FioriIcon(
resId = R.drawable.ic_sap_icon_email,
contentDescription = "email",
)
),
Action(
icon = FioriIcon(
resId = R.drawable.ic_sap_icon_call,
contentDescription = "email",
),
)
),
),
styles = FioriObjectCellDefaults.styles(
divider = true,
),
cellType = CellType.CONTACT
)

Contact Cell can have avatar,headline,subheadline and footnote in the left. Contact Cell only can have contact actions in the right accessories.
Fiori Object Cell List¶
FioriObjectCellList allows you to display the Fiori Object Cell in a list. It accepts a list of FioriObjectCellData as a parameter.
@Composable
fun FioriObjectCellList(
modifier: Modifier = Modifier,
cellList: List<FioriObjectCellData>,
loadMore: (() -> Unit)? = null,
onSwipeToStart: ((Int, FioriObjectCellData) -> Unit)? = null,
onSwipeToStartIcon: Painter = PainterBuilder.build(
icon = FioriIcon(resId = R.drawable.ic_sap_icon_delete_filled)
),
onSwipeToEnd: ((Int, FioriObjectCellData) -> Unit)? = null,
onSwipeToEndIcon: Painter = PainterBuilder.build(
icon = FioriIcon(resId = R.drawable.ic_sap_icon_add)
),
onClick: ((Int, FioriObjectCellData) -> Unit)? = { _, cellCommonData ->
cellCommonData.setDisplayReadIndicator(false)
},
onLongPress: ((Int, FioriObjectCellData) -> Unit)? = null,
progress: (@Composable () -> Unit)? = {
CircularProgressIndicator(
modifier = Modifier.size(FioriObjectCellDefaults.styles().actionButtonSize()),
color = FioriObjectCellDefaults.colors().progressIndicatorColor()
)
},
colors: FioriObjectCellColors = FioriObjectCellDefaults.colors(),
textStyles: FioriObjectCellTextStyles = FioriObjectCellDefaults.textStyles(),
styles: FioriObjectCellStyles = FioriObjectCellDefaults.styles(),
)
Load More (Lazy Loading)¶
The loadMore function is used to implement lazy loading in the FioriObjectCellList. This function is invoked when the user has scrolled to a certain point near the end of the list, defined by the startToLoad style. The loadMore function should then load more items and add them to the cellList.
Here's an example of how you might use the loadMore function:
val currentPage = 0
val pageSize = 10
fun loadMoreItems() {
viewModelScope.launch(Dispatchers.IO) {
val newItems = retriveData(currentPage * pageSize, pageSize)
_objectCellStateList.value =
((_objectCellStateList.value!! + newItems) as MutableList<FioriObjectCellData>)
currentPage++
}
}
Styles¶
The style of the component can be customized. The recommended approach is to override FioriObjectCellDefaults.styles and only customize the attributes you desire. This means that other attributes can be retained and all the components in the application will have a consistent look and feel. For example:
FioriObjectCell(
cellData = myCellData,
modifier: Modifier = Modifier,
progress = { MyProgressButton() },
styles = FioriObjectCellDefaults.styles(avatarSize = 40.dp)
)
Flexibility Configuration In Styles¶
The FioriObjectCell composable supports flexible placement and ordering of elements using the BlockArea enum and specific properties for each UI element. This configuration allows developers to determine which block (e.g., Block 1, Block 2, and Right Accessory) each element should appear in, as well as its order within that block.
Each UI element, such as sub-headlines, footnotes, descriptions, tags, and avatar stacks, has associated parameters that configure its block area and order within that block. Here’s how each parameter functions:
- Block Assignment: The
BlockAreaproperties such assubheadlineBlockandfootnoteBlockdefines the block where the element should appear. Setting this toBlockArea.BLOCK1places the element in the primary block. - Order Within Block: The Order properties such as
subheadlineOrderandfootnoteOrderdetermine the relative position of the element within its assigned block. Lower values appear higher in the block, with 1 being the top-most position.
fun styles(
subheadlineBlock: BlockArea = BlockArea.BLOCK1,
subheadlineOrder: Int = 1,
footnoteBlock: BlockArea = BlockArea.BLOCK1,
footnoteOrder: Int = 2,
descriptionBlockInPhone: BlockArea = BlockArea.BLOCK2,
descriptionOrder: Int = 1,
tagBlock: BlockArea = BlockArea.BLOCK2,
tagOrder: Int = 2,
avatarStackBlock: BlockArea = BlockArea.BLOCK2,
avatarStackOrder: Int = 3,
ratingsStackBlock: BlockArea = BlockArea.BLOCK2,
ratingsOrder: Int = 4,
statusInfoBlock: BlockArea = BlockArea.RIGHTACCESSORY,
statusInfoOrder: Int = 1,
attributeBlock: BlockArea = BlockArea.RIGHTACCESSORY,
attributeOrder: Int = 2,
overflowActionBlock: BlockArea = BlockArea.RIGHTACCESSORY,
overflowActionOrder: Int = 3,
actionButtonBlock: BlockArea = BlockArea.RIGHTACCESSORY,
actionButtonOrder: Int = 1,
kpiBlock: BlockArea = BlockArea.RIGHTACCESSORY,
kpiOrder: Int = 4,
labelButtonBlock: BlockArea = BlockArea.RIGHTACCESSORY,
labelButtonOrder: Int = 5
)





