Layouts

Composition patterns for declarative and lower-level APIs

ImmyGo provides layout at two levels:

  1. Declarative ui packageVStack, HStack, Centered, Spacer, Flex, Scroll (no Gio imports)
  2. Lower-level layout package — Avalonia-inspired panels with layout.Context

Declarative Layouts (ui package)

VStack — Vertical Layout

Lays out children top to bottom with configurable spacing.

ui.VStack(
    ui.Text("Title").Title(),
    ui.Text("Subtitle"),
    ui.Button("OK").OnClick(save),
).Spacing(12)
Method Description
VStack(children ...View) Create with children
.Spacing(dp) Gap between children (default 8)
.Center() Center children on cross axis
.End() Align children to end of cross axis
.Padding(dp) Add padding (returns *Styled)

HStack — Horizontal Layout

Lays out children left to right.

ui.HStack(
    ui.Text("Name:"),
    ui.Spacer(),
    ui.Input().Placeholder("Enter name"),
).Spacing(8)

Same API as VStack — just flows horizontally.

Spacer — Flexible Space

Expands to fill available room. Use between items to push them apart:

ui.HStack(
    ui.Text("Left"),
    ui.Spacer(),      // pushes Right to the far end
    ui.Text("Right"),
)

Fixed spacer for explicit gaps:

ui.FixedSpacer(16, 0)   // 16dp horizontal
ui.FixedSpacer(0, 20)   // 20dp vertical

Flex — Weighted Proportional Sizing

Give children weighted shares of remaining space:

ui.HStack(
    ui.Flex(2, sidebar),   // gets 2/3 of space
    ui.Flex(1, content),   // gets 1/3 of space
)

Flex children can be mixed with regular children:

ui.HStack(
    ui.Icon(ui.IconMenu),           // fixed size (rigid)
    ui.Flex(1, ui.Text("Title")),   // fills remaining
    ui.Button("Save"),              // fixed size (rigid)
)

Centered — Center in Available Space

Centers a view both horizontally and vertically:

ui.Centered(
    ui.Card(
        ui.Text("Centered card"),
    ),
)

Expanded — Fill Available Space

Makes a child fill all available space:

ui.Expanded(ui.Text("I fill everything"))

Scroll — Scrollable Container

Wraps content in a scrollable area:

// Vertical scroll (most common)
ui.Scroll(
    ui.VStack(
        ui.Text("Line 1"),
        ui.Text("Line 2"),
        // ... many items
    ).Spacing(8),
)

// Horizontal scroll
ui.ScrollH(content)

// Efficient scrollable list (only renders visible items)
ui.ScrollList(item1, item2, item3, item4).Spacing(4)

Divider — Horizontal Separator

ui.VStack(
    ui.Text("Section 1"),
    ui.Divider(),
    ui.Text("Section 2"),
)

ZStack — Overlapping Layers with Alignment

ZStack overlay layout

Overlays multiple children with configurable alignment:

ui.ZStack().
    Child(ui.ZCenter, backgroundImage).
    Child(ui.ZTopRight, closeButton).
    Child(ui.ZBottomCenter, caption)

Alignment constants: ZCenter, ZTopLeft, ZTopCenter, ZTopRight, ZCenterLeft, ZCenterRight, ZBottomLeft, ZBottomCenter, ZBottomRight.

Grid — Row/Column Layout

Declarative wrapper for the lower-level GridPanel:

ui.Grid(ui.GridStar(1), ui.GridStar(2), ui.GridFixed(100)).
    Rows(ui.GridStar(1), ui.GridAuto()).
    Cell(0, 0, ui.Text("Row 0, Col 0")).
    Cell(0, 1, ui.Text("Row 0, Col 1")).
    Cell(1, 0, ui.Text("Row 1, Col 0")).
    SpanCell(1, 1, 1, 2, ui.Text("Spans 2 columns")).
    Spacing(8)

Sizing modes:

  • GridAuto() — fit content
  • GridStar(weight) — proportional share of remaining space
  • GridFixed(dp) — fixed size in dp

AspectRatio — Constrained Proportions

ui.AspectRatio(16.0/9.0, ui.Image(img))   // 16:9
ui.AspectRatio(1.0, ui.Card(content))       // Square

Responsive — Breakpoint-Based Layout

Switch layouts based on available width:

ui.Responsive(
    ui.At(0, mobileLayout),      // 0dp+ (fallback)
    ui.At(600, tabletLayout),    // 600dp+
    ui.At(1024, desktopLayout),  // 1024dp+
)

Visible — Hidden but Space-Preserving

Unlike If() which removes the view entirely, Visible() keeps the space:

ui.Visible(isShown, ui.Text("I take up space even when hidden"))

Modifier Chaining on Layouts

All layouts support modifier chaining via the bridge pattern:

ui.VStack(children...).
    Spacing(12).
    Padding(16).                      // returns *Styled
    Background(ui.RGB(240,240,240)).
    Rounded(8).
    Border(1, ui.RGB(200,200,200)).
    Cursor(ui.CursorPointer)          // NEW: cursor style

Conditional Rendering

If / IfElse

ui.If(loggedIn, ui.Text("Welcome back!"))

ui.IfElse(loading,
    ui.Text("Loading..."),
    ui.Text("Content loaded"),
)

Switch

ui.Switch(currentTab.Get(),
    controlsPage(),   // index 0
    formsPage(),      // index 1
    aboutPage(),      // index 2
)

ForEach

ui.ForEach(items, func(i int, item Item) ui.View {
    return ui.Text(fmt.Sprintf("%d. %s", i+1, item.Name))
})

// With spacing
ui.ForEachSpaced(items, 8, func(i int, item Item) ui.View {
    return ui.Card(ui.Text(item.Name))
})

Group

Compose multiple views at the same origin (like a simple ZStack without alignment):

ui.Group(background, foreground, overlay)

Empty

Zero-size invisible placeholder:

ui.If(showError, ui.Text("Error!"))  // returns Empty() when false

Declarative Composition Patterns

App Shell

ui.VStack(
    ui.AppBar("My App"),
    ui.Divider(),
    ui.TabBar("Home", "Settings").OnSelect(fn),
    ui.Divider(),
    ui.Scroll(pageContent()),
).Spacing(0)

Two-Column Layout

ui.HStack(
    ui.Flex(1, ui.SideNav(items...).OnSelect(fn)),
    ui.Flex(3, ui.Scroll(mainContent())),
).Spacing(0)

Form Layout

ui.Card(
    ui.VStack(
        ui.Text("Sign Up").Title(),
        ui.Divider(),
        ui.Text("Name").Small(),
        nameInput,
        ui.Text("Email").Small(),
        emailInput,
        ui.Divider(),
        ui.HStack(
            ui.Spacer(),
            ui.Button("Cancel").Outline(),
            ui.Button("Submit").OnClick(submit),
        ).Spacing(8),
    ).Spacing(10),
).Elevation(2).CornerRadius(12)

Escape Hatch to Raw Gio

When you need fine-grained control for a single component:

ui.VStack(
    ui.Text("Declarative above"),
    ui.ViewFunc(func(gtx layout.Context, th *theme.Theme) layout.Dimensions {
        // Full Gio access: layout.Flex, op.Offset, clip, paint
        return layout.Flex{}.Layout(gtx,
            layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
                return myCustomWidget.Layout(gtx, th)
            }),
        )
    }),
    ui.Text("Declarative below"),
)

Lower-Level Layouts (layout package)

For users who prefer direct Gio access or need features not yet in the declarative API.

All layout panels are in github.com/amken3d/immygo/layout. Import as:

import immylayout "github.com/amken3d/immygo/layout"

VStack / HStack

immylayout.NewVStack().WithSpacing(16).
    Child(func(gtx layout.Context) layout.Dimensions {
        return widget.H2("Title").Layout(gtx, th)
    }).
    Child(func(gtx layout.Context) layout.Dimensions {
        return myButton.Layout(gtx, th)
    }).
    Layout(gtx)

DockPanel

Pin children to edges of available space:

immylayout.NewDockPanel().
    Child(immylayout.DockTop, func(gtx layout.Context) layout.Dimensions {
        return appBar.Layout(gtx, th)
    }).
    Child(immylayout.DockLeft, func(gtx layout.Context) layout.Dimensions {
        return sideNav.Layout(gtx, th)
    }).
    Child(immylayout.DockFill, func(gtx layout.Context) layout.Dimensions {
        return mainContent(gtx, th)
    }).
    Layout(gtx)
Position Description
DockTop Pinned to top edge
DockBottom Pinned to bottom edge
DockLeft Pinned to left edge
DockRight Pinned to right edge
DockFill Fills remaining space (use last)

WrapPanel

Lays out children horizontally, wrapping to the next row:

immylayout.NewWrapPanel().Children(
    func(gtx layout.Context) layout.Dimensions { return tag1.Layout(gtx, th) },
    func(gtx layout.Context) layout.Dimensions { return tag2.Layout(gtx, th) },
    func(gtx layout.Context) layout.Dimensions { return tag3.Layout(gtx, th) },
).Layout(gtx)

Center / Padding / Expanded

// Center
immylayout.Center{}.Layout(gtx, child)

// Padding
immylayout.Uniform(16).Layout(gtx, child)
immylayout.Symmetric(24, 12).Layout(gtx, child)

// Expanded
immylayout.NewExpanded(child).Layout(gtx)

Using Raw Gio

You can always use Gio’s native layout.Flex, layout.Stack, layout.Inset, and layout.List directly:

layout.Flex{Axis: layout.Vertical}.Layout(gtx,
    layout.Rigid(func(gtx layout.Context) layout.Dimensions {
        return appBar.Layout(gtx, th)
    }),
    layout.Flexed(1, func(gtx layout.Context) layout.Dimensions {
        return immylayout.NewVStack().WithSpacing(16).Children(...).Layout(gtx)
    }),
)