Responsive layout
PAM Native has a real responsive layout system. Use Grid when placement
depends on columns and breakpoints, Row or Column for one-dimensional flex
layout, and FlatList for large recycled collections.
Twelve-column grid
Section titled “Twelve-column grid”Grid defaults to 12 columns and accepts any PAM element as a child. Every
item can define span, offset, order, and breakpoint variants.
<Grid columns="12" gutterX="16" gutterY="16"> <Column span="12" spanSm="6" spanMd="4"> <Image :source="$photo->url" aspectRatio="1" /> <Text>{{ $photo->title }}</Text> <Button @press="openPhoto">Open</Button> </Column></Grid>Rows size to their tallest child. The grid recalculates for orientation, split screen, and foldable posture, so a cell must not assume the initial screen width.
Two images per row
Section titled “Two images per row”Do not alternate items into two manually maintained arrays. A six-column span expresses two cells in every twelve-column row:
<Grid gutterX="12" gutterY="12"> <Column p-for="$photo in $photos" :key="$photo->id" span="6"> <Image :source="$photo->url" aspectRatio="1" resizeMode="cover" /> </Column></Grid>The cell is rich content, not a TextView placeholder. Images, buttons,
application components, and nested layout are supported.
Responsive placement
Section titled “Responsive placement”<Grid gutterX="16" gutterY="20"> <Column span="12" spanSm="6" spanMd="4" spanLg="3" offsetMd="0" order="2" orderMd="1" > <ProductCard :product="$product" /> </Column></Grid>Use offsets for deliberate empty columns, not for ordinary card padding. Use
gutterX and gutterY for spacing between cells; use padding inside a cell for
its content.
Flex layout
Section titled “Flex layout”Use Row and Column when only one axis determines placement:
<Row class="items-center gap-3"> <Image :source="$avatar" width="48" height="48" /> <Column class="flex-1 gap-1"> <Text>{{ $name }}</Text> <Text>{{ $subtitle }}</Text> </Column> <Button @press="follow">Follow</Button></Row>Give growing text content flex-1 so trailing actions retain their measured
width. Avoid fixed screen-width calculations.
Repeat an integer
Section titled “Repeat an integer”p-for accepts a positive integer directly:
<Column p-for="$item in $count" :key="$item"> <Text>Repeated item {{ $item }}</Text></Column>$item is 1, 2, …, $count. Zero or a negative integer produces no
children. Arrays and Traversable values continue to work normally.
Recycled rich grids
Section titled “Recycled rich grids”Use VirtualGrid for hundreds or thousands of rich items. Android materializes
only the visible and prefetched cell trees through RecyclerView:
<VirtualGrid columns="2" rowHeight="224" prefetch="6"> <Pressable p-for="$photo in $photos" :key="$photo->id" @press="openPhoto" > <Column class="p-2 gap-2"> <Image :source="$photo->url" aspectRatio="1" /> <Text>{{ $photo->title }}</Text> </Column> </Pressable></VirtualGrid>Choose stable keys, keep cell layout deterministic, and avoid nesting a large vertical list inside another vertical scroll container.
FlatList numColumns="2" remains the specialized fast path for scalar string
rows. It deliberately does not run a PHP render callback while scrolling.
Custom chrome and safe-area metrics
Section titled “Custom chrome and safe-area metrics”SafeAreaView is the default for ordinary screen content. When a custom tab
bar, player, or floating control must calculate its own geometry, read the
platform insets through DeviceInfo:
use Pam\Native\System\DeviceInfo;
DeviceInfo::get(function (DeviceInfo $device): void { $this->bottomSpacing = max($device->safeAreaBottom + 12.0, 20.0);});safeAreaTop, safeAreaRight, safeAreaBottom, and safeAreaLeft are
reported in logical points on Android and iOS. PAM reads the active window on
the native UI thread, including system bars and display cutouts, so application
code does not need model-specific status-bar or navigation-bar guesses.
Keep a sensible initial value in component state because the native result is
asynchronous. Let the custom chrome own the corresponding edge instead of also
applying that edge through an ancestor SafeAreaView; otherwise the inset
would be counted twice.
Layout checklist
Section titled “Layout checklist”- Wrap edge-to-edge screens in
SafeAreaView. - Use
DeviceInfosafe-area metrics when custom chrome owns an inset edge. - Use grid gutters or flex gaps instead of per-item negative margins.
- Give images an intrinsic size or
aspectRatio. - Allow text to wrap unless truncation is intentional.
- Keep touch targets at least 44×44 points.
- Test the smallest supported width, font scaling, landscape, and keyboard-open state.