Mutablestateof - State hoisting - kotlin

What to do to manage one state in two functions? (openDialog)
How to transfer openDialog state from ProductDialog() to AppBar()?
I know I can make a separate class per state but I don't want to.
#Composable
fun ProductDialog() {
val openDialog = rememberSaveable() { mutableStateOf(true) }
val productNameState = remember { mutableStateOf("") }
AlertDialog(
onDismissRequest = { openDialog.value = false },
title = { Text(
modifier = Modifier.padding(bottom = 8.dp),
text = "Add new product",
fontSize = 20.sp,
fontWeight = FontWeight.Bold
) },
confirmButton = { Button(onClick = { openDialog.value = false }) {
Text(text = "Add") }
},
dismissButton = { Button(onClick = { openDialog.value = false }) {
Text(text = "Cancel")
}
},
text = {
Column() {
TextField(value = productNameState.value, onValueChange = {
})
TextField(value = productNameState.value, onValueChange = {})
}
}
)
}
#Composable
fun AppBar() {
val openDialog = rememberSaveable() { mutableStateOf(false) }
if (openDialog.value) ProductDialog()
Scaffold(
topBar = {
TopAppBar(
actions = { Icon(imageVector = Icons.Default.Menu, contentDescription = null) },
title = { Text(text = "Shopping List Compose") })
},
floatingActionButton = {
FloatingActionButton(modifier = Modifier.padding(10.dp), onClick = { openDialog.value = true }) {
Icon(Icons.Default.Add, contentDescription = null)
}}
) {
ListItems()
}
}
What to do to manage one state in two functions? (openDialog)

From the example you shared, it seems that the only use of openDialog in ProductDialog is to close the dialog. You can instead paas a lambda to ProductDialog to close the dialog.
#Composable
fun ProductDialog(closeDialog: () -> Unit) {
AlertDialog(
onDismissRequest = closeDialog,
title = { ... },
confirmButton = {
Button(onClick = closeDialog) {
Text(text = "Add")
}
},
dismissButton = {
Button(onClick = closeDialog) {
Text(text = "Cancel")
}
},
text = { ... }
)
}
#Composable
fun AppBar() {
val openDialog by rememberSaveable() { mutableStateOf(false) }
if (openDialog) ProductDialog { openDialog = false}
...
}

Related

Overflow alignment in innerTextField in kotlin jetpack compose?

I have a similar search bar I pulled out from this other stackoverflow question. I, however changed lots of it and ended up with a much simpler SearchTextField composable.
#Composable
fun SearchTextField(
text: String,
placeholder: String,
modifier: Modifier = Modifier,
onTextChange: (String) -> Unit = { },
onIconClick: () -> Unit = { },
onFocusChange: (Boolean) -> Unit = { }
) {
val focusRequester = remember { FocusRequester() }
val isTextEmpty = text.isEmpty()
TextField(
value = text,
maxLines = 1,
singleLine = true,
placeholder = { Text(text = placeholder, overflow = TextOverflow.Visible) },
onValueChange = onTextChange,
shape = RoundedCornerShape(percent = 50),
trailingIcon = { IconDecoration(isTextEmpty = isTextEmpty, onClick = onIconClick) },
colors = TextFieldDefaults.textFieldColors(
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent),
modifier = modifier
.wrapContentHeight()
.padding(top = 0.dp, bottom = 0.dp, start = 0.dp, end = 0.dp)
.onFocusChanged { onFocusChange(it.isFocused) }
.focusRequester(focusRequester))
}
#Composable
fun IconDecoration(
isTextEmpty: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier) {
IconButton(onClick = onClick, modifier = modifier) {
Icon(
imageVector = when {
isTextEmpty -> { Icons.Default.Search }
else -> { Icons.Default.Close }
},
contentDescription = null,
tint = Color.DarkGray)
}
}
And from the the previews I get, it looks just as expected:
However, when I try to implement it on a TopAppBar title, it looks like the inner text field is not vertically aligned correctly. Below is an example of the implementation and its preview. I noticed this happens due to the SearchTextField padding modifier.
I do need that padding, but there is nowhere to set that in my current TextField, since the current modifier affects the whole composable. Where am I wrong?
#Composable
fun TopBar(
state: TopBarState,
modifier: Modifier = Modifier
) {
TopAppBar(
modifier = modifier,
title = {
SearchTextField(
text = state.query,
placeholder = "Search",
onTextChange = { query ->
state.query = query
state.suggestions = state.onSearchSuggestions(query)
},
onFocusChange = { state.isSearchFocused = it },
onIconClick = { state.query = "" },
modifier = Modifier
.padding(vertical = 8.dp) // This is causing the overflow
.wrapContentHeight(Alignment.Top)) // This won't work
},
navigationIcon = {
NavigationIcon(
isSearchFocused = state.isSearchFocused,
onMenuClick = state.onMenuClick,
onBackClick = { state.isSearchFocused = false })
},
actions = {
ActionItems(
pages = arrayListOf(Page.Notifications),
onNavigate = state.onNavigate)
})
}
#Composable
fun NavigationIcon(
isSearchFocused: Boolean,
onMenuClick: () -> Unit,
onBackClick: () -> Unit
) {
if(isSearchFocused) {
IconButton(onClick = onBackClick) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = null)
}
} else {
IconButton(onClick = onMenuClick) {
Icon(
imageVector = Icons.Filled.Menu,
contentDescription = null)
}
}
}
#Composable
fun ActionItems(
pages: List<Page>,
onNavigate: (Page) -> Unit
) {
pages.forEach { page ->
IconButton(onClick = { onNavigate(page) }) {
Icon(
imageVector = page.icon,
contentDescription = null)
}
}
}

Agora - 1-1 video call - jetpack compose - not showing remote video

I am building a video call app . I have implemented refering to agora official sample repsoitoryRefer.
In my implementation call s getting connected , but the video of remote user is rendered for 2 seconds and it gets stuck . Is any thing wrong in my implemengtations?
This is my whole composable screen code
#OptIn(ExperimentalAnimationApi::class)
#Composable
fun VideoCallScreenV2(
viewModel: CallViewModel,
navController: NavHostController,
onBack: () -> Unit = {}
) {
val context = LocalContext.current
val localSurfaceView: TextureView? by remember {
mutableStateOf(RtcEngine.CreateTextureView(context))
}
var remoteUserMap by remember {
mutableStateOf(mapOf<Int, TextureView?>())
}
var remoteUser = remember {
mutableStateOf<RemoteUser?>(null)
}
val permissionNotEnabled = remember {
mutableStateOf(!viewModel.hasPermissions)
}
var isInPictureInPicMode by remember {
mutableStateOf(false)
}
val screenState = remember {
viewModel.callScreenState
}
var networkQuality by remember {
mutableStateOf(-1)
}
PermissionHandler(visible = permissionNotEnabled,
permissions = viewModel.getRequiredPermissionList(),
permissionText = stringResource(
id = R.string.camera_audio_permission_text
),
permissionNotAvailable = {
context.showToast(context.getString(R.string.agora_video_perms_denied))
//If permissions are not given, video calls should not take place. Then navigate to chat screen
// navController.popBackStack(Routes.INBOX_V2, true)
}) {
permissionNotEnabled.value = false
viewModel.hasPermissions = true
}
val mEngine = remember(viewModel.callScreenState.rtcToken) {
initEngine(
context,
getRtcEngineEventHandler(
onUserJoined = { uid, elapsed ->
remoteUser.value = RemoteUser(uid)
},
onUserOffline = { uid, reason ->
remoteUser.value = null
context.findActivity()?.finish()
},
onNetworkQuality = { uid, txQuality, rxQuality ->
networkQuality = rxQuality
},
onLeaveChannel = {
Timber.e("hhp-CallScreen onLeaveChannel Success")
context.findActivity()?.finish()
}
),
viewModel.callScreenState.chatRoom?.channelName ?: "",
viewModel.userRole,
token = viewModel.callScreenState.rtcToken?.token ?: ""
)
}
if (viewModel.userRole == "Broadcaster") {
mEngine.setupLocalVideo(VideoCanvas(localSurfaceView, Constants.RENDER_MODE_FIT, 0))
}
var isRemoteFullScreen by remember {
mutableStateOf(true)
}
val remoteViewZindex by animateFloatAsState(targetValue = if (isRemoteFullScreen) 1.0f else 1.5f)
val localViewZindex by animateFloatAsState(targetValue = if (isRemoteFullScreen) 1.5f else 1.0f)
val remoteViewSize by animateFloatAsState(targetValue = if (isRemoteFullScreen) 1f else .25f)
val localViewSize by animateFloatAsState(targetValue = if (isRemoteFullScreen) .35f else 1f)
var showControls by remember {
mutableStateOf(true)
}
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomEnd) {
RemoteView(
remoteListInfo = remoteUserMap,
remoteUser = remoteUser.value,
mEngine = mEngine,
modifier = Modifier
.padding(
end = if (!isRemoteFullScreen) 24.dp else 0.dp,
bottom = if (!isRemoteFullScreen) 54.dp else 0.dp
)
.fillMaxSize(remoteViewSize)
.zIndex(remoteViewZindex)
//.background(green)
.clickable {
if (!isRemoteFullScreen)
isRemoteFullScreen = true
else if (isRemoteFullScreen)
showControls = !showControls
}
)
localSurfaceView?.let { local ->
AndroidView(
factory = {
local
},
update = { it ->
it.setOnClickListener {
if (isRemoteFullScreen)
isRemoteFullScreen = false
else if (!isRemoteFullScreen)
showControls = !showControls
}
},
modifier = Modifier
.fillMaxSize(localViewSize)
.zIndex(localViewZindex)
.padding(
end = if (isRemoteFullScreen) 24.dp else 0.dp,
bottom = if (isRemoteFullScreen) 54.dp else 0.dp
)
)
NetworkQualityIndicator(quality = networkQuality)
}
AnimatedVisibility(visible = showControls, modifier = Modifier.zIndex(3f)) {
UserControls(
isMuted = viewModel.muted,
isVideoDisabled = viewModel.videoDisabled,
onEndCallClicked = {
viewModel.apply {
whatIf(
given = isUsingByDoctor(),
whatIf = {
onEvent(CallEvents.DoctorEndedCall(mEngine))
}, whatIfNot = {
onEvent(CallEvents.PatientEndedCall(mEngine))
}
)
}
}, onToggleMute = {
viewModel.muted = !viewModel.muted
mEngine.muteLocalAudioStream(viewModel.muted)
}, onToggleVideo = {
})
}
}
}
fun initEngine(
current: Context,
eventHandler: IRtcEngineEventHandler,
channelName: String,
userRole: String,
token: String
): RtcEngine =
RtcEngine.create(current, BuildConfig.AGORA_APPID, eventHandler).apply {
enableVideo()
setChannelProfile(1)
if (userRole == "Broadcaster") {
setClientRole(1)
} else {
setClientRole(0)
}
setDefaultAudioRoutetoSpeakerphone(true)
setVideoEncoderConfiguration(
VideoEncoderConfiguration(
VideoEncoderConfiguration.VideoDimensions(640, 480),
VideoEncoderConfiguration.FRAME_RATE.FRAME_RATE_FPS_15,
0,
VideoEncoderConfiguration.ORIENTATION_MODE.ORIENTATION_MODE_FIXED_PORTRAIT
)
)
if (token.isNotEmpty() && channelName.isNotEmpty()){
joinChannel(token, channelName, "", 0)
Timber.e("hhp-CallScreen initEngine token $token channel name $channelName userRole $userRole")
}
}
#Composable
private fun RemoteView(
remoteListInfo: Map<Int, TextureView?>,
remoteUser: RemoteUser?,
mEngine: RtcEngine,
modifier: Modifier
) {
val context = LocalContext.current
Row(
modifier = modifier
) {
if (remoteUser != null) {
Timber.e("hhp-CallScreen Remote User $remoteUser")
val remoteTextureView = remoteUser.textureView ?: RtcEngine.CreateTextureView(context)
AndroidView(
factory = { remoteTextureView!! }
)
mEngine.setupRemoteVideo(
VideoCanvas(
remoteTextureView,
Constants.RENDER_MODE_HIDDEN,
remoteUser.uid
)
)
} else {
Timber.e("hhp-CallScreen Remote User Null")
}
}
}
#Composable
private fun UserControls(
isMuted: Boolean,
isVideoDisabled: Boolean,
onEndCallClicked: () -> Unit,
onToggleMute: (Boolean) -> Unit,
onToggleVideo: (Boolean) -> Unit
) {
val activity = (LocalContext.current as? Activity)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 50.dp),
Arrangement.SpaceEvenly,
Alignment.Bottom
) {
OutlinedButton(
onClick = {
onToggleMute(isMuted)
},
shape = CircleShape,
modifier = Modifier.size(50.dp),
contentPadding = PaddingValues(0.dp),
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = if (isMuted) Color.Blue else Color.White)
) {
if (isMuted) {
Icon(
Icons.Rounded.MicOff,
contentDescription = "Tap to unmute mic",
tint = Color.White
)
} else {
Icon(Icons.Rounded.Mic, contentDescription = "Tap to mute mic", tint = Color.Blue)
}
}
OutlinedButton(
onClick = {
//mEngine.leaveChannel()
// activity?.finish()
onEndCallClicked()
},
shape = CircleShape,
modifier = Modifier.size(70.dp),
contentPadding = PaddingValues(0.dp),
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = Color.Red)
) {
Icon(
Icons.Rounded.CallEnd,
contentDescription = "Tap to disconnect Call",
tint = Color.White
)
}
OutlinedButton(
onClick = {
//videoDisabled = !videoDisabled
//mEngine.muteLocalVideoStream(videoDisabled)
onToggleVideo(isVideoDisabled)
},
shape = CircleShape,
modifier = Modifier.size(50.dp),
contentPadding = PaddingValues(0.dp),
colors = ButtonDefaults.outlinedButtonColors(backgroundColor = if (isVideoDisabled) Color.Blue else Color.White)
) {
if (isVideoDisabled) {
Icon(
Icons.Rounded.VideocamOff,
contentDescription = "Tap to enable Video",
tint = Color.White
)
} else {
Icon(
Icons.Rounded.Videocam,
contentDescription = "Tap to disable Video",
tint = Color.Blue
)
}
}
}
}
#Composable
fun NetworkQualityIndicator(quality: Int) {
if (BuildConfig.DEBUG) {
when (updateNetworkStatus(quality)) {
CallQuality.Excellent -> QualityLabel(label = "Excellent", color = green)
CallQuality.Good -> QualityLabel(label = "Good", color = cream)
CallQuality.Poor -> QualityLabel(label = "Poor", color = lightRed)
CallQuality.Unknown -> QualityLabel(label = "Analysing", color = white)
}
}
}
#Composable
fun QualityLabel(
label: String,
color: Color
) {
Text(
text = "Call Quality $label", modifier = Modifier
.fillMaxWidth()
.zIndex(3f)
.background(
color
),
textAlign = TextAlign.Center
)
}
private fun updateNetworkStatus(quality: Int): CallQuality {
return if (quality in 1..2)
CallQuality.Excellent
else if (quality <= 4) CallQuality.Good
else if (quality <= 6) CallQuality.Poor
else CallQuality.Unknown
}

Jetpack Compose: How to show Alert Dialog with a single item's information from LazyColumn clicked item?

I'm working on a simple project where I import a database of inventory items with room and can modify inventory according to changes. I'm struggling with the LazyColumn.
What I need: When an item from LazyColumn is clicked, show alert dialog with item info.
What happens: When an item from LazyColumn is clicked, it shows all the items alert dialog (generates 100+ alert dialogs and only the last one is visible).
Application Main page with LazyColumn:
After clicking on item shows huge shadow of 100+ Alert dialogs and only the last item is visible:
//custom alert dialog taking parameters from the database class of the inventory items
#Composable
fun ItemAlertDialog(
onDismiss: () -> Unit,
onNegativeClick: () -> Unit,
onPositiveClick: () -> Unit,
currentInventory: Int,
itemDescription: String,
itemNumber: String,
) {
var incoming by remember { mutableStateOf(0f) }
var outgoing by remember { mutableStateOf(0f) }
Dialog(onDismissRequest = onDismiss) {
Card(
elevation = 8.dp,
shape = RoundedCornerShape(12.dp)
) {
Column(modifier = Modifier.padding(8.dp)) {
Text(
text = itemNumber,
fontWeight = FontWeight.Bold,
fontSize = 20.sp,
modifier = Modifier.padding(8.dp)
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = itemDescription,
fontWeight = FontWeight.Normal,
fontSize = 15.sp,
modifier = Modifier.padding(8.dp)
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = currentInventory.toString(),
fontWeight = FontWeight.ExtraBold,
color = Color(0xFF0FFC107),
fontSize = 20.sp,
modifier = Modifier.padding(8.dp)
)
// Update Inventory
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Column {
Text(text = "Incoming ${incoming.toInt()}")
Slider(
value = incoming,
onValueChange = { incoming = it },
valueRange = 0f..100f,
onValueChangeFinished = {}
)
Spacer(modifier = Modifier.height(8.dp))
Text(text = "Outgoing ${outgoing.toInt()}")
Slider(
value = outgoing,
onValueChange = { outgoing = it },
valueRange = 0f..100f,
onValueChangeFinished = {}
)
}
}
// Buttons
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier.fillMaxWidth()
) {
TextButton(onClick = onNegativeClick) {
Text(text = "CANCEL")
}
Spacer(modifier = Modifier.width(4.dp))
TextButton(onClick = {
onPositiveClick.invoke()
}) {
Text(text = "OK")
}
}
}
}
}
}
Items row code with the onclick I want to fire the dialog with:
#Composable
fun MainScreenItemRow(
itemNumber: String,
itemDescription: String,
currentInventory: Int,
onclick: () -> Unit
) {
Row(modifier = Modifier
.clickable {
onclick.invoke()
}
.border(width = 1.dp, color = Color.Gray, shape = RoundedCornerShape(8.dp))
.padding(16.dp)
.clip(shape = RoundedCornerShape(8.dp)),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = itemNumber,
fontSize = 12.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.weight(3f)
)
Spacer(modifier = Modifier.width(10.dp))
Text(
text = itemDescription,
fontSize = 12.sp,
maxLines = 3,
modifier = Modifier.weight(4f)
)
Spacer(modifier = Modifier.width(30.dp))
Text(
text = currentInventory.toString(),
fontSize = 20.sp,
color = Color(0xFF0FFC107),
modifier = Modifier.weight(1f)
)
}
}
MainActivity code (apologies for messy code, notes annotated with //***):
#AndroidEntryPoint
class MainActivity : ComponentActivity() {
private val mainViewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val systemUiController = rememberSystemUiController()
val useDarkIcons = MaterialTheme.colors.isLight
val inventoryList by mainViewModel.getInventoryItems.collectAsState(initial = emptyList())
val showItemDialog = remember { mutableStateOf(false)}
val result = remember { mutableStateOf("") }
val expanded = remember { mutableStateOf(false) }
val randomList = listOf("")
val selectedItem = remember{ mutableStateOf(randomList[0])}
//*** getting inventoryList from the flow, added remember to selectedItem and showItemDialog
SimpleInventoryTheme {
ItemModelDrawer{drawerState, drawerScopeState ->
SideEffect {
systemUiController.setSystemBarsColor(
color = Color.Transparent,
darkIcons = useDarkIcons
)
}
Scaffold(
topBar = {
TopAppBar(
title = {
Text(text = "Inventory Manager")
},
actions = {
IconButton(onClick = {
drawerScopeState.launch {
drawerState.open()
}
}) {
Icon(Icons.Outlined.Search, contentDescription = "Search")
}
Box(
Modifier
.wrapContentSize(Alignment.TopEnd)
) {
IconButton(onClick = {
expanded.value = true
result.value = "More icon clicked"
}) {
Icon(
Icons.Filled.MoreVert,
contentDescription = "Localized description"
)
}
DropdownMenu(
expanded = expanded.value,
onDismissRequest = { expanded.value = false },
) {
DropdownMenuItem(onClick = {
expanded.value = false
result.value = "First item clicked"
}) {
Text("First Item")
}
Divider()
DropdownMenuItem(onClick = {
expanded.value = false
result.value = "Second item clicked"
}) {
Text("Second item")
}
}
}
},
backgroundColor = Color.White,
elevation = AppBarDefaults.TopAppBarElevation
)
},
content = {
Column(modifier = Modifier.fillMaxSize()) {
MainScreenTitlesRow()
Divider()
//*** Showing the items in the main page and adding onclick to fire up the alert dialog
LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
itemsIndexed(inventoryList) { idx, item ->
item.currentInventory?.let {
MainScreenItemRow(
itemNumber = item.itemNumber,
itemDescription = item.itemDescription,
currentInventory = it,
onclick = {
//*** trying to set the clicked item into "selectedItem" val
selectedItem.value = item.itemNumber
selectedItem.value = item.itemDescription
selectedItem.value = item.currentInventory.toString()
Log.d("MainActivityItemClicked", "$selectedItem ")
showItemDialog.value = true
}
)
}
}
}
//*** Trying to check which item was clicked and only present its information in the alert dialog but it loads up all the items
if (showItemDialog.value) {
LazyColumn {
itemsIndexed(inventoryList) { _, item ->
item.currentInventory?.let {
ItemAlertDialog(
itemNumber = item.itemNumber,
itemDescription = item.itemDescription,
currentInventory = it,
onDismiss = {
showItemDialog.value = !showItemDialog.value
Toast.makeText(
this#MainActivity,
"Dialog dismissed!",
Toast.LENGTH_SHORT
)
.show()
},
onNegativeClick = {
showItemDialog.value = !showItemDialog.value
Toast.makeText(
this#MainActivity,
"Cancel!",
Toast.LENGTH_SHORT
)
.show()
},
onPositiveClick = {
showItemDialog.value = !showItemDialog.value
Toast.makeText(
this#MainActivity,
"Saved!",
Toast.LENGTH_SHORT
)
.show()
}
)
}
}
}
}
}
}
)
}
}
}
}
}
Thank you #Philip Dukhov
I've tried doing this previously and couldn't get the item.Number, item.description, item.inventory at all (val not resolved) so used the inventoryList since I could get it at least smething which was the wrong approach per your suggestion.
I learned a thing or two in the last few days struggling, so with your tip I managed to achive what I need. Not sure it's the right way but it works now. On to the next struggle!
//***Modified the selected Item from val selectedItem = remember{ mutableStateOf(randomList[0])} to separate 3:
val selectedItemNumber = remember{ mutableStateOf(randomList[0])}
val selectedItemDescription = remember{ mutableStateOf(randomList[0])}
val selectedItemInventory = remember{ mutableStateOf(randomList[0])}
LazyColumn(
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
) {
itemsIndexed(inventoryList) { idx, item ->
item.currentInventory?.let {
MainScreenItemRow(
itemNumber = item.itemNumber,
itemDescription = item.itemDescription,
currentInventory = it,
onclick = {
selectedItemNumber.value = item.itemNumber
selectedItemDescription.value = item.itemDescription
selectedItemInventory.value = item.currentInventory.toString()
Log.d("MainActivityItemClicked", "$selectedItemDescription ")
showItemDialog.value = true
}
)
}
}
}
if (showItemDialog.value) {
ItemAlertDialog(
itemNumber = selectedItemNumber.value,
itemDescription = selectedItemDescription.value,
currentInventory = selectedItemInventory.value.toInt(),
onDismiss = {
showItemDialog.value = !showItemDialog.value
Toast.makeText(
this#MainActivity,
"Dialog dismissed!",
Toast.LENGTH_SHORT
)
.show()
},
onNegativeClick = {
showItemDialog.value = !showItemDialog.value
Toast.makeText(
this#MainActivity,
"Cancel!",
Toast.LENGTH_SHORT
)
.show()
},
onPositiveClick = {
showItemDialog.value = !showItemDialog.value
Toast.makeText(
this#MainActivity,
"Saved!",
Toast.LENGTH_SHORT
)
.show()
}
)
}
}

Change BottomDrawer's gesturesEnabled according to drawerState in Jetpack Compose?

I have an BottomDrawer but i want that when BottomDrawer is closed the gesturesEnabled should be false else if its open then gesturesEnabled should be true
Here's the
code I know its not my but same
val scope = rememberCoroutineScope()
val drawerState = rememberBottomDrawerState(BottomDrawerValue.Closed)
val gesturesEnabled = !drawerState.isClosed
Column {
Row(
modifier = Modifier.fillMaxWidth()
) {
Checkbox(gesturesEnabled, null)
Text(text = if (gesturesEnabled) "Gestures Enabled" else "Gestures Disabled")
}
BottomDrawer(
gesturesEnabled = gesturesEnabled,
drawerState = drawerState,
drawerContent = {
Button(
modifier = Modifier.align(Alignment.CenterHorizontally).padding(top = 16.dp),
onClick = { scope.launch { drawerState.close() } },
content = { Text("Close Drawer") }
)
LazyColumn {
items(25) {
ListItem(
text = { Text("Item $it") },
icon = {
Icon(
Icons.Default.Favorite,
contentDescription = "Localized description"
)
}
)
}
}
},
content = {
Column(
modifier = Modifier.fillMaxSize().padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
val openText = if (gesturesEnabled) "▲▲▲ Pull up ▲▲▲" else "Click the button!"
Text(text = if (drawerState.isClosed) openText else "▼▼▼ Drag down ▼▼▼")
Spacer(Modifier.height(20.dp))
Button(onClick = { scope.launch { drawerState.open() } }) {
Text("Click to open")
}
}
}
)
}

Jetpack compose: scrolling in dialog gives strange effects

I'm trying to create a dialog where you can pick a number. I use FlowRow from Accompanist. But it gives strange results when scrolling. I guess that I'm missing something here.
The code:
#Composable
fun AlertDialogErrorTest() {
var showDlg by remember { mutableStateOf(false)}
val scrollState = rememberScrollState()
if (showDlg) {
AlertDialog(
onDismissRequest = { showDlg = false },
title = { Text(text = "Pick something from the list") },
text = {
FlowRow(modifier = Modifier.verticalScroll(scrollState)) {
for (i in 1..100) {
Box (modifier = Modifier.size(48.dp).padding(8.dp).background(Color.LightGray), contentAlignment = Alignment.Center) {
Text (i.toString())
}
}
}
},
confirmButton = {
TextButton(onClick = { showDlg = false }) {
Text("Done")
}
},
dismissButton = {
TextButton(onClick = { showDlg = false }) {
Text("Cancel")
}
}
)
}
Button(onClick = {showDlg = true}) {
Text("Show dialog")
}
}
The result when scrolling:
This is because AlertDialog does not currently support scrollable content. If you think this is a bug, you can report it in the Compose issue tracker.
Meanwhile, you can use a plain Dialog, which lays under the AlertDialog.
Dialog(
onDismissRequest = { showDlg = false },
content = {
Column(Modifier.background(Color.White)) {
Text(
text = "Pick something from the list",
style = MaterialTheme.typography.subtitle1,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
FlowRow(
modifier = Modifier
.verticalScroll(scrollState)
.weight(1f)
) {
for (i in 1..100) {
Box(
modifier = Modifier
.size(48.dp)
.padding(8.dp)
.background(Color.LightGray),
contentAlignment = Alignment.Center
) {
Text(i.toString())
}
}
}
FlowRow(
mainAxisSpacing = 8.dp,
crossAxisSpacing = 12.dp,
modifier = Modifier
.align(Alignment.End)
.padding(horizontal = 8.dp, vertical = 2.dp)
) {
TextButton(onClick = { showDlg = false }) {
Text("Cancel")
}
TextButton(onClick = { showDlg = false }) {
Text("Done")
}
}
}
},
)
Result:
I have faced the same issue, and the work around is to pass null in both title , and text, and to put the whole content in the buttons, you might check the below example, and please forgive my naming :).
AlertDialog(
onDismissRequest = {
state.onDismiss(state.data)
},
title = null,
text = null,
buttons = {
Column {
val expandedState =
state.data.expandedState
expandedState?.let {
RenderFieldPerCorrespondingState(it.title)
LazyColumnViewGroupComposable(
viewGroupState = it.itemsState,
scrollTo = scrollTo
)
ViewGroupComposable(viewGroupState = it.actionsState)
}
}
}
)