Skip to content

Widget Sizing & Previews (Android)

Grid Cells vs Density-Independent Pixels (dp)

Section titled “Grid Cells vs Density-Independent Pixels (dp)”

Android widgets are sized two different ways depending on the OS version. Android 12+ places widgets using targetCellWidth/targetCellHeight, in grid cells. Android 11 and older don’t understand those attributes at all and place widgets using minWidth/minHeight, in dp, so Voltra always emits both.

When minWidth/minHeight aren’t set explicitly, they’re derived from the widget’s cell size using Google’s published figures for a 5x4 handset grid:

  • minWidth (dp) = (cellCount × 73) - 16
  • minHeight (dp) = (cellCount × 66) - 15

For example, 2 cells comes out to 130 dp wide and 117 dp tall, and 4 cells to 276 dp wide and 249 dp tall.

Cell size varies by device, launcher and orientation, so this conversion is only an approximation, and it only matters on Android 11 and older — Android 12+ sizes the widget from targetCellWidth/ targetCellHeight directly. If you need precise placement on Android 11 and older, set minWidth and minHeight explicitly in dp instead of relying on the conversion.

Family Cells Default DP Typical Use
Small 2×1 130 × 51 Quick glance info
Medium 2×2 130 × 117 Main widget size
Large 4×2 276 × 117 Rich content
Extra Large 4×4 276 × 249 Complex layouts

Voltra can also emit minResizeWidth, minResizeHeight, maxResizeWidth, and maxResizeHeight in the generated appwidget-provider XML, all optional and all in dp:

  • minResizeWidth/minResizeHeight set the smallest size the user can resize the widget to, and are supported on all Android versions.
  • maxResizeWidth/maxResizeHeight set the largest size the user can resize the widget to. They were introduced in Android 12 and are ignored on older versions.

These bounds only take effect on axes the widget can resize along, per resizeMode — a maxResizeHeight on a widget with resizeMode: "horizontal" has no effect, for instance. Set only the ones you need; Voltra emits exactly what you provide and nothing otherwise, so existing widgets are unaffected.

Android also ignores a bound that contradicts the widget’s minimum size: minResizeWidth has no effect if it’s greater than minWidth, and maxResizeWidth has no effect if it’s smaller than minWidth — likewise for the height pair against minHeight. Since minWidth/minHeight are often derived from targetCellWidth/targetCellHeight rather than written explicitly (see above), Voltra warns at build time when it detects one of these contradictions, naming the attribute, rather than failing the build.

For example:

{
"widgets": [
{
"id": "weather",
"displayName": "Weather Widget",
"targetCellWidth": 2,
"targetCellHeight": 2,
"resizeMode": "horizontal|vertical",
"minResizeWidth": 110,
"minResizeHeight": 100,
"maxResizeWidth": 276,
"maxResizeHeight": 249
}
]
}

This lets the widget resize down to 110×100 dp, and up to 276×249 dp on Android 12+.

When users add a widget to their home screen, Android displays a preview in the widget picker. Voltra supports three preview methods, with automatic fallback:

  1. previewLayout (Android 12+) - Custom XML layout for scalable preview
  2. previewImage (All versions) - Static image or auto-generated layout
  3. Default - System placeholder layout

Static preview image for all Android versions:

{
"widgets": [
{
"id": "weather",
"displayName": "Weather Widget",
"targetCellWidth": 2,
"targetCellHeight": 2,
"previewImage": "./assets/widgets/weather-preview.png"
}
]
}

When only previewImage is specified, Voltra automatically generates a layout that displays the image with proper scaling.

Custom XML layout for scalable previews (Android 12+):

{
"widgets": [
{
"id": "todos",
"displayName": "Todo Widget",
"targetCellWidth": 2,
"targetCellHeight": 2,
"previewLayout": "./assets/widgets/todos-preview.xml"
}
]
}

Example todos-preview.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
android:background="#FFFFFF">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Todo List"
android:textSize="18sp"
android:textStyle="bold" />
<!-- Add more layout elements here -->
</LinearLayout>

The preview layout is rendered at the widget’s target size and displayed in the widget picker.

For best results across Android versions, provide both:

{
"widgets": [
{
"id": "weather",
"displayName": "Weather Widget",
"targetCellWidth": 2,
"targetCellHeight": 2,
"previewImage": "./assets/widgets/weather-preview.png",
"previewLayout": "./assets/widgets/weather-preview.xml",
"initialStatePath": "./widgets/weather-initial.tsx"
}
]
}

This uses previewLayout on Android 12+, falls back to previewImage on Android 11 and earlier, and shows actual widget content on the home screen via initialStatePath once available.