Giter Site home page Giter Site logo

el-mubarok / flexible-chat-box Goto Github PK

View Code? Open in Web Editor NEW

This project forked from smarttoolfactory/flexible-chat-box

0.0 0.0 0.0 2.19 MB

Flexible chat row written with Jetpack Compose that positions message and message status based on number of message lines, message width and parent width. And resizable Subcomposelayout that remasures sibling composables to match their widths' to longest composable that matches quote and message width to max width.

License: Apache License 2.0

Kotlin 100.00%

flexible-chat-box's Introduction

Flexible Chat Row and Resizable SubcomposeLayout

Flexible chat row, ChatFlexBoxLayout, positions its elements based on number of lines message text has, parent width, message and message status width. SubcomposeColumn created using SubComposeLayout which remeasures its children based on longest children. This is useful for matching quote message and message length after position calculation. These two composables together create dynamic message rows that position children, and positions message, message date and message status.

There are 3 implementation files to to try out ChatFlexBoxLayout, and SubcomposeColumn are DemoFullChat.kt, DemoChatAndWidth.kt, and DemoResizableColumn.kt

Full Chat Chat Width Resizable

ChatFlexBoxLayout

This layout measures and positions message, and another container that uses message date or message date + message received status like messaging apps do.

There are 4 possible conditions to position message and stats

  • Single line message text and status is shorter than parent width(Magenta in sample)
  • Single line message text and status is longer than parent width(Green in sample)
  • Multiple line message with last line width and message status is shorter message text length + right padding(Red in sample)
  • Multiple line message with last line width and message status is longer message text length + right padding(Yellow in sample)

Usage

ChatFlexBoxLayout(
    modifier: Modifier = Modifier,
    text: String,
    color: Color = Color.Unspecified,
    fontSize: TextUnit = 16.sp,
    fontStyle: FontStyle? = null,
    fontWeight: FontWeight? = null,
    fontFamily: FontFamily? = null,
    letterSpacing: TextUnit = TextUnit.Unspecified,
    textDecoration: TextDecoration? = null,
    textAlign: TextAlign? = null,
    lineHeight: TextUnit = TextUnit.Unspecified,
    overflow: TextOverflow = TextOverflow.Clip,
    softWrap: Boolean = true,
    maxLines: Int = Int.MAX_VALUE,
    messageStat: @Composable () -> Unit,
    onMeasure: ((ChatRowData) -> Unit)? = null
)

Since TextLayout is required to get text length, last line width and other properties it's internal to this composable but properties of Textcomposable can be set with same as it's done using Text.

onMeasure returns internal layout data of this row, that's how i set colors differently in chat width sample. messageStat is composable that contains message text or status if you need to.

    ChatFlexBoxLayout(
        modifier = Modifier
            .background(color, shape = RoundedCornerShape(8.dp))
            .padding(start = 2.dp, top = 2.dp, end = 4.dp, bottom = 2.dp),
        text = text,
        messageStat = {
            MessageTimeText(
                modifier = Modifier.wrapContentSize(),
                messageTime = messageTime,
                messageStatus = messageStatus
            )
        },
        onMeasure = { chatRowData ->
            color = when (chatRowData.measuredType) {
                0 -> Color.Yellow
                1 -> Color.Red
                2 -> Color.Green
                else -> Color.Magenta
            }
        }
    )

Another overload of ChatFlexBoxLayout takes two Composables as arguments which custom message Composable can be used instead of String or AnnotatedString.

@Composable
fun ChatFlexBoxLayout(
    modifier: Modifier,
    message: @Composable () -> Unit,
    messageStat: @Composable () -> Unit = {},
    chatRowData: ChatRowData,
    onMeasure: ((ChatRowData) -> Unit)? = null
) {
  //...
}

Use with remember { ChatRowData() } to provide stats and invoke measureText(chatRowData, it) to set text properties to this data

            val chatRowData = remember { ChatRowData() }

            ChatFlexBoxLayout(
                modifier = Modifier.padding(
                    start = 2.dp,
                    top = 2.dp,
                    end = 8.dp,
                    bottom = 2.dp
                ),
                message = {
                    Text(
                        modifier = Modifier.padding(horizontal = 6.dp, vertical = 4.dp),
                        text = text,
                        fontSize = 16.sp,
                        onTextLayout = {
                            // ⚠️ THIS IS REQUIRED TO MEASURE Text size and get line count
                            measureText(chatRowData, it)
                        }
                    )
                },
                messageStat = {
                    MessageTimeText(
                        modifier = Modifier.wrapContentSize(),
                        messageTime = messageTime,
                        messageStatus = messageStatus
                    )
                },
                chatRowData = chatRowData
            )
        }

SubcomposeColumn

This layout uses SubcomposeLayout to find longest child then remeasure its children and set every child to max width. There are 2 overloads of this Composable if you only need to use direct 2 children you can use which returns size of main component as IntSize

fun SubcomposeColumn(
    modifier: Modifier = Modifier,
    mainContent: @Composable () -> Unit = {},
    dependentContent: @Composable (IntSize) -> Unit
) {
...
}

This overloaded function is suitable for layout with any number of children

@Composable
fun SubcomposeColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit = {},
) {

    SubcomposeLayout(modifier = modifier) { constraints ->

        var recompositionIndex = 0

        var placeables: List<Placeable> = subcompose(recompositionIndex++, content).map {
            it.measure(constraints)
        }

        val maxSize =
            placeables.fold(IntSize.Zero) { currentMax: IntSize, placeable: Placeable ->
                IntSize(
                    width = maxOf(currentMax.width, placeable.width),
                    height = currentMax.height + placeable.height
                )
            }

        // Remeasure every element using width of longest item as minWidth of Constraint
        if (!placeables.isNullOrEmpty() && placeables.size > 1) {
            placeables = subcompose(recompositionIndex, content).map { measurable: Measurable ->
                measurable.measure(Constraints(maxSize.width, constraints.maxWidth))
            }
        }

        layout(maxSize.width, maxSize.height) {
            var yPos = 0
            placeables.forEach { placeable: Placeable ->
                placeable.placeRelative(0, yPos)
                yPos += placeable.height
            }

        }
    }
}

flexible-chat-box's People

Contributors

smarttoolfactory avatar dev-gvs avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.