How to use null as default value - Compose mutableState - android

I have a need like on first time on UI load the mutableStateOf Boolean value must be null , instead of default value as true/false similar to regular boolean value in Kotlin as
var isBooleanValue: Boolean = null
what I need is as below
var isClassAccessedAtleastOnce = remember { mutableStateOf(null) }
Kindly let me know is there any possibility from the useCase

Define type of variable as
var isClassAccessedAtleastOnce = remember { mutableStateOf<Boolean?>(null) }

Related

Custom Setter for mutableStateOf() / MutableState in Kotlin?

Is there a way to override the setter for the underlying value of a MutableState
Basically I want to achieve the exact behaviour of a non MutableState
object MyClass2 {
val myProperty2: Int = 0
set(value) {
if (value>= 0) {
field = value
}
}
}
Using myProperty2 = -3 will execute my custom setter and not update the value since the if condition is not met.
object MyClass1 {
val myProperty1: MutableState<Int> = mutableStateOf(0)
set(value) {
if (value.value >= 0) {
field = value
}
}
}
Using myProperty1.value = -3 will NOT execute my custom setter since im changing the underlying value and not the MutableState itself
This is related to this question but the answers look for state changes only.(updates the value automatically). I want to change the state only if a condition is met.
As long as our code is based on interfaces, we can always use composition and delegation to solve this kind of problems. In this case we can create our own implementation of MutableState and then implement everything according to our needs, possibly delegating to the original mutable state wherever it makes sense:
val myProperty1 = run {
val state = mutableStateOf(0)
object : MutableState<Int> by state {
override var value: Int
get() = state.value
set(value) {
if (value > 0) {
state.value = value
}
}
}
}

Android Studio Kotlin Boolean.Companion problem

In my code i want to execute holder.task.setChecked() command with item.getStatus()) as a parameter.
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = data[position]
holder.task.text = item.getTask()
holder.task.setChecked(item.getStatus())
}
The problem is that setChecked requires type Boolean, but from getStatus function i get Boolean.Companion in return.
class Model(itemView: View) : RecyclerView.ViewHolder(itemView) {
private var id = Int
private var status = Boolean
private var task = String
fun getStatus(): Boolean.Companion {
return status
}
When i try to change the return type of this function, i get thrown with an error.
Been thinking for a while about how to change the types to be equal, but i cannot seem to find the solution.
To be clear, setChecked function is a default function which im executing on Checkbox object
Your example code is very unusual, and I'm not sure you actually meant to do what you're doing here.
This for example
private var id = Int
doesn't hold an Int. id is not a number. It's actually the companion object for the Int class, which in this case holds the constants MAX_VALUE, MIN_VALUE, SIZE_BYTES and SIZE_BITS. Usually you'd access these directly on the Int class, like Int.MAX_VALUE - when you do that you're actually accessing them on the companion object, which is why assigning the value Int to a variable gives you that companion object.
So the same goes for var status = Boolean - it's Boolean's companion object. And that's why your function has to return Boolean.Companion, because it's returning status, and that's what status is. It's not a boolean value of true or false.
There may be reasons why you'd want to do this - it's very unlikely though, and the way you're trying to use this (wanting a Boolean from getStatus) suggests that this is a mistake, and you're not familiar with the language. I'd really recommend running through the basic intro stuff to get a feel for how you define variables and their types, but this is probably what you wanted:
class Model(itemView: View) : RecyclerView.ViewHolder(itemView) {
private var id: Int = -1
private var status: Boolean = false
private var task: String = ""
fun getStatus(): Boolean {
return status
}
}
I've added default values to each because you have to initialise them to something - if you have an init block where that happens, you can omit the values. If you're assigning them right there, you can omit the types too (e.g. var id = -1) unless you need to be more specific than the value is (e.g. if you want it to be a nullable Int? type).
You could also put these values in the constructor:
class Model(
itemView: View,
private var id: Int,
private var status: Boolean,
private var task: String
)
which would require the caller to provide some initial values. It depends what you want!
If you wanted, you could also replace the getter function by doing this:
var status: Boolean = false
private set
which makes the status property public, but read-only from the outside.
And just FYI, your Model class looks like your data for your RecyclerView's Adapter - it should not be a ViewHolder, those are completely separate things. Just use a basic class for your Model (or even better, a data class) and use that in your data list.
ViewHolders are special objects that get reused to display different data items - and by definition there's usually more data items than there are ViewHolders (that's the whole point of a RecyclerView - it recycles them). You shouldn't be keeping them in a list, or storing individual items' state in them.
Keep your data items in a list, fetch the one at position in onBindViewHolder, then display it in the ViewHolder you're provided with. What you have right now isn't going to work, so you'll need to look at a tutorial for setting one up, e.g. this one from the docs. You need to store references to the Views you're using in the VH, like a TextView and a Checkbox, so you can do things like
holder.taskTextView.text = item.getTask()
The setChecked function requires a Boolean param, but the return type of getStatus is Boolean.Companion. This is because you have defined the status field as of Boolean.Companion type.
To fix this, you may define status as a normal class level field and either assign a default value, or provide a constructor to initialize the value appropriately.
private var status: Boolean = false // or true, depending on the default value that suits here
Then you can change the getStatus function to return a Boolean type instead of Boolean.Companion
fun getStatus(): Boolean {
return status
}
and then use it to call holder.task.setChecked(item.getStatus()).

return int value from dataStore preferences

I have an application where I'm saving int value using datastore preferences , when I get the value , I execute the code and want to return that int value so that I can use it in different places in my code but could not figure it out , if anyone can help , thank you in advance
I tried to make a global member to assign that value and then return but since the value is returned asynchronously , it is crashing the app .
This is my code
private fun setTextSize() : Int {
val dataStore = requireContext().createDataStore("textSize")
lifecycleScope.launch {
dataStore.data.collect {
val textSize = it[Common.TEXT_SIZE_PREFERENCE]
}
}
}
If you want to get the value directly you should use runBlocking.
Something like this should do the trick:
val textSize = runBlocking { dataStore.data.first() }[Common.TEXT_SIZE_PREFERENCE]
You can add suspend modifier to your function and access it using CoroutineScope as
suspend fun setTextSize() = dataStore.data.firstOrNull()[Common.TEXT_SIZE_PREFERENCE] ?: -1
While accesing
lifeCycleScope.launch{
val textSize = setTextSize()
}

How to "lock" static object in Kotlin

Well, I've a situation, where in Class A I get "X DATA".
I want to store this "X DATA" in Object X one time and then make sure, the values of this object is not possible to change. (Set it once and forget about it).
My approach:
object X {
var attribute1: String
var attribute2: String
}
Obviously, as object attributes are var they are changeable in future. How could I avoid this? Is there a way to assign values (in some time..) and then lock the object till application is exited?
You could use a delegate property
class MyProperty<T : Any> {
private var value: T? = null
operator fun getValue(myObject: MyObject, property: KProperty<*>): T =
value ?: throw UninitializedPropertyAccessException()
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) = when (this.value) {
null -> this.value = value
else -> throw IllegalAccessException("Property already initialized")
}
}
and then use var in your object
object MyObject {
var myProperty by MyProperty<String>()
}
In the sample above, if you try to access myProperty before setting a value, an exception is thrown, but you could handle that as you wish (return a default value? maybe null?).
If you try to assign the value more than once, you get an exception as well but you could handle that differently, for instance, by simply not setting the value anymore so that
MyObject.myProperty = "foo"
MyObject.myProperty = "bar"
println(MyObject.myProperty)
will print "foo"
You can use lateinit var attribute1: String to tell the compiler that you will manage setting attribute1 to a non-null value before it's used.
There's no such thing as lateinit val to "lock" the value as you say.
The docs have more information.
I suggest you make those properties final by replacing var with val.
var is like general variable and its known as a mutable variable in kotlin and can be assigned multiple times.
val is like constant variable and its known as immutable in kotlin and can be initialized only single time.
You can't lateinit immutable properties in kotlin and lateinit var does not allow custom setters.
So my approach would be implementing the lateinit var behavior with a backing property, custom getter and custom setter. This is quite a similar approach to lellomans solution.
object X {
private lateinit var backingprop: String
var attribute: String
set(arg) {
if (this::backingprop.isInitialized) throw IllegalAccessException("Property already initialized")
backingprop = arg
}
get() = backingprop
}
I warn you! But you can use such example:
object Immutable {
val immutableString: String = mutableStaticString ?: "some default value, just in case"
}
var mutableStaticString: String? = null
class App : Application() {
override fun onCreate() {
super.onCreate()
mutableStaticString = "hello duct tape solutions!"
android.util.Log.d("ductTape", mutableStaticString)
android.util.Log.d("ductTape", Immutable.immutableString)
}
}

Set value of a field without calling set method - Kotlin

I've been using Kotlin to develop some apps in Android and what i want to do currently is to set a field value inside the defining class without calling the setter method.
Here is the code inside my class:
var projectList: List<Project>? = null
set(value) {
saveProjects(value as ArrayList<Project>)
field = value
}
//GO to the database and retrieve list of projects
fun loadProjects(callback: Project.OnProjectsLoadedListener) {
database.projectDao().getAll().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ success ->
callback.onProjectsLoaded(success)
//Here i don't want to save the projects, because i've loaded them from the database
this.projectList = success
},
{ error -> GenericErrorHandler.handleError(error,callback.retrieveContext())}
)
}
Does anybody know a way to do this without calling the set(value) method?
You can only gain access to the field directly from the setter. Inside a setter, the field can be accessed through the invisible field variable.
There are perhaps some other ways around your requirements though. Here are 2 examples. You wouldn't have to follow them exactly, but could instead also combine them to make whatever solution you want.
You could use another shell property to act as the setter for your actual property:
class Example1 {
var fieldProperty: Int = 0
var setterPropertyForField: Int
get() = fieldProperty
set(value) {
fieldProperty = value
}
}
You could use setters as you actually would in Java with a JVM field and a set method. The #JvmField is probably not necessary.
class Example2 {
#JvmField var fieldProperty: Int = 0
fun setField(value: Int) {
fieldProperty = value
}
}
You could probably access the field and change it through reflection, but I don't recommend that approach. That would likely only lead to problems.

Categories

Resources