How to get the current Activity from a Composable function?
You can get current context using LocalContext
. With Compose usually it's gonna be your activity already, but to be sure you can unwrap it like this:
fun showInterstitial(context: Context) {
val activity = context.findActivity()
}
@Composable
fun View() {
val context = LocalContext.current
Button(onClick = {
val activity = context.findActivity()
}) {
}
}
fun Context.findActivity(): AppCompatActivity? = when (this) {
is AppCompatActivity -> this
is ContextWrapper -> baseContext.findActivity()
else -> null
}
If you need to use context inside your view model, or in an other place where your handler function is located, you can pass context to your handler function.
fun showInterstitial(context: Context) {
val activity = context.findActivity()
}
@Composable
fun View() {
val context = LocalContext.current
Button(onClick = {
showInterstitial(context)
}) {
}
}
How to get Context in Jetpack Compose
Update March 2021: The previous answer has been deprecated. You should now use:
val context = LocalContext.current
Previous answer for reference:
You can access to context with define ambientContext
.
Example:
val context = ContextAmbient.current
Get activity context inorder to open alert dialog from non-composable function using compose context
The error says that the dialog requires a theme. You can provide it with ContextThemeWrapper
, either a custom one declared in res/values/themes
, or a default one, like R.style.Theme_MaterialComponents_Dialog
:
MaterialAlertDialogBuilder(ContextThemeWrapper(context, R.style.Theme_MaterialComponents_Dialog))
More info about custom theming can be found here
How to navigate from a composable to an activity or a fragment in Jetpack Compose?
In newer version of compose use LocalContext
.
In older versions (1.0.0-alpha08 and before) use AmbientContext
:
@Composable
fun MainScreen() {
val context = LocalContext.current
Button(onClick = {
context.startActivity(Intent(context, ListActivity::class.java))
}) {
Text(text = "Show List")
}
}
Best practise of using view model in jetpack compose
I don't think there is not only one best practice but choosing approaches that suits your needs.
You should decide if your ViewModel needs to be in memory while your app is alive or scoped to navigation graph or a Composable.
Second thing to consider is if you will use same Composable in another screen or another app. If so, instead of passing ViewModel to Composable you might consider passing states as params and events to ViewModel via a callback.
Instead of this
fun Input(optionData: OptionData,viewModel: InputViewModel = viewModel()) {
InputItem(viewModel)
}
I tend to go with this if i need to use, or think i will use Input
in different sections or in another app in the future
fun Input(optionData: OptionData, someOtherData, onOptionDataChanged:()->Unit, onSomeOtherDataChanged: () -> Unit) {
InputItem(viewModel)
}
State in jetpack Compose from codelabs is a good article to read about this subject.
@Composable
fun WellnessScreen(
modifier: Modifier = Modifier,
wellnessViewModel: WellnessViewModel = viewModel()
) {
Column(modifier = modifier) {
StatefulCounter()
WellnessTasksList(
list = wellnessViewModel.tasks,
onCheckedTask = { task, checked ->
wellnessViewModel.changeTaskChecked(task, checked)
},
onCloseTask = { task ->
wellnessViewModel.remove(task)
}
)
}
}
@Composable
fun WellnessTasksList(
list: List<WellnessTask>,
onCheckedTask: (WellnessTask, Boolean) -> Unit,
onCloseTask: (WellnessTask) -> Unit,
modifier: Modifier = Modifier
) {
LazyColumn(
modifier = modifier
) {
items(
items = list,
key = { task -> task.id }
) { task ->
WellnessTaskItem(
taskName = task.label,
checked = task.checked,
onCheckedChange = { checked -> onCheckedTask(task, checked) },
onClose = { onCloseTask(task) }
)
}
}
}
@Composable
fun WellnessTaskItem(
taskName: String,
checked: Boolean,
onCheckedChange: (Boolean) -> Unit,
onClose: () -> Unit,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier, verticalAlignment = Alignment.CenterVertically
) {
Text(
modifier = Modifier
.weight(1f)
.padding(start = 16.dp),
text = taskName
)
Checkbox(
checked = checked,
onCheckedChange = onCheckedChange
)
IconButton(onClick = onClose) {
Icon(Icons.Filled.Close, contentDescription = "Close")
}
}
}
Last but not least if it's UI logic that is not dependent of any business logic or if you are building a standalone custom Composables as counterpart of custom Views you can consider capturing UI logic in a class that is wrapped in remember
instead of ViewModel.
Examples for this approach are any rememberX functions we use with Lists, Scaffolds and other default Composables.
remmeberScrollState
for instance
@Stable
class ScrollState(initial: Int) : ScrollableState {
/**
* current scroll position value in pixels
*/
var value: Int by mutableStateOf(initial, structuralEqualityPolicy())
private set
/**
* maximum bound for [value], or [Int.MAX_VALUE] if still unknown
*/
var maxValue: Int
get() = _maxValueState.value
internal set(newMax) {
_maxValueState.value = newMax
if (value > newMax) {
value = newMax
}
}
/**
* [InteractionSource] that will be used to dispatch drag events when this
* list is being dragged. If you want to know whether the fling (or smooth scroll) is in
* progress, use [isScrollInProgress].
*/
val interactionSource: InteractionSource get() = internalInteractionSource
internal val internalInteractionSource: MutableInteractionSource = MutableInteractionSource()
private var _maxValueState = mutableStateOf(Int.MAX_VALUE, structuralEqualityPolicy())
/**
* We receive scroll events in floats but represent the scroll position in ints so we have to
* manually accumulate the fractional part of the scroll to not completely ignore it.
*/
private var accumulator: Float = 0f
private val scrollableState = ScrollableState {
val absolute = (value + it + accumulator)
val newValue = absolute.coerceIn(0f, maxValue.toFloat())
val changed = absolute != newValue
val consumed = newValue - value
val consumedInt = consumed.roundToInt()
value += consumedInt
accumulator = consumed - consumedInt
// Avoid floating-point rounding error
if (changed) consumed else it
}
override suspend fun scroll(
scrollPriority: MutatePriority,
block: suspend ScrollScope.() -> Unit
): Unit = scrollableState.scroll(scrollPriority, block)
override fun dispatchRawDelta(delta: Float): Float =
scrollableState.dispatchRawDelta(delta)
override val isScrollInProgress: Boolean
get() = scrollableState.isScrollInProgress
/**
* Scroll to position in pixels with animation.
*
* @param value target value in pixels to smooth scroll to, value will be coerced to
* 0..maxPosition
* @param animationSpec animation curve for smooth scroll animation
*/
suspend fun animateScrollTo(
value: Int,
animationSpec: AnimationSpec<Float> = SpringSpec()
) {
this.animateScrollBy((value - this.value).toFloat(), animationSpec)
}
/**
* Instantly jump to the given position in pixels.
*
* Cancels the currently running scroll, if any, and suspends until the cancellation is
* complete.
*
* @see animateScrollTo for an animated version
*
* @param value number of pixels to scroll by
* @return the amount of scroll consumed
*/
suspend fun scrollTo(value: Int): Float = this.scrollBy((value - this.value).toFloat())
companion object {
/**
* The default [Saver] implementation for [ScrollState].
*/
val Saver: Saver<ScrollState, *> = Saver(
save = { it.value },
restore = { ScrollState(it) }
)
}
}
Extra
Also depending on your needs or applicability favoring using states with Modifier
over Composable
might make it easy to use with other Composasbles.
For instance
class MyState(val color:Color)
@composable
fun rememberMyState(color:Color) = remember{MyState(color)}
Wrapping UI logic inside Modifier
fun Modifier.myModifier(myState:State)= this.then(
Modifier.color(myState.color)
)
might have more reusability than Composable in some scenarios
@Composable
fun MyComposable(myState: MyState) {
Column(Modifier.background(color){...}
}
If you use a Composable
in the example above we limit layout to Column
while you can use first one with any Composable
you wish. Implementation depends on what's your preferences are mostly.
Best way to get the context of an activity in a viewmodel
I'd recommend creating a single method that accepts a context and displays the notification. This way you'll avoid memory leaks which can be caused if you store the activity's context instead.
Related Topics
How to Setup Alertbox from Broadcastreceiver
Why Does Flag_Activity_Clear_Top Not Work
Android: Starting an Activity for a Different Third Party App
Sqlitedatabase Close() Function Causing Nullpointerexception When Multiple Threads
Android: Support Multiple Screens
How to Setup Alertbox from Broadcastreceiver
Using Collate in Android Sqlite - Locales Is Ignored in Like Statement
Eclipse Hangs at The Android Sdk Content Loader
Modifying The Color of an Android Drawable
Trying to Fix Networkonmainthreadexception But Gives Toast Error
Listview: Textview with Linkmovementmethod Makes List Item Unclickable
Pcm -> Aac (Encoder) -> Pcm(Decoder) in Real-Time with Correct Optimization
Android Clickablespan Not Calling Onclick
Air 3 Native Extensions for Android - Can I/How to Include 3Rd Party Libraries
Searchview in Centre and After Focus It Goes to App Bar in Android