Jetpack Compose can't import constrainAs - android-jetpack-compose

I can't import constrainAs in Jetpack Compose.
Text("Text", Modifier.constrainAs(text) {
top.linkTo(button.bottom, margin = 16.dp)
})
I added constraintlayout dependency:
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-alpha08"
But it still says Unresolved reference: constrainAs

constrainAs belongs to ConstraintLayoutScope
it means that in can be used for any view inside ConstraintLayout, but only on a first level child.
ConstraintLayout {
val ref = createRef()
Box(
// OK
modifier = Modifier.constrainAs(ref) {
}
) {
Box(
// Not OK
modifier = Modifier.constrainAs(ref) {
}
)
}
}

Are you adding the following import?
androidx.constraintlayout.compose.ConstraintLayout
You don't need to import constraintsAs separately.

Better to use another version :
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0-beta02"

Related

Access scoped viewModel from TopAppBar in jetpack compose

Folks I am stuck engineering a proper solution to access a viewModel scoped to a nav graph , from a button that exists in the TopAppBar in a compose application
Scaffold{
TopAppBar-> Contains the Save Button
Body->
BioDataGraph() -> Contains 5 screens to gather biodata information , and a viewmodel scoped to the graph
}
}
My BioDataViewModel looks like this
class BioDataViewModel{
fun gatherPersonalInformation()
fun gatherPhotos()
...
fun onSaveEverything()
}
The issue i came across is as i described above , how should i go about access the BioDataViewModel , such that i can invoke onSaveEverything when save is clicked in the TopAppBar.
What I have tried
private val performSave by mutableStateOf(false)
Scaffold(
topBar = {
TopAppBar(currentDestination){
//save is clicked.
performSave = true
}
})
{
NavHost(
navController = navController,
startDestination = homeNavigationRoute,
modifier = Modifier
.padding(padding)
.consumedWindowInsets(padding),
) {
composable(route = bioDataRoute) {
val viewModel = hiltViewModel<BioDataViewModel>()
if (performSave){
viewModel.onSaveEverything()
}
BioDataScreen(
viewModel
)
}
}
}
The problem with the approach above is that how and when should i reset the state of performSave ? . Because if i do not set it to false; on every recomposition onSaveEverything would get called.
What would be the best way to engineer a solution for this ? . I checked to see if a similar situation was tackled in jetpack samples , but i found nothing there .
I'm not sure if I understand you correctly, but you can define the BioDataViewModel in activity level, and you can access it in the TopAppBar like this
class MyActivity: ComponentActivity() {
// BioDataViewModel definition here
private val viewModel: BioDataViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Scaffold(
topBar = {
TopAppBar(currentDestination) {
//save is clicked.
viewModel.onSaveEverything() // call onSaveEverything here
}
})
{
...
...
}
...
...
Edit:
If you want to have the same instance of ViewModel from activity and NavGraph level, you can consider this, a reference from my other answer.
You can define the ViewModelStoreOwner in the navigation graph level.
NavHost(
navController = navController,
startDestination = homeNavigationRoute,
modifier = Modifier
.padding(padding)
.consumedWindowInsets(padding),
) {
val viewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
"LocalViewModelStoreOwner not available"
}
composable(route = bioDataRoute) {
val viewModel = hiltViewModel<BioDataViewModel>(viewModelStoreOwner)
if (performSave){
viewModel.onSaveEverything()
}
BioDataScreen(
viewModel
)
}
}

How to preview compose layout when you're getting data from api

I'm setting up ui for my Lazy column and I'm getting data from api
#Preview
#Composable
fun MatchesRow(data: Data ) {
Card(
modifier = Modifier
.height(180.dp),
backgroundColor = MaterialTheme.colors.background
) {
}}
i get erros because my parameters are empty , how can i Preview ??
you have to create two function, one of which receives the data from the ViewModel, and the other is the case you mentioned
Note : One Stateful and one Stateless
But in order to be able to preview, you must act as follows:
#Composable
fun MatchesRow(data: Data ) {
Card(
modifier = Modifier
.height(180.dp),
backgroundColor = MaterialTheme.colors.background
) {
}}
#Preview
#Composable
fun MatchesRowPreview() {
val data = .....
MatchesRow(
data = data
)
}
If you want to preview a composable with a parameter, you can use #PreviewParameter annotation.
Say your data class looks like this,
data class Data(
val dataValue: String
)
and you have this composable with text inside a red box in the middle of the screen,
#Preview
#Composable
fun DataDetails(details: Data) {
Box(
modifier = Modifier
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.wrapContentSize()
.background(Color.Red)
) {
Text(
modifier = Modifier.padding(8.dp),
text = details.dataValue
)
}
}
}
and you want to preview it, you have to create a PreviewParemeterProvider first,
class DataPreviewProvider : PreviewParameterProvider<Data> {
override val values = listOf(Data(dataValue = "Hello")).asSequence()
}
and annotate the parameter with #PreviewParameter and pass the DataPreviewProvider to it like this.
#Preview
#Composable
fun DataDetails(#PreviewParameter(DataPreviewProvider::class) details: Data) {
...
}
I would also suggest to just simply create another composable solely for "previewing" purposes.
#Preview
#Composable
fun DataDetailsPreview(
#PreviewParameter(DataPreviewProvider::class) dataDetails: Data) {
DataDetails(userDetailsDisplay)
}
You can also check this #PreviewParemeter

JetPack Compose, animateColorAsSate

Getting error when use this line:
val surfaceColor: Color by animateColorAsState(
if (isExpanded) MaterialTheme.colors.primary else MaterialTheme.colors.surface,
)
It showing the following warning:
Property delegate must have a 'getValue(Nothing?, KProperty*>)' method. None of the following functions are suitable.
State.getValue(Any?, KProperty<>)   where T = Color for   inline operator fun State.getValue(thisObj: Any?, property: KProperty<>): T defined in androidx.compose.runtime
Make sure you are using correct import statements.
Compare and try replacing your import statements with correct one from below.
import androidx.compose.animation.animateColorAsState
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
#Composable
fun YourFunction() {
var isExpanded by remember { mutableStateOf(false) }
val surfaceColor: Color by animateColorAsState(
if (isExpanded) MaterialTheme.colors.primary else MaterialTheme.colors.surface
)
Column(
modifier = Modifier.clickable { isExpanded = !isExpanded }
) {
}
}

How to share a viewmodel between two or more Jetpack composables inside a Compose NavGraph?

Consider this example.
For authentication, we'll be using 2 screens - one screen to enter phone number and the other to enter OTP.
Both these screens were made in Jetpack Compose and the for the NavGraph, we are using compose navigation.
Also I have to mention that DI is being handled by Koin.
val navController = rememberNavController()
NavHost(navController) {
navigation(
startDestination = "phone_number_screen",
route = "auth"
) {
composable(route = "phone_number_screen") {
// Get's a new instance of AuthViewModel
PhoneNumberScreen(viewModel = getViewModel<AuthViewModel>())
}
composable(route = "otp_screen") {
// Get's a new instance of AuthViewModel
OTPScreen(viewModel = getViewModel<AuthViewModel>())
}
}
}
So how can we share the same viewmodel among two or more composables in a Jetpack compose NavGraph?
You can to pass your top viewModelStoreOwner to each destination
directly passing to .viewModel() call, composable("first") in my example
overriding LocalViewModelStoreOwner for the whole content, so each composable inside CompositionLocalProvider will have access to the same view models, composable("second") in my example
val viewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
"No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
}
val navController = rememberNavController()
NavHost(navController = navController, startDestination = "first") {
composable("first") {
val model = viewModel<SharedModel>(viewModelStoreOwner = viewModelStoreOwner)
}
composable("second") {
CompositionLocalProvider(
LocalViewModelStoreOwner provides viewModelStoreOwner
) {
SecondScreen()
}
}
}
In the second case, you can get your model at any level of the composition tree, which is inside the CompositionLocalProvider:
#Composable
fun SecondScreen() {
val model = viewModel<SharedModel>()
SomeView()
}
#Composable
fun SomeView() {
val model = viewModel<SharedModel>()
}
Using Hilt you could do something like the below. But since you are using Koin I don't know the way of Koin yet.
#Composable
fun MyApp() {
NavHost(navController, startDestination = startRoute) {
navigation(startDestination = innerStartRoute, route = "Parent") {
// ...
composable("exampleWithRoute") { backStackEntry ->
val parentEntry = remember {
navController.getBackStackEntry("Parent")
}
val parentViewModel = hiltViewModel<ParentViewModel>(
parentEntry
)
ExampleWithRouteScreen(parentViewModel)
}
}
}
}
Official doc: https://developer.android.com/jetpack/compose/libraries#hilt
Here is an other way with Koin.
It strictly do the same than the validated answer but simpler to write. It will have exactly the same viewModelStoreOwner without having to write it explicitly. Please tell me if i'm wrong.
val navController = rememberNavController()
val sharedViewModel = getViewModel()
NavHost(navController = navController, startDestination = "first") {
composable("first") {
// You can use sharedViewModel
}
composable("second") {
// You can use sharedViewModel
}
}

How to preview LazyPagingItems/Paging 3 Library in Jetpack Compose

I think the title says it all.
LazyPagingItems constructor is internal. I can't pass LazyPagingItems as parameter in Preview Composable, let alone passing sample data. But I want to show preview of my composable, how should I do this?
#Composable
fun MainUi(users: LazyPagingItems<User>) {
Scaffold {
LazyColumn() {
items(users) {
// Rest of the code..
}
}
}
}
#Preview
#Composable
fun Preview() {
DefaultTheme {
MainUi(users = ) // How to pass sample data here?
}
}
You can use PagingData.empty() or PagingData.from(List<T>), like this:
#Preview
#Composable
fun Preview() {
DefaultTheme {
MainUi(users = flowOf(PagingData.from(listOf(User(..)))).collectAsLazyPagingItems()
}
}
I'm not sure if the items are shown in the preview, but at least you get to render it...

Resources