Skip to content

Conversation

@gpunto
Copy link
Contributor

@gpunto gpunto commented Jan 8, 2026

🎯 Goal

AND-995: Implement new buttons

Introduce StreamButton and StreamIconButton

🛠 Implementation details

I created base buttons kinda following the familiar Material/Compose API but with Stream-specific styling. They are not meant to be arbitrarily flexible. Rather, they are made to follow closely the Stream design system. And anyway, as we discussed for similar cases, integrators will be able to either customize colors through the theme or replace the component altogether.

Nothing is public for the time being, until we decide if we actually want to expose these base buttons to integrators.

🎨 UI Changes

StreamButton StreamIconButton
Screenshot 2026-01-08 at 15 55 47 Screenshot 2026-01-08 at 15 39 03

🧪 Testing

You can go to the previews and launch them on a device.

☑️Contributor Checklist

General

  • I have signed the Stream CLA (required)
  • Assigned a person / code owner group (required)
  • Thread with the PR link started in a respective Slack channel (#android-chat-core or #android-chat-ui) (required)
  • PR is linked to the GitHub issue it resolves

Code & documentation

  • Changelog is updated with client-facing changes
  • New code is covered by unit tests
  • Comparison screenshots added for visual changes
  • Affected documentation updated (KDocs, docusaurus, tutorial)

☑️Reviewer Checklist

  • UI Components sample runs & works
  • Compose sample runs & works
  • UI Changes correct (before & after images)
  • Bugs validated (bugfixes)
  • New feature tested and works
  • Release notes and docs clearly describe changes
  • All code we touched has new or updated KDocs
  • Check the SDK Size Comparison table in the CI logs

🎉 GIF

Please provide a suitable gif that describes your work on this pull request

@github-actions
Copy link
Contributor

github-actions bot commented Jan 8, 2026

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 5.25 MB 5.25 MB 0.00 MB 🟢
stream-chat-android-offline 5.48 MB 5.48 MB 0.00 MB 🟢
stream-chat-android-ui-components 10.60 MB 10.60 MB 0.00 MB 🟢
stream-chat-android-compose 12.81 MB 11.68 MB -1.13 MB 🚀

* limitations under the License.
*/

package io.getstream.chat.android.compose.ui.components.button
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed the existing package structure, but if we decide to extract this as part of the core design system, we should change the package to a more generic one

internal fun StreamButtonStyle.border(enabled: Boolean) =
if (enabled) border else disabledBorder

internal object StreamButtonStyleDefaults {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For how the tokens are structured at the moment, these styles are not trivial to extract away from this repository. The reason is that, as far as I understand, the colors in Figma all belong to the same set which contains both "design system" generic colors & chat-specific ones.

So with the current state, what we could extract is everything except the actual instantiation of the button styles, i.e. this object.

@Suppress("ObjectPropertyNaming")
internal object StreamSpacings {
val none = 0.dp
val _2xs = 4.dp
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we should follow the naming here as

xxs instead of _2xs
xxl or xxxl instead of _2xl or _3xl

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about that, but my main concern is that it makes it easier to mistake them 🤷‍♂️. But in the end, when these are autogenerated, we'll inherit what comes from Figma, so maybe we can check with Jurgen

@gpunto gpunto marked this pull request as ready for review January 8, 2026 16:18
@gpunto gpunto requested a review from a team as a code owner January 8, 2026 16:18
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
style: StreamButtonStyle = StreamButtonStyleDefaults.primarySolid,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Figma Primary is the type and solid is the style. I think it's better if we keep them as separate variables here as well. I have a comment in figma that not all types implement all styles.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment, StreamButtonStyle contains a complete styling description for the button and for rendering we just take the values as they are.

If we split it into, let's say, ButtonType and ButtonStyle, what would you put in each? I guess we could decide colors based on the type and then conditionally apply them to the container/border/content based on the style, but wouldn't that add some complexity at rendering time that we can avoid? 🤔

val primarySolid: StreamButtonStyle
@Composable
get() {
val colors = ChatTheme.colors
Copy link

@renefloor renefloor Jan 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want to extract colors from ChatTheme into a more general StreamTheme so the colors can also be used for other products later.

What I'm thinking is to create a primary app color separate from the primary button color, so you probably only have to set your app color and not all the colors on all the components per se.

So it becomes: colors.buttonTypePrimaryBg ?? colors.primaryColor

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we want to extract colors from ChatTheme into a more general StreamTheme so the colors can also be used for other products later.

I agree, but I have a concern (basically what I mention here). Atm, in Figma there's no distinction between design system and chat-specific tokens (right?), so all the colors live in the same place.

What I'm thinking is to create a primary app color separate from the primary button color, so you probably only have to set your app color and not all the colors on all the components per se.

I'm a bit worried if we deviate from Figma that it will make it more likely to have inconsistencies. I think that if we want this, we should do it in Figma, as we can easily have tokens referencing other tokens, and then trickle it down to the code.

So it becomes: colors.buttonTypePrimaryBg ?? colors.primaryColor

If we go for this, I'd centralize this check in the colors instantiation rather than in component-specific code. Meaning the button (and any other component) doesn't need to know each default and doesn't perform any check at rendering time. For example a primary button would always uses buttonTypePrimaryBg, which has its default already resolved and only once, when Chat colors were instantiated.

import io.getstream.chat.android.compose.ui.theme.StreamSpacings

@Composable
internal fun StreamIconButton(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is IconButton a different component, or is IconButton just a regular StreamButton with no label?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made them different components to enforce padding and shapes, but I can check what it would take to merge them into one

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think the only issue is padding. As far as I can see in Figma, the button has padding specified when it's IconOnly=false but no padding in IconOnly=true

Screenshot 2026-01-09 at 13 04 43 Screenshot 2026-01-09 at 13 04 49

@gpunto gpunto force-pushed the redesign/buttons branch 2 times, most recently from ca183f4 to 5bf6948 Compare January 9, 2026 12:15
@sonarqubecloud
Copy link

sonarqubecloud bot commented Jan 9, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
31.1% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants