I have an Activity and MyApp composable function. In the function I need to show either list with details or just list screen depending on the available width. How to determine available width for the content I want to show using Jetpack Compose? What is a good practice for this using Compose?
class MyActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ProjectTheme {
MyApp()
}
}
}
}
#Composable
fun MyApp() {
val isLarge: Boolean = ...// how to determine whether the available width is large enough?
if (isLarge) {
ListScreenWithDetails()
} else {
ListScreen()
}
}
#Composable
fun ProjectTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: #Composable () -> Unit) {
// ... project theme
}
You can use :
val configuration = LocalConfiguration.current
Then:
val isLargeWidth = configuration.screenWidthDp > 840
For a generic solution, consider using the Window Size Classes, see this and this for reference.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
val windowSizeClass = calculateWindowSizeClass(this)
MyApp(windowSizeClass)
}
}
Related
I'm trying to implement a first time using screen (like any other app when you have to fill some options before using the app for the first time).
I can't go to another Jetpack compose on an main activity on-create state because it check that every recomposition, and take me to the navigation path (I'd like to check the datastore entry once during launch), this what I already try, not seem to be working:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launch {
val onBoardingStatus = dataStoreManager.onBoard.first()
setContent {
val navController = rememberNavController()
OnBoardingNavHost(navController)
navController.navigate(if (onBoardingStatus) "on_boarding" else "main") {
launchSingleTop = true
popUpTo(0)
}
}
}
}
it is possible to check that only once (in application class for example and not in oncreate?)
please advice,
thanks in advance
You have to use LaunchedEffect for this, you can do something like this
enum class OnboardState {
Loading,
NoOnboarded,
Onboarded,
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
var onboardingState by remember {
mutableStateOf(OnboardState.Loading)
}
LaunchedEffect(Unit) {
onboardingState = getOnboardingState()
}
when (onboardingState) {
OnboardState.Loading -> showSpinner()
OnboardState.NoOnboarded -> LaunchedEffect(onboardingState) {
navigateToOnboarding()
}
OnboardState.Onboarded -> showContent()
}
}
}
I use this code to change composable View orientation but it doesn't work and I don't need to change app orientation
val configuration = Configuration()
configuration.orientation = Configuration.ORIENTATION_LANDSCAPE
CompositionLocalProvider(LocalConfiguration provides configuration) {
Screen()
}
You can change the screen orientation using this:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
startActivity(intent)
setContent {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
Text("I'm a cat person")
}
}
}
I have run into an issue with Jetpack Compose. In my application's old classes (activity and fragment) I am using a custom Context wrapper for my custom Resources wrapper. This is because string resources values are downloaded from a server; the Strings resource files do contain string IDs, their values are empty and are replaced by the server's values.
In activities and fragments I can simply replace the context in getContext() or attachBaseContext(newBase: Context?) methods, but how do I do this in Jetpack Compose?
Jetpack Compose does have these Context methods: LocalConfiguration.current and LocalContext.current. I love how straightforward this is, but I was not able to find where this Context object is inited. At first, I thought it was in the parent Activity in attachBaseContext() method when I set content, but this code does not seem to be useful:
#AndroidEntryPoint
class MyComposeActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
MyComposeActivityScreen()
}
}
}
override fun attachBaseContext(newBase: Context?) {
val language = Locale(Utils.getLanguage(newBase))
super.attachBaseContext(MyCustomContextWrapper.wrap(MyCustomResourcesContextWrapper(newBase), language))
}
You can try specifying the context manually with CompositionLocalProvider:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
updateComposeView(this)
}
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(newBase)
updateComposeView(newBase ?: this)
}
fun updateComposeView(context: Context) {
setContent {
CompositionLocalProvider(
LocalContext provides context
) {
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
MyComposeActivityScreen()
}
}
}
}
Also a more Compose way to solve the original problem is to create your own string holder, something like LocalStrings, kind of as shown in this answer.
I'm a beginner to jetpack compose.
Following codelabs, I found they teach me only using top-level function.
I can use the composable function in Activity, but I can't found whoever use this way.
I wanna know which is the best practice.
my code
class RecyclerViewActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApp {
Test()
}
}
}
#Composable
fun MyApp(content: #Composable () -> Unit) {
ComposeDemoTheme {
content()
}
}
#Composable
fun Test() {
}
}
codelabs
class RecyclerViewActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApp {
Test()
}
}
}
}
#Composable
fun MyApp(content: #Composable () -> Unit) {
ComposeDemoTheme {
content()
}
}
#Composable
fun Test() {
}
You expected to split your code by screens/views, and each screen/view is placed inside it's own file
Usually you don't wanna have any composables in you Activity. You can set theme and other context variables right inside setContent or move it to MyApp if you have too many logic, but in this case I'd stay you move this composable into MyApp.kt too
I usually do structure like this:
screenName: package
Screen.kt
ScreenRow.kt: if screen has a list
ButtomBar.kt: if screen has buttom bar and the code is not trivial
...
So usually when you see that you file contains too many composables you move logically independent parts into other files
I use the navigation of official example OwlApp as my main navigation system, and I want to achieve the camerax in my project. But there is no easy way to combine camera in compose, so I think if have a way to let a activity which have a camera screen act as a compose destination, then I can navigate it normally as the app does.
You could handle this by passing a lambda into the composable function and implementing the logic outside of the composable function (inside your activity).
class MainActivity : AppCompatActivity() {
lateinit var onCameraClick : () -> Unit
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SampleTheme {
Surface(color = MaterialTheme.colors.background) {
MainView(onCameraClick = onCameraClick)
}
}
}
onCameraClick = {
// Camera logic inside activity scope.
}
}
}
#Composable
fun MainView(onCameraClick : () -> Unit) {
Button(onClick = onCameraClick) {
Text(text = "Camera")
}
}