When coding with compose we use MaterialTheme code so much. Is there a way to shorten this part of the code? for example: mColor.primay
This is less of a Jetpack Compose solution - more of "using Kotlin language features".
You can use the with scope function to make MaterialTheme, which is an object, an implicit receiver. Then you can refer to colors.primary or colors.whatever without saying MaterialTheme.
You can surround the entire enclosing function with a with block:
#Composable
fun foo() {
with(MaterialTheme) {
// compose your view here...
// and you can say "colors.primary" instead of
// "MaterialTheme.colors.primary" in here
}
}
Alternatively, simply use a type alias to make the name MaterialTheme shorter:
typealias MT = MaterialTheme
// now you can say "MT.colors.primary" instead of "MaterialTheme.colors.primary"
I guess this can be done in various ways. One of them being
val mColor
get() = #Composable{
MaterialTheme.colors
}
usage
mColor().primary
I think the best answer is this:
val mColors: MColor() #Composable get() = MyTheme.colors
and we can call colors like this: mColors.primary
We can do this for typography, shapes and dimensions as well.
You can do it with function.
#Composable
fun materialThemeColor() = MaterialTheme.colors
While using you can use
color = materialThemeColor().primary
Related
I hope to remember a string which is from stringArrayResource in Code A , but I get the the Error A. How can I fix it?
And more, I find more variables can't be wrapped with remember, such as val context = LocalContext.current , why?
Error A
Composable calls are not allowed inside the calculation parameter of inline fun remember(calculation: () -> TypeVariable(T)): TypeVariable(T)
Code A
#Composable
fun DialogForDBWarningValue(
preferenceState:PreferenceState
) {
val context = LocalContext.current //I can't wrap with remember
val itemName =remember{ stringArrayResource(R.array.dBWarning_Name) } //I can't wrap with remember
}
#Composable
inline fun <T> remember(calculation: #DisallowComposableCalls () -> T): T =
currentComposer.cache(false, calculation)
The reason for that error is #DisallowComposableCalls annotation
This will prevent composable calls from happening inside of the
function that it applies to. This is usually applied to lambda
parameters of inline composable functions that ought to be inlined but
cannot safely have composable calls in them.
I don't know if accessing resources and getting strings would have any impact on performance but as an alternative this can be done using nullable properties, i don't think it's good practice to have nullable objects while you don't have to, by only getting resources once your String is null or an object that holds Strings and sets them on Composition or configuration changes if you wish to change new ones.
class StringHolder() {
var str: String = ""
}
val stringHolder = remember(LocalConfiguration.current) {
StringHolder()
}.apply {
this.str = getString(R.string.dBWarning_Name)
}
I have simple three functions returning arrow-kt data types
fun validate(input): Validated<Error, Input> = ...
fun fetch(input): Option<Error, InputEntity> = ...
fun performAction(inputEntity): Either<Error, Output> = ...
And want to chain something like this (can use any available function instead of map)
validate(input)
.map{fetch(it)}
.map{performAction(it)}
Only solution I could come up with is to replace Validated and Option with Either and chain using flatMap. Is there any better functional way to make it work without updating the existing functions?
👋 What #pablisco described is correct, but you can keep it simpler by using some syntax extensions we provide to convert from one type to the other. Note that both options are correct, but Monad Transformers can be a bit convoluted and too powerful, and they're also prone to get removed from Arrow soon, once we finally figure out our delimited continuations style completely. But that is out of scope here. Here is how you could solve it by using the extensions I mentioned:
import arrow.core.*
import arrow.core.extensions.fx
sealed class Error {
object Error1 : Error()
object Error2 : Error()
}
data class InputEntity(val input: String)
data class Output(val input: InputEntity)
fun validate(input: String): Validated<Error, InputEntity> = InputEntity(input).valid()
fun fetch(input: String): Option<InputEntity> = InputEntity(input).some()
fun performAction(inputModel: InputEntity): Either<Error, Output> = Output(inputModel).right()
fun main() {
val input = "Some input"
Either.fx<Error, Output> {
val validatedInput = !validate(input).toEither()
val fetched = !fetch(validatedInput.input).toEither { Error.Error1 /* map errors here */ }
!performAction(fetched)
}
}
Hope it was useful 👍
What you are looking for is called a Monad Transformer. In Arrow, you may have seen them already, they end with a T at the end. Like OptionT or EitherT.
There are some good examples here for EitherT:
https://arrow-kt.io/docs/0.10/arrow/mtl/eithert/
And here for OptionT:
https://arrow-kt.io/docs/0.10/arrow/mtl/optiont/
The idea would be that to choose what your final value is going to be (let's say Either) and using an FX block you can then use EitherT to convert the other types to an Either.
I'd like to pass a function reference on a nullable object. To take an Android example, say I want to use Activity#onBackPressed from a fragment that is a child of that actvity.
If I wanted to invoke this function, I could easily do
activity?.onBackPressed()
However, say I wanted to pass that as a reference instead:
val onBackPressedRef = activity::onBackPressed
This gives the familiar null safe error of Only safe or non null assserted calls are allowed...
I can get the error to go away with the following, but using !! is obviously not ideal:
val onBackPressedRef = activity!!::onBackPressed
Attemping activity?::onBackPressed was my first instinct, but this also breaks with several errors, where the interpreter seems confused.
val onBackPressedRef = activity?.let { it::onBackPressed }
This last variation works, but it's a lot more ugly than just using ?::. I checked all the docs I could find, but I feel like I'm missing something. Any ideas?
You are right, there is no ?:: operator in Kotlin.
You have several alternatives:
1. let and run
Thus, you have to use a helper function. Instead of let(), you can also use run(), making the expression a tiny bit shorter:
val onBackPressedRef = activity?.let { it::onBackPressed }
val onBackPressedRef = activity?.run { ::onBackPressed }
But keep in mind that either way, the invocation will be more verbose, too:
onBackPressedRef?.invoke(args)
Thus you should ask yourself, if this is really what you want, or if a no-op function call is also acceptable.
2. Closures
You could use a closure -- this will change semantics however:
val onBackPressedRef = { activity?.onBackPressed() }
Here, onBackPressedRef is not nullable anymore, so you can call it using the () operator, and in case of null activity it will have no effect.
3. Helper function
If function references with nullable objects are something you encounter a lot, you can write your own little abstraction:
// Return type: () -> Unit
fun <T> funcRef(obj: T?, function: T.() -> Unit) = { obj?.function() }
This trades a different syntax for a non-null function variable:
// activity can be null
val onBackPressedRef = funcRef(activity, Activity::onBackPressed)
// Callable directly
onBackPressedRef()
Recently I meet some problem about code style in Kotlin. I can't tell which code style is better.
Assume there are nullable field here:
var scoreView: TextView? = null
val bgImageView: ImageView? = null
And I'd like to write like:
fun foo() {
scoreView?.apply {
text = getScore()
textColor = getColor()
...
}
bgImageView?.apply {
Glide.with(context)
.load(xxx)
.into(this)
}
}
And my team leader want to change it to :
fun foo() {
scoreView?.text = getScore()
scoreView?.textColor = getColor()
...
Glide.with(context)
.load(xxx)
.into(bgImageView?:return)
}
I feel both are ok to me, but prefer the first one because I could write less 'xxView?.'
I wonder if there is some code style or rule about this. Or some common view about it.
Thanks.
According to Kotlin's official coding style, when you're calling multiple functions which primarily interact with one object, putting that code inside a scope function like .apply is the idiomatic approach. Of course, your workplace may use different conventions, so definitely ask your team leader about this.
In your first example, you're using apply for exactly the purpose it was designed: to set multiple properties on one object, and enhance readability in these situations.
scoreView?.apply {
text = getScore()
textColor = getColor()
...
}
In your second example, the apply function serves to separate code that acts on your nullable object bgImageView. This use is supported by the style guide, although it seems to recommend let more strongly in these nullable cases.
bgImageView?.apply {
Glide.with(context)
.load(xxx)
.into(this)
}
bgImageView?.let {
Glide.with(context)
.load(xxx)
.into(it)
}
The style guide also describes intended uses of the with, also, and run functions.
I am trying to write a kotlin extension function for view to update the current view margin. I know that for the same you need to make change to view's layoutParams, although I am trying to create a generic method & unable to understand how to pass the parent viewgroup / layout type to handle this.
fun <T> View.setMargins(margin:Int){
var lp = // don't know how to get layout params here
// how to get RelativeLayout.LayoutParams or ViewGroup.LayoutParams etc..
// if I do this.layoutParams then I am unable to call setMargins function on that since it is only available for RelativeLayout.LayoutParams / VeiwGroup.LayoutParams etc etc..
}
When you are using extension function your receiver type will become parameter as this inside that block.
So you can use like this way.
fun <T> View.setMargins(margin:Int){
if (this.layoutParams is ViewGroup.MarginLayoutParams) {
val params = this.layoutParams as ViewGroup.MarginLayoutParams
params.setMargins(<your_margins>);
}
}
I think kotlinx already done that, check it out