primitives/box.go

Functions Structs

Functions

func AccessibilityActions

AccessibilityActions returns nil. Containers have no actions.

func (b *BoxWidget) AccessibilityActions() []a11y.Action {
	return nil
}

func AccessibilityHint

AccessibilityHint returns an empty string. Containers typically have no hint.

func (b *BoxWidget) AccessibilityHint() string {
	return ""
}

func AccessibilityLabel

AccessibilityLabel returns the custom label, or empty string if none set.

func (b *BoxWidget) AccessibilityLabel() string {
	return b.accessibilityLabel
}

func AccessibilityRole

AccessibilityRole returns [a11y.RoleGenericContainer].

func (b *BoxWidget) AccessibilityRole() a11y.Role {
	return a11y.RoleGenericContainer
}

func AccessibilityState

AccessibilityState returns the default state.

func (b *BoxWidget) AccessibilityState() a11y.State {
	return a11y.State{
		Disabled:	!b.IsEnabled(),
		Hidden:		!b.IsVisible(),
	}
}

func AccessibilityValue

AccessibilityValue returns an empty string. Containers have no value.

func (b *BoxWidget) AccessibilityValue() string {
	return ""
}

func Background

Background sets the background color.

func (b *BoxWidget) Background(c widget.Color) *BoxWidget {
	b.style.Background = c
	return b
}

func BorderStyle

BorderStyle sets the border width and color.

func (b *BoxWidget) BorderStyle(width float32, color widget.Color) *BoxWidget {
	b.style.Border = Border{Width: width, Color: color}
	return b
}

func Box

Box creates a new container widget with the given children.

 

Children are laid out vertically from top to bottom by default. Use

[BoxWidget.SetDirection] to switch to horizontal layout, or use the

[HBox] and [VBox] convenience constructors.

 

card := primitives.Box(

primitives.Text("Title").Bold(),

primitives.Text("Body"),

).Padding(16).Background(widget.Hex(0xFFFFFF))

func Box(children ...widget.Widget) *BoxWidget {
	b := &BoxWidget{
		children: children,
	}
	b.SetVisible(true)
	b.SetEnabled(true)
	// Establish parent chain for upward dirty propagation (ADR-028).
	// Flutter: RenderObject.adoptChild sets parent on each child.
	for _, child := range children {
		type parentSetter interface{ SetParent(widget.Widget) }
		if ps, ok := child.(parentSetter); ok {
			ps.SetParent(b)
		}
	}
	return b
}

func Children

Children returns the box's child widgets.

func (b *BoxWidget) Children() []widget.Widget {
	if len(b.children) == 0 {
		return nil
	}
	result := make([]widget.Widget, len(b.children))
	copy(result, b.children)
	return result
}

func CrossAlign

CrossAlign sets the cross-axis alignment for child widgets.

For VBox: controls horizontal alignment (center, start, end, stretch).

For HBox: controls vertical alignment.

Default is CrossAxisStart (left-aligned for VBox).

 

Flutter equivalent: CrossAxisAlignment on Column/Row.

 

primitives.VBox(spinner).CrossAlign(primitives.CrossAxisCenter)

func (b *BoxWidget) CrossAlign(a CrossAxisAlignment) *BoxWidget {
	b.crossAlign = a
	return b
}

func DirectionSignal

DirectionSignal binds the layout direction to a read-only reactive signal.

When set, the signal value takes precedence over the static direction set

via [BoxWidget.SetDirection].

 

Because BoxWidget is a container (not user-editable), only

[state.ReadonlySignal] is accepted (no write-back capability is needed).

 

dir := state.NewSignal(primitives.DirectionHorizontal)

box := primitives.Box(a, b).DirectionSignal(dir)

func (b *BoxWidget) DirectionSignal(sig state.ReadonlySignal[Direction]) *BoxWidget {
	b.directionSignal = sig
	return b
}

func Draw

Draw renders the box background, border, shadow, and then draws all children.

func (b *BoxWidget) Draw(ctx widget.Context, canvas widget.Canvas) {
	if !b.IsVisible() {
		return
	}

	bounds := b.Bounds()

	// Draw shadow layers (outermost first, innermost last).
	if !b.style.Shadow.IsZero() {
		b.drawShadow(canvas, bounds)
	}

	// Draw background
	if !b.style.Background.IsTransparent() {
		if b.style.Radius > 0 {
			canvas.DrawRoundRect(bounds, b.style.Background, b.style.Radius)
		} else {
			canvas.DrawRect(bounds, b.style.Background)
		}
	}

	// Clip children when the box has a border or rounded corners,
	// so content doesn't overflow the visual boundary.
	// Uses PushClipRoundRect for rounded corners (GPU SDF clip),
	// falls back to rectangular PushClip otherwise.
	clipChildren := !b.style.Border.IsZero() || b.style.Radius > 0
	if clipChildren {
		if b.style.Radius > 0 {
			canvas.PushClipRoundRect(bounds, b.style.Radius)
		} else {
			canvas.PushClip(bounds)
		}
	}

	// Draw children with transform offset for this box's position.
	// No viewport culling here — DrawChild skip pattern makes offscreen
	// boundary children cheap (O(1) stamp + skip). Compositor-level culling
	// via CompositorClip handles GPU texture rendering and compositing.
	// Flutter: PaintingContext.paintChild always calls paint on all children;
	// visibility culling is in the compositor (addRetained vs addToScene).
	canvas.PushTransform(bounds.Min)
	for _, child := range b.children {
		widget.StampScreenOrigin(child, canvas)
		widget.DrawChild(child, ctx, canvas)
	}
	canvas.PopTransform()

	if clipChildren {
		canvas.PopClip()
	}

	// Draw border AFTER children so it renders on top of content.
	if !b.style.Border.IsZero() {
		if b.style.Radius > 0 {
			canvas.StrokeRoundRect(bounds, b.style.Border.Color, b.style.Radius, b.style.Border.Width)
		} else {
			canvas.StrokeRect(bounds, b.style.Border.Color, b.style.Border.Width)
		}
	}
}

func Event

Event dispatches the event to children. Returns true if any child consumed it.

func (b *BoxWidget) Event(ctx widget.Context, e event.Event) bool {
	if !b.IsVisible() || !b.IsEnabled() {
		return false
	}

	// For mouse events, translate position to local coordinates and
	// dispatch only to children whose bounds contain the position.
	if me, ok := e.(*event.MouseEvent); ok {
		return b.dispatchMouseEvent(ctx, me)
	}

	// For wheel events, translate position to local coordinates
	// so hit-testing in children works correctly.
	if we, ok := e.(*event.WheelEvent); ok {
		return b.dispatchWheelEvent(ctx, we)
	}

	// Non-mouse events (keyboard, focus) broadcast to all children.
	for i := len(b.children) - 1; i >= 0; i-- {
		if b.children[i].Event(ctx, e) {
			return true
		}
	}
	return false
}

func Gap

Gap sets the spacing between children. The gap is applied in the layout

direction: vertically for [DirectionVertical], horizontally for [DirectionHorizontal].

func (b *BoxWidget) Gap(v float32) *BoxWidget {
	b.style.Gap = v
	return b
}

func HBox

HBox creates a new container that lays out children horizontally (left to right).

 

This is a convenience constructor equivalent to Box(children...).SetDirection(DirectionHorizontal).

 

row := primitives.HBox(icon, label, spacer).Gap(8)

func HBox(children ...widget.Widget) *BoxWidget {
	b := Box(children...)
	b.direction = DirectionHorizontal
	return b
}

func Height

Height sets an explicit height.

func (b *BoxWidget) Height(v float32) *BoxWidget {
	b.style.ExplicitHeight = v
	return b
}

func Label

Label sets a custom accessibility label for this container.

func (b *BoxWidget) Label(label string) *BoxWidget {
	b.accessibilityLabel = label
	return b
}

func Layout

Layout calculates the box size by laying out children in the resolved

direction (vertical or horizontal) with padding and gap, then constraining

the result.

func (b *BoxWidget) Layout(ctx widget.Context, constraints geometry.Constraints) geometry.Size {
	if b.ResolvedDirection() == DirectionHorizontal {
		return b.layoutHorizontal(ctx, constraints)
	}
	return b.layoutVertical(ctx, constraints)
}

func MaxHeightValue

MaxHeightValue sets the maximum height.

func (b *BoxWidget) MaxHeightValue(v float32) *BoxWidget {
	b.style.MaxHeight = v
	return b
}

func MaxWidthValue

MaxWidthValue sets the maximum width.

func (b *BoxWidget) MaxWidthValue(v float32) *BoxWidget {
	b.style.MaxWidth = v
	return b
}

func MinHeightValue

MinHeightValue sets the minimum height.

func (b *BoxWidget) MinHeightValue(v float32) *BoxWidget {
	b.style.MinHeight = v
	return b
}

func MinWidthValue

MinWidthValue sets the minimum width.

func (b *BoxWidget) MinWidthValue(v float32) *BoxWidget {
	b.style.MinWidth = v
	return b
}

func Mount

Mount creates signal bindings for push-based invalidation.

Implements [widget.Lifecycle].

func (b *BoxWidget) Mount(ctx widget.Context) {
	sched := ctx.Scheduler()
	if sched == nil {
		return
	}
	if b.directionSignal != nil {
		binding := state.BindToScheduler(b.directionSignal, b, sched)
		b.AddBinding(binding)
	}
}

func Padding

Padding sets uniform padding on all edges.

func (b *BoxWidget) Padding(v float32) *BoxWidget {
	b.style.Padding = geometry.UniformInsets(v)
	return b
}

func PaddingBottom

PaddingBottom sets the bottom padding.

func (b *BoxWidget) PaddingBottom(v float32) *BoxWidget {
	b.style.Padding.Bottom = v
	return b
}

func PaddingLeft

PaddingLeft sets the left padding.

func (b *BoxWidget) PaddingLeft(v float32) *BoxWidget {
	b.style.Padding.Left = v
	return b
}

func PaddingRight

PaddingRight sets the right padding.

func (b *BoxWidget) PaddingRight(v float32) *BoxWidget {
	b.style.Padding.Right = v
	return b
}

func PaddingTop

PaddingTop sets the top padding.

func (b *BoxWidget) PaddingTop(v float32) *BoxWidget {
	b.style.Padding.Top = v
	return b
}

func PaddingXY

PaddingXY sets separate horizontal and vertical padding.

func (b *BoxWidget) PaddingXY(x, y float32) *BoxWidget {
	b.style.Padding = geometry.SymmetricInsets(x, y)
	return b
}

func ResolvedDirection

ResolvedDirection returns the effective layout direction.

Priority: ReadonlySignal > Static.

func (b *BoxWidget) ResolvedDirection() Direction {
	if b.directionSignal != nil {
		return b.directionSignal.Get()
	}
	return b.direction
}

func Rounded

Rounded sets a uniform border radius.

func (b *BoxWidget) Rounded(r float32) *BoxWidget {
	b.style.Radius = r
	return b
}

func SetDirection

SetDirection sets the layout direction for child widgets.

 

primitives.Box(a, b, c).SetDirection(primitives.DirectionHorizontal)

func (b *BoxWidget) SetDirection(d Direction) *BoxWidget {
	if b.direction != d {
		b.direction = d
		b.SetNeedsRedraw(true)
	}
	return b
}

func ShadowLevel

ShadowLevel sets the elevation shadow level (0-5).

func (b *BoxWidget) ShadowLevel(level int) *BoxWidget {
	if level < 0 {
		level = 0
	}
	if level > maxShadowLevel {
		level = maxShadowLevel
	}
	b.style.Shadow = Shadow{Level: level}
	return b
}

func Style

Style returns the current box style (read-only snapshot).

func (b *BoxWidget) Style() BoxStyle {
	return b.style
}

func Unmount

Unmount is called when the box widget is removed from the widget tree.

Implements [widget.Lifecycle].

func (b *BoxWidget) Unmount() {
	// Bindings are cleaned up automatically by WidgetBase.CleanupBindings().
}

func VBox

VBox creates a new container that lays out children vertically (top to bottom).

 

This is a convenience constructor equivalent to Box(children...).SetDirection(DirectionVertical).

Since vertical is the default direction, VBox is primarily useful for readability

when paired with [HBox] in the same codebase.

 

column := primitives.VBox(title, subtitle, body).Gap(4)

func VBox(children ...widget.Widget) *BoxWidget {
	return Box(children...)
}

func Width

Width sets an explicit width.

func (b *BoxWidget) Width(v float32) *BoxWidget {
	b.style.ExplicitWidth = v
	return b
}

Structs

type BoxStyle struct

BoxStyle holds all visual styling for a [BoxWidget].

type BoxStyle struct {
	Padding		geometry.Insets
	Background	widget.Color
	Radius		float32
	Border		Border
	Shadow		Shadow
	Gap		float32

	// Explicit dimensions. Zero means unconstrained in that dimension.
	ExplicitWidth	float32
	ExplicitHeight	float32
	MinWidth	float32
	MinHeight	float32
	MaxWidth	float32
	MaxHeight	float32
}

type BoxWidget struct

BoxWidget is a container that lays out children vertically or horizontally

with optional padding, background, border, rounded corners, shadow, and gap.

 

BoxWidget implements [widget.Widget], [a11y.Accessible], and [widget.Lifecycle].

 

Create a BoxWidget with the [Box] constructor. Use [HBox] or [VBox] for

convenience constructors with a pre-set direction.

type BoxWidget struct {
	widget.WidgetBase

	style			BoxStyle
	direction		Direction
	directionSignal		state.ReadonlySignal[Direction]
	crossAlign		CrossAxisAlignment
	children		[]widget.Widget
	accessibilityLabel	string
}