If I add floating action button to the bottom bar in bottomnavigation as extended, the menu will be covered by location.
If I'm set up in the center, you can see both menus.
But if I leave it as end, you can only see one menu.
When I set it to end, I want to see both menus.
Here is my code
Scaffold(
topBar = {
if (menu.name != null) {
TopAppBarCompose(title = menu.name)
}
},
floatingActionButton = {
FloatingActionButton(
onClick = {
val route = Screen.BarcodeScan.route
onNavigateToBarcodeScreen(route)
},
shape = RoundedCornerShape(50),
backgroundColor = MaterialTheme.colors.primary
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Icon(
imageVector = Icons.Default.QrCodeScanner,
contentDescription = "BarcodeScan",
modifier = Modifier.padding(start = 30.dp, top = 20.dp, bottom = 20.dp, end = 5.dp)
)
Text(
text = "Scan",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(start = 5.dp, top = 20.dp, bottom = 20.dp, end = 30.dp)
)
}
}
},
isFloatingActionButtonDocked = true,
floatingActionButtonPosition = FabPosition.End,
bottomBar = {
BottomAppBar(
cutoutShape = RoundedCornerShape(50),
content = {
BottomNavigation {
BottomNavigationItem(
selected = selectedItem.value == "send",
onClick = {
content.value = "Send Screen"
selectedItem.value = "send"
},
icon = {
Icon(Icons.Filled.SwapVert, contentDescription = "send")
},
label = { Text(text = "send") },
alwaysShowLabel = false
)
BottomNavigationItem(
selected = selectedItem.value == "input",
onClick = {
content.value = "input Screen"
selectedItem.value = "input"
},
icon = {
Icon(Icons.Filled.Create, contentDescription = "input")
},
label = { Text(text = "Input") },
alwaysShowLabel = false
)
}
}
)
}
)
When I set in center, I can see two menu
But I set extened fab button to end, It's going to be as follows.
I want to set the place of the extended fab button to end and see two menus. Is there a way to change the place?
ps.The image was simply uploaded to help understand.
I think all you need is to just offset the FAB using the offset modifier. Here's an example:
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomEnd) {
var selectedItem by remember { mutableStateOf(0) }
val items = listOf("Songs", "Artists", "Playlists")
BottomNavigation {
items.forEachIndexed { index, item ->
BottomNavigationItem(
icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
label = { Text(item) },
selected = selectedItem == index,
onClick = { selectedItem = index }
)
}
}
ExtendedFloatingActionButton(
modifier = Modifier.offset(x = -10.dp, y = -70.dp),
icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
text = { Text("ADD TO BASKET") },
onClick = { /*do something*/ }
)
}
FAB inside a scaffold
val scaffoldState = rememberScaffoldState()
val scope = rememberCoroutineScope()
var selectedItem by remember { mutableStateOf(0) }
val items = listOf("Songs", "Artists", "Playlists")
Scaffold(
scaffoldState = scaffoldState,
floatingActionButtonPosition = FabPosition.End,
floatingActionButton = {
ExtendedFloatingActionButton(
modifier = Modifier.offset(x = -10.dp, y = -70.dp),
icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
text = { Text("ADD TO BASKET") },
onClick = { /*do something*/ }
)
},
content = { innerPadding ->
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomStart) {
BottomNavigation {
items.forEachIndexed { index, item ->
BottomNavigationItem(
icon = { Icon(Icons.Filled.Favorite, contentDescription = null) },
label = { Text(item) },
selected = selectedItem == index,
onClick = { selectedItem = index }
)
}
}
}
}
)
Related
I'm making a TextField with ExposedDropDownMenu for selecting a Country.
When i select something i use mutable state variable 'isReadOnly' for setting the readOnly parameter of TextField composable.
So when I select a country, i want it to be readOnly, but when I click on dismiss icon, I want it to not be readOnly.
I works very well, except one part, when I click on dismiss icon, I can type to my TextField, but software keyboard is not showing up. Do you know the solution to this problem?
val selectedFromController = selected.value
var selectedItem by remember {
mutableStateOf(
selectedFromController.ifEmpty { "" }
)
}
var isReadOnly by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = expanded.value,
onExpandedChange = { expanded.value = !expanded.value }
) {
TextField(
value = selectedItem,
onValueChange = {
selectedItem = it
expanded.value = true
},
readOnly = isReadOnly,
modifier = Modifier
.menuAnchor()
.fillMaxWidth(),
label = { Text(stringResource(id = label)) },
trailingIcon = { IconButton(
onClick = {
selectedItem = ""
selected.value = ""
savedId.value = -1
isReadOnly = false
keyboardController.show()
},
content = {
Icon(Icons.Filled.Clear, null)
}
) },
colors = ExposedDropdownMenuDefaults.textFieldColors()
)
val filteringOptions =
menuItems.filter { it.contains(selectedItem, ignoreCase = true) }
if (filteringOptions.isNotEmpty()) {
ExposedDropdownMenu(
expanded = expanded.value,
onDismissRequest = {
expanded.value = false
}
) {
filteringOptions.take(5).map {
DropdownMenuItem(
text = { Text(text = it) },
onClick = {
selectedItem = it
selected.value = it
expanded.value = false
isReadOnly = true
},
contentPadding = ExposedDropdownMenuDefaults.ItemContentPadding
)
}
}
}
}
I tried to bring it up with keyboardController, but it didn't help.
I am doing a post request, show the value in an alert dialog box and pass that information to another composable.
I don't know how to show the result in an alert dialog box and pass the information to another composable.
Here is what I have done so far.
#Composable
fun Lookup() {
val number = remember {
mutableStateOf(TextFieldValue())
}
val name = remember {
mutableStateOf(TextFieldValue())
}
val response = remember {
mutableStateOf("")
}
var showDialog by remember { mutableStateOf(false) }
if (showDialog) {
LookupDialog()
}
val openDialog by viewModel.open.observeAsState(false)
if (openDialog){
DialogBoxLoading()
}
Column(
modifier = Modifier.padding(20.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(20.dp))
TextField(
label = { Text(text = "Number") },
value = number.value,
onValueChange = { number.value = it })
Spacer(modifier = Modifier.height(20.dp))
Box(modifier = Modifier.padding(40.dp, 0.dp, 40.dp, 0.dp)) {
Button(
onClick = {
postDataUsingRetrofit("fjsjsdf")
LookupDialog
},
shape = RoundedCornerShape(50.dp),
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
) {
Text(text = "Search Number")
}
}
}
}
fun postDataUsingRetrofit(number: String) {
val url = "https://baseurl/slookup"
val postBody = String.format(
"""{
"number": "%1${"$"}s",
"name": "%2${"$"}s"
}""", number, name
)
val requestBody = postBody.toRequestBody()
val okHttpClient = OkHttpClient()
val retrofit = Request.Builder()
.method("POST", requestBody)
.url(url)
.build()
okHttpClient.newCall(retrofit).enqueue(object : okhttp3.Callback {
override fun onFailure(call: okhttp3.Call, e: IOException) {
Log.d("itfailed", e.toString())
}
override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
val json = JSONObject(response.body?.string())
val responseStatus = json.getString("status")
val responseDesc = json.getString("desc")
}
})
}
#Composable
fun LookupDialog(title: String = "",
isSuccessful: Boolean = false,
content: (#Composable () -> Unit)? = null,
clicked: () -> Unit = {}) {
AlertDialog(
onDismissRequest = { },
confirmButton = {
TextButton(onClick = {
//Move to a new composable(page)
})
{ Text(text = "OK") }
},
dismissButton = {
TextButton(onClick = {})
{ Text(text = "Cancel") }
},
title = { Text(text = "This should contain information responseStatus from postDataUsingRetrofit ") },
text = { Text(text = "This should contain information responseDesc from postDataUsingRetrofit ") }
)
}
#Composable
fun NewComposable(action: (Result?) -> Unit) {
Column(
modifier = Modifier.padding(20.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(20.dp))
TextField(
label = { Text(text = "This should contain information responseStatus from postDataUsingRetrofit ") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
value = number.value,
onValueChange = { number.value = it })
Spacer(modifier = Modifier.height(20.dp))
TextField(
label = { Text(text = "This should contain information responseDesc from postDataUsingRetrofit ") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
value = msisdn.value,
onValueChange = { name.value = it })
}
}
After postDataUsingRetrofit is done, the information shows in LookupDialog, that information passed to NewComposable.
Trying to request READ_EXTERNAL_STORAGE to select an image from the users gallery permission but the permissions launcher automatically hits the denied branch of the if statement without even attempting to open the permission dialog. This code works fine on Android 12 and below, but not on Android 13. Does anyone know what could be causing this issue?
#Composable
fun CreateComposable(
navigateToStorePreview: (String) -> Unit
) {
val viewModel = hiltViewModel<CreateViewModel>()
val state by viewModel.state.collectAsState()
LaunchedEffect(key1 = viewModel.effects) {
viewModel.effects.collect { effect ->
when (effect) {
}
}
}
CreateScreen(state = state, eventHandler = viewModel::postEvent)
}
#OptIn(ExperimentalCoilApi::class)
#Composable
internal fun CreateScreen(
state: CreateState,
eventHandler: (CreateEvent) -> Unit
) {
val context = LocalContext.current
val galleryLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.GetContent()
) { uri: Uri? ->
uri?.let { eventHandler(CreateEvent.SetUriFromCamera(uri = uri)) }
}
val permissionLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
galleryLauncher.launch("image/*")
} else {
showToast(context, context.getString(R.string.permissions_denied_resolution))
}
}
Column(
modifier = Modifier
.padding(horizontal = 16.dp)
.padding(bottom = 24.dp)
.fillMaxSize()
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.SpaceBetween,
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(modifier = Modifier.height(1.dp))
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
OutlinedTextField(
modifier = Modifier
.widthIn(200.dp),
value = state.flashCardName,
onValueChange = {
eventHandler(CreateEvent.FlashCardNameUpdated(input = it))
},
colors = TextFieldDefaults.outlinedTextFieldColors(
backgroundColor = Color.Transparent,
focusedBorderColor = Color.Transparent,
unfocusedBorderColor = Color.Transparent,
cursorColor = WildBlueYonder
),
label = {
Text(
text = stringResource(id = R.string.enter_flash_card_name),
color = Color.LightGray
)
},
leadingIcon = {
Image(
painter = painterResource(id = R.drawable.add),
contentDescription = null,
colorFilter = ColorFilter.tint(WildBlueYonder)
)
},
textStyle = TextStyle(
color = WildBlueYonder,
fontFamily = Baloo2,
fontSize = 16.sp
),
shape = RoundedCornerShape(16.dp),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Password
)
)
if (state.imageUri.isEmpty()) {
Box(
modifier = Modifier
.padding(horizontal = 16.dp)
.height(300.dp)
.width(200.dp)
.clip(RoundedCornerShape(16.dp))
.border(4.dp, WildBlueYonder, RoundedCornerShape(16.dp))
.clickable {
when (ContextCompat.checkSelfPermission(
context,
Manifest.permission.READ_EXTERNAL_STORAGE
)) {
PackageManager.PERMISSION_GRANTED -> {
galleryLauncher.launch("image/*")
}
else -> {
permissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
}
}
}
) {
Image(
modifier = Modifier.align(Alignment.Center),
painter = painterResource(id = R.drawable.add),
contentDescription = null,
colorFilter = ColorFilter.tint(TeaGreen)
)
}
} else {
Image(
modifier = Modifier
.height(300.dp)
.width(200.dp),
painter = rememberImagePainter(data = state.imageUri.toUri()),
contentDescription = null
)
}
}
BouncyButton(
modifier = Modifier.fillMaxWidth(),
enabled = true,
text = stringResource(id = R.string.bottom_bar_create),
onClick = {
eventHandler(CreateEvent.CreateClicked)
}
)
}
}
Hi i want to show popup when something happens . I have this popup:
#Composable
fun popup(message:String,height:Dp,width:Dp,icon:String=""){
Column() {
val openDialog = remember { mutableStateOf(true) }
val dialogWidth = width/(1.3F)
val dialogHeight = height/2
if (openDialog.value) {
Dialog(onDismissRequest = { openDialog.value = false }) {
// Draw a rectangle shape with rounded corners inside the dialog
Box(
Modifier
.size(dialogWidth, dialogHeight)
.background(Color.White)){
Column(modifier = Modifier.fillMaxWidth().padding()) {
Text(text = message)
}
}
}
}
Button(onClick = {
openDialog.value=!openDialog.value
}) {
}
}
}
But i am trying to call him inside onclick Button event :
Button(modifier = Modifier
.padding(start = 6.dp, end = 6.dp),
colors = ButtonDefaults.buttonColors(backgroundColor = Azul99),
onClick = {
if (vm.validateCredentials()=="ok"){
vm.createUser()
}else{
popup(vm.validateCredentials(),200.dp,200.dp,"fill")
}
},
shape = RoundedCornerShape(percent = 28)
) {
Text(text = "Registrarme",
modifier= Modifier.fillMaxWidth(),
style= TextStyle(fontWeight = FontWeight.Bold),
color= Color.White,
textAlign = TextAlign.Center)
}
and Android Studio says: "#Composable invocations can only happen from the context of a #Composable function" How can i call the popup ??
Store showPopUp boolean as state and show popUp by that state;
val showPopUp by remember { mutableStateOf(false)} // -> STATE
Button(
modifier = Modifier
.padding(start = 6.dp, end = 6.dp),
colors = ButtonDefaults.buttonColors(backgroundColor = Azul99),
onClick = {
if (vm.validateCredentials()=="ok"){
vm.createUser()
}else{
showPopUp = !showPopUp // -> CHANGE IN HERE
}
},
shape = RoundedCornerShape(percent = 28)
) {
Text(
text = "Registrarme",
modifier= Modifier.fillMaxWidth(),
style= TextStyle(fontWeight = FontWeight.Bold),
color= Color.White,
textAlign = TextAlign.Center
)
}
if(showPopUp){
popup(vm.validateCredentials(),200.dp,200.dp,"fill") // -> SHOW HERE
}
Change
In the previous text field, when focused, there was a method in which all letters were selected. I found a way to make it remembered on the screen, but I wonder how to do it on the mvvm pattern.
#Composable
fun MainScreen(text: String, viewModel: HomeViewModel) {
val textState = remember { mutableStateOf(TextFieldValue()) }
val state = viewModel.mainState.text.collectAsState()
Column(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = state.value,
color = Color.Blue,
fontSize = 40.sp
)
Button(
onClick = { viewModel.mainState.text.value = "New text" },
colors = ButtonDefaults.buttonColors(
backgroundColor = Color.Green
),
modifier = Modifier.padding(16.dp)
) {
Text(text)
}
TextField(
value = textState.value,
onValueChange = { textState.value = it },
label = { Text("Input text") }
)
}
}
The code above is from screen to remeber.
But I understand that remember is only declared within #Composable.
The view model does not declare #Composable, so I want to know how to do it in the mvvm pattern.
Below is my code.
LoginScreen
val text = viewModel.user_id.value
OutlinedTextField(
value = barcode,
onValueChange = {
viewModel.changeBarcode(it)
},
modifier = Modifier
.fillMaxWidth()
.padding(all = 4.dp)
.onFocusChanged { focusState ->
if (focusState.isFocused) {
//monitor value
}
},
label = { Text(text = "Barcode") },
singleLine = true,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = {
keyboardController?.hide()
viewModel.onTriggerEvent(MenuStateEvent.ScanEvent)
}
)
)
LoginViewModel
val user_id: MutableState<String> = mutableStateOf("")
How change it to mvvm pattern?
Maybe this will be the method you need.
in ViewModel:
val user_id = mutableStateOf(TextFieldValue(""))
in Compose:
TextField(
value = viewModel.user_id.value,
onValueChange = { viewModel.user_id.value = it },
label = { Text("Input text") }
)
Just change the mutableStateOf in ViewModel from "String" to "TextFieldValue(String)" to access it.