Constructing pleasant UIs with Compose

0
2
Constructing pleasant UIs with Compose



Constructing pleasant UIs with Compose

Posted by Rebecca Franks – Developer Relations Engineer

Androidify is a brand new pattern app we constructed utilizing the most recent finest practices for cellular apps. Beforehand, we lined all of the completely different options of the app, from Gemini integration and CameraX performance to adaptive layouts. On this publish, we dive into the Jetpack Compose utilization all through the app, constructing upon our base information of Compose so as to add pleasant and expressive touches alongside the best way!

Materials 3 Expressive

Materials 3 Expressive is an enlargement of the Materials 3 design system. It’s a set of latest options, up to date parts, and design techniques for creating emotionally impactful UX.

It’s been launched as a part of the alpha model of the Materials 3 artifact (androidx.compose.material3:material3:1.4.0-alpha10) and comprises a variety of latest parts you should utilize inside your apps to construct extra customized and pleasant experiences. Study extra about Materials 3 Expressive’s part and theme updates for extra partaking and user-friendly merchandise.

Material Expressive Component updates

Materials Expressive Element updates

Along with the brand new part updates, Materials 3 Expressive introduces a brand new movement physics system that is encompassed within the Materials theme.

In Androidify, we’ve utilized Materials 3 Expressive in a couple of alternative ways throughout the app. For instance, we’ve explicitly opted-in to the brand new MaterialExpressiveTheme and chosen MotionScheme.expressive() (that is the default when utilizing expressive) so as to add a little bit of playfulness to the app:

@Composable
enjoyable AndroidifyTheme(
   content material: @Composable () -> Unit,
) {
   val colorScheme = LightColorScheme


   MaterialExpressiveTheme(
       colorScheme = colorScheme,
       typography = Typography,
       shapes = shapes,
       motionScheme = MotionScheme.expressive(),
       content material = {
           SharedTransitionLayout {
               CompositionLocalProvider(LocalSharedTransitionScope gives this) {
                   content material()
               }
           }
       },
   )
}

A number of the new componentry is used all through the app, together with the HorizontalFloatingToolbar for the Immediate sort choice:

moving example of expressive button shapes in slow motion

The app additionally makes use of MaterialShapes in varied places, that are a preset record of shapes that enable for simple morphing between one another. For instance, try the lovable cookie form for the digital camera seize button:

Material Expressive Component updates

Digicam button with a MaterialShapes.Cookie9Sided form

Animations

Wherever potential, the app leverages the Materials 3 Expressive MotionScheme to acquire a themed movement token, making a constant movement feeling all through the app. For instance, the dimensions animation on the digital camera button press is powered by defaultSpatialSpec(), a specification used for animations that transfer one thing throughout a display screen (comparable to x,y or rotation, scale animations):

val interactionSource = keep in mind { MutableInteractionSource() }
val animationSpec = MaterialTheme.motionScheme.defaultSpatialSpec()
Spacer(
   modifier
       .indication(interactionSource, ScaleIndicationNodeFactory(animationSpec))
       .clip(MaterialShapes.Cookie9Sided.toShape())
       .measurement(measurement)
       .drawWithCache {
           //.. and so on
       },
)

Camera button scale interaction

Digicam button scale interplay

Shared ingredient animations

The app makes use of shared ingredient transitions between completely different display screen states. Final yr, we showcased how one can create shared parts in Jetpack Compose, and we’ve prolonged this within the Androidify pattern to create a enjoyable instance. It combines the brand new Materials 3 Expressive MaterialShapes, and performs a transition with a morphing form animation:

moving example of expressive button shapes in slow motion

To do that, we created a customized Modifier that takes within the goal and resting shapes for the sharedBounds transition:

@Composable
enjoyable Modifier.sharedBoundsRevealWithShapeMorph(
   sharedContentState: 
SharedTransitionScope.SharedContentState,
   sharedTransitionScope: SharedTransitionScope = 
LocalSharedTransitionScope.present,
   animatedVisibilityScope: AnimatedVisibilityScope = 
LocalNavAnimatedContentScope.present,
   boundsTransform: BoundsTransform = 
MaterialTheme.motionScheme.sharedElementTransitionSpec,
   resizeMode: SharedTransitionScope.ResizeMode = 
SharedTransitionScope.ResizeMode.RemeasureToBounds,
   restingShape: RoundedPolygon = RoundedPolygon.rectangle().normalized(),
   targetShape: RoundedPolygon = RoundedPolygon.circle().normalized(),
)

Then, we apply a customized OverlayClip to supply the morphing form, by tying into the AnimatedVisibilityScope supplied by the LocalNavAnimatedContentScope:

val animatedProgress =
   animatedVisibilityScope.transition.animateFloat(targetValueByState = targetValueByState)


val morph = keep in mind {
   Morph(restingShape, targetShape)
}
val morphClip = MorphOverlayClip(morph, { animatedProgress.worth })


return this@sharedBoundsRevealWithShapeMorph
   .sharedBounds(
       sharedContentState = sharedContentState,
       animatedVisibilityScope = animatedVisibilityScope,
       boundsTransform = boundsTransform,
       resizeMode = resizeMode,
       clipInOverlayDuringTransition = morphClip,
       renderInOverlayDuringTransition = renderInOverlayDuringTransition,
   )

View the full code snippet for this Modifer on GitHub.

Autosize textual content

With the most recent launch of Jetpack Compose 1.8, we added the flexibility to create textual content composables that routinely alter the font measurement to suit the container’s accessible measurement with the brand new autoSize parameter:

BasicText(textual content,
type = MaterialTheme.typography.titleLarge,
autoSize = TextAutoSize.StepBased(maxFontSize = 220.sp),
)

That is used entrance and heart for the “Customise your personal Android Bot” textual content:

Text reads Customize your own Android Bot with an inline moving image

“Customise your personal Android Bot” textual content with inline GIF

This textual content composable is attention-grabbing as a result of it wanted to have the enjoyable dancing Android bot in the course of the textual content. To do that, we use InlineContent, which permits us to append a composable in the course of the textual content composable itself:

@Composable
personal enjoyable DancingBotHeadlineText(modifier: Modifier = Modifier) {
   Field(modifier = modifier) {
       val animatedBot = "animatedBot"
       val textual content = buildAnnotatedString {
           append(stringResource(R.string.customise))
           // Connect "animatedBot" annotation on the placeholder
           appendInlineContent(animatedBot)
           append(stringResource(R.string.android_bot))
       }
       var placeHolderSize by keep in mind {
           mutableStateOf(220.sp)
       }
       val inlineContent = mapOf(
           Pair(
               animatedBot,
               InlineTextContent(
                   Placeholder(
                       width = placeHolderSize,
                       top = placeHolderSize,
                       placeholderVerticalAlign = PlaceholderVerticalAlign.TextCenter,
                   ),
               ) {
                   DancingBot(
                       modifier = Modifier
                           .padding(prime = 32.dp)
                           .fillMaxSize(),
                   )
               },
           ),
       )
       BasicText(
           textual content,
           modifier = Modifier
               .align(Alignment.Middle)
               .padding(backside = 64.dp, begin = 16.dp, finish = 16.dp),
           type = MaterialTheme.typography.titleLarge,
           autoSize = TextAutoSize.StepBased(maxFontSize = 220.sp),
           maxLines = 6,
           onTextLayout = { consequence ->
               placeHolderSize = consequence.layoutInput.type.fontSize * 3.5f
           },
           inlineContent = inlineContent,
       )
   }
}

Composable visibility with onLayoutRectChanged

With Compose 1.8, a brand new modifier, Modifier.onLayoutRectChanged, was added. This modifier is a extra performant model of onGloballyPositioned, and contains options comparable to debouncing and throttling to make it performant inside lazy layouts.

In Androidify, we’ve used this modifier for the colour splash animation. It determines the place the place the transition ought to begin from, as we connect it to the “Let’s Go” button:

var buttonBounds by keep in mind {
   mutableStateOf(null)
}
var showColorSplash by keep in mind {
   mutableStateOf(false)
}
Field(modifier = Modifier.fillMaxSize()) {
   PrimaryButton(
       buttonText = "Let's Go",
       modifier = Modifier
           .align(Alignment.BottomCenter)
           .onLayoutRectChanged(
               callback = { bounds ->
                   buttonBounds = bounds
               },
           ),
       onClick = {
           showColorSplash = true
       },
   )
}

We use these bounds as a sign of the place to begin the colour splash animation from.

moving image of a blue color splash transition between Androidify demo screens

Study extra pleasant particulars

From enjoyable marquee animations on the outcomes display screen, to animated gradient buttons for the AI-powered actions, to the trail drawing animation for the loading display screen, this app has many pleasant touches so that you can expertise and be taught from.

animated marquee example

animated gradient button for AI powered actions example

animated loading screen example

Try the total codebase at github.com/android/androidify and be taught extra in regards to the newest in Compose from utilizing Materials 3 Expressive, the brand new modifiers, auto-sizing textual content and naturally a few pleasant interactions!

Discover this announcement and all Google I/O 2025 updates on io.google beginning Might 22.

LEAVE A REPLY

Please enter your comment!
Please enter your name here