Phase 2 · Building Interactive UIsModule 16~35 min read

Adaptive Layouts & Accessibility

Build for changing window space, large text and assistive technology with responsive layouts and meaningful semantics.

Adaptive Layouts & Accessibility: learning goals

Build a planner that stays usable when its window shrinks, text grows, or a student uses a screen reader. Layout decisions should respond to the space available to the app and preserve the same user task.

Project setup and scope

Use the Compose Foundation and Material 3 dependencies from the existing project. This example uses local BoxWithConstraints for a small component; application-level navigation and pane decisions should use Material 3 Adaptive window information. Check the adaptive library's current API: currentWindowAdaptiveInfoV2 replaces the older function in newer releases.

Build the mental model

A tablet can have a narrow split-screen window, and a phone in landscape can have very little vertical room. A permanent isTablet flag cannot describe either situation. Begin with a compact single-column layout, then add a two-pane arrangement only when both content and available space justify it.

Adaptive UI changes arrangement while retaining identity and user intent. Resizing should not silently clear a selected session or create a second independent copy of form state. Hoist state above the layout branches and pass the same values and callbacks to each arrangement.

Accessibility includes meaning and interaction as well as appearance. Material controls already expose roles and state; custom drawings and interactions need equivalent semantics. Use text labels for actions, meaningful descriptions for informative images, null descriptions for decorative images, and sufficient touch space. Test with TalkBack rather than assuming a visible label is announced correctly.

Text uses sp so user font settings can affect it. Fixed-height containers can clip enlarged labels. Allow wrapping, avoid unnecessary hard height constraints, and test the longest meaningful translations as well as your shortest sample text. Contrast and selected states should be understandable without relying on color alone.

Choose the right approach

NeedApproach
Local card widthBoxWithConstraints can adapt a reusable component to its parent's actual constraints.
Whole app panes/navigationUse window size classes/adaptive scaffolds; include height and fold posture where relevant.
Standard button or switchPrefer Material controls and their existing semantics/touch behavior.
Custom chartExpose a concise text summary and progress semantics; color alone cannot convey meaning.

Predict and trace the behavior

Android concept labInteractive walkthrough

Adaptive Layouts & Accessibility

Predict what happens next. Follow the diagram, then explain the change in your own words.

Representative concept map

Constraints down · sizes up

layout
Parentwidth: 120…280 dp
Available bounds
Child content200 × 56 dpA size within the constraints
↓ constraints↑ measured size→ placement

Illustrative dp values; real constraints are measured in pixels.

A child chooses a legal size, then the parent places it. Measurement and placement are separate responsibilities.

Explore this general model independently. The lesson sequence below follows its own example.

Constraints: A parent sends minimum and maximum width and height constraints to its child.

Lesson sequence1 / 4
Step 1 of 4

Read available space

The containing layout provides current constraints. Device category does not determine the app window.

Up nextChoose arrangement
A teaching simulation; diagrams illustrate the concept. Run the Kotlin examples in Android Studio.

Worked Kotlin example

AdaptivePlanner.kt
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp

@Composable
fun AdaptivePlanner(
    summary: @Composable () -> Unit,
    sessions: @Composable () -> Unit,
    modifier: Modifier = Modifier,
) {
    BoxWithConstraints(modifier.fillMaxWidth()) {
        // Component policy, not a universal device breakpoint.
        if (maxWidth >= 720.dp) {
            Row(horizontalArrangement = Arrangement.spacedBy(24.dp)) {
                Column(Modifier.weight(1f)) { summary() }
                Column(Modifier.weight(2f)) { sessions() }
            }
        } else {
            Column(verticalArrangement = Arrangement.spacedBy(16.dp)) {
                summary()
                sessions()
            }
        }
    }
}
// Example content: AdaptivePlanner(summary = { Text("2 sessions") },
//     sessions = { Text("Compose state — planned") })
Focused teaching example. Keep your generated package/theme and complete the explicitly labeled integration points.
  1. The two content slots are stateless. Put session data and form inputs in a caller above AdaptivePlanner.
  2. 720 dp is a deliberate component policy chosen for this example's content, not a replacement definition of Android window size classes. Test near both sides of it.
  3. A lazy list placed in a scrolling parent still needs valid height constraints. In a full-screen planner, give the list a bounded area and apply Scaffold insets at the appropriate boundary.

Student practice and expected behavior

Adapt the Phase 2 planner to two arrangements. Add a labeled completion control and a text summary of completion. Keep the selected filter identical while resizing.

  • At compact width the form and sessions are stacked; at a useful wider width they occupy separate panes.
  • At 200% font scale, controls remain reachable and important labels are readable without clipping.
  • TalkBack announces each action and current completion state once, in a logical order.
  • Resizing and rotating preserve explicitly saved small inputs; list durability remains the responsibility of the later data layer.

Write down the input, expected outcome and observed outcome for each check. Include a screenshot, a short recording or a nonpersonal diagnostic trace appropriate to the behavior you are proving.

Debugging clinic

  • Calling both screen width and local component width 'the available width' can hide the source of a bad breakpoint. Inspect the actual parent constraints.
  • Adding contentDescription to every Text can create duplicate announcements; standard Text already has text semantics.
  • A visually small icon can be used inside a sufficiently large IconButton. Shrinking the whole interactive control makes it hard to activate.
  • Do not fix clipping by disabling font scaling or preventing resizing. Repair layout constraints and reachability.

Instructor prompts

  1. Give one team a compact window and another a large-font layout; have them complete the same task and compare failures.
  2. Ask students to point to the single owner of a filter that survives an arrangement change.
  3. Turn off the display or look away during a TalkBack demonstration. Ask which labels convey an action rather than an icon's shape.

Teach-back checkpoint

Ask a student to describe one incorrect approach, reproduce the resulting behavior, and explain the repair using ownership, lifetime, constraints or trust boundaries from this lesson.

Quick check and references

Quick check

1. What should select the app's high-level layout?

2. Which is the best first choice for a normal completion toggle?

Continue with the phase capstone after demonstrating the practice checks. Use the official documentation to verify APIs against your project's selected dependency versions.