Skip to main content

Icons and images

Replace any icon, reaction image or screen-state illustration of the community UI with your own asset. Every slot you leave out keeps the SDK default.

Android availableiOS availableFlutter ≥ 1.14.0React Native ≥ 1.14.0Unity ≥ 1.14.0

Before you begin​

  • Complete the Quickstart and read Theming: icons travel with the rest of your theme on Android, iOS, Flutter and React Native.
  • Prepare square assets, ideally 24×24 with the drawn content in the central 14.5×14.5 area (a 4.75 transparent margin on each side). A non-square asset is displayed in fit mode and looks smaller.
  • The SDK tints every icon with your theme colors and ignores the asset's own colors. Reaction images and screen-state illustrations are the exception: they keep their original colors.

Icons are grouped by area of the UI:

GroupCovers
groupsThe group list and the group selector
contentPosts, comments, replies, video, polls and reactions
profileProfile screens
gamificationBadges and the rules sheet
settingsThe settings screens
commonRadio buttons, checkboxes, toggles, close, more actions, activity button, list navigation arrow
screenStatesEmpty lists and failed first loads
warning

Icons follow the UI, so this API changes more often than the rest of the SDK: a slot can stop being used and be deprecated quickly.


Override icons in a group​

Set only the icons you want to change, at the level of the group that holds them. This example replaces three icons of the post creation screen.

The common icons sit at the top level of OctopusIconsDefaults.icons(): there is no common namespace.

Every icon is a @Composable () -> Painter lambda. Build the icons with the OctopusIconsDefaults helpers, then pass them to OctopusTheme through OctopusImagesDefaults.images(icons = …).

import com.octopuscommunity.sdk.ui.OctopusIconsDefaults
import com.octopuscommunity.sdk.ui.OctopusImagesDefaults
import com.octopuscommunity.sdk.ui.OctopusTheme
import com.octopuscommunity.sdk.ui.octopusComposables

octopusComposables(
navController = navController,
container = { _, content ->
OctopusTheme(
images = OctopusImagesDefaults.images(
icons = OctopusIconsDefaults.icons(
content = OctopusIconsDefaults.content(
post = OctopusIconsDefaults.post(
creation = OctopusIconsDefaults.postCreation(
create = { painterResource(R.drawable.my_create_post) },
addPicture = { painterResource(R.drawable.my_add_picture) },
addPoll = { painterResource(R.drawable.my_add_poll) },
),
),
),
),
),
content = content,
)
},
)

Shared icon parameters​

Android availableiOS availableFlutter not availableReact Native not availableUnity not available

Some icons mean the same thing in several places, such as the report icon on a post and on a profile. The builders take convenience parameters that set every icon of the same meaning at once. They are builder arguments, not properties of the resulting icon set. An icon you set individually always wins over the convenience parameter.

BuilderParameterApplies to
icons()reportcontent.report, profile.report
icons()closecontent.deletePicture, and through it the deletePicture of post, comment and reply creation
content()notAvailablepost.notAvailable, comment.notAvailable
content()likeNotSelectedlikeNotSelected of post, comment and reply
content()addPictureaddPicture of post, comment and reply creation
content()deletePicturedeletePicture of post, comment and reply creation
content()sendcreate of comment and reply creation
content()openResponseCreationpost.openCommentCreation, comment.openReplyCreation

A nested group you pass yourself, such as post = OctopusIconsDefaults.post(…), no longer receives the convenience parameters of its parent. Here the post creation screen gets its own "add picture" icon, while comments and replies get the shared one.

import com.octopuscommunity.sdk.ui.OctopusIconsDefaults
import com.octopuscommunity.sdk.ui.OctopusImagesDefaults

val images = OctopusImagesDefaults.images(
icons = OctopusIconsDefaults.icons(
report = { painterResource(R.drawable.my_report) },
content = OctopusIconsDefaults.content(
addPicture = { painterResource(R.drawable.my_add_picture) },
post = OctopusIconsDefaults.post(
creation = OctopusIconsDefaults.postCreation(
addPicture = { painterResource(R.drawable.my_post_add_picture) },
),
),
),
),
)

Radio, checkbox and toggle icons​

Radio buttons, checkboxes and toggles take a pair of images: one for the on state, one for the off state. A pair replaces the default control only when both images are set.

radio, checkbox and toggle are parameters of icons() that take an OctopusIcons.OnOff(on, off). Their default is null, which keeps the Material3 RadioButton, Checkbox and Switch.

import com.octopuscommunity.sdk.ui.OctopusIcons
import com.octopuscommunity.sdk.ui.OctopusIconsDefaults
import com.octopuscommunity.sdk.ui.OctopusImagesDefaults

val images = OctopusImagesDefaults.images(
icons = OctopusIconsDefaults.icons(
radio = OctopusIcons.OnOff(
on = { painterResource(R.drawable.my_radio_on) },
off = { painterResource(R.drawable.my_radio_off) },
),
checkbox = OctopusIcons.OnOff(
on = { painterResource(R.drawable.my_checkbox_on) },
off = { painterResource(R.drawable.my_checkbox_off) },
),
toggle = OctopusIcons.OnOff(
on = { painterResource(R.drawable.my_toggle_on) },
off = { painterResource(R.drawable.my_toggle_off) },
),
),
)

Reaction images​

Six reaction images appear on posts, comments and replies: heart, joy, mouthOpen, clap, cry and rage. They keep their original colors, so provide assets that already match your visual identity.

warning

Each image must match the meaning of its reaction. The SDK shows the reaction name next to the image on some screens and uses it as the accessibility label read by TalkBack and VoiceOver: heart is "Like", joy "Haha", mouthOpen "Wow", clap "Celebrate", cry "Sad" and rage "Angry". A party image on cry would be announced as "Sad".

Pass OctopusIconsDefaults.reaction(…) as the reaction parameter of content().

import com.octopuscommunity.sdk.ui.OctopusIconsDefaults
import com.octopuscommunity.sdk.ui.OctopusImagesDefaults

val images = OctopusImagesDefaults.images(
icons = OctopusIconsDefaults.icons(
content = OctopusIconsDefaults.content(
reaction = OctopusIconsDefaults.reaction(
heart = { painterResource(R.drawable.my_heart) },
joy = { painterResource(R.drawable.my_joy) },
mouthOpen = { painterResource(R.drawable.my_mouth_open) },
clap = { painterResource(R.drawable.my_clap) },
cry = { painterResource(R.drawable.my_cry) },
rage = { painterResource(R.drawable.my_rage) },
),
),
),
)

Screen state illustrations​

Android ≥ 1.14.0iOS ≥ 1.14.0Flutter ≥ 1.14.0React Native ≥ 1.14.0Unity not available

The screenStates group holds the illustrations shown when a list is empty or its first load fails. They appear on every list of the SDK: main feed, group list, group detail, post and comment detail, profile tabs and notification center. They keep their original colors: draw them on a transparent background so they sit on your community's background color.

SlotShown on
emptyContentAn empty list of posts or comments
emptyNotificationsAn empty notification center
networkErrorA first load that failed because the device is offline, next to a retry button
errorA first load that failed for any other reason, next to a retry button

Pass OctopusIconsDefaults.screenStates(…) as the screenStates parameter of icons(). You can also replace a default without any theme code: add a drawable with one of these names to your app's src/main/res/drawable/. A screenStates lambda takes precedence over the drawable.

Drawable nameSlot
ic_octopus_screen_state_empty_contentemptyContent
ic_octopus_screen_state_empty_notificationsemptyNotifications
ic_octopus_screen_state_network_errornetworkError
ic_octopus_screen_state_errorerror
import com.octopuscommunity.sdk.ui.OctopusIconsDefaults
import com.octopuscommunity.sdk.ui.OctopusImagesDefaults

val images = OctopusImagesDefaults.images(
icons = OctopusIconsDefaults.icons(
screenStates = OctopusIconsDefaults.screenStates(
emptyContent = { painterResource(R.drawable.my_empty_content) },
emptyNotifications = { painterResource(R.drawable.my_empty_notifications) },
networkError = { painterResource(R.drawable.my_network_error) },
error = { painterResource(R.drawable.my_error) },
),
),
)

OctopusIconsDefaults.post(emptyFeed = …) has no effect: use screenStates.emptyContent instead.


Full example​

One theme that combines a logo, the top-level icons, a group override, the control pairs and the screen states.

import com.octopuscommunity.sdk.ui.OctopusIcons
import com.octopuscommunity.sdk.ui.OctopusIconsDefaults
import com.octopuscommunity.sdk.ui.OctopusImagesDefaults
import com.octopuscommunity.sdk.ui.OctopusTheme
import com.octopuscommunity.sdk.ui.octopusComposables

octopusComposables(
navController = navController,
container = { _, content ->
OctopusTheme(
images = OctopusImagesDefaults.images(
logo = { painterResource(R.drawable.my_logo) },
icons = OctopusIconsDefaults.icons(
close = { painterResource(R.drawable.my_close) },
moreActions = { painterResource(R.drawable.my_more_actions) },
activityButton = { painterResource(R.drawable.my_activity) },
report = { painterResource(R.drawable.my_report) },
content = OctopusIconsDefaults.content(
addPicture = { painterResource(R.drawable.my_add_picture) },
),
profile = OctopusIconsDefaults.profile(
defaultAvatar = { painterResource(R.drawable.my_default_avatar) },
),
radio = OctopusIcons.OnOff(
on = { painterResource(R.drawable.my_radio_on) },
off = { painterResource(R.drawable.my_radio_off) },
),
toggle = OctopusIcons.OnOff(
on = { painterResource(R.drawable.my_toggle_on) },
off = { painterResource(R.drawable.my_toggle_off) },
),
screenStates = OctopusIconsDefaults.screenStates(
emptyContent = { painterResource(R.drawable.my_empty_content) },
networkError = { painterResource(R.drawable.my_network_error) },
),
),
),
content = content,
)
},
)

Behavior and limits​

  • An image the SDK cannot load keeps the default of its slot.
  • The former empty-state icons are no longer displayed: content.post.emptyFeedInGroups, emptyFeedInCurrentUserProfile, emptyFeedInOtherUserProfile, content.comment.emptyFeed, and the former empty-notifications icon (notifications.emptyNotifications or profile.emptyNotifications, depending on the platform). Set screenStates.emptyContent and screenStates.emptyNotifications instead.

Next steps​