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

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()
}
)
}
}

Related

How can I put the bottom bar inside the surface in kotlin compose?

I want the bottom bar to be where I show it at the bottom, but it will overlap with the surface, so I want the bottom bar at the bottom and the bottom bar on the surface. I tried to put it in the box, but I couldn't do that and I don't want to create your own dictionary text corrupted, I want to adjust accordingly. I couldn't adapt this to my own code. Please help me how can I do this.
my screen
#Composable
fun CreateDicScreen() {
var text = remember { mutableStateOf("") }
Card()
Surface(color = Color.White, modifier = Modifier
.requiredHeight(600.dp)
.fillMaxWidth(),
shape = RoundedCornerShape(60.dp).copy(topStart = ZeroCornerSize, topEnd = ZeroCornerSize)) {
Column(modifier = Modifier
.padding(5.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
painter = painterResource(id = R.drawable.resim ),
contentDescription = "image",
modifier = Modifier
.padding(5.dp)
.requiredHeight(300.dp)
.requiredWidth(300.dp)
)
Spacer(modifier = Modifier.padding(16.dp))
OutlinedTextField(
value = text.value,
onValueChange = { text.value = it },
modifier= Modifier
.fillMaxWidth()
.padding(5.dp)
,
textStyle = TextStyle(color = Color.Black),
label = { Text(text = "dictionary name") },
colors = TextFieldDefaults.outlinedTextFieldColors(
//focusedBorderColor = Color.Blue,
unfocusedBorderColor = Color.Blue
),
)
Spacer(modifier=Modifier.padding(5.dp))
Button(onClick = {},
modifier= Modifier
.padding(5.dp)
.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(
backgroundColor = orangish,
contentColor = Color.White),
shape = shapes.medium,
contentPadding = PaddingValues(8.dp),
) {
Text(text = "Save")
}
}
}
}
#Composable
fun Card(){
Surface(color = purplish, modifier = Modifier.fillMaxSize()) {
Column(verticalArrangement = Arrangement.Bottom,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.offset(y = (-30).dp)
) {
Spacer(modifier = Modifier.padding(vertical = 8.dp))
Text(text = "create your own dictionary", color = Color.White)
}
}
}
#Composable
fun bottomBar(){
var bottomState = remember {
mutableStateOf("Home")
}
Scaffold(
bottomBar = {
BottomNavigation(
modifier = Modifier
.clip(shape = RoundedCornerShape(topStart = 30.dp, topEnd = 30.dp)),
backgroundColor = Color(0xFFFEDBD0),
contentColor = Color(0xFF442c2E)
) {
BottomNavigationItem(
selected = bottomState.equals("Home") ,
onClick = { bottomState.equals("Home")},
label = { Text(text = "Home")},
icon = { Icon(imageVector = Icons.Default.Home, contentDescription = null)}
)
BottomNavigationItem(
selected = bottomState.equals("Account") ,
onClick = { bottomState.equals("Account")},
label = { Text(text = "Account")},
icon = { Icon(imageVector = Icons.Default.AccountCircle, contentDescription = null)}
)
BottomNavigationItem(
selected = bottomState.equals("Search") ,
onClick = { bottomState.equals("Search")},
label = { Text(text = "Search")},
icon = { Icon(imageVector = Icons.Default.Search, contentDescription = null)}
)
BottomNavigationItem(
selected = bottomState.equals("Setting") ,
onClick = { bottomState.equals("Setting") },
label = { Text(text = "Setting")},
icon = { Icon(imageVector = Icons.Default.Settings, contentDescription = null)}
)
}
}
){
}
}
my screen right now
First, you can show bottomNavigation with Scaffold as you wrote in your code, but you don't need to use Scaffold if it's only purpose is to show bottomNavigation (inside your bottomBar() composable). You can use Box.
#Composable
fun bottomBar() {
val bottomState = remember {
mutableStateOf("Home")
}
Box {
BottomNavigation(
modifier = Modifier
.clip(shape = RoundedCornerShape(topStart = 30.dp, topEnd = 30.dp)),
backgroundColor = Color(0xFFFEDBD0),
contentColor = Color(0xFF442c2E)
) {
BottomNavigationItem(
selected = bottomState.equals("Home"),
onClick = { bottomState.equals("Home") },
label = { Text(text = "Home") },
icon = { Icon(imageVector = Icons.Default.Home, contentDescription = null) }
)
BottomNavigationItem(
selected = bottomState.equals("Account"),
onClick = { bottomState.equals("Account") },
label = { Text(text = "Account") },
icon = {
Icon(imageVector = Icons.Default.AccountCircle,
contentDescription = null)
}
)
BottomNavigationItem(
selected = bottomState.equals("Search"),
onClick = { bottomState.equals("Search") },
label = { Text(text = "Search") },
icon = { Icon(imageVector = Icons.Default.Search, contentDescription = null) }
)
BottomNavigationItem(
selected = bottomState.equals("Setting"),
onClick = { bottomState.equals("Setting") },
label = { Text(text = "Setting") },
icon = { Icon(imageVector = Icons.Default.Settings, contentDescription = null) }
)
}
}
}
To show your bottomBar() on screen, you have several ways to do that. You can use Box again:
#Composable
fun CreateDicScreen() {
var text = remember { mutableStateOf("") }
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
Card()
bottomBar()
Box(modifier = Modifier.fillMaxHeight()) {
Surface(
color = Color.White,
modifier = Modifier
.requiredHeight(600.dp)
.fillMaxWidth(),
shape = RoundedCornerShape(60.dp).copy(topStart = ZeroCornerSize,
topEnd = ZeroCornerSize)
) {
...
}
}
}
}
With this approach we were forced to set contentAlignment to Bottom, and to wrap Surface with another Box layout and fill height of it.
Second way if you want bottomBar() to be in your Surface (inside composable "Card" (as you named it)), you can do that like this:
#Composable
fun CreateDicScreen() {
var text = remember { mutableStateOf("") }
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
Card()
Box(modifier = Modifier.fillMaxHeight()) {
Surface(
color = Color.White,
modifier = Modifier
.requiredHeight(600.dp)
.fillMaxWidth(),
shape = RoundedCornerShape(60.dp).copy(topStart = CornerSize(0.dp),
topEnd = CornerSize(0.dp))
) {
...
}
}
}
}
#Composable
fun Card() {
Surface(color = Color.Magenta, modifier = Modifier.fillMaxSize()) {
Column(
verticalArrangement = Arrangement.Bottom,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Spacer(modifier = Modifier.padding(vertical = 8.dp))
bottomBar()
}
}
}
and the bottomBar() stays as I mentioned above.
Extra:
I recommend you to use Scaffold for you design (if you don't have specific reason to put your bottomBar in Surface) because it has all features you need here. Here is your design only with Scaffold:
#Composable
fun CreateDicScreen() {
var text = remember { mutableStateOf("") }
Scaffold(
modifier = Modifier
.fillMaxSize(),
bottomBar = { bottomBar() }
) {
Surface(
color = Color.LightGray,
modifier = Modifier
.requiredHeight(600.dp)
.fillMaxWidth(),
shape = RoundedCornerShape(60.dp).copy(topStart = CornerSize(0.dp),
topEnd = CornerSize(0.dp))
) {
...
}
}
}
P.S. While trying to run your code on my Android I've received several times
java.lang.IllegalArgumentException: Corner size in Px can't be negative(topStart = 78.75, topEnd = -196350.0, bottomEnd = 0.0, bottomStart = 0.0)!
change your ZeroCornerSize for Surface to CornerSize(0.dp) to avoid this error.
Your Screen on phone

Push Front View up with Keyboard, don't push Background View up in Kotlin Compose

I want to do something like this:
Where there is a view that pops on top of the open keyboard.
I've tried to do this, and I have this so far:
However, when I put this view in a Box, as the second view, the first view is also pushed up, here's the code:
#OptIn(ExperimentalFoundationApi::class, ExperimentalLayoutApi::class)
#ExperimentalComposeUiApi
fun Modifier.bringIntoViewAfterImeAnimation(): Modifier = composed {
var focusState by remember { mutableStateOf<FocusState?>(null) }
val relocationRequester = remember { BringIntoViewRequester() }
val isImeVisible = WindowInsets.isImeVisible
LaunchedEffect(
isImeVisible,
focusState,
relocationRequester
) {
if (isImeVisible && focusState?.isFocused == false) {
relocationRequester.bringIntoView()
}
relocationRequester.bringIntoView()
}
bringIntoViewRequester(relocationRequester).onFocusChanged { focusState = it }
}
#OptIn(ExperimentalComposeUiApi::class)
#Composable
fun SpaceCreator(navController: NavController) {
Column(
modifier = Modifier.fillMaxSize().clip(RoundedCornerShape(10.dp)),
verticalArrangement = Arrangement.Bottom
) {
SpaceCreatorContainer()
}
}
#OptIn(ExperimentalComposeUiApi::class)
#Composable
fun SpaceCreatorContainer() {
Card(
modifier = Modifier
.bringIntoViewAfterImeAnimation()
.shadow(elevation = 10.dp, shape = RoundedCornerShape(10.dp), clip = true)
.background(color = colors.background)
) {
SpaceCreatorWrapper()
}
}
#Composable
fun SpaceCreatorWrapper() {
val localFocusManager = LocalFocusManager.current
Column(
modifier = Modifier.padding(15.dp).clip(RoundedCornerShape(10.dp))
) {
Text(
modifier = Modifier.fillMaxWidth(),
text = "Top Text"
)
Text(text = "Content")
OutlinedTextField(
value = "ss",
onValueChange = { },
label = { Text("Email Address") },
singleLine = true,
modifier = Modifier.fillMaxWidth(),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Email,
imeAction = ImeAction.Done,
),
keyboardActions = KeyboardActions(
onDone = { localFocusManager.clearFocus() }
)
)
OutlinedTextField(
value = "ss",
onValueChange = { },
label = { Text("Email Address") },
singleLine = true,
modifier = Modifier.fillMaxWidth(),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Email,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = { localFocusManager.clearFocus() }
)
)
Text(text = "Content")
}
}
That's the code for SpaceCreator(), and then I add it to a Box where it's supposed to float over another view, like so:
BoxWithConstraints(
propagateMinConstraints = true
) {
Box(
modifier = Modifier
.fillMaxSize()
) {
MainView()
SpaceCreator(navController = navController)
}
}
How do I only push up the second view in the Box when the keyboard is opened, and not the entire box?
Also,
Currently, I have a AnimationNavigation screen which is a Box(fillMaxSize), and the content of the keyboard modal stuck to the bottom. I also have a auto focus on the input, which is making the navigation a bit slow, unlike other apps I've seen.
Is there a smooth way to do this on low-mid-range devices like Samsung A23?
Thank you.

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")
}
}
}
)
}

How to move DropdownMenu to preferable location in Jepack Compose

Is it possible to center DropdownMenu in my example? or show it wherever I click or tap?
I tried alignment and arrangements and none of them work. I prefer showing the DropdownMenu wherever I tab but I couldn't find a way to do it.
fun main() = Window {
var helloText by remember { mutableStateOf("") }
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Item("Darian", "Russ") {
helloText = it
}
Item("Maynerd", "Andre") {
helloText = it
}
Item("Sandra", "Victoria") {
helloText = it
}
Spacer(modifier = Modifier.height(2.dp))
Text(text = helloText)
}
}
#Composable
fun Item(text: String, text2: String, onMenuTab: (String) -> Unit) {
var expanded by remember { mutableStateOf(false) }
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
val modifier = Modifier.clickable {
expanded = true
}
DropdownMenu(
expanded = expanded,
onDismissRequest = {
expanded = false
}
) {
DropdownMenuItem(onClick = {
onMenuTab("hello $text $text2")
expanded = false
}, modifier = Modifier.align(Alignment.CenterHorizontally)) {
Text("Hello")
}
DropdownMenuItem(onClick = { /* Handle settings! */ }) {
Text("Settings")
}
Divider()
DropdownMenuItem(onClick = { /* Handle send feedback! */ }) {
Text("Send Feedback")
}
}
Text(text = text, modifier = modifier)
Text(text = text2, modifier = modifier)
Divider(modifier = Modifier.height(2.dp))
Spacer(modifier = Modifier.height(2.dp))
}
}
For me fixing the alignment of the popup did the trick-
Popup(alignment = Alignment.TopStart)
Earlier it was Popup(alignment = Alignment.CenterStart)
and that was taking my popup view to the top of the screen. But now comes below the clicked item.

Navigation in Jetpack Compose

I am working on this shopping app which uses radio buttons. The customer should be able to select a radio button and go it by clicking the 'select button'.
Can anyone help me get get started.
the code is below.
I hope this makes sense to everyone. I have made a page for each of the radio buttons. pages are name the same as the buttons.
#Composable
fun MyRadioGroup() {
val navController = rememberNavController()
val radioOptions = listOf(
"Dollar Store", "Fred Myers",
"Wall Mart", "SafeWay", "Sherm's", "CostCo", "Other"
)
val (selectedOption, onOptionSelected) = remember {
mutableStateOf(radioOptions[1])
}
Scaffold() {
TopAppBar(
title = { Text(stringResource(id = R.string.app_name)) },
)
}
Column(
modifier = Modifier
.padding(start = 16.dp, top = 160.dp)
) {
Text(
text = "Select Store from List Below")
Divider()
Text(
text = "Then push the 'Select'")
}
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
radioOptions.forEach { text ->
Row(
Modifier
.fillMaxWidth()
.padding(top = 8.dp)
.selectable(
selected = (text == selectedOption),
onClick = { onOptionSelected(text) }
)
.padding(horizontal = 16.dp)
) {
RadioButton(
selected = (text == selectedOption),
modifier = Modifier
.padding(start = 16.dp, top = 12.dp),
onClick = {
onOptionSelected(text)
}
)
Text(
text = text,
modifier = Modifier.padding(start = 16.dp, top = 16.dp)
)
}
}
}
Column(modifier = Modifier
.padding(start = 24.dp, top = 640.dp)
) {
Button(onClick = {
TODO()
}) {
Text(text = "Select")
}
}
}
The 'select' button is near the bottom of the code.
First create a sealed class Screen to link radio buttons text to composable as recommended by Google like this:
sealed class Screen(val route: String, val label: String){
object MyRadioGroup: Screen(route = "myRadioGroup", label = "APP NAME")
object DollarStore: Screen(route = "Dollar Store", label = "Dollar Store")
object FreedMyers: Screen(route = "Fred Myers", label = "FreedMyers")}
then modify NavHost or AnimatedNavHost(if you are using accompanist's Navigatio Animatation library as:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val navController = rememberNavController()
Surface(color = MaterialTheme.colors.background) {
NavHost(navController = navController, startDestination = Screen.MyRadioGroup.route){
composable(Screen.MyRadioGroup.route){
MyRadioGroup(navController = navController)
}
composable(Screen.DollarStore.route){
//Call here the associated composable function
DollarStore()
}
composable(Screen.FreedMyers.route){
//Call here the associated composable function
FreedMyers()
}
}
}
}
}}
Finally add some code to MyRadioGroup Composable
fun MyRadioGroup(navController: NavController) {
val radioOptions = listOf(
"Dollar Store", "Fred Myers",
"Wall Mart", "SafeWay", "Sherm's", "CostCo", "Other"
)
val (selectedOption, onOptionSelected) = remember {
mutableStateOf(radioOptions[1])
}
Scaffold(
topBar = {
TopAppBar(
title = { Text(stringResource(id = R.string.app_name)) },
)
}
) {
Column(modifier = Modifier.verticalScroll(rememberScrollState())) {
Column(
modifier = Modifier.padding(start = 16.dp, top = 16.dp)
) {
Text(text = "Select Store from List Below")
Divider()
Text(text = "Then push the 'Select'")
}
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally,
) {
radioOptions.forEach { text ->
Row(
Modifier
.fillMaxWidth()
.padding(top = 8.dp)
.selectable(
selected = (text == selectedOption),
onClick = { onOptionSelected(text) }
)
.padding(horizontal = 16.dp)
) {
RadioButton(
selected = (text == selectedOption),
modifier = Modifier
.padding(start = 16.dp, top = 12.dp),
onClick = { onOptionSelected(text) }
)
Text(
text = text,
modifier = Modifier.padding(start = 16.dp, top = 16.dp)
)
}
}
}
Column(
modifier = Modifier
.padding(start = 24.dp, top = 24.dp)
) {
Button(
onClick = {
navController.navigate(selectedOption) //navigate to selected route
}
) {
Text(text = "Select")
}
}
}
}}