prat
prat

Reputation: 657

Prominent top app bar using jetpack compose

I wanted to create and add gestures on top app bar some thing similar to below screenshot using jetpack compose:

enter image description here

I am able to create collapsible top bar using the below android docs link: documentation link but not able to do gestures to expand and collapse along with change in layout using compose. Below is the code I have tried for collapsible toolbar.

val toolbarHeight = 48.dp
val toolbarHeightPx = with(LocalDensity.current) { toolbarHeight.roundToPx().toFloat() }
// our offset to collapse toolbar
val toolbarOffsetHeightPx =

    remember { mutableStateOf(0f) }
// now, let's create connection to the nested scroll system and listen to the scroll
// happening inside child LazyColumn
val nestedScrollConnection = remember {
    object : NestedScrollConnection {
        override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset {
            // try to consume before LazyColumn to collapse toolbar if needed, hence pre-scroll
            val delta = available.y
            val newOffset = toolbarOffsetHeightPx.value + delta
            toolbarOffsetHeightPx.value = newOffset.coerceIn(-toolbarHeightPx, 0f)
            // here's the catch: let's pretend we consumed 0 in any case, since we want
            // LazyColumn to scroll anyway for good UX
            // We're basically watching scroll without taking it
            return Offset.Zero
        }
    }
}

And below is the gesture link which I want to implement in topbvar topbar gesture video

Please help me with the links. Thanks!

Upvotes: 7

Views: 5886

Answers (1)

S Haque
S Haque

Reputation: 7281

If you are looking to implement collapsing toolbar like below where the title will animate based on collapsing state this code reference might help you. You need to build a custom layout for it.

CollapsingToolbar

@Composable
fun CollapsingTopBar(
modifier: Modifier = Modifier,
collapseFactor: Float = 1f, // A value from (0-1) where 0 means fully expanded
content: @Composable () -> Unit
) {
val map = mutableMapOf<Placeable, Int>()
Layout(
    modifier = modifier,
    content = content
) { measurables, constraints ->
    map.clear()
    val placeables = mutableListOf<Placeable>()
    measurables.map { measurable ->
        when (measurable.layoutId) {
            BACK_ID -> measurable.measure(constraints)
            SHARE_ID -> measurable.measure(constraints)
            TITLE_ID -> 
   measurable.measure(Constraints.fixedWidth(constraints.maxWidth 
   - (collapseFactor * (placeables.first().width * 2)).toInt()))
            else -> throw IllegalStateException("Id Not found")
        }.also { placeable ->
            map[placeable] = measurable.layoutId as Int
            placeables.add(placeable)
        }
    }

    // Set the size of the layout as big as it can
    layout(constraints.maxWidth, constraints.maxHeight) {
        placeables.forEach { placeable ->
            when (map[placeable]) {
                BACK_ID -> placeable.placeRelative(0, 0)
                SHARE_ID -> placeable.run {
                    placeRelative(constraints.maxWidth - width, 0)
                }
                TITLE_ID -> placeable.run {
                    val widthOffset = (placeables[0].width * collapseFactor).roundToInt()
                    val heightOffset = (placeables.first().height - placeable.height) / 2
                    placeRelative(
                        widthOffset,
                        if (collapseFactor == 1f) heightOffset else constraints.maxHeight - height
                    )
                }
            }
        }
    }
}
}

object CollapsingTopBar {
   const val BACK_ID = 1001
   const val SHARE_ID = 1002
   const val TITLE_ID = 1003
   const val COLLAPSE_FACTOR = 0.6f
}


@Composable
fun TopBar(
modifier: Modifier = Modifier,
currentHeight: Int,
title: String,
onBack: () -> Unit,
shareShow: () -> Unit
) {

Box(
    modifier = modifier.height(currentHeight.dp)
) {
    CollapsingTopBar(
        collapseFactor = // calculate collapseFactor based on max and min height of the toolbar,
        modifier = Modifier
            .statusBarsPadding()
    ) {
        Icon(
            modifier = Modifier
                .wrapContentWidth()
                .layoutId(CollapsingTopBar.BACK_ID)
                .clickable { onBack() }
                .padding(16.dp),
            imageVector = Icons.Filled.ArrowBack,
            tint = MaterialTheme.colors.onPrimary,
            contentDescription = stringResource(id = R.string.text_back)
        )
        Icon(
            modifier = Modifier
                .wrapContentSize()
                .layoutId(CollapsingTopBar.SHARE_ID)
                .clickable { }
                .padding(16.dp),
            imageVector = Icons.Filled.Share,
            tint = MaterialTheme.colors.onPrimary,
            contentDescription = stringResource(id = R.string.title_share)
        )
        Text(
            modifier = Modifier
                .layoutId(CollapsingTopBar.TITLE_ID)
                .wrapContentHeight()
                .padding(horizontal = 16.dp),
            text = title,
            style = MaterialTheme.typography.h4.copy(color = MaterialTheme.colors.onPrimary),
            overflow = TextOverflow.Ellipsis
        )
    }
}
}

Sample reference from Google

Upvotes: 4

Related Questions