Show result from a post request in alert dialog box and send the details to another composable - android-jetpack-compose

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.

Related

Alarm runs immediately instead of specified time with jetpack compose

I am trying to set an alarm using a TimePicker but the alarm ignores the time sent by parameter and instead it is executed immediately !!
This is my code:
class MainActivity : ComponentActivity() {
lateinit var navController: NavHostController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
navController = rememberNavController()
NotePadReminderTheme {
SetupNavGraph(navController = navController)
}
}
}
}
#RequiresApi(Build.VERSION_CODES.M)
fun createAlarm(context: Context,time:Long){
val alarmManager =context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
val intent =Intent(context, AlarmReceiver::class.java)
Log.d("receivedtime","time $time")
val pendingIntent = PendingIntent.getBroadcast(context, Date().seconds, intent, PendingIntent.FLAG_IMMUTABLE)
alarmManager?.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, time, pendingIntent)
Toast.makeText(context,"Alarma creada",Toast.LENGTH_LONG).show()
}
#RequiresApi(Build.VERSION_CODES.M)
#Composable
fun AddPage(navController: NavController, vm: TaskViewModel = viewModel()) {
val scope= rememberCoroutineScope()
val title = remember {
mutableStateOf("")
}
val description = remember {
mutableStateOf("")
}
val date = remember {
mutableStateOf("")
}
val time = Calendar.getInstance()
val iscompleted = remember {
mutableStateOf(false)
}
val context = LocalContext.current
Column(
modifier = Modifier
.fillMaxSize()
.padding(top = 30.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Agregar Tarea",
style = NoteTheme.typography.h1
)
RowOfThis(title, "Título", true)
RowOfThis(description, "Descripción", false)
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
DatePick(date)
}
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
TimePick(time)
}
Button(
onClick = {
if (title.value.isNullOrBlank() || description.value.isNullOrBlank() ||
date.value.isNullOrBlank()
) {
Log.d("date","date:${date.value}")
Toast.makeText(context, "Por favor ingrese todos los datos", Toast.LENGTH_LONG)
.show()
return#Button
}
vm.task.value.title = title.value
vm.task.value.description = description.value
vm.task.value.date = date.value
vm.task.value.time = time.timeInMillis
Log.d("tmepickerreceived","date:${time.timeInMillis}")
scope.launch {
vm.createTask(context, vm.task.value)
createAlarm(context, time=time.timeInMillis). //here set the alarm ****************
Toast.makeText(context, "Tarea creada correctamente", Toast.LENGTH_LONG).show()
vm.resetTask()
}
},
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Blue)
) {
Text(text = "Guardar", color = Color.White, style = NoteTheme.typography.subtitle)
}
}
}
#Composable
fun RowOfThis(value: MutableState<String>, label: String, single: Boolean) {
val focusManager = LocalFocusManager.current
Row(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp)
) {
TextField(
modifier = Modifier
.fillMaxWidth()
.border(BorderStroke(width = 1.dp, color = Color.Black)),
singleLine = single,
maxLines = 3,
textStyle = NoteTheme.typography.body,
value = value.value, onValueChange = {
value.value = it
}, label = {
Text(
text = label, textAlign = TextAlign.Start,
color = Color.DarkGray,
style = NoteTheme.typography.subtitle
)
},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.White),
keyboardActions = KeyboardActions(onNext =
{ focusManager.moveFocus((FocusDirection.Next)) })
)
}
}
#Composable
fun DatePick(mDate:MutableState<String>) {
val mContext = LocalContext.current
val mYear: Int
val mMonth: Int
val mDay: Int
val mCalendar = Calendar.getInstance()
mYear = mCalendar.get(Calendar.YEAR)
mMonth = mCalendar.get(Calendar.MONTH)
mDay = mCalendar.get(Calendar.DAY_OF_MONTH)
mCalendar.time = Date()
val mDatePickerDialog = DatePickerDialog(
mContext,
{ _: DatePicker, mYear: Int, mMonth: Int, mDayOfMonth: Int ->
mDate.value = "$mDayOfMonth/${mMonth + 1}/$mYear"
}, mYear, mMonth, mDay
)
mDatePickerDialog.datePicker.minDate=Date().time
Log.d("datepicker","dateset:${mDate.value}")
Button(modifier = Modifier
.padding(7.dp)
.fillMaxWidth(0.5f), onClick = {
mDatePickerDialog.show()
}, colors = ButtonDefaults.buttonColors(backgroundColor = Color(0XFF227093))) {
Text(text = "Seleccionar fecha",
color = Color.White, style = NoteTheme.typography.subtitle)
}
Text(text = "${mDate.value}",style = NoteTheme.typography.caption, fontSize = 22.sp, color = Color(0XFF227093))
}
#Composable
fun TimePick(mCalendar:Calendar) {
val mContext = LocalContext.current
val mHour = mCalendar[Calendar.HOUR_OF_DAY]
val mMinute = mCalendar[Calendar.MINUTE]
val mTime = remember { mutableStateOf("") }
val mTimePickerDialog = TimePickerDialog(
mContext,
{_, mHour : Int, mMinute: Int ->
mTime.value = "$mHour:$mMinute"
}, mHour, mMinute, false
)
Log.d("tmepicker","time:${mCalendar.timeInMillis}")
Button(modifier = Modifier
.padding(7.dp)
.fillMaxWidth(0.5f), onClick = {
mTimePickerDialog.show()
}, colors = ButtonDefaults.buttonColors(backgroundColor = Color(0XFF2c2c54))) {
Text(text = "Seleccionar hora",
color = Color.White, style = NoteTheme.typography.subtitle)
}
Text(text = "${mTime.value}",style = NoteTheme.typography.caption, fontSize = 22.sp, color = Color(0XFF2c2c54))
}
The problem I have is that I want the alarm to run at the scheduled time of the DatePicker ad TimePicker but it is running immediately !!
I don't know if this has something to do with the context, but how can I make the alarm be "scheduled" at the time and date of the DatePicker and Timepicker instead of firing at once
Well this was because when you get the calendar month you have to add one for some reason, so that was what caused an inaccurate time when setting the alarm. Final code:
class MainActivity : ComponentActivity() {
#RequiresApi(Build.VERSION_CODES.M)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val context= LocalContext.current
AlarmTestTheme {
mainPage(context)
}
}
}
}
#RequiresApi(Build.VERSION_CODES.M)
#Composable
fun mainPage(context: Context) {
val scope= rememberCoroutineScope()
val calendar=Calendar.getInstance().apply {
timeInMillis=System.currentTimeMillis()
}
val title = remember {
mutableStateOf("")
}
val description = remember {
mutableStateOf("")
}
val year= remember {
mutableStateOf(calendar.get(Calendar.YEAR))
}
val month= remember {
mutableStateOf(calendar.get(Calendar.MONTH)+1)
}
val day= remember {
mutableStateOf(calendar.get(Calendar.DAY_OF_MONTH))
}
val hour= remember {
mutableStateOf(calendar.get(Calendar.HOUR_OF_DAY))
}
val minutes= remember {
mutableStateOf(calendar.get(Calendar.MINUTE))
}
val iscompleted = remember {
mutableStateOf(false)
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(top = 30.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
text = "Agregar Tarea"
)
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
DatePick(context = context, year = year, month = month, day = day)
}
Row(modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
TimePick(context = context, hours = hour, minutes = minutes)
}
Button(
onClick = {
Log.d("tmepickerreceived","date and time :${year.value}/${month.value}/${day.value} : ${hour.value}:${minutes.value}")
calendar.apply {
set(Calendar.YEAR,year.value)
set(Calendar.MONTH,(month.value-1))
set(Calendar.DAY_OF_MONTH,day.value)
set(Calendar.HOUR_OF_DAY,hour.value)
set(Calendar.MINUTE,minutes.value)
}
val alarmMgr= context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val shuff=(1..99999).shuffled().first()
val intent= Intent(context,AlarmReceiver::class.java)
intent.action="alarma"
intent.putExtra("ALARMA_STRING","alarm number: $shuff")
val pending=
PendingIntent.getBroadcast(context,shuff,intent, PendingIntent.FLAG_IMMUTABLE)
Log.e("time final","compare ${System.currentTimeMillis()}" +
" mytime: ${calendar.timeInMillis} " +
"hour: ${calendar.get(Calendar.HOUR_OF_DAY)} " +
"minute: ${calendar.get(Calendar.MINUTE)} " +
"year: ${calendar.get(Calendar.YEAR)} " +
"month: ${calendar.get(Calendar.MONTH)+1} " +
"day: ${calendar.get(Calendar.DAY_OF_MONTH)}")
alarmMgr?.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,calendar.timeInMillis,pending)
Toast.makeText(context, "Alarm created", Toast.LENGTH_LONG)
.show()
},
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Blue)
) {
Text(text = "Guardar", color = Color.White)
}
}
}
#Composable
fun DatePick(context: Context, year: MutableState<Int>,month: MutableState<Int>,day: MutableState<Int>) {
val mDatePickerDialog = DatePickerDialog(
context,{d,mYear,mMonth,mDay ->
year.value=mYear
month.value=mMonth+1
day.value=mDay
}, year.value, month.value, day.value
)
mDatePickerDialog.datePicker.minDate=System.currentTimeMillis() - 1000
Log.d("datepicker","${day.value}/${month.value}/${year.value}")
Button(modifier = Modifier
.padding(7.dp)
.fillMaxWidth(0.5f), onClick = {
mDatePickerDialog.show()
}, colors = ButtonDefaults.buttonColors(backgroundColor = Color(0XFF227093))) {
Text(text = "Seleccionar fecha",
color = Color.White)
}
Text(text = "${day.value}/${month.value}/${year.value}", fontSize = 22.sp, color = Color(0XFF227093))
}
#Composable
fun TimePick(context: Context,hours:MutableState<Int>,minutes:MutableState<Int>) {
val mTimePickerDialog = TimePickerDialog(
context,
{_, mHour : Int, mMinute: Int ->
hours.value=mHour
minutes.value=mMinute
}, hours.value, minutes.value, false
)
Log.d("tmepicker","hour selected :${hours.value}: munute selected: ${minutes.value}")
Button(modifier = Modifier
.padding(7.dp)
.fillMaxWidth(0.5f), onClick = {
mTimePickerDialog.show()
}, colors = ButtonDefaults.buttonColors(backgroundColor = Color(0XFF2c2c54))) {
Text(text = "Seleccionar hora",
color = Color.White)
}
Text(text = "${hours.value}:${minutes.value}", fontSize = 22.sp, color = Color(0XFF2c2c54))
}

Jetpack Compose permissions bug

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

Is there a better way to wrap bottom sheet height for ModalBottomSheetLayout/BottomSheetScaffold

My solution works, but sometimes when switching between boxes using openSheet() height of content cannot be calculated in time and it's jumps when opens.
Working solution using ModalBottomSheetLayout:
val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
val scope = rememberCoroutineScope()
val density = LocalDensity.current
var topAppBarHeight = Dp.Unspecified
var sumHeight by remember { mutableStateOf(Dp.Unspecified) }
var currentSheet by remember { mutableStateOf(0) }
val openSheet: (sheet: Int) -> Unit = {
currentSheet = it
sumHeight = topAppBarHeight + (currentSheet * 50).dp
scope.launch { sheetState.show() }
}
val closeSheet: () -> Unit = {
scope.launch { sheetState.hide() }
sumHeight = Dp.Unspecified
}
ModalBottomSheetLayout(
modifier = Modifier.fillMaxSize(),
sheetState = sheetState,
sheetContent = {
Scaffold(
modifier = Modifier.height(sumHeight),
topBar = {
TopAppBar(
modifier = Modifier
.clickable(onClick = closeSheet)
.onGloballyPositioned { lc ->
topAppBarHeight = with(density) { lc.size.height.toDp() }
},
title = { Text("TopAppBarSheetContent") }
)
},
content = {
Box(
modifier = Modifier
.height((currentSheet * 50).dp)
.fillMaxWidth()
.background(Color.Blue)
)
},
)
},
content = {
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
TopAppBar(title = { Text("TopAppBarContent") })
},
content = { innerPadding ->
LazyColumn(
modifier = Modifier.padding(innerPadding),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(50) { item ->
Box(
modifier = Modifier
.clip(CircleShape)
.fillMaxWidth()
.height(75.dp)
.background(Color.Red)
.clickable { openSheet(item) }
)
}
}
}
)
}
)
Maybe there is a better way to solve this problem?

Keyboard does not hide, when focused TextField leaves the LazyColumn

Perhaps this is the normal behaviour, but i wish it was different. I had tried to google the solution, but did not find anything suitable (or merely missed it).
Sample code (for simplicity i hold mutable states right here, not using ViewModel):
#Composable
fun Greeting() {
Scaffold(topBar = {
TopAppBar(title = { Text(text = "Some title") })
}) {
val focusManager = LocalFocusManager.current
LazyColumn(
contentPadding = PaddingValues(all = 16.dp),
verticalArrangement = Arrangement.spacedBy(space = 16.dp)
) {
items(count = 20) { index ->
val (value, onValueChange) = rememberSaveable { mutableStateOf("Some value $index") }
TextField(
value = value,
onValueChange = onValueChange,
modifier = Modifier.fillMaxWidth(),
label = { Text(text = "Some label $index") },
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(onNext = {
if (!focusManager.moveFocus(FocusDirection.Down))
focusManager.clearFocus()
}),
singleLine = true
)
}
}
}
}
Compose version 1.0.5
You could try just hiding the keyboard whenever scrolling occurs. This is okay as long as you don't have a large set of items. But since you're using TextFields, it isn't likely that you'll have such a large number. This sample illustrates hiding the keyboard when scrolling occurs:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startActivity(intent)
setContent {
Greeting(this)
}
}
}
#Composable
fun Greeting(activity: Activity) {
Scaffold(topBar = {
TopAppBar(title = { Text(text = "Some title") })
}) {
val lazyListState = rememberLazyListState()
val ctx = LocalContext.current
LaunchedEffect(lazyListState.firstVisibleItemIndex) {
val inputMethodManager = ctx.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(activity.window?.decorView?.windowToken, 0)
}
LazyColumn(
state = lazyListState,
contentPadding = PaddingValues(all = 16.dp),
verticalArrangement = Arrangement.spacedBy(space = 16.dp)
) {
items(
count = 20,
key = { index ->
// Return a stable + unique key for the item
index
}
) { index ->
val (value, onValueChange) = rememberSaveable { mutableStateOf("Some value $index") }
TextField(
value = value,
onValueChange = onValueChange,
modifier = Modifier
.fillMaxWidth(),
label = { Text(text = "Some label $index") },
singleLine = true
)
}
}
}
}

Select all text of TextField in Jetpack Compose with mvvm pattern

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.

Resources