/* (c) naturalprogramming.com 2023-02-12 File created by Kari Laitinen 2023-08-16 Material 3 in use in newer Android Studio. 2023-09-17 Screen size, in Dp, density pixels, is displayed. Screen size changes if the user modifies Display size in Settings. androidManifest.xml was not modified. https://developer.android.com/guide/topics/resources/runtime-changes This example shows how to navigate from one screen to another in Jetpack Compose. The child screen transfers information to the main screen via a common ViewModel. I hope this is a good way to make the operation. The following page should be studied carefully. https://developer.android.com/jetpack/compose/navigation In February 2023 it was necessary to modify the build.gradle file of the project according to the instructions in the above page. This is an Android app made of this single file. If you want to test this app, create an Android Compose project using the project name ChangingScreensC and the package name shown below. Then, store this file to the folder where MainActivity.kt is and make the following change to AndroidManifest.xml: android:name=".ChangingScreensCActivity" Imports may need to be modified. Some 'warnings': - tab size in this file is 3 spaces - underscores are used in the names invented by the programmer */ package changing.screensc import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.viewModels import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.navigation.NavController import androidx.navigation.NavType import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.navigation.navArgument import changing.screensc.ui.theme.ChangingScreensCTheme import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch data class ScreensState( val screen_change_count : Int = 0 ) class GeneralViewModel : ViewModel() { // Expose screen UI state private val _uiState = MutableStateFlow( ScreensState() ) // val uiState: StateFlow = _uiState.asStateFlow() // asStateFlow() represents this mutable state flow as a read-only state flow. fun consumableState() = _uiState.asStateFlow() fun increase_screen_change_count() { _uiState.update { currentState -> currentState.copy( screen_change_count = currentState.screen_change_count + 1 ) } } } @Composable fun AnotherScreen( showOnlyPostsByUser: Boolean = false, navController: NavController, view_model : GeneralViewModel, view_state : State ) { Surface( modifier = Modifier.fillMaxSize(), color = Color.DarkGray ) { // LocalConfiguration.current works only inside a composable val configuration = LocalConfiguration.current val screen_width = configuration.screenWidthDp val screen_height = configuration.screenHeightDp Column( horizontalAlignment = Alignment.CenterHorizontally ) { Spacer( modifier = Modifier.weight( 0.5F ) ) // Text had to be put inside a Box in order to center it // inside the upper half of the screen. Box( modifier = Modifier.weight( 0.5F ), contentAlignment = Alignment.Center ) { Text( "Screen size (Dp): " + screen_width + " x " + screen_height , fontSize = 24.sp, color = Color.LightGray ) } Box( modifier = Modifier.weight( 0.5F ), contentAlignment = Alignment.Center ) { Text("This is AnotherScreen", fontSize = 28.sp, color = Color.LightGray ) } Button( onClick = { // When we started to use the navigateUp() method, the standard 'Back' button // stopped to work for this app. view_model.increase_screen_change_count() navController.navigateUp() }, modifier = Modifier.requiredHeight( 100.dp ). requiredWidth( 200.dp).weight( 1F ), shape = RoundedCornerShape( 20.dp ), colors = ButtonDefaults.buttonColors( containerColor = Color.LightGray ) ) { Text( text = "BACK", fontSize = 32.sp, color = Color.Blue ) } Spacer( modifier = Modifier.weight( 0.5F ) ) } } } @Composable fun MainScreen( navController: NavController, view_model : GeneralViewModel, view_state : State ) { Surface( modifier = Modifier.fillMaxSize(), color = Color.Black ) { // LocalConfiguration.current works only inside a composable val configuration = LocalConfiguration.current val screen_width = configuration.screenWidthDp val screen_height = configuration.screenHeightDp Column( horizontalAlignment = Alignment.CenterHorizontally ) { Spacer( modifier = Modifier.weight( 0.5F ) ) Box( modifier = Modifier.weight( 0.5F ), contentAlignment = Alignment.Center ) { Text( "Screen size (Dp): " + screen_width + " x " + screen_height , fontSize = 24.sp, color = Color.LightGray ) } //Spacer( modifier = Modifier.weight( 0.5F ) ) Box( modifier = Modifier.weight( 0.5F ), contentAlignment = Alignment.Center ) { Text( "" + view_state.value.screen_change_count + " SCREEN CHANGES", fontSize = 24.sp, color = Color.LightGray ) } Button( onClick = { navController.navigate("AnotherScreen/true") }, modifier = Modifier.requiredHeight( 100.dp ). requiredWidth( 200.dp).weight( 1F ), shape = RoundedCornerShape( 20.dp ), colors = ButtonDefaults.buttonColors( containerColor = Color.Cyan ) ) { Text( text = "CHANGE", fontSize = 32.sp, color = Color.Blue ) } Spacer( modifier = Modifier.weight( 0.5F ) ) } } } class ChangingScreensCActivity : ComponentActivity() { private val viewModel : GeneralViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { ChangingScreensCTheme { val navController = rememberNavController() val viewState = viewModel.consumableState().collectAsState() NavHost( navController = navController, startDestination = "MainScreen" ) { composable("MainScreen" ) { MainScreen( navController, viewModel, viewState ) } composable("AnotherScreen/{showOnlyPostsByUser}", arguments = listOf( navArgument("showOnlyPostsByUser") { type = NavType.BoolType defaultValue = false } )) { val showOnlyPostsByUser = it.arguments?.getBoolean("showOnlyPostsByUser") ?: false AnotherScreen(showOnlyPostsByUser, navController, viewModel, viewState ) } } } } } } /* One problem in this app was a light flash between screen changes. It was solved 2023-08-16 by making the res/values/themes.xml look like: