primitives/box.go
Functions
func AccessibilityActions
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
}
AccessibilityActions returns nil. Containers have no actions.