I am using Android Studio Electric Eel | 2022.1.1.
I am fairly new. I was trying out the checkbox in Compose with Material3 in Android. I am not able to align the text next to check box. Please help. Here is the code and an screenshot image of the emulator screen.
project level build.gradle firl
buildscript {
ext {
compose_version = '1.2.0'
}
}// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '7.4.0' apply false
id 'com.android.library' version '7.4.0' apply false
id 'org.jetbrains.kotlin.android' version '1.7.0' apply false
}
module level build.gradle
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
namespace 'com.example.test'
compileSdk 33
defaultConfig {
applicationId "com.example.test"
minSdk 27
targetSdk 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion '1.2.0'
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation 'androidx.activity:activity-compose:1.3.1'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.compose.material3:material3:1.0.0-alpha11'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
}
MainActivity.kt
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
TestTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
Test()
}
}
}
}
}
#OptIn(ExperimentalMaterial3Api::class)
#Composable
fun Test() {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.padding(0.dp)
) {
Row(
modifier = Modifier.padding(3.dp)
)
{
Checkbox(
checked = true,
onCheckedChange = { },
modifier = Modifier.absoluteOffset((-12).dp, 0.dp)
)
Text(
text = "Check me",
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
textAlign = TextAlign.Left
)
}
TextButton(
onClick = { },
modifier = Modifier.padding(end = 0.dp)
) {
Text(
text = "Right text",
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
textAlign = TextAlign.Right
)
}
}
}
I have attached a screen shot of the emulator screen to show how the text is aligned.
Question:
How can I align the text "Check me" along with check box.
How can I bring it closer to check box.
Please help.
I tried to place text next to checkbox in my android studio material 3 project. The text is not aligned in line with the checkbox.
You can you this guide to align: https://developer.android.com/jetpack/compose/layouts/alignment-lines
I was playing around with your code, and here is what I get:
fun Modifier.firstBaselineToTop(
firstBaselineToTop: Dp
) = layout { measurable, constraints ->
// Measure the composable
val placeable = measurable.measure(constraints)
// Check the composable has a first baseline
check(placeable[FirstBaseline] != AlignmentLine.Unspecified)
val firstBaseline = placeable[FirstBaseline]
// Height of the composable with padding - first baseline
val placeableY = firstBaselineToTop.roundToPx() - firstBaseline
val height = placeable.height + placeableY
layout(placeable.width, height) {
// Where the composable gets placed
placeable.placeRelative(0, placeableY)
}
}
#Composable
fun Test() {
Row(
horizontalArrangement = Arrangement.Start,
modifier = Modifier.padding(0.dp)
) {
Row {
Checkbox(
checked = true,
onCheckedChange = { },
modifier = Modifier.absoluteOffset((-12).dp, 0.dp)
)
Text(
text = "Check me",
Modifier.padding(vertical =10.dp),
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
textAlign = TextAlign.Left
)
}
TextButton(
onClick = { },
modifier = Modifier.padding(end = 0.dp)
) {
Text(
text = "Right text",
Modifier.firstBaselineToTop(16.dp),
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
textAlign = TextAlign.Right
)
}
}
}
Related
On my Samsung Galaxy S22+ with One UI 5.0 and Android 13, compose AlertDialog always takes up full width, on other devices it works just as expected.
Compose version is 1.3.1
You can reproduce this by simply just downloading material catalog app from Google Play store.
I suspect this is most likely a bug on Compose side, if there's a quick fix, I'd appreciate it.
#Composable
fun AlertDialogSample() {
val openDialog = remember { mutableStateOf(true) }
if (openDialog.value) {
AlertDialog(
onDismissRequest = {
// Dismiss the dialog when the user clicks outside the dialog or on the back
// button. If you want to disable that functionality, simply use an empty
// onCloseRequest.
openDialog.value = false
},
title = {
Text(text = "Title")
},
text = {
Text(
"This area typically contains the supportive text " +
"which presents the details regarding the Dialog's purpose."
)
},
confirmButton = {
TextButton(
onClick = {
openDialog.value = false
}
) {
Text("Confirm")
}
},
dismissButton = {
TextButton(
onClick = {
openDialog.value = false
}
) {
Text("Dismiss")
}
}
)
}
}
After Android 13 update on my device, Dialog with XML layouts are taking expected width. But Compose AlertDialog & Dialog are taking up full width. We are facing this issue with Compose Dialogs only,
I am using Samsung Galaxy M32 with One UI 5.0 & Android 13, App uses Compose version 1.1.0-beta01 & targetSdkVersion 33,
using usePlatformDefaultWidth = true did not help,
This issue is most likely a bug on Compose side,
You can find quick fixes for both Dialog and AlertDialog in compose,
For Compose AlertDialog()
I have used modifier and set DialogProperty usePlatformDefaultWidth to false & set fillMaxWidth with fraction 0.92f.
modifier = Modifier.fillMaxWidth(0.92f),
properties =DialogProperties(usePlatformDefaultWidth =false),
Compose AlertDialog() code snippet:
AlertDialog(
modifier = Modifier.fillMaxWidth(0.92f),
properties = DialogProperties(
usePlatformDefaultWidth = false
),
onDismissRequest = { ... },
buttons = {
Column(
modifier = Modifier
.fillMaxWidth()
) {
...
}
},
title = {
},
text = {
Column(
modifier = Modifier
.fillMaxWidth()
) {
........
}
}
)
For Compose Dialog()
I have used Surface to wrap the dialog content with modifier = Modifier.fillMaxWidth(0.92f),
RoundedCornerShape with radius, set Color.Transparent to background color and also set DialogProperty usePlatformDefaultWidth to false
Surface(
modifier = Modifier.fillMaxWidth(0.92f),
shape = RoundedCornerShape(8.dp),
color = Color.Transparent,
content = {})
Compose Dialog() code snippet:
Dialog(
onDismissRequest = { },
properties = DialogProperties(
dismissOnClickOutside = true,
dismissOnBackPress = true,
usePlatformDefaultWidth = false
),
content = {
Surface(
modifier = Modifier.fillMaxWidth(0.92f),
shape = RoundedCornerShape(8.dp),
color = Color.Transparent,
content = {
Column(
modifier = Modifier
.background(color = colorResource(id = android.R.color.white))
.fillMaxWidth(1f)
.wrapContentHeight(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
........
}
})
})
The Alert-Dialog-Composable accepts DialogProperties
#Composable
fun AlertDialog(
properties: DialogProperties = DialogProperties()
...
)
/**
* Properties used to customize the behavior of a [Dialog].
...
* #property usePlatformDefaultWidth Whether the width of the dialog's content should
* be limited to the platform default, which is smaller than the screen width.
*/
class DialogProperties #ExperimentalComposeUiApi constructor(
val usePlatformDefaultWidth: Boolean = true
...
)
By default, usePlatformDefaultWidth = true, so the Dialog should not fill the screen width.
-> What you see is most probably a bug & should be reported
I am using compose 1.1.1 in my jetpack compose. I cannot update to latest version. I am want something like this solution. I am getting error on my weight modifier. Can someone guide me how can I get my weight modifier in Row?
implementation "androidx.compose.runtime:runtime:$compose_version"
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.foundation:foundation:$compose_version"
implementation "androidx.compose.foundation:foundation-layout:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
androidTestImplementation "androidx.compose.ui:ui-test:$compose_version"
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
implementation "androidx.activity:activity-compose:$compose_version"
Row.kt
Column {
Row(
modifier = Modifier.weight(1f, false)
) {
//...
}
}
Error
Expression 'weight' cannot be invoked as a function. The function 'invoke()' is not found
Many Thanks
UPDATE
I am adding my whole code here please have a look...
#Composable
fun Input(optionData: OptionData) {
Column(
modifier = Modifier
.fillMaxSize()
) {
Item(optionData)
}
}
#Composable
fun Item(optionData: OptionData) {
/// more item of compose i.e. Text, Textfield
Submit()
}
#Composable
fun Submit() {
Row(
modifier = Modifier.weight(1f, false)
) {
//...
}
}
UPDATE 2
After trying #Thracian solution it solve the problem of weight but my view is not going to bottom.
#Composable
fun Submit() {
Column {
OnSubmit {
PrimaryMaterialButton(text = stringResource(id = R.string.submit)) {
}
}
}
}
#Composable
fun ColumnScope.OnSubmit(content: #Composable () -> Unit) {
Row(
modifier = Modifier.weight(1f, false)
) {
content()
}
}
#Composable
fun Submit() {
Row(
modifier = Modifier.weight(1f, false)
) {
//...
}
}
For this code to be able to access Modifier.weight() from Column it should have receiver as ColumnScope. Some modifiers are created with scope so they are available only inside that scope such as Modifier.weight for Row or Column or Modifier.matchParentSize for Box.
#Composable
fun ColumnScope.Submit() {
Row(
modifier = Modifier.weight(1f, false)
) {
//...
}
}
// Allowed
Column {
Submit()
}
//Not Allowed
Row() {
Submit()
}
I am practicing Jetpack compose, by making a simple Application with multiple destinations (screen), I happen to be referencing an Android Developer codelab, for a destination.
But the final looks is very different from the codelab's, most noticably the fonts are way bigger on my code than it is in the codelab even though I'm using the same font style, h3, and I've tried as much as possible to be using the same or a more recent dependecency than was used in the codelab.
Here's what I mean.
The Android Developer look;
And my code looks like;
Here's the Android Developer compose code;
#Composable
fun FavoriteCollectionCard(
#StringRes text: Int,
#DrawableRes drawable: Int,
modifier: Modifier = Modifier
) {
Surface(
shape = MaterialTheme.shapes.small,
modifier = modifier
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.width(192.dp)
) {
Image(
painter = painterResource(drawable),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.size(56.dp)
)
Text(
text = stringResource(text),
style = MaterialTheme.typography.h3,
modifier = Modifier.padding(horizontal = 16.dp)
)
}
}
}
Here's the Android Developer compose code;
#Composable
fun DashBoardCard(
#StringRes text: Int,
#DrawableRes drawable: Int,
modifier: Modifier = Modifier
) {
Surface(
shape = MaterialTheme.shapes.small,
modifier = modifier
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.width(192.dp)
) {
Image(
painter = painterResource(drawable),
contentDescription = null,
contentScale = ContentScale.Crop,
modifier = Modifier
.size(56.dp)
)
Text(
text = stringResource(text),
style = MaterialTheme.typography.h3,
modifier = Modifier.padding(horizontal = 16.dp)
)
}
}
}
As I said before I tried to make the dependencies the same or more recent, I don't want this question to be anymore bucky, but I'm just going to add a few dependency, please whatever you need I'm happy to provide.
Android Developer Codelab dependency;
ext {
compose_version = '1.2.0-alpha05'
}
plugins {
id 'com.android.application' version '7.2.0' apply false
id 'com.android.library' version '7.2.0' apply false
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
id 'com.diffplug.spotless' version '6.3.0'
}
My dependency;
ext {
compose_version = '1.2.0-alpha05'
nav_version = "2.5.1"
}
plugins {
id 'com.android.application' version '7.2.1' apply false
id 'com.android.library' version '7.2.1' apply false
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
}
EDIT;
I will greatly appreciate any type of help, pointers, corrections whatever help you can offer, I will be much greatful.
Thanks a lot for your help, in advance.
Are you using same implementation of MaterialTheme as they are and are you wrapping your screen in it? Their implementation is here and h3 is defined here like this:
h3 = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 14.sp,
letterSpacing = 0.sp
)
Your image definitely doesn't look like 14sp, it also looks like different color, so I guess you are missing the theme completely.
I'm testing my application for accessibility compatibility using TalkBack. However, certain OutlinedTextFields are being skipped and are unselectable even by clicking if TalkBack is enabled. I created a sample app using the latest versions of Kotlin/Gradle/Compose to make sure it wasn't something to do with my project setup.
Changing the "placeholder" text to certain values allows TalkBack selection, and other values make it unselectable (e.g. "MM/DD/YYYY" makes TalkBack skip the field, but "Hello World" allows TalkBack to select the field).
Code is as follows:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
PlaygroundTheme {
// A surface container using the 'background' color from the theme
Column(
modifier = Modifier.fillMaxSize(),
) {
Greeting("Android")
OutlinedTextField(
value = remember{mutableStateOf("")}.value,
onValueChange = {
},
label = {
Text(text = "Date of Birth")
},
placeholder = {
Text(text = "MM/DD/YYYY") //TalkBack won't select the field with this placeholder
// Text(text = "Hello World") //TalkBack WILL select the field with this placeholder
}
)
}
}
}
}
}
#Composable
fun Greeting(name: String) {
Text(text = "Hello $name!")
}
Here are the dependencies I'm using:
buildscript {
ext {
compose_version = '1.3.0-alpha01'
}
}// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '7.2.1' apply false
id 'com.android.library' version '7.2.1' apply false
id 'org.jetbrains.kotlin.android' version '1.7.0' apply false
}
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.example.playground"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary true
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
compose true
}
composeOptions {
kotlinCompilerExtensionVersion '1.2.0'
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.8.0'
implementation "androidx.compose.ui:ui:$compose_version"
implementation 'androidx.compose.material3:material3:1.0.0-alpha14'
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.0'
implementation 'androidx.activity:activity-compose:1.5.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
}
Are there certain strings or characters that are forbidden by TalkBack?
I have the same problem... I ran some test (with androidx.compose.ui:ui:1.3.0-beta03):
OutlinedTextField empty.❌
OutlinedTextField with placeholder.✅
OutlinedTextField with placeholder and label.❌
OutlinedTextField with label.✅
TextField empty.❌
TextField with placeholder.✅
TextField with placeholder and label.✅
TextField with label.✅
There is a difference of behavior between TextField and OutlinedTextField when we put a placeholder and a label. There are some similar issues on Google's issue tracker here, here or here.
I found no others solutions than using only label with an OutlinedTextField, as accessibility is not optional for us. I made a new issue on Google's tracker with the following sample I made, and the result:
Column(
modifier = Modifier
.fillMaxSize()
.padding(all = 16.dp)
.verticalScroll(state = rememberScrollState()),
) {
// OutlinedTextField empty.❌
Text(text = "OutlinedTextField empty")
Spacer(modifier = Modifier.height(height = 4.dp))
OutlinedTextField(
value = "",
onValueChange = { },
modifier = Modifier.fillMaxWidth(),
)
Spacer(modifier = Modifier.height(height = 12.dp))
// OutlinedTextField with placeholder.✅
Text(text = "OutlinedTextField with placeholder")
Spacer(modifier = Modifier.height(height = 4.dp))
OutlinedTextField(
value = "",
placeholder = { Text(text = "Placeholder") },
onValueChange = { },
modifier = Modifier.fillMaxWidth(),
)
Spacer(modifier = Modifier.height(height = 12.dp))
// OutlinedTextField with placeholder and label.❌
Text(text = "OutlinedTextField with placeholder and label")
OutlinedTextField(
value = "",
placeholder = { Text(text = "Placeholder") },
label = { Text(text = "Label") },
onValueChange = { },
modifier = Modifier.fillMaxWidth(),
)
Spacer(modifier = Modifier.height(height = 12.dp))
// OutlinedTextField with label.✅
Text(text = "OutlinedTextField with label")
OutlinedTextField(
value = "",
label = { Text(text = "Label") },
onValueChange = { },
modifier = Modifier.fillMaxWidth(),
)
Spacer(modifier = Modifier.height(height = 12.dp))
// TextField empty.❌
Text(text = "TextField empty")
Spacer(modifier = Modifier.height(height = 4.dp))
TextField(
value = "",
onValueChange = { },
modifier = Modifier.fillMaxWidth(),
)
Spacer(modifier = Modifier.height(height = 12.dp))
// TextField with placeholder.✅
Text(text = "TextField with Placeholder")
Spacer(modifier = Modifier.height(height = 4.dp))
TextField(
value = "",
placeholder = { Text(text = "Placeholder") },
onValueChange = { },
modifier = Modifier.fillMaxWidth(),
)
Spacer(modifier = Modifier.height(height = 12.dp))
// TextField with placeholder and label.✅
Text(text = "TextField with Placeholder and label")
Spacer(modifier = Modifier.height(height = 4.dp))
TextField(
value = "",
placeholder = { Text(text = "Placeholder") },
label = { Text(text = "Label") },
onValueChange = { },
modifier = Modifier.fillMaxWidth(),
)
Spacer(modifier = Modifier.height(height = 12.dp))
// TextField with label.✅
Text(text = "TextField with label")
Spacer(modifier = Modifier.height(height = 4.dp))
TextField(
value = "",
label = { Text(text = "Label") },
onValueChange = { },
modifier = Modifier.fillMaxWidth(),
)
Spacer(modifier = Modifier.height(height = 12.dp))
}
As #BerHug notes, there seems to be a bug. In order to make it read something out you can add a content description by adding a semantics modifier:
OutlinedTextField(
modifier = modifier.semantics {
contentDescription = "this is a text entry box"
},
) {}
This will also cause the screen reader to add the additional instructions on how to activate the text entry field when it reads it out.
There's some useful additional information in these links:
https://developer.android.com/jetpack/compose/accessibility
https://bryanherbst.com/2020/11/03/compose-semantics-talkback/
A few days ago I bumped on a problem where a part of my view is overlaped by keyboard.
Let's say we have 3 different dialogs (could be any content), which looks like this:
When I want to write in anything, last dialog is covered by keyboard:
And there's no way to see what user wrote. Here's my code:
#Composable
fun BuildWordsView(navController: NavController, sharedViewModel: SharedViewModel) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(PrimaryLight)
.fillMaxSize()
) {
BuildWordsScreenContents()
}
}
#Composable
fun BuildWordsScreenContents() {
Column(
Modifier
.fillMaxSize()
.padding(all = 16.dp)
) {
val inputBoxModifier = Modifier
.clip(RoundedCornerShape(10.dp))
.background(Primary)
.weight(12f)
.wrapContentHeight()
InputBlock("Dialog1", inputBoxModifier)
Spacer(Modifier.weight(1f))
InputBlock("Dialog2", inputBoxModifier)
Spacer(Modifier.weight(1f))
InputBlock("Dialog3", inputBoxModifier)
}
}
#Composable
fun InputBlock(dialogText: String, inputBlockModifier: Modifier) {
Column(modifier = inputBlockModifier) {
Text(
dialogText,
fontSize = 30.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
var text by remember { mutableStateOf("") }
TextField(
value = text,
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center),
onValueChange = { text = it },
label = { Text("Label") }
)
}
}
This question seems to be similar to mine but answers modificate the content of view which I want to avoid:
Software keyboard overlaps content of jetpack compose view
By now I figured out how to solve this problem and I share my approach as an answer
My approach to deal with this problem is using Insets for Jetpack Compose:
https://google.github.io/accompanist/insets/
In order to start dealing with problem you need to add depency to gradle (current version is 0.22.0-rc).
dependencies {
implementation "com.google.accompanist:accompanist-insets:0.22.0-rc"
}
Then you need to wrap your content in your activity with ProvideWindowInsets
setContent {
ProvideWindowInsets {
YourTheme {
//YOUR CONTENT HERE
}
}
}
Additionaly you need to add following line in your activity onCreate() function:
WindowCompat.setDecorFitsSystemWindows(window, false)
Update: Despite this function is recommended, to my experience it may make this approach not work. If you face any problem, you may need to delete this line.
Now your project is set up to use Insets
In the next steps I'm gonna use code I provided in question
First of all you need to wrap your main Column with
ProvideWindowInsets(windowInsetsAnimationsEnabled = true)
Then let's modificate a modifier a bit by adding:
.statusBarsPadding()
.navigationBarsWithImePadding()
.verticalScroll(rememberScrollState())
As you can see the trick in my approach is to use verticalScroll(). Final code of main column should look like this:
#Composable
fun BuildWordsView(navController: NavController, sharedViewModel: SharedViewModel) {
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(PrimaryLight)
.statusBarsPadding()
.navigationBarsWithImePadding()
.verticalScroll(rememberScrollState())
.fillMaxSize()
) {
BuildWordsScreenContents()
}
}
}
Now let's modificate the modifier of Column in fun BuildWordsScreenContents()
The main modification is that we provide a height of our screen by:
.height(LocalConfiguration.current.screenHeightDp.dp)
This means that height of our Column would fit our screen perfectly. So when keyboard is not opened the Column will not be scrollable
There is the full code:
#Composable
fun BuildWordsView(navController: NavController, sharedViewModel: SharedViewModel) {
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(PrimaryLight)
.statusBarsPadding()
.navigationBarsWithImePadding()
.verticalScroll(rememberScrollState())
.fillMaxSize()
) {
BuildWordsScreenContents()
}
}
}
#Composable
fun BuildWordsScreenContents() {
Column(
Modifier
.height(LocalConfiguration.current.screenHeightDp.dp)
.padding(all = 16.dp)
) {
val inputBoxModifier = Modifier
.clip(RoundedCornerShape(10.dp))
.background(Primary)
.weight(12f)
.wrapContentHeight()
InputBlock("Dialog1", inputBoxModifier)
Spacer(Modifier.weight(1f))
InputBlock("Dialog2", inputBoxModifier)
Spacer(Modifier.weight(1f))
InputBlock("Dialog3", inputBoxModifier)
}
}
#Composable
fun InputBlock(dialogText: String, inputBlockModifier: Modifier) {
Column(modifier = inputBlockModifier) {
Text(
dialogText,
fontSize = 30.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
var text by remember { mutableStateOf("") }
TextField(
value = text,
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center),
onValueChange = { text = it },
label = { Text("Label") }
)
}
}
The final code allows us to scroll down the view:
Important Note For APIs 30-
For APIs lower then 30 you need to modificate the AndroidManifest.xml file
In <activity you need to add android:windowSoftInputMode="adjustResize" in order to make it work. It do not resize your components but it is obligatory to make this approach work
Manifest should look like this:
<activity
android:name=".MainActivity"
android:windowSoftInputMode="adjustResize"
Feel free to give me any tips how can I improve my question. AFAIK this problem is as old as android and I wanted to create a quick tutorial how to manage that. Happy coding!
Here's my solution, using the experimental features in Compose 1.2.0
In build.gradle (:project)
...
ext {
compose_version = '1.2.0-beta03'
}
...
In build.gradle (:app)
...
dependencies {
implementation 'androidx.core:core-ktx:1.8.0'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation "androidx.compose.foundation:foundation-layout:$compose_version"
...
}
In AndroidManifest.xml
<activity
...
android:windowSoftInputMode="adjustResize" >
In AuthScreen.kt
#OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
#Composable
fun AuthScreen(
val focusManager = LocalFocusManager.current
val coroutineScope = rememberCoroutineScope()
// Setup the handles to items to scroll to.
val bringIntoViewRequesters = mutableListOf(remember { BringIntoViewRequester() })
repeat(6) {
bringIntoViewRequesters += remember { BringIntoViewRequester() }
}
val buttonViewRequester = remember { BringIntoViewRequester() }
fun requestBringIntoView(focusState: FocusState, viewItem: Int) {
if (focusState.isFocused) {
coroutineScope.launch {
delay(200) // needed to allow keyboard to come up first.
if (viewItem >= 2) { // force to scroll to button for lower fields
buttonViewRequester.bringIntoView()
} else {
bringIntoViewRequesters[viewItem].bringIntoView()
}
}
}
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top,
modifier = Modifier
.fillMaxSize()
.statusBarsPadding()
.navigationBarsPadding()
.imePadding()
.padding(10.dp)
.verticalScroll(rememberScrollState())
) {
repeat(6) { viewItem ->
Row(
modifier = Modifier
.bringIntoViewRequester(bringIntoViewRequesters[viewItem]),
) {
TextField(
value = "",
onValueChange = {},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(
onNext = { focusManager.moveFocus(FocusDirection.Down) }),
modifier = Modifier
.onFocusEvent { focusState ->
requestBringIntoView(focusState, viewItem)
},
)
}
}
Button(
onClick = {},
modifier = Modifier
.bringIntoViewRequester(buttonViewRequester)
) {
Text(text = "I'm Visible")
}
}
}
Try to google into such keywords: Modifier.statusBarsPadding(), systemBarsPadding(), navigationBarsPadding().
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
makeStatusBarTransparent()
//WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
Box(
Modifier
.background(Color.Blue)
.fillMaxSize()
.padding(top = 10.dp, bottom = 10.dp)
.statusBarsPadding() //systemBarsPadding
) {
//Box(Modifier.background(Color.Green).navigationBarsPadding()) {
Greeting("TopStart", Alignment.TopStart)
Greeting("BottomStart", Alignment.BottomStart)
Greeting("TopEnd", Alignment.TopEnd)
Greeting("BottomEnd", Alignment.BottomEnd)
//}
}
}
/* setContent {
MyComposeApp1Theme {
// A surface container using the 'background' color from the theme
Surface(modifier = Modifier.fillMaxSize(), color = Color.Red) {
Box(Modifier
.fillMaxSize()
.padding(top = 34.dp)
) {
Greeting("Android")
}
}
}
}*/
}
}
#Composable
fun Greeting(name: String, contentAlignment: Alignment) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = contentAlignment
) {
Text(
text = "Hello $name!",
Modifier
.background(color = Color.Cyan)
)
}
}
#Preview(showBackground = true)
#Composable
fun DefaultPreview() {
MyComposeApp1Theme {
Greeting("Android", Alignment.TopStart)
}
}
#Suppress("DEPRECATION")
fun Activity.makeStatusBarTransparent() {
window.apply {
clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
statusBarColor = android.graphics.Color.GREEN//android.graphics.Color.TRANSPARENT
}
}
val Int.dp
get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
toFloat(),
Resources.getSystem().displayMetrics
)