Skip to content

Latest commit

 

History

History
210 lines (175 loc) · 6.17 KB

compose-layout.md

File metadata and controls

210 lines (175 loc) · 6.17 KB

Compose Layout library

Important

All the examples on this page make use of imports from Horologist's compose-layout and compose-material. Add these dependencies and imports to ensure you are using the Horologist version of components with the same name in Wear Compose, for example Chip.

ScalingLazyColumn Responsive layout.

The rememberResponsiveColumnState() method will ensure that your ScalingLazyColumn is positioned correctly on all screen sizes.

Pass in a padding configuration based on your item types.

The overloaded ScalingLazyColumn composable with ScalingLazyColumnState param, when combined with rememberResponsiveColumnState() will handle all the following:

  • Position the first item near the top on all screen sizes.
  • Ensure the last item can be scrolled into view.
  • Handle RSB/Bezel scrolling with Fling.
  • Size side margins based on a percentage, adapting to different screen sizes.
val columnState = rememberResponsiveColumnState(
    contentPadding = ScalingLazyColumnDefaults.padding(
        first = ItemType.Text,
        last = ItemType.Chip
    )
)

ScalingLazyColumn(
    modifier = Modifier.fillMaxSize(),
    columnState = columnState,
) {
    item {
        ResponsiveListHeader(contentPadding = firstItemPadding()) {
            Text(text = "Main")
        }
    }
    items(10) {
        Chip("Item $it", onClick = {})
    }
}

App/Screen Scaffold.

Syncs the TimeText, PositionIndicator and Scaffold to the current navigation destination state. The TimeText will scroll out of the way of content automatically.

AppScaffold {
    SwipeDismissableNavHost(
        startDestination = "home",
        navController = navController
    ) {
        composable(
            "home",
        ) {
            val columnState = rememberResponsiveColumnState()
            ScreenScaffold(scrollState = columnState) {
                ScalingLazyColumn(
                    modifier = Modifier
                        .fillMaxSize(),
                    columnState = columnState
                ) {
                    items(100) {
                        Text("i = $it")
                    }
                }
            }
        }

        composable(
            "settings"
        ) {
            val scrollState = rememberScrollState()
            ScreenScaffold(scrollState = scrollState) {
                Column(
                    modifier = Modifier
                        .fillMaxSize()
                        .rotaryWithScroll(scrollState, rememberActiveFocusRequester())
                        .verticalScroll(scrollState),
                    horizontalAlignment = Alignment.CenterHorizontally
                ) {
                    (1..100).forEach {
                        Text("i = $it")
                    }
                }
            }
        }
    }
}

Typesafe Navigation.

This provides an implementation of typesafe navigation for Wear Compose. See https://developer.android.com/guide/navigation/design/type-safety for more information.

import com.google.android.horologist.compose.nav.SwipeDismissableNavHost
import com.google.android.horologist.compose.nav.composable

@kotlinx.serialization.Serializable
object Prompt

@kotlinx.serialization.Serializable
object Settings

@Composable
fun WearApp(
    modifier: Modifier = Modifier,
    navController: NavHostController = rememberSwipeDismissableNavController(),
) {
    AppScaffold(modifier = modifier) {
        SwipeDismissableNavHost(
            startDestination = Prompt,
            navController = navController,
        ) {
            composable<Prompt> {
                SamplePromptScreen(
                    onSettingsClick = { navController.navigate(Settings) },
                )
            }
            composable<Settings> {
                SettingsScreen()
            }
        }
    }
}

Box Inset Layout.

Use as a break glass for simple layout to fit within a safe square.

Box(
    modifier = Modifier
        .fillMaxRectangle()
) {
    // App Content here        
}

{: loading=lazy width=70% align=center }

AmbientAware composable

AmbientAware allows your UI to react to ambient mode changes. For more information on how Ambient mode and Always-on work on Wear OS, see the developer guidance.

You should place this composable high up in your design, as it alters the behavior of the Activity.

@Composable
fun WearApp() {
    AmbientAware { ambientStateUpdate ->
        when (val state = ambientStateUpdate.ambientState) {
            is AmbientState.Ambient -> {
                val ambientDetails = state.ambientDetails
                val burnInProtectionRequired = ambientDetails?.burnInProtectionRequired
                val deviceHasLowBitAmbient = ambientDetails?.deviceHasLowBitAmbient
                // Device is in ambient (low power) mode
            }
            is AmbientState.Interactive -> {
                // Device is in interactive (high power) mode
            }
        }
    }
}

If you need some screens to use always-on, and others not to, then you can use the additional argument supplied to AmbientAware to indicate whether a recomposition should be triggered when the system goes into ambient mode (i.e. whether the composable wants to handle ambient mode or not).

For example, in a workout app, it is desirable that the main workout screen uses always-on, but the workout summary at the end does not. See the ExerciseClient guide and samples for more information on building a workout app.

@Composable
fun WearApp() {
    // Hoist state here for your current screen logic
    
    AmbientAware(isAlwaysOnScreen = currentScreen.useAlwaysOn) { ambientStateUpdate ->
        // App Content here
    }
}

Download

repositories {
    mavenCentral()
}

dependencies {
    implementation "com.google.android.horologist:horologist-compose-layout:<version>"
}