Text is a central piece of any UI, and Jetpack Compose makes it easier to display or write text. Compose leverages composition of its building blocks, meaning you don’t need to overwrite properties and methods or extend big classes to have a specific composable design and logic working the way you want.
As its base, Compose provides a BasicText
and BasicTextField
which are the
barebones to display text and handle user input. At a higher level, Compose
provides Text
and TextField
, which are composables following Material Design
guidelines. It’s recommended to use them as they have the right look and feel
for users on Android, and includes other options to simplify their customization
without having to write a lot of code.
Displaying text
The most basic way to display text is to use the Text
composable with a
String
as an argument:
@Composable
fun SimpleText() {
Text("Hello World")
}
Display text from resource
We recommend you use string resources instead of hardcoding Text
values, as
you can share the same strings with your Android Views as well as preparing your
app for internationalization:
@Composable
fun StringResourceText() {
Text(stringResource(R.string.hello_world))
}
Styling text
The Text
composable has multiple optional parameters to style its content.
Below, we’ve listed parameters that cover most common use cases with text. To
see all the parameters of Text
, we recommend you to look at the Compose Text
source
code.
Whenever you set one of these parameters, you’re applying the style to the whole text value. If you need to apply multiple styles within the same line or paragraphs, have a look at the section on multiple inline styles.
Changing the text color
@Composable
fun BlueText() {
Text("Hello World", color = Color.Blue)
}
Changing the text size
@Composable
fun BigText() {
Text("Hello World", fontSize = 30.sp)
}
Making the text italic
@Composable
fun ItalicText() {
Text("Hello World", fontStyle = FontStyle.Italic)
}
Making the text bold
@Composable
fun BoldText() {
Text("Hello World", fontWeight = FontWeight.Bold)
}
Text alignments
The textAlign
parameter allows to set the alignment of the text within a
Text
composable surface area.
By default, Text
will select the natural text alignment depending on its
content value:
- Left edge of the
Text
container for left-to-right alphabets such as Latin, Cyrillic, or Hangul - Right edge of the
Text
container for right-to-left alphabets such as Arabic or Hebrew
@Preview(showBackground = true)
@Composable
fun CenterText() {
Text("Hello World", textAlign = TextAlign.Center,
modifier = Modifier.width(150.dp))
}
If you want to set manually the text alignment of a Text
composable, prefer
using TextAlign.Start
and TextAlign.End
instead of TextAlign.Left
and
TextAlign.Right
respectively, as they resolve to the right edge of the Text
composable depending on the preferred language text orientation. For example,
TextAlign.End
aligns to the right side for French text and to the left side
for Arabic text, but TextAlign.Right
aligns to the right side no matter what
alphabet is used.
Working with fonts
Text
has a fontFamily
parameter to allow setting the font used in the
composable. By default, serif, sans-serif, monospace and cursive font families
are included:
@Composable
fun DifferentFonts() {
Column {
Text("Hello World", fontFamily = FontFamily.Serif)
Text("Hello World", fontFamily = FontFamily.SansSerif)
}
}
You can use the fontFamily
attribute to work with custom fonts and typefaces
defined in res/fonts
folder:
This example shows how you would define a fontFamily
based on those font
files:
val firaSansFamily = FontFamily(
Font(R.font.firasans_light, FontWeight.Light),
Font(R.font.firasans_regular, FontWeight.Normal),
Font(R.font.firasans_italic, FontWeight.Normal, FontStyle.Italic),
Font(R.font.firasans_medium, FontWeight.Medium),
Font(R.font.firasans_bold, FontWeight.Bold)
)
Finally, you can pass this fontFamily
to your Text
composable. Because a
fontFamily
can include different weights, you can set manually fontWeight
to
select the right weight for your text:
Column {
Text(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Light)
Text(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Normal)
Text(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Normal,
fontStyle = FontStyle.Italic)
Text(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Medium)
Text(..., fontFamily = firaSansFamily, fontWeight = FontWeight.Bold)
}
To learn how to set the typography in your entire app, look at the themes documentation.
Multiple styles in a text
To set different styles within the same Text
composable, you have to use an
AnnotatedString
, a string that can be annotated with styles of arbitrary
annotations.
AnnotatedString
is a data class containing:
- A
Text
value - A
List
ofSpanStyleRange
, equivalent to inline styling with position range within the text value - A
List
ofParagraphStyleRange
, specifying text alignment, text direction, line height, and text indent styling
TextStyle
is for use in the Text
composable , whereas SpanStyle
and
ParagraphStyle
is for use in AnnotatedString
.
The difference between SpanStyle
and ParagraphStyle
is that ParagraphStyle
can be applied to a whole paragraph, while SpanStyle
can be applied at the
character level. Once a portion of the text is marked with a ParagraphStyle
,
that portion is separated from the remaining as if it had line feeds at the
beginning and end.
AnnotatedString
has a type-safe
builder
to make it easier to create one:
@Composable
fun MultipleStylesInText() {
Text(buildAnnotatedString {
withStyle(style = SpanStyle(color = Color.Blue)) {
append("H")
}
append("ello ")
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold, color = Color.Red)) {
append("W")
}
append("orld")
})
}
We can set paragraph styles in the same way:
@Composable
fun ParagraphStyle() {
Text(buildAnnotatedString {
withStyle(style = ParagraphStyle(lineHeight = 30.sp)) {
withStyle(style = SpanStyle(color = Color.Blue)) {
append("Hello\n")
}
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold,
color = Color.Red)) {
append("World\n")
}
append("Compose")
}
})
}
Maximum number of lines
To limit the number of visible lines in a Text
composable, set the maxLines
parameter:
@Composable
fun LongText() {
Text("hello ".repeat(50), maxLines = 2)
}
Text overflow
When limiting a long text, you may want to indicate a text overflow, which is
only shown if the displayed text is truncated. To do so, set the textOverflow
parameter:
@Composable
fun OverflowedText() {
Text("Hello Compose ".repeat(50), maxLines = 2, overflow = TextOverflow.Ellipsis)
}
Theming
To use the app theme for text styling, see the themes documentation.
User interactions
Jetpack Compose enables fine-grained interactivity in Text
. Text selection is
now more flexible and can be done across composable layouts. User interactions
in text are different from other composable layouts, as you can’t add a modifier
to a portion of a Text
composable. This section highlights the different APIs
to enable user interactions.
Selecting text
By default, composables aren’t selectable, which means by default users can't
select and copy text from your app. To enable text selection, you need to wrap
your text elements with a SelectionContainer
composable:
@Composable
fun SelectableText() {
SelectionContainer {
Text("This text is selectable")
}
}
You may want to disable selection on specific parts of a selectable area. To do
so, you need to wrap the unselectable part with a DisableSelection
composable:
@Composable
fun PartiallySelectableText() {
SelectionContainer {
Column {
Text("This text is selectable")
Text("This one too")
Text("This one as well")
DisableSelection {
Text("But not this one")
Text("Neither this one")
}
Text("But again, you can select this one")
Text("And this one too")
}
}
}
Getting the position of a click on text
To listen for clicks on Text
, you can add the clickable
modifier. However,
if you’re looking to get the position of a click within a Text
composable,
in the case where you have different actions based on different parts of the
text, you need to use a ClickableText
instead:
@Composable
fun SimpleClickableText() {
ClickableText(
text = AnnotatedString("Click Me"),
onClick = { offset ->
Log.d("ClickableText", "$offset -th character is clicked.")
}
)
}
Click with annotation
When a user clicks on a Text
composable, you may want to attach extra
information to a part of the Text
value, like a URL attached to a specific
word to be opened in a browser for exemple. To do so, you need to attach an
annotation, which takes a tag (String
), an item (String
), and a text range
as parameters. From an AnnotatedString
, these annotations can be filtered with
their tags or text ranges. Here’s an example:
@Composable
fun AnnotatedClickableText() {
val annotatedText = buildAnnotatedString {
append("Click ")
// We attach this *URL* annotation to the following content
// until `pop()` is called
pushStringAnnotation(tag = "URL",
annotation = "https://developer.android.com")
withStyle(style = SpanStyle(color = Color.Blue,
fontWeight = FontWeight.Bold)) {
append("here")
}
pop()
}
ClickableText(
text = annotatedText,
onClick = { offset ->
// We check if there is an *URL* annotation attached to the text
// at the clicked position
annotatedText.getStringAnnotations(tag = "URL", start = offset,
end = offset)
.firstOrNull()?.let { annotation ->
// If yes, we log its value
Log.d("Clicked URL", annotation.item)
}
}
)
}
Entering and modifying text
TextField
allows users to enter and modify text. There are two levels of
TextField
implementations:
TextField
is the Material Design implementation. We recommend you choose this implementation as it follows material design guidelines:BasicTextField
enables users to edit text via hardware or software keyboard, but provides no decorations like hint or placeholder.
@Composable
fun SimpleFilledTextFieldSample() {
var text by remember { mutableStateOf("Hello") }
TextField(
value = text,
onValueChange = { text = it },
label = { Text("Label") }
)
}
@Composable
fun SimpleOutlinedTextFieldSample() {
var text by remember { mutableStateOf("") }
OutlinedTextField(
value = text,
onValueChange = { text = it },
label = { Text("Label") }
)
}
Styling TextField
TextField
and BasicTextField
share a lot of common parameters to customize
them. The complete list for TextField
is available in the TextField
source
code.
This is a non-exhaustive list of some of the useful parameters:
singleLine
maxLines
textStyle
@Composable
fun StyledTextField() {
var value by remember { mutableStateOf("Hello\nWorld\nInvisible") }
TextField(
value = value,
onValueChange = { value = it },
label = { Text("Enter text") },
maxLines = 2,
textStyle = TextStyle(color = Color.Blue, fontWeight = FontWeight.Bold),
modifier = Modifier.padding(20.dp)
)
}
We recommend TextField
over BasicTextField
when your design calls for a
Material TextField or OutlineTextField. However, BasicTextField
should be used
when building designs that don't need the decorations from the Material spec.
Keyboard options
TextField
lets you set keyboard configurations options, such as the keyboard
layout, or enable the autocorrect if it’s supported by the keyboard. Some
options may not be guaranteed if the software keyboard doesn't comply with the
options provided here. Here is the list of the supported keyboard
options:
capitalization
autoCorrect
keyboardType
imeAction
Formatting
TextField
allows you to set a visual formatting to the input value, like
replacing characters with *
for passwords, or inserting hyphens every 4 digits
for a credit card number:
@Composable
fun PasswordTextField() {
var password by rememberSaveable { mutableStateOf("") }
TextField(
value = password,
onValueChange = { password = it },
label = { Text("Enter password") },
visualTransformation = PasswordVisualTransformation(),
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password)
)
}
More examples are available in the VisualTransformSamples source code.