Problems changing the Color with MaterialTheme in a project of Kotlin - kotlin

I'm having troubles to change the colors of my material theme, this is what I have:
Surface(
modifier = Modifier
.fillMaxWidth()
.height(400.dp)
.constrainAs(surface) {
bottom.linkTo(parent.bottom)
},
color = MaterialTheme.colors.Background,
shape = RoundedCornerShape(topEndPercent = 8, topStartPercent = 8)
)
additionally I have this:
private val LightColorPalette = lightColors(
primary = Purple500,
primaryVariant = Purple700,
secondary = Teal200,
background = Background
)
and this
val Purple200 = Color(0xFFBB86FC)
val Purple500 = Color(0xFF6200EE)
val Purple700 = Color(0xFF3700B3)
val Teal200 = Color(0xFF03DAC5)
val Background =Color(red = 252, green = 237, blue = 203)
but when I see the app the background is white and I don't know why, what would be the problem?

You have to wrap your content with MaterialTheme composable that is using your color palette, like this:
MaterialTheme(
colors = LightColorPalette,
) {
Surface(
...
)
}
It's described here: https://developer.android.com/jetpack/compose/themes/material

as Jan Bina said the problem was that I wasn't using my MatherialTheme so in the MainActivity I made this change:
From this:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
LoginScreen()
}
}
}
To this:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
OrderForYouTheme {
LoginScreen()
}
}
}
}

Related

Invocations can only happen from the context of an #composable function using Compose Navigation

Hi Im currently struggling with navigation in Jetpack Compose due to #composable invocations can only happen from the context of an #composable function. I have a function:
private fun signInResult(result: FirebaseAuthUIAuthenticationResult) {
val response = result.idpResponse
if (result.resultCode == RESULT_OK) {
user = FirebaseAuth.getInstance().currentUser
Log.e("MainActivity.kt", "Innlogging vellykket")
ScreenMain()
} else {
Log.e("MainActivity.kt", "Feil med innlogging" + response?.error?.errorCode)
}
}
and used with my navigation class shown under I only get the error message shown above, how do I fix it?
#Composable
fun ScreenMain(){
val navController = rememberNavController()
NavHost(navController = navController, startDestination = Routes.Vareliste.route) {
composable(Routes.SignUp.route) {
SignUp(navController = navController)
}
composable(Routes.ForgotPassword.route) { navBackStack ->
ForgotPassword(navController = navController)
}
composable(Routes.Vareliste.route) { navBackStack ->
Vareliste(navController = navController)
}
composable(Routes.Handlekurv.route) { navBackStack ->
Handlekurv(navController = navController)
}
composable(Routes.Profileromoss.route) { navBackStack ->
Profileromoss(navController = navController)
}
}
}
EDIT WITH COMPLETE CODE
Here is the whole code for the class if you guys wanted to see it!
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
JetpackComposeDemoTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
LoginPage()
}
}
}
}
private var user: FirebaseUser? = FirebaseAuth.getInstance().currentUser
private lateinit var auth: FirebaseAuth
#Composable
fun LoginPage() {
Box(modifier = Modifier.fillMaxSize()) {
}
Column(
modifier = Modifier.padding(20.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(text = "Velkommen til ITGuys", style = TextStyle(fontSize = 36.sp))
Spacer(modifier = Modifier.height(20.dp))
Box(modifier = Modifier.padding(40.dp, 0.dp, 40.dp, 0.dp)) {
Button(
onClick = { signIn() },
shape = RoundedCornerShape(50.dp),
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
) {
Text(text = "Logg inn")
}
}
}
}
private fun signIn() {
val providers = arrayListOf(
AuthUI.IdpConfig.EmailBuilder().build(),
AuthUI.IdpConfig.GoogleBuilder().build()
)
val signinIntent = AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(providers)
.build()
signInLauncher.launch(signinIntent)
}
private val signInLauncher = registerForActivityResult(
FirebaseAuthUIActivityResultContract()
) {
res -> this.signInResult(res)
}
private fun signInResult(result: FirebaseAuthUIAuthenticationResult) {
val response = result.idpResponse
if (result.resultCode == RESULT_OK) {
user = FirebaseAuth.getInstance().currentUser
Log.e("MainActivity.kt", "Innlogging vellykket")
ScreenMain()
} else {
Log.e("MainActivity.kt", "Feil med innlogging" + response?.error?.errorCode)
}
}
}
I need to add more text to be allowed to post this much code you can ignore this text cause it is just for being able to post.
As #z.y mentioned, you can pass a lambda with a onFirebaseAuthSuccess. I would also add that as you are passing the navController to the signup screen, the lambda callback you need to pass should look something like
onFirebaseAuthSuccess = { navController.navigate(Routes.Profileromoss.route) } - or whatever route you need
Based on
composable(Routes.SignUp.route) {
SignUp(navController = navController)
}
I would assume your signIn screen is called from inside the scope of a composable. If you can add the extract of code containing how you are calling the signInResult function we can be sure about it.
I'm not familiar with Firebase Authentication so I'm not sure where do you call or how you use your signInResult function but you cannot invoke a function that is annotated with #Composable (ScreenMain) from a scope that is not annotated by it such as ordinary function (signInResult).
You can consider adding a lambda callback for signInResult which will be called in the RESULT_OK condition block.
private fun signInResult(result: FirebaseAuthUIAuthenticationResult, onFirebaseAuthSuccess: () -> Unit) {
val response = result.idpResponse
if (result.resultCode == RESULT_OK) {
...
...
onFirebaseAuthSuccess() // this callback
} else {
...
}
}
Edit: #sgtpotatoe has better answer, you can invoke a navigation in your root composable from the lambda callback that will navigate to your target screen.
Ok so, in your MainActivity, you want your navigational component to be at the top:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
JetpackComposeDemoTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
ScreenMain()
}
}
}
}
Then add a route to your navhost for the login page:
composable(Routes.LoginPage.route) {
LoginPage(navController = navController)
}
I think its a bit of a major change, but you would have to rely on a view model to make the authentication, so it can handle the calls, not blocking the ui or showing a loading screen, and communicate with the view
It would look something like this:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val viewModel = MyViewModel()
setContent {
JetpackComposeDemoTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
ScreenMain(viewModel)
}
}
}
}
In the LoginPage you want to access the viewmodel to start the auth service calls
In the LoginPage you want to access the viewmodel to observe if the call is succesfull, and in that case do the navigation
In the MyViewModel you want to have the authentication calls, and to update the variable that triggers the navigation if auth is succesfull
Here is an example of a sample firebase authentication app in compose, I would use it as a guide https://firebase.blog/posts/2022/05/adding-firebase-auth-to-jetpack-compose-app

Jetpack Compose navigation crashes app when button clicked

I am just playing around with navigation compose and trying to figure out how it works. I read some articles and watch tutorials how to implement it in my app. So I choose the simpliest way to do this, but when I clicked the buttot to navigate to second screen, app crashed and exited. What am I doing wrong?
I am not doing any fancy stuff like bottom navigation, splash screens and etc, just navigate to the second screen.
Here I created navigation's logic
#Composable
fun navigationDraft(navController: NavController) {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = ScreenNavigation.Home.routeName
) {
composable(route = ScreenNavigation.Home.routeName) {
Home( navController = navController)
}
composable(route = ScreenNavigation.DetailedScreen.routeName) {
DetailedScreen(navController = navController)
}
}
}
Here I created navigation's route:
sealed class ScreenNavigation(var routeName: String, ){
object Home : ScreenNavigation(routeName = "home")
object DetailedScreen : ScreenNavigation(routeName = "detailed")
}
HomeScreen:
#Composable
fun Home(navController: NavController) {
Button(onClick = {navController.navigate(ScreenNavigation.DetailedScreen.routeName) }) {
}
}
Detailed Screen
#Composable
fun DetailedScreen(navController: NavController) {
Scaffold() {
TopAppBar(elevation = 2.dp, backgroundColor = Color.Magenta) {
Text(text = "Second Screen With Detail", fontStyle = FontStyle.Italic)
}
Column(verticalArrangement = Arrangement.Center) {
Text(text = "Hi", fontSize = 30.sp)
}
}
}
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Users_plofile_kotlinTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
val navController = rememberNavController()
Home(navController = navController)
nameViewModel.getUserNameList()
}
}
}
The error I have got:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.users_plofile_kotlin, PID: 24321
java.lang.NullPointerException
at androidx.navigation.NavController.navigate(NavController.kt:1652)
at androidx.navigation.NavController.navigate(NavController.kt:1984)
Ok, I think I found the issue. I created another #Composable MainScreen function instead of default #Composable Greetings function and put there all routes I would like to use, so my code now a little bit fixed but the main idea is still the same:
This should be instead of #Composable Greeting function
#Composable
fun MainScreen(){
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = "home"
) {
composable(route = ScreenNavigation.ButtonToCkeck.route) {
Home(navController = navController )
}
composable(route = ScreenNavigation.DetailedSreen.route) {
DetailedSreen()
}
}
}
And put it in the MainAcrivity:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
NavigationAppTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
MainScreen()
}
}
}
}
}
Now it works smoothly. Also the version of navigation composable is:
implementation("androidx.navigation:navigation-compose:2.5.1")

Text in jetpack compose not showing text?

This class simply emit character after 1-sec delay
class MainViewModel: ViewModel() {
var bouncingName = flow<Char> {
val text="hemant"
for (letter in text){
delay(1000L)
emit(letter)
}
}
}
This main class displays string after concatenating string display on the app
class MainActivity : ComponentActivity() {
#SuppressLint("CoroutineCreationDuringComposition")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
CompleteGuideKotlinFlowTheme {
// A surface container using the 'background' color from the theme
var letter by remember {
mutableStateOf(StringBuilder())
}
val viewModel = viewModel<MainViewModel>()
// var currentValue=viewModel.countDownValue.collectAsState(initial = 10)
lifecycleScope.launch {
viewModel.bouncingName.collect {
letter.append(it)
Log.d("TAG", "Character in main $letter")
}
}
Box(modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center) {
Text(
text = letter.toString(),
fontSize = 30.sp
)
}
}
}
}
}
But nothing is showing on the app.
I don't understand why?
StringBuilder is mutable. When you append to it, Compose doesn't get notified and the recomposition doesn't happen. You should use immutable types with MutableState and update it using MutableState's setter:
var letter by remember { mutableStateOf("") }
...
letter = letter + it

Receive data from Adapter to DetailsActivity?

I want to know how I can receive the data from adapter (recyclerview) to DetailsActivity. I tried many times but the images from recyclerview doesn't show in DetailsActivity.
this is my code.
ImageAdapter
class ImageAdapter (private var items:List<Item>, private val context:Context):
RecyclerView.Adapter<ImageAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int):ViewHolder {
return ViewHolder(
LayoutInflater.from(context).inflate(R.layout.item, parent, false)
)
}
override fun getItemCount(): Int {
return items.size
}
override fun onBindViewHolder(holder: ImageAdapter.ViewHolder, position: Int) {
val item = items[position]
Picasso.get().load(item.imageUrl).into(holder.imageView)
holder.imageView.setOnClickListener {
val intent = Intent(context, DetailsActivity::class.java)
intent.putExtra("iImages", item.imageUrl)
context.startActivity(intent)
}
}
class ViewHolder(view:View):RecyclerView.ViewHolder(view) {
val imageView : ImageView = view.findViewById(R.id.imageView)
}
}
DetailsActivity
class DetailsActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_details)
val aImageView = intent.getIntExtra("iImages", 0)
details_image.setImageResource(aImageView)
}
}
RecyclerActivity
class RecyclerActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_recycler)
val storage = FirebaseStorage.getInstance()
val storageRef = storage.reference.child("wallpapers")
val imageList : ArrayList<Item> = ArrayList()
//progressBar.visibility = View.VISIBLE
val listAllTask : Task<ListResult> = storageRef.listAll()
listAllTask.addOnCompleteListener { result ->
val items: List<StorageReference> = result.result!!.items
//add cycle for add image url to list
items.forEachIndexed { index,item ->
item.downloadUrl.addOnSuccessListener {
Log.d("item", "$it")
imageList.add(Item(it.toString()))
}.addOnCompleteListener {
recyclerview.adapter = ImageAdapter(imageList, this)
recyclerview.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
//progressBar.visibility = View.GONE
}
}
}
}
}
Item
data class Item(
var imageUrl: String
)
The problem is this line:
details_image.setImageResource(aImageView)
You are setting a resource int but what you pass seems to be a url, so instead load the "iImages" value with Picasso for that view
Edit: Replace this:
val aImageView = intent.getIntExtra("iImages", 0)
details_image.setImageResource(aImageView)
with this
val retrievedImageUrl = intent.getIntExtra("iImages", 0)
Picasso.get().load(retrievedImageUrl).into(details_image)
The Answer
val imageView : ImageView = findViewById(R.id.details_image)
Glide.with(this).load(intent.getStringExtra("iImages")).into(imageView)

Does CameraX allow pinch-zoom?

I know CameraX is kinda new but does it allow to zoom?
I don't mean zooming on image but directly on the screen (TextureView) right before taking a picture?
Pinch to zoom:
val camera = cameraProvider.bindToLifecycle(...
val scaleGestureDetector = ScaleGestureDetector(this,
object : ScaleGestureDetector.SimpleOnScaleGestureListener() {
override fun onScale(detector: ScaleGestureDetector): Boolean {
val scale = camera.cameraInfo.zoomState.value!!.zoomRatio * detector.scaleFactor
camera.cameraControl.setZoomRatio(scale)
return true
}
})
previewView.setOnTouchListener { view, event ->
view.performClick()
scaleGestureDetector.onTouchEvent(event)
return#setOnTouchListener true
}
They have added the support recently for the same. It will be included in there next release.
Change ID . https://android.googlesource.com/platform/frameworks/support/+/f60a1ac99a4144d5bfb0529fa0c769f43eb10c9f
Assuming these dependencies:
dependencies {
implementation "androidx.camera:camera-core:1.0.0-alpha08"
implementation "androidx.camera:camera-camera2:1.0.0-alpha08"
implementation "androidx.camera:camera-lifecycle:1.0.0-alpha02"
implementation "androidx.camera:camera-view:1.0.0-alpha05"
implementation "androidx.camera:camera-extensions:1.0.0-alpha05"
}
This would be the CameraFragment.kt for the 1.0.0-alpha08 library:
class CameraFragment: Fragment(), View.OnTouchListener, ScaleGestureDetector.OnScaleGestureListener {
private lateinit var scaleDetector: ScaleGestureDetector
private lateinit var container: ConstraintLayout
private lateinit var previewView: PreviewView
private lateinit var cameraControl: CameraControl
private lateinit var cameraInfo: CameraInfo
private var lastScaleFactor = 0f
...
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
scaleDetector = ScaleGestureDetector(context, this);
...
}
#SuppressLint("MissingPermission", "ClickableViewAccessibility")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
container = view as ConstraintLayout
previewView = container.findViewById(R.id.previewView)
previewView.setOnTouchListener(this)
...
}
#SuppressLint("ClickableViewAccessibility")
override fun onTouch(view: View?, event: MotionEvent?): Boolean {
scaleDetector.onTouchEvent(event)
return true
}
override fun onScaleBegin(detector: ScaleGestureDetector?): Boolean {
return true
}
override fun onScaleEnd(detector: ScaleGestureDetector?) {
}
override fun onScale(detector: ScaleGestureDetector?): Boolean {
val zoomRatio: Float? = cameraInfo.zoomRatio.value?.toFloat()
val minZoomRatio: Float? = cameraInfo.minZoomRatio.value?.toFloat()
val maxZoomRatio: Float? = cameraInfo.maxZoomRatio.value?.toFloat()
val scaleFactor = scaleDetector.getScaleFactor()
if (lastScaleFactor == 0f || (Math.signum(scaleFactor) == Math.signum(lastScaleFactor))) {
cameraControl.setZoomRatio(Math.max(minZoomRatio!!, Math.min(zoomRatio!! * scaleFactor, maxZoomRatio!!)))
lastScaleFactor = scaleFactor
} else {
lastScaleFactor = 0f
}
return true
}
}
CameraXBasic/1.0.0-alpha08 already has the PreviewView.