I'm trying to get behaviour where I can toggle visibility of a FAB, with a bit of entry/exit animation, in the Compose Scaffold.
The issue
The FAB disappears fine. However, whatever I try, I can't get it to reappear - it's like it totally disappears from the tree, never to return!
This code reproduces the issue:
class TestActivity : ComponentActivity() {
#OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
var fabVisible by remember { mutableStateOf(true) }
Scaffold(
floatingActionButton = {
AnimatedVisibility(visible = fabVisible) {
FloatingActionButton(onClick = {}) {
Icon(Icons.Default.Star, contentDescription = null)
}
}
}
) {
Button(onClick = { fabVisible = !fabVisible }) {
Text("Click to toggle FAB")
}
}
}
}
}
Here's a demo - the FAB does a sort of wipe out, but then never returns:
Workarounds I've attempted
If I do any of the following, the FAB will toggle:
Put the FAB in another area, such as the Scaffold content or topbar area.
Remove the AnimatedVisibility.
Put the FAB inside an explicitly-sized Box:
…
Scaffold(
floatingActionButton = {
Box(
modifier = Modifier
.width(56.0.dp)
.height(56.0.dp)
) {
AnimatedVisibility(visible = fabVisible) {
FloatingActionButton(onClick = {}) {
Icon(Icons.Default.Star, contentDescription = null)
}
}
}
}
)
…
Obviously solutions 1 and 2 don't achieve what I want, and solution 3 (the Box solution) isn't ideal - I have to know the size of the FAB to do this (I imagine there's probably a way to remember its intrinsic size when first visible?) and the animation is odd - it goes from the corner from the middle rather than from the centre, and the elevation suddenly 'pops' right at the end:
What should I do to get the FAB entering and exiting as expected?
Try with scaleIn and scaleOut animations.
AnimatedVisibility(
visible = fabVisible,
enter = scaleIn(),
exit = scaleOut(),
) {
FloatingActionButton(onClick = {}) {
Icon(Icons.Default.Star, contentDescription = null)
}
}
Related
I am experiencing flicker or overlapping when having a compose tabBar implementation with webviews as content. If I change the webviews with another view (ex. Box{Text}) it does not happen.
It seems as if the webview is filling more than it's border for a short while (See .gif below)
Update: I have been looking into if it was a recomposition issue (hence the simple test project) and I cannot identify any reason why it should recompose the tab bar.
When I add height to the tab bar, I can see the text is in the tab bar at all times.
A test project can be fetched here: https://github.com/msuhl/ComposeTabTest and is a very standard implementation
#Composable
private fun MyTabRow(
pagerState: PagerState,
coroutineScope: CoroutineScope,
) {
TabRow(
selectedTabIndex = pagerState.currentPage,
indicator = { tabPositions ->
TabRowDefaults.Indicator(
Modifier.pagerTabIndicatorOffset(pagerState, tabPositions),
color = MaterialTheme.colors.secondary
)
},
) {
tabRowItems.forEachIndexed { index, item ->
Tab(
selected = pagerState.currentPage == index,
onClick = { coroutineScope.launch { pagerState.animateScrollToPage(index) } },
icon = {
Icon(imageVector = item.icon, contentDescription = "")
},
text = {
Text(
text = item.title,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
)
},
)
}
}
HorizontalPager(
count = tabRowItems.size,
state = pagerState,
) {
ShowWebView("http://google.com")
}
}
It was related to the lazy loading of webview and I was not able to make a direct fix.
Instead I ended up with a working, although kind og hackish, solution
If a LazyColumn is introduced around the webview, the issue does not occur.
In code:
HorizontalPager(
count = tabRowItems.size,
state = pagerState,
) {
LazyColumn {
item {
ShowWebView(url)
}
}
}
Is there a something like a spoiler component in Compose (that doesn't look too much Android-ish)? Or can it be done fast using LazyVerticalGrid or something else? I'm looking for a box that opens and clothes by clicking on its header.
What you look for is AnimatedVisibility Composable.
var visible by remember {
mutableStateOf(true)
}
Column(modifier = Modifier.fillMaxSize()) {
Text("Spoiler", modifier = Modifier
.fillMaxWidth()
.clickable {
visible = !visible
}
)
AnimatedVisibility(visible = visible) {
Column {
Text("Lorem ipsum...Rest of the text")
}
}
}
https://developer.android.com/jetpack/compose/animation#animatedvisibility
The top app bar container color not change on scroll, the color keep the same. Working with XML views it works fine, but compose doesn't.
If is a bug or the featured was not ported for compose yet, but if ins't a bug, i'm miss something in the code?
I'm using version 1.0.1 of the material 3 library.
Sample code:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
val uiController = rememberSystemUiController()
uiController.setStatusBarColor(Color.Transparent)
MyApplication5Theme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Scaffold(
topBar = {
TopAppBar(
title = { Text("k") },
modifier = Modifier.statusBarsPadding(),
)
}
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(it)
) {
items((1..100).toList()) {
Text(it.toString(), modifier = Modifier.padding(8.dp))
}
}
}
}
}
}
}
}
I tried create a simple layout with a top app bar and a lazy column, i expected top app bar container color changes on scroll of the lazy column, but the color keep the same.
The TopAppBar composable has a scrollBehavior argument, which is null be default. If you leave it null, it will not react to scroll events, you have to connect it with your scrollable component. You can do that like this:
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = { TopAppBar(scrollBehavior = scrollBehavior, ...) },
) { LazyColumn() {} }
I'm using Scaffold for my main screen with a fixed bottomBar that is visible in every screen of the app, and I'm applying the innerPadding of the Scaffold to its content.
I want the keyboard to appear over the bottomBar, and for that I'm applying the imePadding() only to the Scaffold's content.
However, when the keyboard is opened, both the Scaffold's innerPading and imePadding() are applied to the contents padding.
I've tried to go through the Accompanist Insets migration, but no lucky.
Is there anyway that I can prevent it and apply only one or the other?
Here is a piece of my code:
Scaffold(
topBar = { },
bottomBar = { },
modifier = Modifier
.systemBarsPadding()
) { innerPadding ->
Content(
modifier = Modifier
.padding(innerPadding)
.imePadding()
)
}
And this is the result:
With the now, deprecated, Accompanist Insets, I was using the following solution:
val isImeVisible = LocalWindowInsets.current.ime.isVisible
val contentPadding = remember(isImeVisible) {
if (isImeVisible) PaddingValues(top = innerPadding.calculateTopPadding()) else innerPadding
}
According to Accompanist Insets migration, LocalWindowInsets.current.ime should be replaced with WindowInsets.ime.
It doesn't have isVisible for now, until this bug is fixed. Here's how I've re-created it for now:
val WindowInsets.Companion.isImeVisible: Boolean
#Composable
get() {
val density = LocalDensity.current
val ime = this.ime
return remember {
derivedStateOf {
ime.getBottom(density) > 0
}
}.value
}
Usage:
val isImeVisible = WindowInsets.isImeVisible
This should work with your old remember(isImeVisible) code.
Another solution would be to set BringIntoViewRequester to your content inside Scaffold. Then when textField is focused, you could call bringIntoViewRequester.bringIntoView(). This way you wouldn't need to set any paddings.
val bringIntoViewRequester = remember { BringIntoViewRequester() }
Column(
modifier = Modifier.bringIntoViewRequester(bringIntoViewRequester)
) {
TextField(
value = "",
onValueChange = {},
modifier = Modifier
.onFocusEvent {
if (it.isFocused) {
coroutineScope.launch {
delay(350)
bringIntoViewRequester.bringIntoView()
}
}
}
)
}
Try using something like this (WARNING: consumedWindowInsets is Experimental, but it's working):
Scaffold(
topBar = { },
bottomBar = { },
modifier = Modifier
.systemBarsPadding()
) { innerPadding ->
Content(
modifier = Modifier
.consumedWindowInsets(innerPadding)
.padding(innerPadding)
.imePadding()
)
}
I've got a dialog where the user can select something and depending on the choice, the layout will be updated. The problem is that the height of the dialog is never updated to reflect the layout changes.
How to recompose the dialog to make the layout fit in the dialog?
Example:
#Composable
fun AlertDialogTest() {
var showDialog by remember { mutableStateOf(false)}
var showExtra by remember { mutableStateOf(false)}
Button(onClick = { showDialog = true }) {
Text("Open dialog")
}
if (showDialog) {
AlertDialog(
text = {
Column { Button(onClick = {showExtra = true}) {
Text ("Show rest of dialog")
}
if (showExtra) {
Text("More text", style = MaterialTheme.typography.h5)
Text("Even more text", style = MaterialTheme.typography.h5)
}
}
},
confirmButton = { TextButton(onClick = { showDialog = false }) {
Text("Close")
}},
onDismissRequest = {showDialog = false},
)
}
}
And the result:
I've also faced the same issue, and found the solution for a longer text or other contents in Jetpack Compose Alert Dialog
AlertDialog(
onDismissRequest = { },
properties = DialogProperties(usePlatformDefaultWidth = false),
modifier = Modifier
.padding(28.dp)
.fillMaxWidth()
.wrapContentHeight(),
title = null,
text = {
// Your alert dialog content
},confirmButton = {
TextButton(onClick = { /*TODO*/ }) {
Text(text = "OK")
}
})
It did work as expected for me in beta08. Then, after upgrate to rc02 it stopped working - popup dialogs, drop down menus (basicaly all elements that are backed by platform windows) stopped resizing properly on content size change. And indeed I've found a bug for it - https://issuetracker.google.com/issues/194911971. And for now I, sadly, haven't found a workaround, so we better wait until it's fixed.
UPD.
As is commented in aforementioned issue, there is a workaround. Check the answer here.
Another possible workaround consists in forcing recomposition of the dialogue when data changes: https://issuetracker.google.com/issues/221643630#comment8