Skip to main content

widgets

Package widgets provides UI components for building widget trees.

This package contains the concrete widget implementations that developers use to build user interfaces, including layout widgets (Row, Column, Stack), display widgets (Text, Icon, Image), input widgets (Button, TextField, Checkbox), and container widgets (Container, Padding, SizedBox).

Widget Construction

Drift widgets use a two-tier construction pattern:

Tier 1: Struct Literal \(canonical, full control\)

btn := Button{
Label: "Submit",
OnTap: handleSubmit,
Color: colors.Primary,
Disabled: !isValid,
Haptic: true,
}

This is the PRIMARY way to create widgets. All fields are accessible. For themed styling, use theme.XxxOf constructors from pkg/theme instead.

Tier 2: Layout Helpers \(ergonomics for layout widgets\)

Convenience helpers for common layout patterns:

StackOf, VSpace, HSpace, Centered.

WithX Chaining \(for themed widgets\)

WithX methods on widgets allow overriding themed defaults:

btn := theme.ButtonOf(ctx, "Submit", handleSubmit).
WithBorderRadius(0). // Sharp corners
WithPadding(layout.EdgeInsetsAll(20))

WithX methods return COPIES; they never mutate the receiver.

API Rules

  • Canonical = struct literal. Always works, always documented.
  • Layout helpers (StackOf, VSpace, HSpace, Centered) exist for ergonomics.
  • For themed widgets, use theme.XxxOf constructors from pkg/theme.
  • WithX returns copies. Doc comment must state "returns a copy".

Layout Widgets

Use Row and Column struct literals for horizontal and vertical layouts:

widgets.Row{Children: []core.Widget{...}}
widgets.Column{Children: []core.Widget{...}}

Input Widgets

Button, TextField, Checkbox, Radio, and Switch handle user input. Use struct literals for explicit control or theme.XxxOf for themed widgets:

// Struct literal (explicit)
widgets.Button{Label: "Submit", OnTap: onTap, Color: colors.Primary, Haptic: true}

// Themed constructor (from pkg/theme)
theme.ButtonOf(ctx, "Submit", onTap)

Scrolling

ScrollView provides scrollable content with customizable physics:

widgets.ScrollView{Child: content, Physics: widgets.BouncingScrollPhysics{}}

Style Guide for Widget Authors

When adding WithX methods:

  • Use value receiver, not pointer
  • Return the modified copy, never mutate
  • Doc comment: "returns a copy of X with..."

Constants

const (
// ActivityIndicatorSizeMedium is a medium spinner.
ActivityIndicatorSizeMedium = platform.ActivityIndicatorSizeMedium
// ActivityIndicatorSizeSmall is a small spinner.
ActivityIndicatorSizeSmall = platform.ActivityIndicatorSizeSmall
// ActivityIndicatorSizeLarge is a large spinner.
ActivityIndicatorSizeLarge = platform.ActivityIndicatorSizeLarge
)

Variables

Common snap point presets.

var (
SnapThird = SnapPoint{FractionalHeight: 0.33, Name: "third"}
SnapHalf = SnapPoint{FractionalHeight: 0.5, Name: "half"}
SnapFull = SnapPoint{FractionalHeight: 1.0, Name: "full"}
)

DefaultSnapPoints is used when no snap points are provided.

var DefaultSnapPoints = []SnapPoint{SnapFull}

func Clamp

func Clamp(value, min, max float64) float64

Clamp constrains a value between min and max bounds.

func DeviceScaleOf

func DeviceScaleOf(ctx core.BuildContext) float64

DeviceScaleOf returns the current device scale, defaulting to 1 if not found.

func HandleDropdownPointerDown

func HandleDropdownPointerDown(entries []layout.RenderObject)

HandleDropdownPointerDown dismisses open dropdowns on outside taps.

func HasActiveBallistics

func HasActiveBallistics() bool

HasActiveBallistics returns true if any scroll simulations are running.

func RegisterRestartAppFn

func RegisterRestartAppFn(fn func())

RegisterRestartAppFn registers the function to restart the app. This is called by the engine package during initialization.

func SafeAreaBottomOf

func SafeAreaBottomOf(ctx core.BuildContext) float64

SafeAreaBottomOf returns only the bottom safe area inset. Widgets calling this will only rebuild when the bottom inset changes.

func SafeAreaLeftOf

func SafeAreaLeftOf(ctx core.BuildContext) float64

SafeAreaLeftOf returns only the left safe area inset. Widgets calling this will only rebuild when the left inset changes.

func SafeAreaOf

func SafeAreaOf(ctx core.BuildContext) layout.EdgeInsets

SafeAreaOf returns the current safe area insets from context. Widgets calling this will rebuild when any inset changes.

func SafeAreaPadding

func SafeAreaPadding(ctx core.BuildContext) layout.EdgeInsets

SafeAreaPadding returns the safe area insets as EdgeInsets for use with ScrollView.Padding or other widgets. The returned EdgeInsets can be modified using chainable methods:

ScrollView{
Padding: widgets.SafeAreaPadding(ctx), // just safe area
Child: ...,
}
ScrollView{
Padding: widgets.SafeAreaPadding(ctx).Add(24), // safe area + 24px all sides
Child: ...,
}
ScrollView{
Padding: widgets.SafeAreaPadding(ctx).OnlyTop().Add(24), // only top safe area + 24px
Child: ...,
}

func SafeAreaRightOf

func SafeAreaRightOf(ctx core.BuildContext) float64

SafeAreaRightOf returns only the right safe area inset. Widgets calling this will only rebuild when the right inset changes.

func SafeAreaTopOf

func SafeAreaTopOf(ctx core.BuildContext) float64

SafeAreaTopOf returns only the top safe area inset. Widgets calling this will only rebuild when the top inset changes.

func StepBallistics

func StepBallistics()

StepBallistics advances any active scroll simulations.

func ValidateInitialSnap

func ValidateInitialSnap(index int, points []SnapPoint) int

ValidateInitialSnap ensures the initial snap index is valid.

type ActivityIndicator

ActivityIndicator displays a native platform spinner. Uses UIActivityIndicatorView on iOS and ProgressBar on Android.

type ActivityIndicator struct {
core.StatefulBase

// Animating controls whether the indicator is spinning.
// Defaults to true.
Animating bool

// Size is the indicator size (Small, Medium, Large).
// Defaults to Medium.
Size ActivityIndicatorSize

// Color is the spinner color (optional, uses system default if not set).
Color graphics.Color
}

func (ActivityIndicator) CreateState

func (a ActivityIndicator) CreateState() core.State

type ActivityIndicatorSize

ActivityIndicatorSize represents the size of the activity indicator.

type ActivityIndicatorSize = platform.ActivityIndicatorSize

type Align

Align positions its child within itself according to the given alignment.

Align expands to fill available space, then positions the child within that space according to the Alignment field. The child is given loose constraints, allowing it to size itself.

Example:

Align{
Alignment: layout.AlignmentBottomRight,
Child: Text{Content: "Bottom right"},
}

See also:

  • Center for centering (equivalent to Align with AlignmentCenter)
  • Container for combined alignment, padding, and decoration
type Align struct {
core.RenderObjectBase
Child core.Widget
Alignment layout.Alignment
}

func (Align) ChildWidget

func (a Align) ChildWidget() core.Widget

func (Align) CreateRenderObject

func (a Align) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (Align) UpdateRenderObject

func (a Align) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type AnimatedContainer

AnimatedContainer is a Container that animates changes to its properties.

When properties like Color, Width, Height, Padding, or Alignment change, the widget automatically animates from the old value to the new value over the specified Duration using the specified Curve.

Note: Gradient is not animated; changes to Gradient will apply immediately.

Example:

widgets.AnimatedContainer{
Duration: 300 * time.Millisecond,
Curve: animation.EaseInOut,
Color: s.isActive ? colors.Primary : colors.Surface,
Width: 100,
Height: 100,
Child: child,
}
type AnimatedContainer struct {
core.StatefulBase

// Duration is the length of the animation.
Duration time.Duration
// Curve transforms the animation progress. If nil, uses linear interpolation.
Curve func(float64) float64
// OnEnd is called when the animation completes.
OnEnd func()

// Container properties that will be animated when they change.
Padding layout.EdgeInsets
Width float64
Height float64
Color graphics.Color
Gradient *graphics.Gradient
Alignment layout.Alignment
Child core.Widget
}

Example:

This example shows implicit animation with AnimatedContainer.

package main

import (
"time"

"github.com/go-drift/drift/pkg/animation"
"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
var isExpanded bool

// Properties animate automatically when they change
container := widgets.AnimatedContainer{
Duration: 300 * time.Millisecond,
Curve: animation.EaseInOut,
Width: func() float64 {
if isExpanded {
return 200
} else {
return 100
}
}(),
Height: func() float64 {
if isExpanded {
return 200
} else {
return 100
}
}(),
Color: func() graphics.Color {
if isExpanded {
return graphics.RGB(100, 149, 237)
} else {
return graphics.RGB(200, 200, 200)
}
}(),
Child: widgets.Center{Child: widgets.Text{Content: "Tap to toggle"}},
}
_ = container
}

func (AnimatedContainer) CreateState

func (a AnimatedContainer) CreateState() core.State

type AnimatedOpacity

AnimatedOpacity animates changes to opacity over a duration.

When the Opacity property changes, the widget automatically animates from the old value to the new value over the specified Duration.

Example:

widgets.AnimatedOpacity{
Duration: 200 * time.Millisecond,
Curve: animation.EaseOut,
Opacity: s.isVisible ? 1.0 : 0.0,
Child: child,
}
type AnimatedOpacity struct {
core.StatefulBase

// Duration is the length of the animation.
Duration time.Duration
// Curve transforms the animation progress. If nil, uses linear interpolation.
Curve func(float64) float64
// OnEnd is called when the animation completes.
OnEnd func()

// Opacity is the target opacity (0.0 to 1.0).
Opacity float64
// Child is the widget to which opacity is applied.
Child core.Widget
}

Example:

This example shows fade animation with AnimatedOpacity.

package main

import (
"time"

"github.com/go-drift/drift/pkg/animation"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
var isVisible bool

opacity := widgets.AnimatedOpacity{
Duration: 200 * time.Millisecond,
Curve: animation.EaseOut,
Opacity: func() float64 {
if isVisible {
return 1.0
} else {
return 0.0
}
}(),
Child: widgets.Text{Content: "Fading content"},
}
_ = opacity
}

func (AnimatedOpacity) CreateState

func (a AnimatedOpacity) CreateState() core.State

type Axis

Axis represents the layout direction. AxisVertical is the zero value, making it the default for ScrollDirection fields.

type Axis int
const (
AxisVertical Axis = iota
AxisHorizontal
)

func (Axis) String

func (a Axis) String() string

String returns a human-readable representation of the axis.

type BackdropFilter

BackdropFilter applies a blur effect to content behind this widget. The blur is applied within the widget's bounds and affects any content drawn before this widget in the compositing order.

type BackdropFilter struct {
core.RenderObjectBase
Child core.Widget
SigmaX float64
SigmaY float64
}

func NewBackdropFilter

func NewBackdropFilter(sigma float64, child core.Widget) BackdropFilter

NewBackdropFilter creates a BackdropFilter with uniform blur in both directions.

func (BackdropFilter) ChildWidget

func (b BackdropFilter) ChildWidget() core.Widget

func (BackdropFilter) CreateRenderObject

func (b BackdropFilter) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (BackdropFilter) UpdateRenderObject

func (b BackdropFilter) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type BottomSheet

BottomSheet is the widget for rendering a bottom sheet with animations and gestures. Use BottomSheetController to programmatically dismiss or snap the sheet.

type BottomSheet struct {
core.StatefulBase

// Builder creates the sheet content.
Builder func(ctx core.BuildContext) core.Widget
// Controller allows programmatic control of the sheet.
Controller *BottomSheetController
// SnapPoints defines the snap positions as fractions of available height.
// When empty, the sheet sizes to content height.
SnapPoints []SnapPoint
// InitialSnap is the index into SnapPoints to open at.
InitialSnap int

// EnableDrag toggles drag gestures for the sheet.
EnableDrag bool
// DragMode defines how drag gestures interact with content.
DragMode DragMode
// ShowHandle displays the drag handle at the top of the sheet.
ShowHandle bool
// UseSafeArea applies safe area insets to the sheet layout.
UseSafeArea bool

// Theme configures the sheet's visual styling.
Theme BottomSheetTheme
// SnapBehavior customizes snapping and dismiss thresholds.
SnapBehavior SnapBehavior
// OnDismiss fires after the sheet finishes its dismiss animation.
OnDismiss func(result any)
}

func (BottomSheet) CreateState

func (b BottomSheet) CreateState() core.State

type BottomSheetController

BottomSheetController controls a bottom sheet's behavior. Create with NewBottomSheetController and pass to BottomSheet widget. Content inside the sheet can access the controller via BottomSheetScope.Of(ctx).

type BottomSheetController struct {
// contains filtered or unexported fields
}

func NewBottomSheetController

func NewBottomSheetController() *BottomSheetController

NewBottomSheetController creates a new controller for a bottom sheet.

func (*BottomSheetController) AddProgressListener

func (c *BottomSheetController) AddProgressListener(listener func(float64)) func()

AddProgressListener registers a callback for progress changes.

func (*BottomSheetController) Close

func (c *BottomSheetController) Close(result any)

Close triggers the sheet's dismiss animation with the given result. The result will be passed to OnDismiss when animation completes. Safe to call multiple times - only the first call has effect.

func (*BottomSheetController) Extent

func (c *BottomSheetController) Extent() float64

Extent returns the current sheet extent in pixels.

func (*BottomSheetController) Progress

func (c *BottomSheetController) Progress() float64

Progress returns the current open progress from 0.0 to 1.0.

func (*BottomSheetController) SnapTo

func (c *BottomSheetController) SnapTo(index int)

SnapTo animates the sheet to the snap point at the given index.

func (*BottomSheetController) SnapToFraction

func (c *BottomSheetController) SnapToFraction(fraction float64)

SnapToFraction animates the sheet to a fractional snap point. The value is clamped to [0, 1].

type BottomSheetScope

BottomSheetScope provides access to the bottom sheet controller from within sheet content. Use BottomSheetScope.Of(ctx).Close(result) to dismiss the sheet with animation.

type BottomSheetScope struct{}

func (BottomSheetScope) Of

func (BottomSheetScope) Of(ctx core.BuildContext) *BottomSheetController

Of returns the BottomSheetController for the enclosing bottom sheet. Returns nil if not inside a bottom sheet or if the sheet has no controller.

type BottomSheetScrollable

BottomSheetScrollable bridges a scroll controller to the enclosing bottom sheet so the sheet can coordinate content-aware dragging.

Example:

return widgets.BottomSheetScrollable{
Builder: func(controller *widgets.ScrollController) core.Widget {
return widgets.ListView{
Controller: controller,
Children: items,
}
},
}
type BottomSheetScrollable struct {
core.StatefulBase

Controller *ScrollController
Builder func(controller *ScrollController) core.Widget
}

func (BottomSheetScrollable) CreateState

func (b BottomSheetScrollable) CreateState() core.State

type BottomSheetTheme

BottomSheetTheme holds theming data for a bottom sheet. This is passed from the route which has access to the theme system.

type BottomSheetTheme struct {
BackgroundColor graphics.Color
HandleColor graphics.Color
BorderRadius float64
HandleWidth float64
HandleHeight float64
HandleTopPadding float64
HandleBottomPadding float64
}

func DefaultBottomSheetTheme

func DefaultBottomSheetTheme() BottomSheetTheme

DefaultBottomSheetTheme returns default theme values.

type BouncingScrollPhysics

BouncingScrollPhysics adds resistance near edges (iOS style). AllowsOverscroll returns true, enabling spring-back animation.

type BouncingScrollPhysics struct{}

func (BouncingScrollPhysics) AllowsOverscroll

func (BouncingScrollPhysics) AllowsOverscroll() bool

AllowsOverscroll returns true; bouncing physics allow scrolling past boundaries.

func (BouncingScrollPhysics) ApplyBoundaryConditions

func (BouncingScrollPhysics) ApplyBoundaryConditions(position *ScrollPosition, value float64) float64

ApplyBoundaryConditions still clamps to avoid runaway offsets.

func (BouncingScrollPhysics) ApplyPhysicsToUserOffset

func (BouncingScrollPhysics) ApplyPhysicsToUserOffset(position *ScrollPosition, offset float64) float64

ApplyPhysicsToUserOffset reduces delta when overscrolling.

type Button

Button is a tappable button widget with customizable styling and haptic feedback.

Styling Model

Button is explicit by default — all visual properties (Color, TextColor, Padding, FontSize, BorderRadius) use their struct field values directly. A zero value means zero, not "use theme default." For example:

  • Color: 0 means transparent background
  • Padding: zero EdgeInsets means no padding
  • BorderRadius: 0 means sharp corners

For theme-styled buttons, use [theme.ButtonOf] which pre-fills visual properties from the current theme's [theme.ButtonThemeData].

Creation Patterns

Struct literal (full control):

widgets.Button{
Label: "Submit",
OnTap: handleSubmit,
Color: graphics.RGB(33, 150, 243),
TextColor: graphics.ColorWhite,
Padding: layout.EdgeInsetsSymmetric(24, 14),
BorderRadius: 8,
Haptic: true,
}

Themed (reads from current theme):

theme.ButtonOf(ctx, "Submit", handleSubmit)
// Pre-filled with theme colors, padding, font size, border radius

Themed with overrides:

theme.ButtonOf(ctx, "Submit", handleSubmit).
WithBorderRadius(0). // explicit zero for sharp corners
WithPadding(layout.EdgeInsetsAll(20))

Automatic Features

The button automatically provides:

  • Visual feedback on press (opacity change)
  • Haptic feedback on tap (when Haptic is true)
  • Accessibility support (label announced by screen readers)
  • Disabled state handling (when Disabled is true)
type Button struct {
core.StatelessBase

// Label is the text displayed on the button.
Label string

// OnTap is called when the button is tapped. Ignored when Disabled is true.
OnTap func()

// Disabled prevents interaction and applies disabled styling when true.
Disabled bool

// Color is the background color. Zero means transparent.
Color graphics.Color

// Gradient is an optional background gradient that replaces Color when set.
// Ignored when Disabled is true.
Gradient *graphics.Gradient

// TextColor is the label text color. Zero means transparent (invisible text).
TextColor graphics.Color

// FontSize is the label font size in logical pixels. Zero means no text rendered.
FontSize float64

// Padding is the space between the button edge and label.
// Zero means no padding.
Padding layout.EdgeInsets

// BorderRadius is the corner radius in logical pixels.
// Zero means sharp corners.
BorderRadius float64

// Haptic enables haptic feedback on tap when true.
Haptic bool

// DisabledColor is the background color when disabled.
// If zero, falls back to 0.5 opacity on the normal Color.
DisabledColor graphics.Color

// DisabledTextColor is the text color when disabled.
// If zero, falls back to 0.5 opacity on the normal TextColor.
DisabledTextColor graphics.Color
}

Example:

This example shows how to create a basic button with a tap handler.

package main

import (
"fmt"

"github.com/go-drift/drift/pkg/widgets"
)

func main() {
button := widgets.Button{
Label: "Click Me",
OnTap: func() {
fmt.Println("Button tapped!")
},
Haptic: true,
}
_ = button
}

Example (With Styles):

This example shows how to customize a button's appearance.

package main

import (
"fmt"

"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/layout"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
button := widgets.Button{
Label: "Submit",
OnTap: func() { fmt.Println("Submitted!") },
Color: graphics.RGB(33, 150, 243),
TextColor: graphics.ColorWhite,
FontSize: 18,
Padding: layout.EdgeInsetsSymmetric(32, 16),
Haptic: true,
}
_ = button
}

func (Button) Build

func (b Button) Build(ctx core.BuildContext) core.Widget

func (Button) WithBorderRadius

func (b Button) WithBorderRadius(radius float64) Button

WithBorderRadius returns a copy of the button with the specified corner radius.

func (Button) WithColor

func (b Button) WithColor(bg, text graphics.Color) Button

WithColor returns a copy of the button with the specified background and text colors.

func (Button) WithDisabled

func (b Button) WithDisabled(disabled bool) Button

WithDisabled returns a copy of the button with the specified disabled state.

func (Button) WithFontSize

func (b Button) WithFontSize(size float64) Button

WithFontSize returns a copy of the button with the specified label font size.

func (Button) WithGradient

func (b Button) WithGradient(gradient *graphics.Gradient) Button

WithGradient returns a copy of the button with the specified background gradient.

func (Button) WithHaptic

func (b Button) WithHaptic(enabled bool) Button

WithHaptic returns a copy of the button with haptic feedback enabled or disabled.

func (Button) WithPadding

func (b Button) WithPadding(padding layout.EdgeInsets) Button

WithPadding returns a copy of the button with the specified padding.

type Center

Center positions its child at the center of the available space.

Center expands to fill available space (like Expanded), then centers the child within that space. The child is given loose constraints, allowing it to size itself.

Center is equivalent to Align{Alignment: layout.AlignmentCenter}.

Example:

Center{Child: Text{Content: "Hello, World!"}}

For more control over alignment, use Container with an Alignment field, or wrap the child in an Align widget.

type Center struct {
core.StatelessBase
Child core.Widget
}

Example:

This example shows how to center a widget.

package main

import (
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
center := widgets.Center{
Child: widgets.Text{Content: "Centered!"},
}
_ = center
}

func Centered

func Centered(child core.Widget) Center

Centered wraps a child in a Center widget.

func (Center) Build

func (c Center) Build(ctx core.BuildContext) core.Widget

type Checkbox

Checkbox displays a toggleable check control with customizable styling.

Styling Model

Checkbox is explicit by default — all visual properties use their struct field values directly. A zero value means zero, not "use theme default." For example:

  • ActiveColor: 0 means transparent fill when checked
  • Size: 0 means zero size (not rendered)
  • BorderRadius: 0 means sharp corners

For theme-styled checkboxes, use [theme.CheckboxOf] which pre-fills visual properties from the current theme's [theme.CheckboxThemeData].

Creation Patterns

Struct literal (full control):

widgets.Checkbox{
Value: isChecked,
OnChanged: func(v bool) { s.SetState(func() { s.isChecked = v }) },
ActiveColor: graphics.RGB(33, 150, 243),
CheckColor: graphics.ColorWhite,
Size: 24,
}

Themed (reads from current theme):

theme.CheckboxOf(ctx, isChecked, onChanged)
// Pre-filled with theme colors, size, border radius

Checkbox is a controlled component - it displays the Value you provide and calls OnChanged when tapped. To toggle the checkbox, update Value in your state in response to OnChanged.

For form integration with validation, wrap in a FormField:

FormField[bool]{
InitialValue: false,
Validator: func(v bool) string {
if !v { return "Must accept terms" }
return ""
},
Builder: func(state *FormFieldState[bool]) core.Widget {
return Checkbox{Value: state.Value(), OnChanged: state.DidChange}
},
}
type Checkbox struct {
core.StatelessBase

// Value indicates whether the checkbox is checked.
Value bool

// OnChanged is called when the checkbox is toggled.
OnChanged func(bool)

// Disabled disables interaction when true.
Disabled bool

// Size controls the checkbox square size. Zero means zero size (not rendered).
Size float64

// BorderRadius controls the checkbox corner radius. Zero means sharp corners.
BorderRadius float64

// ActiveColor is the fill color when checked. Zero means transparent.
ActiveColor graphics.Color

// CheckColor is the checkmark color. Zero means transparent (invisible check).
CheckColor graphics.Color

// BorderColor is the outline color. Zero means no border.
BorderColor graphics.Color

// BackgroundColor is the fill color when unchecked. Zero means transparent.
BackgroundColor graphics.Color

// DisabledActiveColor is the fill color when checked and disabled.
// If zero, falls back to 0.5 opacity on the normal colors.
DisabledActiveColor graphics.Color

// DisabledCheckColor is the checkmark color when disabled.
// If zero, falls back to 0.5 opacity on the normal colors.
DisabledCheckColor graphics.Color
}

Example:

This example shows how to create a checkbox form control.

package main

import (
"fmt"

"github.com/go-drift/drift/pkg/widgets"
)

func main() {
var isChecked bool

checkbox := widgets.Checkbox{
Value: isChecked,
OnChanged: func(value bool) {
isChecked = value
fmt.Printf("Checkbox is now: %v\n", isChecked)
},
Size: 24,
BorderRadius: 4,
}
_ = checkbox
}

func (Checkbox) Build

func (c Checkbox) Build(ctx core.BuildContext) core.Widget

func (Checkbox) WithBackgroundColor

func (c Checkbox) WithBackgroundColor(color graphics.Color) Checkbox

WithBackgroundColor returns a copy of the checkbox with the specified unchecked fill color.

func (Checkbox) WithBorderColor

func (c Checkbox) WithBorderColor(color graphics.Color) Checkbox

WithBorderColor returns a copy of the checkbox with the specified outline color.

func (Checkbox) WithBorderRadius

func (c Checkbox) WithBorderRadius(radius float64) Checkbox

WithBorderRadius returns a copy of the checkbox with the specified corner radius.

func (Checkbox) WithColors

func (c Checkbox) WithColors(activeColor, checkColor graphics.Color) Checkbox

WithColors returns a copy of the checkbox with the specified active fill and checkmark colors.

func (Checkbox) WithSize

func (c Checkbox) WithSize(size float64) Checkbox

WithSize returns a copy of the checkbox with the specified square size.

type CircularProgressIndicator

CircularProgressIndicator displays a circular progress indicator. When Value is nil, it shows an indeterminate animation. When Value is set, it shows determinate progress from 0.0 to 1.0.

Styling Model

CircularProgressIndicator is explicit by default — zero values mean zero (no color). For theme-styled indicators, use [theme.CircularProgressIndicatorOf] which pre-fills Color from Primary and TrackColor from SurfaceVariant.

Creation Patterns

Struct literal (full control):

widgets.CircularProgressIndicator{
Value: nil, // indeterminate
Color: colors.Primary,
TrackColor: colors.SurfaceVariant,
Size: 36,
StrokeWidth: 4,
}

Themed (reads from current theme):

theme.CircularProgressIndicatorOf(ctx, nil)  // indeterminate
theme.CircularProgressIndicatorOf(ctx, &progress) // determinate
type CircularProgressIndicator struct {
core.StatefulBase

// Value is the progress value (0.0 to 1.0). Nil means indeterminate.
Value *float64

// Color is the indicator color. Zero means transparent (invisible).
Color graphics.Color

// TrackColor is the background track color. Zero means no track.
TrackColor graphics.Color

// StrokeWidth is the thickness of the indicator. Zero means zero stroke (invisible).
StrokeWidth float64

// Size is the diameter of the indicator. Zero means zero size (not rendered).
Size float64
}

func (CircularProgressIndicator) CreateState

func (c CircularProgressIndicator) CreateState() core.State

type ClampingScrollPhysics

ClampingScrollPhysics clamps at edges (Android default). AllowsOverscroll returns false.

type ClampingScrollPhysics struct{}

func (ClampingScrollPhysics) AllowsOverscroll

func (ClampingScrollPhysics) AllowsOverscroll() bool

AllowsOverscroll returns false; clamping physics stop at content boundaries.

func (ClampingScrollPhysics) ApplyBoundaryConditions

func (ClampingScrollPhysics) ApplyBoundaryConditions(position *ScrollPosition, value float64) float64

ApplyBoundaryConditions clamps at the min/max extents.

func (ClampingScrollPhysics) ApplyPhysicsToUserOffset

func (ClampingScrollPhysics) ApplyPhysicsToUserOffset(_ *ScrollPosition, offset float64) float64

ApplyPhysicsToUserOffset returns the raw delta for clamping physics.

type ClipRRect

ClipRRect clips its child using rounded corners.

type ClipRRect struct {
core.RenderObjectBase
Child core.Widget
Radius float64
}

func (ClipRRect) ChildWidget

func (c ClipRRect) ChildWidget() core.Widget

func (ClipRRect) CreateRenderObject

func (c ClipRRect) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (ClipRRect) UpdateRenderObject

func (c ClipRRect) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type Column

Column lays out children vertically from top to bottom.

Column is a flex container where the main axis is vertical. Children are laid out in a single vertical run and do not wrap.

Sizing Behavior

By default, Column expands to fill available vertical space. Set MainAxisSizeMin to shrink-wrap the children instead.

Alignment

Use MainAxisAlignment to control vertical spacing (Start, End, Center, SpaceBetween, SpaceAround, SpaceEvenly). Use CrossAxisAlignment to control horizontal alignment (Start, End, Center, Stretch).

Flexible Children

Wrap children in Expanded to make them share remaining space proportionally:

Column{
Children: []core.Widget{
Text{Content: "Header"},
Expanded{Child: ListView{...}}, // Takes remaining space
Text{Content: "Footer"},
},
}

For horizontal layout, use Row.

type Column struct {
core.RenderObjectBase
Children []core.Widget
MainAxisAlignment MainAxisAlignment
CrossAxisAlignment CrossAxisAlignment
MainAxisSize MainAxisSize
}

Example:

This example shows how to create a vertical layout with Column.

package main

import (
"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
column := widgets.Column{
Children: []core.Widget{
widgets.Text{Content: "First"},
widgets.Text{Content: "Second"},
widgets.Text{Content: "Third"},
},
MainAxisAlignment: widgets.MainAxisAlignmentStart,
CrossAxisAlignment: widgets.CrossAxisAlignmentStretch,
}
_ = column
}

Example (Centered):

This example shows creating a column with struct literal.

package main

import (
"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
column := widgets.Column{
MainAxisAlignment: widgets.MainAxisAlignmentCenter,
CrossAxisAlignment: widgets.CrossAxisAlignmentCenter,
MainAxisSize: widgets.MainAxisSizeMin,
Children: []core.Widget{
widgets.Text{Content: "Hello"},
widgets.VSpace(16),
widgets.Text{Content: "World"},
},
}
_ = column
}

Example (With Expanded):

This example shows flexible children in a vertical layout.

package main

import (
"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
column := widgets.Column{
Children: []core.Widget{
// Fixed header
widgets.Container{Height: 60, Color: graphics.RGB(33, 33, 33)},
// Content takes remaining space
widgets.Expanded{
Child: widgets.ScrollView{
Child: widgets.Text{Content: "Scrollable content..."},
},
},
// Fixed footer
widgets.Container{Height: 48, Color: graphics.RGB(66, 66, 66)},
},
}
_ = column
}

func (Column) ChildrenWidgets

func (c Column) ChildrenWidgets() []core.Widget

func (Column) CreateRenderObject

func (c Column) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (Column) UpdateRenderObject

func (c Column) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type Container

Container is a convenience widget that combines common painting, positioning, and sizing operations into a single widget.

Container applies decorations in this order:

  1. Shadow (drawn behind the container, naturally overflows bounds)
  2. Background color or gradient (clipped to BorderRadius when Overflow is OverflowClip)
  3. Border stroke (if BorderWidth > 0)
  4. Child widget (clipped to bounds when Overflow is OverflowClip)

Sizing Behavior

Without explicit Width/Height, Container sizes to fit its child plus padding. With Width and/or Height set, those dimensions become the container's preferred size (subject to parent constraints) and set maximum child size on those axes. Children can be smaller than the container, and Alignment controls their placement within the available content area (after padding).

Note: Parent constraints take precedence. If a parent imposes tight constraints larger than Width/Height, the container will expand to meet those constraints.

Common Patterns

// Rounded card with padding
Container{
Color: colors.Surface,
BorderRadius: 12,
Padding: layout.EdgeInsetsAll(16),
Child: content,
}

// Bordered box
Container{
BorderColor: colors.Outline,
BorderWidth: 1,
BorderRadius: 8,
Padding: layout.EdgeInsetsAll(12),
Child: Text{Content: "Hello"},
}

// Fixed-size centered child
Container{
Width: 200,
Height: 100,
Alignment: layout.AlignmentCenter,
Child: icon,
}

Container supports all DecoratedBox features. For decoration without layout behavior (no padding, sizing, or alignment), use DecoratedBox directly.

type Container struct {
core.RenderObjectBase
Child core.Widget
Padding layout.EdgeInsets
Width float64
Height float64
Color graphics.Color
Gradient *graphics.Gradient
Alignment layout.Alignment
Shadow *graphics.BoxShadow

// Border
BorderColor graphics.Color // Border stroke color; transparent = no border
BorderWidth float64 // Border stroke width in pixels; 0 = no border
BorderRadius float64 // Corner radius for rounded rectangles; 0 = sharp corners
BorderDash *graphics.DashPattern // Dash pattern for border; nil = solid line
// BorderGradient applies a gradient to the border stroke. When set, overrides
// BorderColor. Requires BorderWidth > 0 to be visible. Works with BorderDash
// for dashed gradient borders.
BorderGradient *graphics.Gradient

// Overflow controls clipping behavior for gradients and children.
// Defaults to OverflowClip, which confines gradients and children strictly
// within bounds (clipped to rounded shape when BorderRadius > 0).
// Set to OverflowVisible for glow effects where the gradient should extend
// beyond the container; children will not be clipped.
// Shadows always overflow naturally. Solid background colors never overflow.
Overflow Overflow
}

Example:

This example shows how to create a styled container.

package main

import (
"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/layout"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
container := widgets.Container{
Padding: layout.EdgeInsetsAll(16),
Color: graphics.RGB(245, 245, 245),
Width: 200,
Height: 100,
Child: widgets.Text{
Content: "Centered content",
},
Alignment: layout.AlignmentCenter,
}
_ = container
}

Example (With Gradient):

This example shows a container with gradient and shadow.

package main

import (
"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
container := widgets.Container{
Width: 200,
Height: 100,
Gradient: graphics.NewLinearGradient(
graphics.AlignTopLeft,
graphics.AlignBottomRight,
[]graphics.GradientStop{
{Position: 0.0, Color: graphics.RGB(66, 133, 244)},
{Position: 1.0, Color: graphics.RGB(15, 157, 88)},
},
),
Shadow: &graphics.BoxShadow{
Color: graphics.RGBA(0, 0, 0, 0.25),
BlurRadius: 8,
Offset: graphics.Offset{X: 0, Y: 4},
},
Child: widgets.Center{Child: widgets.Text{Content: "Gradient Card"}},
}
_ = container
}

func (Container) ChildWidget

func (c Container) ChildWidget() core.Widget

func (Container) CreateRenderObject

func (c Container) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (Container) UpdateRenderObject

func (c Container) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

func (Container) WithAlignment

func (c Container) WithAlignment(alignment layout.Alignment) Container

WithAlignment returns a copy of the container with the specified child alignment.

func (Container) WithBorder

func (c Container) WithBorder(color graphics.Color, width float64) Container

WithBorder returns a copy of the container with the specified border color and width.

func (Container) WithBorderGradient

func (c Container) WithBorderGradient(gradient *graphics.Gradient) Container

WithBorderGradient returns a copy with the specified border gradient. The gradient overrides BorderColor when both are set.

func (Container) WithBorderRadius

func (c Container) WithBorderRadius(radius float64) Container

WithBorderRadius returns a copy of the container with the specified corner radius.

func (Container) WithColor

func (c Container) WithColor(color graphics.Color) Container

WithColor returns a copy of the container with the specified background color.

func (Container) WithGradient

func (c Container) WithGradient(gradient *graphics.Gradient) Container

WithGradient returns a copy of the container with the specified background gradient.

func (Container) WithPadding

func (c Container) WithPadding(padding layout.EdgeInsets) Container

WithPadding returns a copy of the container with the specified padding.

func (Container) WithSize

func (c Container) WithSize(width, height float64) Container

WithSize returns a copy of the container with the specified width and height.

type CrossAxisAlignment

CrossAxisAlignment controls how children are positioned along the cross axis (vertical for Row, horizontal for Column).

type CrossAxisAlignment int
const (
// CrossAxisAlignmentStart places children at the start of the cross axis.
CrossAxisAlignmentStart CrossAxisAlignment = iota
// CrossAxisAlignmentEnd places children at the end of the cross axis.
CrossAxisAlignmentEnd
// CrossAxisAlignmentCenter centers children along the cross axis.
CrossAxisAlignmentCenter
// CrossAxisAlignmentStretch stretches children to fill the cross axis.
CrossAxisAlignmentStretch
)

func (CrossAxisAlignment) String

func (a CrossAxisAlignment) String() string

String returns a human-readable representation of the cross axis alignment.

type DatePicker

DatePicker displays a date selection field that opens a native date picker modal.

type DatePicker struct {
core.StatefulBase

// Value is the current selected date (nil = no selection).
Value *time.Time

// OnChanged is called when the user selects a date.
OnChanged func(time.Time)

// Disabled disables interaction when true.
Disabled bool

// MinDate is the minimum selectable date (optional).
MinDate *time.Time

// MaxDate is the maximum selectable date (optional).
MaxDate *time.Time

// Format is the date format string (Go time format). Default: "Jan 2, 2006"
Format string

// Placeholder is shown when Value is nil.
Placeholder string

// Decoration provides styling (label, hint, border, icons, etc.).
Decoration *InputDecoration

// TextStyle for the value text.
TextStyle graphics.TextStyle

// Child overrides the default rendering for full customization.
Child core.Widget
}

func (DatePicker) CreateState

func (d DatePicker) CreateState() core.State

type DebugErrorScreen

DebugErrorScreen is a full-screen error display for development mode. It shows detailed error information including:

  • Error phase (Build, Layout, Paint, HitTest, Frame, Pointer)
  • Widget or RenderObject type that failed
  • Error message
  • Scrollable stack trace
  • Restart button to recover the app

This screen is automatically shown in debug mode (core.DebugMode = true) when an uncaught panic occurs. In production mode, panics crash the app unless caught by an ErrorBoundary.

The restart button calls [engine.RestartApp] to unmount the entire widget tree and re-mount from scratch, clearing all state.

type DebugErrorScreen struct {
core.StatelessBase

// Error is the boundary error to display. If nil, shows "Unknown error".
Error *errors.BoundaryError
}

func (DebugErrorScreen) Build

func (d DebugErrorScreen) Build(ctx core.BuildContext) core.Widget

type DecoratedBox

DecoratedBox paints a background, border, and shadow behind its child.

DecoratedBox applies decorations in this order:

  1. Shadow (drawn behind, naturally overflows bounds)
  2. Background color or gradient (overflow controlled by Overflow field)
  3. Border stroke (drawn on top of background, supports dashing)
  4. Child widget (clipped to bounds when Overflow is OverflowClip)

Use BorderRadius for rounded corners. The Overflow field controls clipping:

With OverflowClip, children are clipped to the widget bounds (rounded rectangle when BorderRadius > 0). This ensures content like images or accent bars at the edges conform to the bounds without needing a separate ClipRRect.

Note: Platform views (native text fields, etc.) are clipped to rectangular bounds only, not rounded corners. This is a platform limitation.

For combined layout and decoration (padding, sizing, alignment), use Container which composes DecoratedBox internally. Use DecoratedBox directly when you need decoration without any layout behavior.

type DecoratedBox struct {
core.RenderObjectBase
Child core.Widget // Child widget to display inside the decoration

// Background
Color graphics.Color // Background fill color
Gradient *graphics.Gradient // Background gradient; overrides Color if set

// Border
BorderColor graphics.Color // Border stroke color; transparent = no border
BorderWidth float64 // Border stroke width in pixels; 0 = no border
BorderRadius float64 // Corner radius for rounded rectangles; 0 = sharp corners
BorderDash *graphics.DashPattern // Dash pattern for border; nil = solid line
// BorderGradient applies a gradient to the border stroke. When set, overrides
// BorderColor. Requires BorderWidth > 0 to be visible. Works with BorderDash
// for dashed gradient borders.
BorderGradient *graphics.Gradient

// Effects
Shadow *graphics.BoxShadow // Drop shadow drawn behind the box; nil = no shadow

// Overflow controls clipping behavior for gradients and children.
// Defaults to OverflowClip, which confines gradients and children strictly
// within bounds (clipped to rounded shape when BorderRadius > 0).
// Set to OverflowVisible for glow effects where the gradient should extend
// beyond the widget; children will not be clipped.
// Shadows always overflow naturally. Solid background colors never overflow.
Overflow Overflow
}

func (DecoratedBox) ChildWidget

func (d DecoratedBox) ChildWidget() core.Widget

func (DecoratedBox) CreateRenderObject

func (d DecoratedBox) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (DecoratedBox) UpdateRenderObject

func (d DecoratedBox) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type DeviceScale

DeviceScale provides the current device pixel scale factor to descendants.

type DeviceScale struct {
core.InheritedBase
Scale float64
Child core.Widget
}

func (DeviceScale) ChildWidget

func (d DeviceScale) ChildWidget() core.Widget

func (DeviceScale) ShouldRebuildDependents

func (d DeviceScale) ShouldRebuildDependents(oldWidget core.InheritedWidget) bool

type DiagnosticsHUD

DiagnosticsHUD displays performance metrics overlay.

type DiagnosticsHUD struct {
core.StatelessBase

// DataSource provides frame timing data.
DataSource DiagnosticsHUDDataSource
// TargetTime is the target frame duration for coloring the graph.
TargetTime time.Duration
// GraphWidth is the width of the frame graph. Defaults to 120.
GraphWidth float64
// GraphHeight is the height of the frame graph. Defaults to 60.
GraphHeight float64
// ShowFPS controls whether to display the FPS counter.
ShowFPS bool
// ShowFrameGraph controls whether to display the frame time graph.
ShowFrameGraph bool
}

func (DiagnosticsHUD) Build

func (d DiagnosticsHUD) Build(ctx core.BuildContext) core.Widget

type DiagnosticsHUDDataSource

DiagnosticsHUDDataSource provides frame timing data to the HUD.

type DiagnosticsHUDDataSource interface {
// FPSLabel returns the current FPS display string.
FPSLabel() string
// SamplesInto copies frame samples into dst and returns count copied.
SamplesInto(dst []time.Duration) int
// SampleCount returns the number of samples available.
SampleCount() int
// RegisterRenderObject registers the HUD render object for targeted repaints.
RegisterRenderObject(ro layout.RenderObject)
}

type Divider

Divider renders a thin horizontal line with optional insets and spacing.

Divider is typically used as a child of a Column or any vertically-stacked layout. It expands to fill the available width and occupies Height pixels of vertical space, drawing a centered line of the given Thickness.

Styling Model

Divider is explicit by default: all visual properties use their struct field values directly. A zero value means zero, not "use theme default." For example:

  • Height: 0 means zero vertical space (not rendered)
  • Thickness: 0 means no visible line
  • Color: 0 means transparent (invisible)

For theme-styled dividers, use [theme.DividerOf] which pre-fills values from the current theme's [DividerThemeData] (color from OutlineVariant, 16px space, 1px thickness).

Creation Patterns

Struct literal (full control):

widgets.Divider{
Height: 16,
Thickness: 1,
Color: graphics.RGB(200, 200, 200),
Indent: 16, // 16px left inset
}

Themed (reads from current theme):

theme.DividerOf(ctx)
type Divider struct {
core.RenderObjectBase
// Height is the total vertical space the divider occupies.
Height float64
// Thickness is the thickness of the drawn line.
Thickness float64
// Color is the line color. Zero means transparent.
Color graphics.Color
// Indent is the left inset from the leading edge.
Indent float64
// EndIndent is the right inset from the trailing edge.
EndIndent float64
}

func (Divider) CreateRenderObject

func (d Divider) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (Divider) UpdateRenderObject

func (d Divider) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type DragEndDetails

DragEndDetails describes the end of a drag.

type DragEndDetails = gestures.DragEndDetails

type DragMode

DragMode controls how drag gestures interact with bottom sheet content.

type DragMode int
const (
// DragModeAuto picks DragModeContentAware for multi-snap sheets and DragModeSheet otherwise.
DragModeAuto DragMode = iota
// DragModeContentAware coordinates drags with scrollable content when possible.
DragModeContentAware
// DragModeSheet drags the entire sheet regardless of content.
DragModeSheet
// DragModeHandleOnly only accepts drags from the handle area.
DragModeHandleOnly
)

type DragStartDetails

DragStartDetails describes the start of a drag.

type DragStartDetails = gestures.DragStartDetails

type DragUpdateDetails

DragUpdateDetails describes a drag update.

type DragUpdateDetails = gestures.DragUpdateDetails

type Dropdown

Dropdown displays a button that opens a menu of selectable items.

Styling Model

Dropdown is explicit by default — all visual properties use their struct field values directly. A zero value means zero, not "use theme default." For example:

  • BackgroundColor: 0 means transparent background
  • BorderRadius: 0 means sharp corners
  • Height: 0 means zero height (not rendered)

For theme-styled dropdowns, use [theme.DropdownOf] which pre-fills visual properties from the current theme's [theme.DropdownThemeData].

Creation Patterns

Explicit with struct literal (full control):

widgets.Dropdown[string]{
Value: selectedCountry,
Items: countryItems,
OnChanged: func(v string) { s.SetState(func() { s.selectedCountry = v }) },
BackgroundColor: graphics.ColorWhite,
BorderColor: graphics.RGB(200, 200, 200),
BorderRadius: 8,
Height: 48,
}

Themed (reads from current theme):

theme.DropdownOf(ctx, selectedCountry, countryItems, onChanged)
// Pre-filled with theme colors, border radius, height, item padding

Dropdown is a generic widget where T is the type of the selection value. When an item is selected, OnChanged is called with the selected item's Value.

Each DropdownItem can have a custom Child instead of a text Label. Items can be individually disabled by setting Disabled: true.

type Dropdown[T comparable] struct {
core.StatefulBase

// Value is the current selected value.
Value T
// Items are the available selections.
Items []DropdownItem[T]
// OnChanged is called when a new value is selected.
OnChanged func(T)
// Hint is shown when no selection matches.
Hint string
// Disabled disables the dropdown when true.
Disabled bool
// Width sets a fixed width (0 uses layout constraints).
Width float64
// Height sets a fixed height. Zero means zero height (not rendered).
Height float64
// BorderRadius sets the corner radius. Zero means sharp corners.
BorderRadius float64
// BackgroundColor sets the trigger background. Zero means transparent.
BackgroundColor graphics.Color
// BorderColor sets the trigger border color. Zero means no border.
BorderColor graphics.Color
// MenuBackgroundColor sets the menu background. Zero means transparent.
MenuBackgroundColor graphics.Color
// MenuBorderColor sets the menu border color. Zero means no border.
MenuBorderColor graphics.Color
// TextStyle sets the text style for labels.
TextStyle graphics.TextStyle
// ItemPadding sets padding for each menu item. Zero means no padding.
ItemPadding layout.EdgeInsets
// SelectedItemColor is the background for the currently selected item.
SelectedItemColor graphics.Color

// DisabledTextColor is the text color when disabled.
// If zero, falls back to 0.5 opacity on the normal styling.
DisabledTextColor graphics.Color
}

Example:

This example shows a dropdown selection menu.

package main

import (
"fmt"

"github.com/go-drift/drift/pkg/widgets"
)

func main() {
var selectedCountry string

dropdown := widgets.Dropdown[string]{
Value: selectedCountry,
Hint: "Select a country",
Items: []widgets.DropdownItem[string]{
{Value: "us", Label: "United States"},
{Value: "ca", Label: "Canada"},
{Value: "mx", Label: "Mexico"},
},
OnChanged: func(value string) {
selectedCountry = value
fmt.Printf("Selected: %s\n", selectedCountry)
},
}
_ = dropdown
}

func (Dropdown[T]) CreateState

func (d Dropdown[T]) CreateState() core.State

func (Dropdown[T]) WithBackgroundColor

func (d Dropdown[T]) WithBackgroundColor(c graphics.Color) Dropdown[T]

WithBackgroundColor returns a copy with the specified trigger background color.

func (Dropdown[T]) WithBorderColor

func (d Dropdown[T]) WithBorderColor(c graphics.Color) Dropdown[T]

WithBorderColor returns a copy with the specified trigger border color.

func (Dropdown[T]) WithBorderRadius

func (d Dropdown[T]) WithBorderRadius(radius float64) Dropdown[T]

WithBorderRadius returns a copy with the specified corner radius.

func (Dropdown[T]) WithHeight

func (d Dropdown[T]) WithHeight(height float64) Dropdown[T]

WithHeight returns a copy with the specified item row height.

func (Dropdown[T]) WithHint

func (d Dropdown[T]) WithHint(hint string) Dropdown[T]

WithHint returns a copy with the specified hint text shown when no selection matches.

func (Dropdown[T]) WithItemPadding

func (d Dropdown[T]) WithItemPadding(padding layout.EdgeInsets) Dropdown[T]

WithItemPadding returns a copy with the specified menu item padding.

func (Dropdown[T]) WithMenuBackgroundColor

func (d Dropdown[T]) WithMenuBackgroundColor(c graphics.Color) Dropdown[T]

WithMenuBackgroundColor returns a copy with the specified menu panel background color.

func (Dropdown[T]) WithMenuBorderColor

func (d Dropdown[T]) WithMenuBorderColor(c graphics.Color) Dropdown[T]

WithMenuBorderColor returns a copy with the specified menu panel border color.

type DropdownItem

DropdownItem represents a selectable value for a dropdown.

type DropdownItem[T comparable] struct {
// Value is the item value.
Value T
// Label is the text shown for the item.
Label string
// Child overrides the label when provided.
Child core.Widget
// Disabled disables selection when true.
Disabled bool
}

type ErrorBoundary

ErrorBoundary catches panics from descendant widgets and displays a fallback widget instead of crashing the app. This provides scoped error handling for subtrees of the widget tree.

Error Handling Behavior

In debug mode (core.DebugMode = true), uncaught panics anywhere in the app display a full-screen DebugErrorScreen with stack traces. In production mode, uncaught panics crash the app. Use ErrorBoundary to catch panics and show graceful fallback UI in production.

ErrorBoundary catches panics during:

  • Build: widget Build() methods
  • Layout: RenderObject PerformLayout()
  • Paint: RenderObject Paint()
  • HitTest: RenderObject HitTest()

Scoped vs Global Error Handling

Wrap specific subtrees to isolate failures while keeping the rest of the app running. Or wrap your entire app to provide custom error UI in production:

// Scoped: only RiskyWidget failures show fallback
Column{
Children: []core.Widget{
HeaderWidget{},
ErrorBoundary{
Child: RiskyWidget{},
FallbackBuilder: func(err *errors.BoundaryError) core.Widget {
return Text{Content: "Failed to load"}
},
},
FooterWidget{},
},
}

// Global: custom error UI for entire app in production
drift.NewApp(ErrorBoundary{
Child: MyApp{},
FallbackBuilder: func(err *errors.BoundaryError) core.Widget {
return MyCustomErrorScreen{Error: err}
},
}).Run()

Programmatic Control

Use [ErrorBoundaryOf] to access the boundary's state from descendant widgets:

state := widgets.ErrorBoundaryOf(ctx)
if state != nil && state.HasError() {
state.Reset() // Clear error and retry
}
type ErrorBoundary struct {
core.StatefulBase

// Child is the widget tree to wrap with error handling.
Child core.Widget
// FallbackBuilder creates a widget to show when an error is caught.
// If nil, uses the default ErrorWidget.
FallbackBuilder func(*errors.BoundaryError) core.Widget
// OnError is called when an error is caught. Use for logging/analytics.
OnError func(*errors.BoundaryError)
// WidgetKey is an optional key for the widget. Changing the key forces
// the ErrorBoundary to recreate its state, clearing any captured error.
WidgetKey any
}

Example:

This example shows error boundary for catching widget errors.

package main

import (
"fmt"

"github.com/go-drift/drift/pkg/core"

drifterrors "github.com/go-drift/drift/pkg/errors"
"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/layout"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
boundary := widgets.ErrorBoundary{
OnError: func(err *drifterrors.BoundaryError) {
fmt.Printf("Widget error: %v\n", err)
},
FallbackBuilder: func(err *drifterrors.BoundaryError) core.Widget {
return widgets.Container{
Padding: layout.EdgeInsetsAll(16),
Color: graphics.RGBA(255, 0, 0, 0.13),
Child: widgets.Text{Content: "Something went wrong"},
}
},
Child: widgets.Text{Content: "Protected content"},
}
_ = boundary
}

func (ErrorBoundary) CreateState

func (e ErrorBoundary) CreateState() core.State

func (ErrorBoundary) Key

func (e ErrorBoundary) Key() any

type ErrorWidget

ErrorWidget displays inline error information when a widget fails. Unlike DebugErrorScreen which takes over the entire screen, ErrorWidget renders as a compact red box that can be embedded in layouts.

It shows a red background with:

  • "Something went wrong" message
  • Detailed error text (in debug mode or when Verbose is true)
  • Restart button to recover the app

This is the default fallback widget used by ErrorBoundary when no FallbackBuilder is provided.

type ErrorWidget struct {
core.StatelessBase

// Error is the boundary error that occurred. If nil, shows "Unknown error".
Error *errors.BoundaryError
// Verbose overrides DebugMode for this widget instance.
// When true, shows detailed error messages. When false, shows generic text.
// If nil (default), uses core.DebugMode.
Verbose *bool
}

func (ErrorWidget) Build

func (e ErrorWidget) Build(ctx core.BuildContext) core.Widget

type ExcludeSemantics

ExcludeSemantics is a widget that excludes its child from the semantics tree. Use this to hide decorative elements from accessibility services.

type ExcludeSemantics struct {
core.RenderObjectBase
// Child is the child widget to exclude.
Child core.Widget

// Excluding controls whether to exclude the child from semantics.
// Set to true to exclude, false to include. Defaults to false (Go zero value).
// For the common case of excluding, use: ExcludeSemantics{Excluding: true, Child: child}
Excluding bool
}

func Decorative

func Decorative(child core.Widget) ExcludeSemantics

Decorative marks a widget as decorative, hiding it from screen readers. Use this for purely visual elements that don't convey information.

Example:

Decorative(dividerLine)

Example:

This example shows how to hide decorative elements from accessibility.

package main

import (
"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
// Hides purely visual elements from screen readers
divider := widgets.Decorative(
widgets.Container{
Height: 1,
Color: graphics.RGB(200, 200, 200),
},
)
_ = divider
}

func NewExcludeSemantics

func NewExcludeSemantics(child core.Widget) ExcludeSemantics

NewExcludeSemantics creates an ExcludeSemantics widget that excludes the child from accessibility.

func (ExcludeSemantics) ChildWidget

func (e ExcludeSemantics) ChildWidget() core.Widget

ChildWidget returns the child widget.

func (ExcludeSemantics) CreateRenderObject

func (e ExcludeSemantics) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (ExcludeSemantics) UpdateRenderObject

func (e ExcludeSemantics) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type Expanded

Expanded makes its child fill all remaining space along the main axis of a Row or Column.

After non-flexible children are laid out, remaining space is distributed among Expanded children proportionally based on their Flex factor. The default Flex is 1; set higher values to allocate more space to specific children.

Expanded is equivalent to Flexible with Fit set to FlexFitTight. Use Flexible instead when the child should be allowed to be smaller than its allocated space.

Note: With MainAxisSizeMin, there is no remaining space to fill, so Expanded children receive zero space. Using Expanded inside a ScrollView (unbounded main axis) will panic, since there is no finite space to divide.

Example

Fill remaining space between fixed-size widgets:

Row{
Children: []core.Widget{
Icon{...}, // Fixed size
Expanded{Child: Text{Content: "..."}}, // Fills remaining space
Button{...}, // Fixed size
},
}

Example with Flex Factors

Distribute space proportionally among multiple Expanded children:

Row{
Children: []core.Widget{
Expanded{Flex: 1, Child: panelA}, // Gets 1/3 of space
Expanded{Flex: 2, Child: panelB}, // Gets 2/3 of space
},
}
type Expanded struct {
core.RenderObjectBase
// Child is the widget to expand into the available space.
Child core.Widget

// Flex determines the ratio of space allocated to this child relative to
// other flexible children. Defaults to 1 if not set or <= 0.
//
// For example, in a Row with two Expanded children where one has Flex: 1
// and the other has Flex: 2, the remaining space is split 1:2.
Flex int
}

Example:

This example shows how to use Expanded for flexible sizing.

package main

import (
"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
row := widgets.Row{
Children: []core.Widget{
widgets.Text{Content: "Label:"},
widgets.HSpace(8),
// Expanded takes all remaining horizontal space
widgets.Expanded{
Child: widgets.Container{
Color: graphics.RGB(240, 240, 240),
Child: widgets.Text{Content: "Flexible content"},
},
},
},
}
_ = row
}

Example (Flex Factors):

This example shows proportional sizing with flex factors.

package main

import (
"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
row := widgets.Row{
Children: []core.Widget{
// Takes 1/3 of available space
widgets.Expanded{
Flex: 1,
Child: widgets.Container{Color: graphics.RGB(255, 0, 0)},
},
// Takes 2/3 of available space
widgets.Expanded{
Flex: 2,
Child: widgets.Container{Color: graphics.RGB(0, 0, 255)},
},
},
}
_ = row
}

func Spacer

func Spacer() Expanded

Spacer fills remaining space along the main axis of a Row or Column. It is equivalent to Expanded{Child: SizedBox{}}.

func (Expanded) ChildWidget

func (e Expanded) ChildWidget() core.Widget

ChildWidget returns the child widget.

func (Expanded) CreateRenderObject

func (e Expanded) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

CreateRenderObject creates the renderFlexChild.

func (Expanded) UpdateRenderObject

func (e Expanded) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

UpdateRenderObject updates the renderFlexChild.

type FlexFactor

FlexFactor reports the flex value for a render box.

type FlexFactor interface {
FlexFactor() int
}

type FlexFit

FlexFit controls how a flexible child fills its allocated space within a Row or Column.

When a flex container distributes remaining space among flexible children, FlexFit determines whether each child must fill its allocated portion (tight) or may be smaller (loose).

See Flexible and Expanded for widgets that use FlexFit.

type FlexFit int
const (
// FlexFitLoose allows the child to size itself up to the allocated space,
// but permits it to be smaller. The child receives constraints with
// MinWidth/MinHeight of 0 and MaxWidth/MaxHeight of the allocated space.
//
// This is the zero value, making it the default for [Flexible].
FlexFitLoose FlexFit = iota

// FlexFitTight forces the child to fill exactly the allocated space.
// The child receives tight constraints where Min equals Max for the
// main axis dimension.
//
// This is the behavior of [Expanded].
FlexFitTight
)

type FlexFitProvider

FlexFitProvider reports the fit mode for a flexible render box.

Render objects used as flex children can implement this interface to control whether they receive tight or loose constraints from the parent Row or Column.

Render objects that don't implement this interface default to FlexFitTight for backward compatibility with existing Expanded behavior.

type FlexFitProvider interface {
FlexFit() FlexFit
}

type Flexible

Flexible allows its child to participate in flex space distribution within a Row or Column without requiring the child to fill all allocated space.

Fit Behavior

By default (zero value), Flexible uses FlexFitLoose, which allows the child to be smaller than its allocated space. The child receives loose constraints where MinWidth/MinHeight is 0 and MaxWidth/MaxHeight is the allocated space.

Set Fit to FlexFitTight for behavior equivalent to Expanded, where the child must fill exactly its allocated space.

When to Use Flexible vs Expanded

Use Flexible when the child should participate in flex distribution but may not need all allocated space (e.g., text that might be short).

Use Expanded when the child must fill all remaining space (e.g., a panel or container that should stretch).

Example

A row where text takes only the space it needs, while a panel fills the rest:

Row{
Children: []core.Widget{
Flexible{Child: Text{Content: "Short"}}, // Uses only needed width
Expanded{Child: panel}, // Fills remaining space
},
}

Example with Flex Factors

Distribute space proportionally while allowing children to be smaller:

Row{
Children: []core.Widget{
Flexible{Flex: 1, Child: smallWidget}, // Gets up to 1/3 of space
Flexible{Flex: 2, Child: largeWidget}, // Gets up to 2/3 of space
},
}
type Flexible struct {
core.RenderObjectBase
// Child is the widget to display within the flexible space.
Child core.Widget

// Flex determines the ratio of space allocated to this child relative to
// other flexible children. Defaults to 1 if not set or <= 0.
//
// For example, in a Row with two Flexible children where one has Flex: 1
// and the other has Flex: 2, the remaining space is split 1:2.
Flex int

// Fit controls whether the child must fill its allocated space.
// The zero value is [FlexFitLoose], allowing the child to be smaller.
// Set to [FlexFitTight] for behavior equivalent to [Expanded].
Fit FlexFit
}

func (Flexible) ChildWidget

func (f Flexible) ChildWidget() core.Widget

ChildWidget returns the child widget.

func (Flexible) CreateRenderObject

func (f Flexible) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

CreateRenderObject creates the renderFlexChild.

func (Flexible) UpdateRenderObject

func (f Flexible) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

UpdateRenderObject updates the renderFlexChild.

type Form

Form is a container widget that groups form fields and provides coordinated validation, save, and reset operations.

Form works with form field widgets that implement the formFieldState interface, such as TextFormField and FormField. These fields automatically register with the nearest ancestor Form when built.

Use FormOf to obtain the FormState from a build context, then call its methods to interact with the form:

  • Validate() validates all registered fields and returns true if all pass
  • Save() calls OnSaved on all registered fields
  • Reset() resets all fields to their initial values

Autovalidate behavior:

  • When Autovalidate is true, individual fields validate themselves when their value changes (after user interaction).
  • This does NOT validate untouched fields, avoiding premature error display.
  • Call Validate() explicitly to validate all fields (e.g., on form submission).

Example:

var formState *widgets.FormState

Form{
Autovalidate: true,
OnChanged: func() {
// Called when any field changes
},
Child: Column{
Children: []core.Widget{
TextFormField{Label: "Email", Validator: validateEmail},
TextFormField{Label: "Password", Obscure: true},
Button{
Child: Text{Content: "Submit"},
OnPressed: func() {
if formState.Validate() {
formState.Save()
}
},
},
},
},
}
type Form struct {
core.StatefulBase

// Child is the form content.
Child core.Widget
// Autovalidate runs validators when fields change.
Autovalidate bool
// OnChanged is called when any field changes.
OnChanged func()
}

Example:

This example shows a form with validation.

package main

import (
"fmt"

"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
form := widgets.Form{
Autovalidate: true,
OnChanged: func() {
fmt.Println("Form changed")
},
Child: widgets.Column{
Children: []core.Widget{
widgets.TextFormField{
Label: "Email",
Validator: func(value string) string {
if value == "" {
return "Email is required"
}
return ""
},
},
widgets.TextFormField{
Label: "Password",
Obscure: true,
Validator: func(value string) string {
if len(value) < 8 {
return "Password must be at least 8 characters"
}
return ""
},
},
},
},
}
_ = form
}

func (Form) CreateState

func (f Form) CreateState() core.State

type FormField

FormField is a generic form field widget for building custom form inputs that integrate with Form for validation, save, and reset operations.

Unlike TextFormField which is specialized for text input, FormField[T] can wrap any input widget type and manage values of any type T.

The Builder function receives the FormFieldState and should return a widget that displays the current value and calls DidChange when the value changes.

Example (custom checkbox field):

FormField[bool]{
InitialValue: false,
Validator: func(checked bool) string {
if !checked {
return "You must accept the terms"
}
return ""
},
Builder: func(state *widgets.FormFieldState[bool]) core.Widget {
return Row{
Children: []core.Widget{
Checkbox{
Value: state.Value(),
OnChanged: func(v bool) { state.DidChange(v) },
},
Text{Content: "I accept the terms"},
if state.HasError() {
Text{Content: state.ErrorText(), Style: errorStyle},
},
},
}
},
OnSaved: func(checked bool) {
// Called when FormState.Save() is invoked
},
}
type FormField[T comparable] struct {
core.StatefulBase

// InitialValue is the field's starting value.
InitialValue T
// Builder renders the field using its state.
Builder func(*FormFieldState[T]) core.Widget
// OnSaved is called when the form is saved.
OnSaved func(T)
// Validator returns an error message or empty string.
Validator func(T) string
// OnChanged is called when the field value changes.
OnChanged func(T)
// Disabled controls whether the field participates in validation.
Disabled bool
// Autovalidate enables validation when the value changes.
Autovalidate bool
}

Example:

This example shows a custom form field with FormField.

package main

import (
"fmt"

"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
formField := widgets.FormField[bool]{
InitialValue: false,
Validator: func(checked bool) string {
if !checked {
return "You must accept the terms"
}
return ""
},
Builder: func(state *widgets.FormFieldState[bool]) core.Widget {
return widgets.Row{
Children: []core.Widget{
widgets.Checkbox{
Value: state.Value(),
OnChanged: func(v bool) { state.DidChange(v) },
},
widgets.HSpace(8),
widgets.Text{Content: "I accept the terms"},
},
}
},
OnSaved: func(checked bool) {
fmt.Printf("Terms accepted: %v\n", checked)
},
}
_ = formField
}

func (FormField[T]) CreateState

func (f FormField[T]) CreateState() core.State

type FormFieldState

FormFieldState stores the mutable state for a FormField and provides methods for querying and updating the field value.

Methods:

  • Value() T: Returns the current field value.
  • ErrorText() string: Returns the current validation error message, or empty string.
  • HasError() bool: Returns true if there is a validation error.
  • DidChange(T): Call this when the field value changes to update state and trigger validation.
  • Validate() bool: Runs the validator and returns true if valid.
  • Save(): Calls the OnSaved callback with the current value.
  • Reset(): Resets to InitialValue and clears errors.
type FormFieldState[T comparable] struct {
// contains filtered or unexported fields
}

func (*FormFieldState[T]) Build

func (s *FormFieldState[T]) Build(ctx core.BuildContext) core.Widget

Build renders the field by calling Builder.

func (*FormFieldState[T]) DidChange

func (s *FormFieldState[T]) DidChange(value T)

DidChange updates the value and triggers validation/notifications.

func (*FormFieldState[T]) DidChangeDependencies

func (s *FormFieldState[T]) DidChangeDependencies()

DidChangeDependencies is a no-op for FormFieldState.

func (*FormFieldState[T]) DidUpdateWidget

func (s *FormFieldState[T]) DidUpdateWidget(oldWidget core.StatefulWidget)

DidUpdateWidget updates value if the initial value changed before interaction.

func (*FormFieldState[T]) Dispose

func (s *FormFieldState[T]) Dispose()

Dispose unregisters the field from the form.

func (*FormFieldState[T]) ErrorText

func (s *FormFieldState[T]) ErrorText() string

ErrorText returns the current error message.

func (*FormFieldState[T]) HasError

func (s *FormFieldState[T]) HasError() bool

HasError reports whether the field has an error.

func (*FormFieldState[T]) InitState

func (s *FormFieldState[T]) InitState()

InitState initializes the field value from the widget.

func (*FormFieldState[T]) Reset

func (s *FormFieldState[T]) Reset()

Reset returns the field to its initial value.

func (*FormFieldState[T]) Save

func (s *FormFieldState[T]) Save()

Save triggers the OnSaved callback.

func (*FormFieldState[T]) SetElement

func (s *FormFieldState[T]) SetElement(element *core.StatefulElement)

SetElement stores the element for rebuilds.

func (*FormFieldState[T]) SetState

func (s *FormFieldState[T]) SetState(fn func())

SetState executes fn and schedules rebuild.

func (*FormFieldState[T]) Validate

func (s *FormFieldState[T]) Validate() bool

Validate runs the field validator.

func (*FormFieldState[T]) Value

func (s *FormFieldState[T]) Value() T

Value returns the current value.

type FormState

FormState manages the state of a Form widget and provides methods to interact with all registered form fields.

Obtain a FormState using FormOf from within a build context, or by storing a reference when building the form.

Methods:

  • Validate() bool: Validates all fields and returns true if all pass.
  • Save(): Calls OnSaved on all fields (typically after successful validation).
  • Reset(): Resets all fields to their initial values and clears errors.

FormState tracks a generation counter that increments on validation, reset, and field changes, triggering rebuilds of dependent widgets.

type FormState struct {
core.StateBase
// contains filtered or unexported fields
}

func FormOf

func FormOf(ctx core.BuildContext) *FormState

FormOf returns the FormState of the nearest ancestor Form widget, or nil if there is no Form ancestor.

Form fields like TextFormField use this internally to register with their parent form. You can also use it to obtain the FormState for calling Validate, Save, or Reset.

Example:

func (s *myWidgetState) Build(ctx core.BuildContext) core.Widget {
formState := widgets.FormOf(ctx)
return Button{
Child: Text{Content: "Submit"},
OnPressed: func() {
if formState != nil && formState.Validate() {
formState.Save()
}
},
}
}

func (*FormState) Build

func (s *FormState) Build(ctx core.BuildContext) core.Widget

Build renders the form scope.

func (*FormState) Dispose

func (s *FormState) Dispose()

Dispose clears registrations.

func (*FormState) InitState

func (s *FormState) InitState()

InitState initializes the form state.

func (*FormState) NotifyChanged

func (s *FormState) NotifyChanged()

NotifyChanged informs listeners that a field changed. When autovalidate is enabled, the calling field is expected to validate itself rather than having the form validate all fields (which would show errors on untouched fields). Form.Validate() can still be called explicitly to validate all.

func (*FormState) RegisterField

func (s *FormState) RegisterField(field formFieldState)

RegisterField registers a field with this form.

func (*FormState) Reset

func (s *FormState) Reset()

Reset resets all fields to their initial values.

func (*FormState) Save

func (s *FormState) Save()

Save calls OnSaved for all fields.

func (*FormState) UnregisterField

func (s *FormState) UnregisterField(field formFieldState)

UnregisterField unregisters a field from this form.

func (*FormState) Validate

func (s *FormState) Validate() bool

Validate runs validators on all fields.

type GestureDetector

GestureDetector wraps a child widget with gesture recognition callbacks.

GestureDetector supports multiple gesture types that can be used together:

  • Tap: Simple tap/click detection via OnTap
  • Pan: Free-form drag in any direction via OnPanStart/Update/End
  • Horizontal drag: Constrained horizontal drag via OnHorizontalDrag*
  • Vertical drag: Constrained vertical drag via OnVerticalDrag*

Example (tap detection):

GestureDetector{
OnTap: func() { handleTap() },
Child: Container{Color: colors.Blue, Child: icon},
}

Example (draggable widget):

GestureDetector{
OnPanStart: func(d DragStartDetails) { ... },
OnPanUpdate: func(d DragUpdateDetails) { ... },
OnPanEnd: func(d DragEndDetails) { ... },
Child: draggableItem,
}

For simple tap handling on buttons, prefer Button which provides visual feedback. GestureDetector is best for custom gestures.

type GestureDetector struct {
core.RenderObjectBase
Child core.Widget
OnTap func()
OnPanStart func(DragStartDetails)
OnPanUpdate func(DragUpdateDetails)
OnPanEnd func(DragEndDetails)
OnPanCancel func()

OnHorizontalDragStart func(DragStartDetails)
OnHorizontalDragUpdate func(DragUpdateDetails)
OnHorizontalDragEnd func(DragEndDetails)
OnHorizontalDragCancel func()

OnVerticalDragStart func(DragStartDetails)
OnVerticalDragUpdate func(DragUpdateDetails)
OnVerticalDragEnd func(DragEndDetails)
OnVerticalDragCancel func()
}

Example:

This example shows how to handle tap gestures.

package main

import (
"fmt"

"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/layout"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
detector := widgets.GestureDetector{
OnTap: func() {
fmt.Println("Tapped!")
},
Child: widgets.Container{
Color: graphics.RGB(200, 200, 200),
Padding: layout.EdgeInsetsAll(20),
Child: widgets.Text{
Content: "Tap me",
},
},
}
_ = detector
}

Example (Drag):

This example shows drag gesture handling.

package main

import (
"fmt"

"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
var offsetX, offsetY float64

detector := widgets.GestureDetector{
OnPanStart: func(details widgets.DragStartDetails) {
fmt.Println("Drag started")
},
OnPanUpdate: func(details widgets.DragUpdateDetails) {
offsetX += details.Delta.X
offsetY += details.Delta.Y
fmt.Printf("Position: (%.0f, %.0f)\n", offsetX, offsetY)
},
OnPanEnd: func(details widgets.DragEndDetails) {
fmt.Printf("Drag ended with velocity: (%.0f, %.0f)\n",
details.Velocity.X, details.Velocity.Y)
},
Child: widgets.Container{
Width: 100,
Height: 100,
Color: graphics.RGB(100, 149, 237),
},
}
_ = detector
}

func Drag

func Drag(onUpdate func(DragUpdateDetails), child core.Widget) GestureDetector

Drag wraps a child with pan (omnidirectional) drag handlers.

func HorizontalDrag

func HorizontalDrag(onUpdate func(DragUpdateDetails), child core.Widget) GestureDetector

HorizontalDrag wraps a child with horizontal-only drag handlers.

func Tap

func Tap(onTap func(), child core.Widget) GestureDetector

Tap wraps a child with a tap handler.

func VerticalDrag

func VerticalDrag(onUpdate func(DragUpdateDetails), child core.Widget) GestureDetector

VerticalDrag wraps a child with vertical-only drag handlers.

func (GestureDetector) ChildWidget

func (g GestureDetector) ChildWidget() core.Widget

func (GestureDetector) CreateRenderObject

func (g GestureDetector) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (GestureDetector) UpdateRenderObject

func (g GestureDetector) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type Icon

Icon renders a single glyph with icon-friendly defaults.

Styling Model

Icon is explicit by default — all visual properties use their struct field values directly. A zero value means zero, not "use theme default." For example:

  • Size: 0 means zero size (not rendered)
  • Color: 0 means transparent (invisible)

For theme-styled icons, use [theme.IconOf] which sets Size to 24 and Color to the theme's OnSurface color.

Creation Patterns

Struct literal (full control):

widgets.Icon{
Glyph: "★",
Size: 32,
Color: graphics.RGB(255, 193, 7),
}

Themed (reads from current theme):

theme.IconOf(ctx, "✓")
// Pre-filled with standard size (24) and theme color

Icon renders the glyph as a Text widget with MaxLines: 1 and the specified size and color. Use Weight to control font weight if needed.

type Icon struct {
core.StatelessBase

// Glyph is the text glyph to render.
Glyph string
// Size is the font size for the icon. Zero means zero size (not rendered).
Size float64
// Color is the icon color. Zero means transparent.
Color graphics.Color
// Weight sets the font weight if non-zero.
Weight graphics.FontWeight
}

func (Icon) Build

func (i Icon) Build(ctx core.BuildContext) core.Widget

type IgnorePointer

IgnorePointer lays out and paints its child normally but optionally blocks all hit testing. When Ignoring is true, pointer events cannot reach the child or any of its descendants. This is useful for disabling interaction during animations without affecting visual output.

type IgnorePointer struct {
core.RenderObjectBase
// Ignoring controls whether pointer events are blocked.
Ignoring bool
// Child is the widget to render.
Child core.Widget
}

func (IgnorePointer) ChildWidget

func (ip IgnorePointer) ChildWidget() core.Widget

func (IgnorePointer) CreateRenderObject

func (ip IgnorePointer) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (IgnorePointer) UpdateRenderObject

func (ip IgnorePointer) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type Image

Image renders a bitmap image onto the canvas with configurable sizing and scaling.

Image accepts a Go image.Image as its Source. The image is rendered using the specified Fit mode to control scaling behavior.

Creation Pattern

Use struct literal:

widgets.Image{
Source: loadedImage,
Width: 200,
Height: 150,
Fit: widgets.ImageFitCover,
SemanticLabel: "Product photo",
}

Image Fit Modes

  • ImageFitContain: Scales to fit within the box while maintaining aspect ratio (default)
  • ImageFitFill: Stretches the image to completely fill the box (may distort)
  • ImageFitCover: Scales to cover the box while maintaining aspect ratio (may crop)
  • ImageFitNone: Uses the image's intrinsic size
  • ImageFitScaleDown: Like Contain, but never scales up

For decorative images that don't convey information, set ExcludeFromSemantics to true to hide them from screen readers.

type Image struct {
core.RenderObjectBase
// Source is the image to render.
Source image.Image
// Width overrides the image width if non-zero.
Width float64
// Height overrides the image height if non-zero.
Height float64
// Fit controls how the image is scaled within its bounds.
Fit ImageFit
// Alignment positions the image within its bounds.
Alignment layout.Alignment
// SemanticLabel provides an accessibility description of the image.
SemanticLabel string
// ExcludeFromSemantics excludes the image from the semantics tree when true.
// Use this for decorative images that don't convey meaningful content.
ExcludeFromSemantics bool
}

Example:

This example shows image display with fit modes.

package main

import (
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
// Example with nil source (placeholder)
placeholder := widgets.Image{
Width: 200,
Height: 150,
Fit: widgets.ImageFitCover,
}
_ = placeholder
}

func (Image) CreateRenderObject

func (i Image) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (Image) UpdateRenderObject

func (i Image) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

func (Image) WithAlignment

func (i Image) WithAlignment(alignment layout.Alignment) Image

WithAlignment returns a copy of the image with the specified alignment.

func (Image) WithFit

func (i Image) WithFit(fit ImageFit) Image

WithFit returns a copy of the image with the specified fit mode.

func (Image) WithSize

func (i Image) WithSize(width, height float64) Image

WithSize returns a copy of the image with the specified width and height.

type ImageFit

ImageFit controls how an image is scaled within its box.

type ImageFit int
const (
// ImageFitContain scales the image to fit within its bounds.
// This is the zero value, making it the default for [Image].
ImageFitContain ImageFit = iota
// ImageFitFill stretches the image to fill its bounds.
ImageFitFill
// ImageFitCover scales the image to cover its bounds.
ImageFitCover
// ImageFitNone leaves the image at its intrinsic size.
ImageFitNone
// ImageFitScaleDown fits the image if needed, otherwise keeps intrinsic size.
ImageFitScaleDown
)

func (ImageFit) String

func (f ImageFit) String() string

String returns a human-readable representation of the image fit mode.

type IndexedStack

IndexedStack is a Stack that only displays one child at a time.

By default, all children are laid out (maintaining their state), but only the child at Index is painted and receives hit tests. This is useful for tab views or wizards where you want to preserve the state of off-screen pages.

With Fit == StackFitExpand, only the active child is laid out because the stack size is constraint-driven (inactive children cannot affect it).

Example:

IndexedStack{
Index: currentTab,
Children: []core.Widget{
HomeTab{},
SearchTab{},
ProfileTab{},
},
}

If Index is out of bounds, nothing is painted.

type IndexedStack struct {
core.RenderObjectBase
Children []core.Widget
Alignment layout.Alignment
Fit StackFit
Index int
}

Example:

This example shows tab-like switching with IndexedStack.

package main

import (
"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
var currentTab int

indexedStack := widgets.IndexedStack{
Index: currentTab,
Children: []core.Widget{
widgets.Text{Content: "Home Tab Content"},
widgets.Text{Content: "Search Tab Content"},
widgets.Text{Content: "Profile Tab Content"},
},
}
_ = indexedStack
}

func (IndexedStack) ChildrenWidgets

func (s IndexedStack) ChildrenWidgets() []core.Widget

func (IndexedStack) CreateRenderObject

func (s IndexedStack) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (IndexedStack) UpdateRenderObject

func (s IndexedStack) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type InputDecoration

InputDecoration provides styling configuration for input widgets like DatePicker and TimePicker.

type InputDecoration struct {
// LabelText is shown above the input.
LabelText string

// HintText is shown when the input is empty.
HintText string

// HelperText is shown below the input.
HelperText string

// ErrorText replaces HelperText when validation fails.
ErrorText string

// PrefixIcon is shown at the start of the input.
PrefixIcon core.Widget

// SuffixIcon is shown at the end of the input (e.g., calendar icon).
SuffixIcon core.Widget

// ContentPadding is the padding inside the input field.
ContentPadding layout.EdgeInsets

// BorderRadius for the input field.
BorderRadius float64

// BorderColor when not focused.
BorderColor graphics.Color

// FocusedBorderColor when focused.
FocusedBorderColor graphics.Color

// BackgroundColor of the input field.
BackgroundColor graphics.Color

// LabelStyle for the label text.
LabelStyle graphics.TextStyle

// HintStyle for the hint text.
HintStyle graphics.TextStyle

// HelperStyle for the helper/error text.
HelperStyle graphics.TextStyle

// ErrorColor for error text. Zero means transparent (error text not visible).
ErrorColor graphics.Color
}

type LayoutBuilder

LayoutBuilder defers child building to the layout phase, providing the parent's constraints to the builder function. This enables responsive layouts that adapt to available space.

Because Drift's pipeline runs Build before Layout, widgets normally cannot observe constraints. LayoutBuilder bridges this gap by invoking the builder during the render object's PerformLayout, once actual constraints are known.

Example:

LayoutBuilder{
Builder: func(ctx core.BuildContext, constraints layout.Constraints) core.Widget {
if constraints.MaxWidth > 600 {
return wideLayout()
}
return narrowLayout()
},
}
type LayoutBuilder struct {
Builder func(ctx core.BuildContext, constraints layout.Constraints) core.Widget
}

func (LayoutBuilder) CreateElement

func (lb LayoutBuilder) CreateElement() core.Element

CreateElement returns a [core.LayoutBuilderElement] that defers child building to the layout phase.

func (LayoutBuilder) CreateRenderObject

func (lb LayoutBuilder) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

CreateRenderObject creates the backing renderLayoutBuilder, which invokes the layout callback during PerformLayout and lays out the resulting child.

func (LayoutBuilder) Key

func (lb LayoutBuilder) Key() any

Key returns nil. LayoutBuilder does not support keyed identity.

func (LayoutBuilder) LayoutBuilder

func (lb LayoutBuilder) LayoutBuilder() func(ctx core.BuildContext, constraints layout.Constraints) core.Widget

LayoutBuilder returns the builder callback. This satisfies the [core.LayoutBuilderWidget] interface so the element can retrieve the builder during the layout callback.

func (LayoutBuilder) UpdateRenderObject

func (lb LayoutBuilder) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

UpdateRenderObject is a no-op. The render object's only mutable state is the layout callback, which is set directly by the element during Mount.

type LinearProgressIndicator

LinearProgressIndicator displays a linear progress indicator. When Value is nil, it shows an indeterminate animation. When Value is set, it shows determinate progress from 0.0 to 1.0.

Styling Model

LinearProgressIndicator is explicit by default — zero values mean zero (no color). For theme-styled indicators, use [theme.LinearProgressIndicatorOf] which pre-fills Color from Primary and TrackColor from SurfaceVariant.

Creation Patterns

Struct literal (full control):

widgets.LinearProgressIndicator{
Value: nil, // indeterminate
Color: colors.Primary,
TrackColor: colors.SurfaceVariant,
Height: 4,
BorderRadius: 2,
}

Themed (reads from current theme):

theme.LinearProgressIndicatorOf(ctx, nil)  // indeterminate
theme.LinearProgressIndicatorOf(ctx, &progress) // determinate
type LinearProgressIndicator struct {
core.StatefulBase

// Value is the progress value (0.0 to 1.0). Nil means indeterminate.
Value *float64

// Color is the indicator color. Zero means transparent (invisible).
Color graphics.Color

// TrackColor is the background track color. Zero means no track.
TrackColor graphics.Color

// Height is the thickness of the indicator. Zero means zero height (not rendered).
Height float64

// BorderRadius is the corner radius. Zero means sharp corners.
BorderRadius float64

// MinWidth is the minimum width for the indicator. Zero uses constraints.
MinWidth float64
}

func (LinearProgressIndicator) CreateState

func (l LinearProgressIndicator) CreateState() core.State

type ListView

ListView displays a scrollable list of widgets.

ListView wraps its children in a ScrollView with either a Row or Column depending on ScrollDirection. All children are built immediately, making it suitable for small lists with a known number of items.

For large lists or dynamic content, use ListViewBuilder which builds children on demand and supports virtualization for better performance.

Example:

ListView{
Padding: layout.EdgeInsetsAll(16),
Children: []core.Widget{
ListTile{Title: "Item 1"},
ListTile{Title: "Item 2"},
ListTile{Title: "Item 3"},
},
}
type ListView struct {
core.StatelessBase

// Children are the widgets to display in the list.
Children []core.Widget
// ScrollDirection is the axis along which the list scrolls. Defaults to vertical.
ScrollDirection Axis
// Controller manages scroll position and provides scroll notifications.
Controller *ScrollController
// Physics determines how the scroll view responds to user input.
Physics ScrollPhysics
// Padding is applied around the list content.
Padding layout.EdgeInsets
// MainAxisAlignment controls how children are positioned along the scroll axis.
MainAxisAlignment MainAxisAlignment
// MainAxisSize determines how much space the list takes along the scroll axis.
MainAxisSize MainAxisSize
}

Example:

This example shows a simple static list.

package main

import (
"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/layout"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
items := []string{"Apple", "Banana", "Cherry"}
children := make([]core.Widget, len(items))
for i, item := range items {
children[i] = widgets.Container{
Padding: layout.EdgeInsetsSymmetric(16, 12),
Child: widgets.Text{Content: item},
}
}

listView := widgets.ListView{
Children: children,
Padding: layout.EdgeInsetsAll(8),
}
_ = listView
}

func (ListView) Build

func (l ListView) Build(ctx core.BuildContext) core.Widget

type ListViewBuilder

ListViewBuilder builds list items on demand for efficient scrolling of large lists.

Unlike ListView which builds all children upfront, ListViewBuilder only builds widgets for visible items plus a cache region, making it suitable for lists with hundreds or thousands of items.

Virtualization

For virtualization to work, ItemExtent must be set to a fixed height (or width for horizontal lists). This allows the list to calculate which items are visible without building all items. If ItemExtent is 0, all items are built immediately.

CacheExtent controls how many pixels beyond the visible area are pre-built, reducing flicker during fast scrolling.

Example:

ListViewBuilder{
ItemCount: 1000,
ItemExtent: 56, // Fixed height per item enables virtualization
CacheExtent: 200, // Pre-build 200px beyond visible area
ItemBuilder: func(ctx core.BuildContext, index int) core.Widget {
return ListTile{Title: fmt.Sprintf("Item %d", index)}
},
}
type ListViewBuilder struct {
core.StatefulBase

// ItemCount is the total number of items in the list.
ItemCount int
// ItemBuilder creates widgets for visible items. Called with the build context and item index.
ItemBuilder func(ctx core.BuildContext, index int) core.Widget
// ItemExtent is the fixed extent of each item along the scroll axis. Required for virtualization.
ItemExtent float64
// CacheExtent is the number of pixels to render beyond the visible area.
CacheExtent float64
// ScrollDirection is the axis along which the list scrolls. Defaults to vertical.
ScrollDirection Axis
// Controller manages scroll position and provides scroll notifications.
Controller *ScrollController
// Physics determines how the scroll view responds to user input.
Physics ScrollPhysics
// Padding is applied around the list content.
Padding layout.EdgeInsets
// MainAxisAlignment controls how children are positioned along the scroll axis.
MainAxisAlignment MainAxisAlignment
// MainAxisSize determines how much space the list takes along the scroll axis.
MainAxisSize MainAxisSize
}

Example:

This example shows how to create a dynamic list with ListViewBuilder.

package main

import (
"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/layout"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
items := []string{"Apple", "Banana", "Cherry", "Date", "Elderberry"}

listView := widgets.ListViewBuilder{
ItemCount: len(items),
ItemExtent: 48,
ItemBuilder: func(ctx core.BuildContext, index int) core.Widget {
return widgets.Container{
Padding: layout.EdgeInsetsSymmetric(16, 12),
Child: widgets.Text{Content: items[index]},
}
},
Padding: layout.EdgeInsetsAll(8),
}
_ = listView
}

func (ListViewBuilder) CreateState

func (l ListViewBuilder) CreateState() core.State

type Lottie

Lottie renders a Lottie animation from a loaded [lottie.Animation].

Loading

The Source field requires a pre-loaded animation. Use [lottie.Load], [lottie.LoadBytes], or [lottie.LoadFile] to obtain one.

Auto\-play Behavior

By default, the animation plays automatically on mount. The Repeat field controls what happens when playback completes: stop, loop, or bounce.

Programmatic Control

Pass a Controller to take full control of playback. When a Controller is provided, the widget does not auto-play and ignores Repeat and OnComplete. The controller's Value (0.0 to 1.0) maps directly to animation progress. Switching between external and self-managed control at runtime is supported.

Sizing Behavior

When Width and/or Height are specified, the animation scales to fit. When both are zero, the widget uses the animation's intrinsic dimensions. When one dimension is specified, the other is calculated from the aspect ratio.

Easing

Lottie animations contain their own easing curves baked into keyframes, so the internal controller uses linear interpolation by default.

Creation Patterns

// Play once
widgets.Lottie{Source: anim, Width: 200, Height: 200}

// Loop
widgets.Lottie{Source: anim, Width: 200, Height: 200, Repeat: widgets.LottieLoop}

// Bounce (forward then reverse)
widgets.Lottie{Source: anim, Width: 200, Height: 200, Repeat: widgets.LottieBounce}

// Completion callback
widgets.Lottie{Source: anim, Width: 200, Height: 200, OnComplete: func() { /* done */ }}

// Intrinsic size
widgets.Lottie{Source: anim, Repeat: widgets.LottieLoop}

// Full programmatic control
widgets.Lottie{Source: anim, Controller: ctrl, Width: 200, Height: 200}

Lifetime

The Source must remain valid for as long as any widget or display list references it. Do not call [lottie.Animation.Destroy] while widgets may still render the animation.

type Lottie struct {
core.StatefulBase

// Source is the pre-loaded Lottie animation to render. Use [lottie.Load],
// [lottie.LoadBytes], or [lottie.LoadFile] to create one. If nil, the
// widget renders nothing.
Source *lottie.Animation

// Width is the desired width. If zero and Height is set, calculated from aspect ratio.
// If both zero, uses the animation's intrinsic width.
Width float64

// Height is the desired height. If zero and Width is set, calculated from aspect ratio.
// If both zero, uses the animation's intrinsic height.
Height float64

// Repeat controls how the animation repeats. Ignored when Controller is set.
Repeat LottieRepeat

// Controller allows external control of the animation. When set, the widget
// does not auto-play and ignores Repeat and OnComplete. The controller's
// Value (0.0 to 1.0) maps directly to animation progress.
Controller *animation.AnimationController

// OnComplete is called when the animation finishes playing.
// Only called in LottiePlayOnce mode without an external Controller.
OnComplete func()
}

func (Lottie) CreateState

func (l Lottie) CreateState() core.State

type LottieRepeat

LottieRepeat controls how a Lottie animation repeats after completing.

type LottieRepeat int
const (
// LottiePlayOnce plays the animation once and stops at the last frame.
LottiePlayOnce LottieRepeat = iota
// LottieLoop replays the animation from the beginning continuously.
LottieLoop
// LottieBounce plays forward then backward continuously (ping-pong).
LottieBounce
)

type MainAxisAlignment

MainAxisAlignment controls how children are positioned along the main axis (horizontal for Row, vertical for Column).

type MainAxisAlignment int
const (
// MainAxisAlignmentStart places children at the start (left for Row, top for Column).
MainAxisAlignmentStart MainAxisAlignment = iota
// MainAxisAlignmentEnd places children at the end (right for Row, bottom for Column).
MainAxisAlignmentEnd
// MainAxisAlignmentCenter centers children along the main axis.
MainAxisAlignmentCenter
// MainAxisAlignmentSpaceBetween distributes free space evenly between children.
// No space before the first or after the last child.
MainAxisAlignmentSpaceBetween
// MainAxisAlignmentSpaceAround distributes free space evenly, with half-sized
// spaces at the start and end.
MainAxisAlignmentSpaceAround
// MainAxisAlignmentSpaceEvenly distributes free space evenly, including
// equal space before the first and after the last child.
MainAxisAlignmentSpaceEvenly
)

func (MainAxisAlignment) String

func (a MainAxisAlignment) String() string

String returns a human-readable representation of the main axis alignment.

type MainAxisSize

MainAxisSize controls how much space the flex container takes along its main axis.

type MainAxisSize int
const (
// MainAxisSizeMax expands to fill all available space along the main axis.
// This is the zero value, making it the default for [Row] and [Column].
MainAxisSizeMax MainAxisSize = iota
// MainAxisSizeMin sizes the container to fit its children (shrink-wrap).
MainAxisSizeMin
)

func (MainAxisSize) String

func (s MainAxisSize) String() string

String returns a human-readable representation of the main axis size.

type MergeSemantics

MergeSemantics is a widget that merges the semantics of its descendants.

type MergeSemantics struct {
core.RenderObjectBase
// Child is the child widget whose semantics will be merged.
Child core.Widget
}

func (MergeSemantics) ChildWidget

func (m MergeSemantics) ChildWidget() core.Widget

ChildWidget returns the child widget.

func (MergeSemantics) CreateRenderObject

func (m MergeSemantics) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (MergeSemantics) UpdateRenderObject

func (m MergeSemantics) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type NativeWebView

NativeWebView embeds a native web browser view.

Create a [platform.WebViewController] with [core.UseDisposable] and pass it to this widget:

s.web = platform.NewWebViewController()
core.UseDisposable(s, s.web)
s.web.OnPageFinished = func(url string) { ... }
s.web.Load("https://example.com")

// in Build:
widgets.NativeWebView{Controller: s.web, Height: 400}

Width and Height set explicit dimensions. If Width is 0, the view expands to fill available width.

type NativeWebView struct {
core.RenderObjectBase
// Controller provides the native web view surface and navigation control.
Controller *platform.WebViewController

// Width of the web view in logical pixels (0 = expand to fill).
Width float64

// Height of the web view in logical pixels.
Height float64
}

func (NativeWebView) CreateRenderObject

func (n NativeWebView) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

CreateRenderObject creates the render object for this widget.

func (NativeWebView) UpdateRenderObject

func (n NativeWebView) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

UpdateRenderObject updates the render object with new widget properties.

type Offstage

Offstage lays out its child but optionally skips painting and hit testing.

This keeps element/render object state alive without contributing to visual output. Use this to keep routes or tabs alive while preventing offscreen paint cost.

type Offstage struct {
core.RenderObjectBase
// Offstage controls whether the child is hidden.
Offstage bool
// Child is the widget to lay out and optionally hide.
Child core.Widget
}

func (Offstage) ChildWidget

func (o Offstage) ChildWidget() core.Widget

func (Offstage) CreateRenderObject

func (o Offstage) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (Offstage) UpdateRenderObject

func (o Offstage) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type Opacity

Opacity applies transparency to its child widget.

Creation Pattern

Use struct literal:

widgets.Opacity{
Opacity: 0.5,
Child: content,
}

The Opacity value should be between 0.0 (fully transparent) and 1.0 (fully opaque). When Opacity is 0.0, the child is not painted at all. When Opacity is 1.0, the child is painted normally without any performance overhead. Intermediate values use SaveLayerAlpha for proper alpha compositing.

Note: The layer bounds are based on this widget's size. Children that paint outside their bounds (e.g., via transforms or overflow) may be clipped.

type Opacity struct {
core.RenderObjectBase
// Opacity is the transparency value (0.0 to 1.0).
Opacity float64
// Child is the widget to which opacity is applied.
Child core.Widget
}

func (Opacity) ChildWidget

func (o Opacity) ChildWidget() core.Widget

func (Opacity) CreateRenderObject

func (o Opacity) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (Opacity) UpdateRenderObject

func (o Opacity) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type Overflow

Overflow controls clipping behavior for Container and DecoratedBox.

This setting affects two things:

  • Whether background gradients extend beyond widget bounds
  • Whether children are clipped to widget bounds

Shadows are always drawn behind the decoration and naturally overflow bounds regardless of this setting. Solid background colors never overflow.

OverflowClip (the default) clips both gradients and children to the widget bounds. When BorderRadius > 0, content is clipped to the rounded shape. OverflowVisible allows gradients to extend beyond and does not clip children.

type Overflow int
const (
// OverflowClip confines content strictly to widget bounds.
// This is the default behavior.
//
// Both background gradients and children are clipped to the widget bounds.
// When BorderRadius > 0, content is clipped to the rounded shape.
//
// This ensures that child content (such as images or colored bars at the
// edges of a card) conforms to the parent's bounds and rounded corners.
OverflowClip Overflow = iota

// OverflowVisible allows content to extend beyond widget bounds.
// Children are not clipped.
//
// This is useful for glow effects where a radial gradient's radius
// exceeds the widget dimensions, or when children need to paint
// outside the parent bounds (e.g., shadows on child widgets).
OverflowVisible
)

type Padding

Padding adds empty space around its child widget.

The child is constrained to the remaining space after padding is applied. If no child is provided, Padding creates an empty box of the padding size.

Use [layout.EdgeInsets] helpers to create padding values:

Padding{Padding: layout.EdgeInsetsAll(16), Child: child}
Padding{Padding: layout.EdgeInsetsSymmetric(24, 12), Child: child}
Padding{Padding: layout.EdgeInsetsOnly(Left: 8, Right: 8), Child: child}

For padding combined with background color, consider Container instead.

type Padding struct {
core.RenderObjectBase
Padding layout.EdgeInsets
Child core.Widget
}

Example:

This example shows various padding helpers.

package main

import (
"github.com/go-drift/drift/pkg/layout"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
// Uniform padding on all sides
all := widgets.Padding{
Padding: layout.EdgeInsetsAll(16),
Child: widgets.Text{Content: "All sides"},
}

// Symmetric horizontal/vertical padding
symmetric := widgets.Padding{
Padding: layout.EdgeInsetsSymmetric(24, 12), // horizontal, vertical
Child: widgets.Text{Content: "Symmetric"},
}

// Different padding per side
custom := widgets.Padding{
Padding: layout.EdgeInsetsOnly(8, 16, 8, 0), // left, top, right, bottom
Child: widgets.Text{Content: "Custom"},
}

_ = all
_ = symmetric
_ = custom
}

func Padded

func Padded(padding layout.EdgeInsets, child core.Widget) Padding

Padded wraps a child with the specified padding.

func PaddingAll

func PaddingAll(value float64, child core.Widget) Padding

PaddingAll wraps a child with uniform padding on all sides.

func PaddingOnly

func PaddingOnly(left, top, right, bottom float64, child core.Widget) Padding

PaddingOnly wraps a child with specific padding on each side.

func PaddingSym

func PaddingSym(horizontal, vertical float64, child core.Widget) Padding

PaddingSym wraps a child with symmetric horizontal and vertical padding.

func (Padding) ChildWidget

func (p Padding) ChildWidget() core.Widget

func (Padding) CreateRenderObject

func (p Padding) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (Padding) UpdateRenderObject

func (p Padding) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type Radio

Radio renders a single radio button that is part of a mutually exclusive group.

Styling Model

Radio is explicit by default — all visual properties use their struct field values directly. A zero value means zero, not "use theme default." For example:

  • ActiveColor: 0 means transparent inner dot when selected
  • Size: 0 means zero size (not rendered)

For theme-styled radio buttons, use [theme.RadioOf] which pre-fills visual properties from the current theme's [theme.RadioThemeData].

Creation Patterns

Explicit with struct literal (full control):

widgets.Radio[string]{
Value: "small",
GroupValue: selectedSize,
OnChanged: func(v string) { s.SetState(func() { s.selectedSize = v }) },
ActiveColor: graphics.RGB(33, 150, 243),
Size: 24,
}

Themed (reads from current theme):

theme.RadioOf(ctx, "small", selectedSize, onChanged)
// Pre-filled with theme colors and size

Radio is a generic widget where T is the type of the selection value. Each Radio in a group has its own Value, and all share the same GroupValue (the current selection). When a Radio is tapped, OnChanged is called with that Radio's Value.

Example (string values):

var selected string = "small"

Column{Children: []core.Widget{
Row{Children: []core.Widget{
Radio[string]{Value: "small", GroupValue: selected, OnChanged: onSelect},
Text{Content: "Small"},
}},
Row{Children: []core.Widget{
Radio[string]{Value: "medium", GroupValue: selected, OnChanged: onSelect},
Text{Content: "Medium"},
}},
Row{Children: []core.Widget{
Radio[string]{Value: "large", GroupValue: selected, OnChanged: onSelect},
Text{Content: "Large"},
}},
}}
type Radio[T comparable] struct {
core.StatelessBase

// Value is the value for this radio.
Value T
// GroupValue is the current group selection.
GroupValue T
// OnChanged is called when this radio is selected.
OnChanged func(T)
// Disabled disables interaction when true.
Disabled bool
// Size controls the radio diameter. Zero means zero size (not rendered).
Size float64
// ActiveColor is the selected inner dot color. Zero means transparent.
ActiveColor graphics.Color
// InactiveColor is the unselected border color. Zero means transparent.
InactiveColor graphics.Color
// BackgroundColor is the fill color when unselected. Zero means transparent.
BackgroundColor graphics.Color

// DisabledActiveColor is the selected inner dot color when disabled.
// If zero, falls back to 0.5 opacity on the normal colors.
DisabledActiveColor graphics.Color

// DisabledInactiveColor is the unselected border color when disabled.
// If zero, falls back to 0.5 opacity on the normal colors.
DisabledInactiveColor graphics.Color
}

Example:

This example shows a radio button group.

package main

import (
"fmt"

"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
var selectedOption string

options := []string{"Small", "Medium", "Large"}
radios := make([]core.Widget, len(options))

for i, option := range options {
opt := option // capture for closure
radios[i] = widgets.Row{
Children: []core.Widget{
widgets.Radio[string]{
Value: opt,
GroupValue: selectedOption,
OnChanged: func(value string) {
selectedOption = value
fmt.Printf("Selected: %s\n", selectedOption)
},
},
widgets.HSpace(8),
widgets.Text{Content: opt},
},
}
}
_ = radios
}

func (Radio[T]) Build

func (r Radio[T]) Build(ctx core.BuildContext) core.Widget

type RepaintBoundary

RepaintBoundary isolates its subtree into a separate paint layer. This allows the subtree to be cached and reused when it doesn't change, which can significantly improve performance for static content next to frequently animating content.

type RepaintBoundary struct {
core.RenderObjectBase
Child core.Widget
}

func (RepaintBoundary) ChildWidget

func (r RepaintBoundary) ChildWidget() core.Widget

func (RepaintBoundary) CreateRenderObject

func (r RepaintBoundary) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (RepaintBoundary) UpdateRenderObject

func (r RepaintBoundary) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type RichText

RichText displays a tree of styled text spans. Unlike Text, which applies a single style to the entire paragraph, RichText supports inline style changes (color, weight, decoration, font) within a single paragraph.

Style provides widget-level defaults (color, font size, etc.) that act as the lowest-priority base. The Content span tree's own styles override Style, and child spans override their parents as usual.

Basic usage:

widgets.RichText{
Content: graphics.Spans(
graphics.Span("Hello "),
graphics.Span("World").Bold(),
),
}.WithStyle(graphics.SpanStyle{Color: colors.OnSurface, FontSize: 16})
type RichText struct {
core.RenderObjectBase
// Content is the root span tree. Child spans inherit any style fields from
// their parent for fields left at their zero value.
Content graphics.TextSpan
// Style is the widget-level default style. Spans inherit these values for
// any zero-valued fields not already set by the span tree.
Style graphics.SpanStyle
Align graphics.TextAlign
MaxLines int
// Wrap controls text wrapping behavior. The zero value
// ([graphics.TextWrapWrap]) wraps text at the constraint width.
// Set to [graphics.TextWrapNoWrap] for single-line text.
Wrap graphics.TextWrap
}

func (RichText) CreateRenderObject

func (r RichText) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (RichText) UpdateRenderObject

func (r RichText) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

func (RichText) WithAlign

func (r RichText) WithAlign(align graphics.TextAlign) RichText

WithAlign returns a copy with the specified text alignment.

func (RichText) WithMaxLines

func (r RichText) WithMaxLines(maxLines int) RichText

WithMaxLines returns a copy with the specified maximum line count.

func (RichText) WithStyle

func (r RichText) WithStyle(style graphics.SpanStyle) RichText

WithStyle returns a copy with the given widget-level default style.

func (RichText) WithWrap

func (r RichText) WithWrap(wrap graphics.TextWrap) RichText

WithWrap returns a copy with the specified wrap mode.

type Row

Row lays out children horizontally from left to right.

Row is a flex container where the main axis is horizontal. Children are laid out in a single horizontal run and do not wrap.

Sizing Behavior

By default, Row expands to fill available horizontal space. Set MainAxisSizeMin to shrink-wrap the children instead.

Alignment

Use MainAxisAlignment to control horizontal spacing (Start, End, Center, SpaceBetween, SpaceAround, SpaceEvenly). Use CrossAxisAlignment to control vertical alignment (Start, End, Center, Stretch).

Flexible Children

Wrap children in Expanded to make them share remaining space proportionally:

Row{
Children: []core.Widget{
Text{Content: "Label"},
Expanded{Child: TextField{...}}, // Takes remaining space
Button{...},
},
}

For vertical layout, use Column.

type Row struct {
core.RenderObjectBase
Children []core.Widget
MainAxisAlignment MainAxisAlignment
CrossAxisAlignment CrossAxisAlignment
MainAxisSize MainAxisSize
}

Example:

This example shows how to create a horizontal layout with Row.

package main

import (
"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
row := widgets.Row{
Children: []core.Widget{
widgets.Text{Content: "Left"},
widgets.Text{Content: "Center"},
widgets.Text{Content: "Right"},
},
MainAxisAlignment: widgets.MainAxisAlignmentSpaceBetween,
CrossAxisAlignment: widgets.CrossAxisAlignmentCenter,
}
_ = row
}

Example (With Expanded):

This example shows how to use Expanded for flexible sizing in Row/Column.

package main

import (
"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
row := widgets.Row{
Children: []core.Widget{
// Fixed width
widgets.Container{Width: 80, Color: graphics.RGB(200, 200, 200)},
// Flexible - takes remaining space
widgets.Expanded{
Child: widgets.Container{Color: graphics.RGB(100, 149, 237)},
},
// Fixed width
widgets.Container{Width: 80, Color: graphics.RGB(200, 200, 200)},
},
}
_ = row
}

func (Row) ChildrenWidgets

func (r Row) ChildrenWidgets() []core.Widget

func (Row) CreateRenderObject

func (r Row) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (Row) UpdateRenderObject

func (r Row) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type RunAlignment

RunAlignment controls how runs are distributed along the cross axis.

type RunAlignment int
const (
// RunAlignmentStart places runs at the start of the cross axis.
RunAlignmentStart RunAlignment = iota
// RunAlignmentEnd places runs at the end of the cross axis.
RunAlignmentEnd
// RunAlignmentCenter centers runs along the cross axis.
RunAlignmentCenter
// RunAlignmentSpaceBetween distributes free space evenly between runs.
// No space before the first or after the last run.
RunAlignmentSpaceBetween
// RunAlignmentSpaceAround distributes free space evenly, with half-sized
// spaces at the start and end.
RunAlignmentSpaceAround
// RunAlignmentSpaceEvenly distributes free space evenly, including
// equal space before the first and after the last run.
RunAlignmentSpaceEvenly
)

func (RunAlignment) String

func (a RunAlignment) String() string

String returns a human-readable representation of the run alignment.

type SafeArea

SafeArea is a convenience widget that applies safe area insets as padding.

type SafeArea struct {
core.StatelessBase

Top bool
Bottom bool
Left bool
Right bool
Child core.Widget
}

func (SafeArea) Build

func (s SafeArea) Build(ctx core.BuildContext) core.Widget

type SafeAreaAspect

SafeAreaAspect identifies which safe area inset a widget depends on.

type SafeAreaAspect int
const (
SafeAreaAspectTop SafeAreaAspect = iota
SafeAreaAspectBottom
SafeAreaAspectLeft
SafeAreaAspectRight
)

type SafeAreaData

SafeAreaData provides safe area insets to descendants via InheritedWidget. It implements [core.AspectAwareInheritedWidget] for granular per-edge tracking.

type SafeAreaData struct {
core.InheritedBase
Insets layout.EdgeInsets
Child core.Widget
}

func (SafeAreaData) ChildWidget

func (s SafeAreaData) ChildWidget() core.Widget

func (SafeAreaData) ShouldRebuildDependent

func (s SafeAreaData) ShouldRebuildDependent(oldWidget core.InheritedWidget, aspects map[any]struct{}) bool

func (SafeAreaData) ShouldRebuildDependents

func (s SafeAreaData) ShouldRebuildDependents(oldWidget core.InheritedWidget) bool

type SafeAreaProvider

SafeAreaProvider is a StatefulWidget that subscribes to platform safe area changes and provides SafeAreaData to descendants. This scopes rebuilds to only the provider and widgets that depend on safe area data, instead of rebuilding the entire tree.

type SafeAreaProvider struct {
core.StatefulBase

Child core.Widget
}

func (SafeAreaProvider) CreateState

func (s SafeAreaProvider) CreateState() core.State

type ScrollController

ScrollController controls scroll position.

type ScrollController struct {
InitialScrollOffset float64
// contains filtered or unexported fields
}

func (*ScrollController) AddListener

func (c *ScrollController) AddListener(listener func()) func()

AddListener registers a callback for scroll changes.

func (*ScrollController) AnimateTo

func (c *ScrollController) AnimateTo(offset float64, _ time.Duration)

AnimateTo moves to a new offset immediately (placeholder for animations).

func (*ScrollController) JumpTo

func (c *ScrollController) JumpTo(offset float64)

JumpTo moves all attached positions to a new offset.

func (*ScrollController) Offset

func (c *ScrollController) Offset() float64

Offset returns the current scroll offset.

func (*ScrollController) ViewportExtent

func (c *ScrollController) ViewportExtent() float64

ViewportExtent returns the current viewport extent.

type ScrollPhysics

ScrollPhysics determines scroll behavior at content boundaries.

Built-in implementations:

Custom implementations must return true from AllowsOverscroll to enable overscroll and spring-back animation.

type ScrollPhysics interface {
ApplyPhysicsToUserOffset(position *ScrollPosition, offset float64) float64
ApplyBoundaryConditions(position *ScrollPosition, value float64) float64
// AllowsOverscroll reports whether scrolling past content boundaries is
// permitted. When true, the scroll view applies resistance during drag
// and uses a spring animation to return to bounds on release.
AllowsOverscroll() bool
}

type ScrollPosition

ScrollPosition stores the current scroll offset and extents.

type ScrollPosition struct {
// contains filtered or unexported fields
}

func NewScrollPosition

func NewScrollPosition(controller *ScrollController, physics ScrollPhysics, onUpdate func()) *ScrollPosition

NewScrollPosition creates a new scroll position.

func (*ScrollPosition) ApplyUserOffset

func (p *ScrollPosition) ApplyUserOffset(delta float64)

ApplyUserOffset applies a drag delta with physics.

func (*ScrollPosition) Offset

func (p *ScrollPosition) Offset() float64

Offset returns the current scroll offset.

func (*ScrollPosition) SetExtents

func (p *ScrollPosition) SetExtents(min, max float64)

SetExtents updates the min/max scroll extents.

func (*ScrollPosition) SetOffset

func (p *ScrollPosition) SetOffset(value float64)

SetOffset updates the scroll offset.

func (*ScrollPosition) StartBallistic

func (p *ScrollPosition) StartBallistic(velocity float64)

StartBallistic begins inertial scrolling with the provided velocity.

func (*ScrollPosition) StopBallistic

func (p *ScrollPosition) StopBallistic()

StopBallistic halts any ongoing inertial scroll.

type ScrollView

ScrollView provides scrollable content in a single direction.

ScrollView wraps a single child widget and enables scrolling when the child exceeds the viewport. It supports both vertical (default) and horizontal scrolling via ScrollDirection.

Scroll Physics

The Physics field controls scroll behavior:

Scroll Controller

Use a ScrollController to programmatically control or observe scroll position:

controller := &widgets.ScrollController{}
controller.AddListener(func() {
fmt.Println("Offset:", controller.Offset())
})

Safe Area Handling

Use SafeAreaPadding for proper inset handling on devices with notches:

ScrollView{
Padding: widgets.SafeAreaPadding(ctx).Add(24),
Child: content,
}

For scrollable lists, consider ListView or ListViewBuilder which provide additional features like item-based layout and virtualization.

type ScrollView struct {
core.StatelessBase

Child core.Widget
// ScrollDirection is the axis along which the view scrolls.
// Defaults to AxisVertical (the zero value).
ScrollDirection Axis
Controller *ScrollController
Physics ScrollPhysics
Padding layout.EdgeInsets
}

Example:

This example shows how to create scrollable content.

package main

import (
"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/layout"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
scrollView := widgets.ScrollView{
Child: widgets.Column{
Children: []core.Widget{
widgets.SizedBox{Height: 1000, Child: widgets.Text{Content: "Tall content"}},
},
},
ScrollDirection: widgets.AxisVertical,
Physics: widgets.BouncingScrollPhysics{},
Padding: layout.EdgeInsetsAll(16),
}
_ = scrollView
}

Example (With Controller):

This example shows how to use a scroll controller for programmatic scrolling.

package main

import (
"time"

"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
// Create a scroll controller to programmatically control scroll position
controller := &widgets.ScrollController{}

scrollView := widgets.ScrollView{
Controller: controller,
Child: widgets.Column{
Children: []core.Widget{
widgets.SizedBox{Height: 2000, Child: widgets.Text{Content: "Long content"}},
},
},
}

// Jump to a specific position
controller.JumpTo(500)

// Or animate the scroll
controller.AnimateTo(1000, 300*time.Millisecond)

_ = scrollView
}

func (ScrollView) Build

func (s ScrollView) Build(ctx core.BuildContext) core.Widget

type Semantics

Semantics is a widget that annotates the widget tree with semantics information for accessibility services.

type Semantics struct {
core.RenderObjectBase
// Child is the child widget to annotate.
Child core.Widget

// Label is the primary accessibility label for this node.
Label string

// Value is the current value (e.g., slider position, text content).
Value string

// Hint provides guidance on the action that will occur.
Hint string

// Tooltip provides additional information shown on hover/long press.
Tooltip string

// Role defines the semantic role of the node.
Role semantics.SemanticsRole

// Flags contains boolean state flags.
Flags semantics.SemanticsFlag

// Container indicates this node creates a semantic boundary.
Container bool

// MergeDescendants merges labels from descendant nodes into this node.
// Use this when a widget contains multiple text elements that should be
// announced as a single unit (e.g., a card with title and subtitle).
MergeDescendants bool

// ExplicitChildNodes indicates whether child nodes should be explicit.
ExplicitChildNodes bool

// CurrentValue for slider-type controls.
CurrentValue *float64

// MinValue for slider-type controls.
MinValue *float64

// MaxValue for slider-type controls.
MaxValue *float64

// HeadingLevel indicates heading level (1-6, 0 for none).
HeadingLevel int

// OnTap is the handler for tap/click actions.
OnTap func()

// OnLongPress is the handler for long press actions.
OnLongPress func()

// OnScrollLeft is the handler for scroll left actions.
OnScrollLeft func()

// OnScrollRight is the handler for scroll right actions.
OnScrollRight func()

// OnScrollUp is the handler for scroll up actions.
OnScrollUp func()

// OnScrollDown is the handler for scroll down actions.
OnScrollDown func()

// OnIncrease is the handler for increase actions.
OnIncrease func()

// OnDecrease is the handler for decrease actions.
OnDecrease func()

// OnDismiss is the handler for dismiss actions.
OnDismiss func()

// CustomActions is a list of custom accessibility actions.
CustomActions []semantics.CustomSemanticsAction

// CustomActionHandlers maps custom action IDs to handlers.
CustomActionHandlers map[int64]func()
}

func SemanticGroup

func SemanticGroup(child core.Widget) Semantics

SemanticGroup groups related widgets into a single accessibility unit. The screen reader will read all children as one combined announcement.

Example:

SemanticGroup(widgets.Row{Children: []core.Widget{icon, priceText, currencyText}})

Example:

This example shows how to group widgets for accessibility.

package main

import (
"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
// Groups related widgets so screen reader announces them together
group := widgets.SemanticGroup(
widgets.Row{
Children: []core.Widget{
widgets.Text{Content: "$"},
widgets.Text{Content: "99"},
widgets.Text{Content: ".99"},
},
},
)
_ = group
}

func SemanticHeading

func SemanticHeading(level int, child core.Widget) Semantics

SemanticHeading marks a widget as a heading at the specified level (1-6). Screen readers use headings for navigation.

Example:

SemanticHeading(1, widgets.Text{Content: "Welcome"})

Example:

This example shows how to create an accessible heading.

package main

import (
"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
// Screen readers use headings for navigation
heading := widgets.SemanticHeading(
1, // heading level 1-6
widgets.Text{
Content: "Welcome",
Style: graphics.TextStyle{FontSize: 32, FontWeight: 700},
},
)
_ = heading
}

func SemanticImage

func SemanticImage(description string, child core.Widget) Semantics

SemanticImage marks a widget as an image with the given description. Use this for meaningful images that convey information.

Example:

SemanticImage("Chart showing sales growth", chartWidget)

Example:

This example shows how to mark an image for accessibility.

package main

import (
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
// Marks a widget as an image with a description
img := widgets.SemanticImage(
"Chart showing sales growth over Q4",
widgets.Image{Width: 300, Height: 200},
)
_ = img
}

func SemanticLabel

func SemanticLabel(label string, child core.Widget) Semantics

SemanticLabel wraps a child with an accessibility label. Use this to provide a description for widgets that don't have built-in semantics.

Example:

SemanticLabel("Company logo", logoImage)

Example:

This example shows how to add an accessibility label.

package main

import (
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
// Provides a description for widgets without built-in semantics
labeled := widgets.SemanticLabel(
"Company logo",
widgets.Image{Width: 100, Height: 100},
)
_ = labeled
}
func SemanticLink(label string, onTap func(), child core.Widget) Semantics

SemanticLink marks a widget as a link with the given label.

Example:

SemanticLink("Visit our website", func() { openURL() }, linkText)

Example:

This example shows how to create an accessible link.

package main

import (
"fmt"

"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
link := widgets.SemanticLink(
"Visit our website",
func() { fmt.Println("Opening website...") },
widgets.Text{
Content: "www.example.com",
Style: graphics.TextStyle{Color: graphics.RGB(33, 150, 243)},
},
)
_ = link
}

func SemanticLiveRegion

func SemanticLiveRegion(child core.Widget) Semantics

SemanticLiveRegion marks a widget as a live region that announces changes. Use this for content that updates dynamically (e.g., status messages, timers).

Example:

SemanticLiveRegion(statusText)

Example:

This example shows a live region for dynamic announcements.

package main

import (
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
// Content changes are announced to screen readers
liveRegion := widgets.SemanticLiveRegion(
widgets.Text{Content: "3 items in cart"},
)
_ = liveRegion
}

func Tappable

func Tappable(label string, onTap func(), child core.Widget) Semantics

Tappable creates an accessible tappable widget with the given label. This is the accessible version of Tap() - use this when the tappable element should be accessible to screen readers.

Example:

Tappable("Submit form", func() { submit() }, myButton)

Example:

This example shows an accessible tappable widget.

package main

import (
"fmt"

"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/layout"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
// Tappable wraps a child with tap handling AND accessibility semantics
tappable := widgets.Tappable(
"Submit form", // accessibility label
func() { fmt.Println("Submitted!") },
widgets.Container{
Padding: layout.EdgeInsetsAll(16),
Color: graphics.RGB(33, 150, 243),
Child: widgets.Text{Content: "Submit"},
},
)
_ = tappable
}

func TappableWithHint

func TappableWithHint(label, hint string, onTap func(), child core.Widget) Semantics

TappableWithHint creates an accessible tappable widget with custom label and hint.

func (Semantics) ChildWidget

func (s Semantics) ChildWidget() core.Widget

ChildWidget returns the child widget.

func (Semantics) CreateRenderObject

func (s Semantics) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (Semantics) UpdateRenderObject

func (s Semantics) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type SemanticsGestureHandler

SemanticsGestureHandler wraps gestures with accessibility semantics. This is used internally by widgets to connect gestures with semantics actions.

type SemanticsGestureHandler struct {
TapHandler *gestures.TapGestureRecognizer
OnTap func()
OnLongPress func()
}

func (*SemanticsGestureHandler) BuildSemanticsActions

func (h *SemanticsGestureHandler) BuildSemanticsActions() *semantics.SemanticsActions

BuildSemanticsActions creates semantics actions from the gesture handlers.

type SizedBox

SizedBox constrains its child to a specific width and/or height.

When both Width and Height are set, SizedBox forces those exact dimensions (constrained by parent). When only one dimension is set, the other uses the child's intrinsic size.

Common uses:

// Fixed-size box
SizedBox{Width: 100, Height: 50, Child: child}

// Horizontal spacer in a Row
SizedBox{Width: 16}

// Vertical spacer in a Column
SizedBox{Height: 24}

// Force child to specific width only
SizedBox{Width: 200, Child: textField}

For convenience, use HSpace and VSpace helper functions for spacers.

type SizedBox struct {
core.RenderObjectBase
Width float64
Height float64
Child core.Widget
}

Example:

This example shows fixed-size boxes.

package main

import (
"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
// Fixed dimensions
box := widgets.SizedBox{
Width: 100,
Height: 50,
Child: widgets.Container{Color: graphics.RGB(200, 200, 200)},
}
_ = box
}

Example (Spacer):

This example shows SizedBox as a spacer.

package main

import (
"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
column := widgets.Column{
Children: []core.Widget{
widgets.Text{Content: "Top"},
widgets.SizedBox{Height: 16}, // Vertical spacer
widgets.Text{Content: "Bottom"},
},
}

row := widgets.Row{
Children: []core.Widget{
widgets.Text{Content: "Left"},
widgets.SizedBox{Width: 24}, // Horizontal spacer
widgets.Text{Content: "Right"},
},
}

_ = column
_ = row
}

func HSpace

func HSpace(width float64) SizedBox

HSpace creates a fixed-width horizontal spacer.

func VSpace

func VSpace(height float64) SizedBox

VSpace creates a fixed-height vertical spacer.

func (SizedBox) ChildWidget

func (s SizedBox) ChildWidget() core.Widget

func (SizedBox) CreateRenderObject

func (s SizedBox) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (SizedBox) UpdateRenderObject

func (s SizedBox) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type SnapBehavior

SnapBehavior configures snapping and dismissal thresholds.

type SnapBehavior struct {
// DismissFactor is the fraction of the minimum snap height below which a drag-down can dismiss.
DismissFactor float64
// MinFlingVelocity is the velocity (px/s) that triggers fast dismiss when dragging downward.
MinFlingVelocity float64
// SnapVelocityThreshold is the velocity (px/s) above which we snap in the direction of travel.
SnapVelocityThreshold float64
}

func DefaultSnapBehavior

func DefaultSnapBehavior() SnapBehavior

DefaultSnapBehavior returns recommended defaults.

type SnapPoint

SnapPoint defines a height position where a bottom sheet can snap. FractionalHeight is relative to available height (screen height minus safe area insets).

type SnapPoint struct {
FractionalHeight float64
Name string
}

func NormalizeSnapPoints

func NormalizeSnapPoints(points []SnapPoint) []SnapPoint

NormalizeSnapPoints validates and normalizes snap points. It clamps values to [0.1, 1.0], sorts ascending, and removes duplicates.

type Stack

Stack overlays children on top of each other.

Children are painted in order, with the first child at the bottom and the last child on top. Hit testing proceeds in reverse (topmost first).

Sizing Behavior

With StackFitLoose (default), the Stack sizes itself to fit the largest child. With StackFitExpand, children are forced to fill the available space.

Positioning Children

Non-positioned children use the Alignment to determine their position. For absolute positioning, wrap children in [Positioned]:

Stack{
Children: []core.Widget{
// Background fills the stack
Container{Color: bgColor},
// Badge in top-right corner
Positioned(badge).Top(8).Right(8),
},
}
type Stack struct {
core.RenderObjectBase
// Children are the widgets to overlay. First child is at the bottom,
// last child is on top.
Children []core.Widget
// Alignment positions non-Positioned children within the stack.
// Defaults to top-left (AlignmentTopLeft).
Alignment layout.Alignment
// Fit controls how children are sized.
Fit StackFit
}

Example:

This example shows how to create a stack with overlapping children.

package main

import (
"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/layout"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
stack := widgets.Stack{
Children: []core.Widget{
// Background
widgets.Container{
Color: graphics.RGB(200, 200, 200),
Width: 200,
Height: 200,
},
// Foreground centered via Alignment
widgets.Container{
Color: graphics.RGB(100, 149, 237),
Width: 100,
Height: 100,
},
},
Alignment: layout.AlignmentCenter,
}
_ = stack
}

Example (With Positioned):

This example shows a Stack with Positioned children for absolute positioning.

package main

import (
"github.com/go-drift/drift/pkg/core"
"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/layout"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
stack := widgets.Stack{
Children: []core.Widget{
// Background fills the stack
widgets.Container{
Color: graphics.RGB(240, 240, 240),
Width: 300,
Height: 200,
},
// Badge in top-right corner
widgets.Positioned(widgets.Container{
Color: graphics.RGB(255, 0, 0),
Width: 20,
Height: 20,
Padding: layout.EdgeInsetsAll(4),
}).Top(8).Right(8),
// Bottom toolbar stretching horizontally
widgets.Positioned(widgets.Container{
Color: graphics.RGB(33, 33, 33),
Height: 48,
}).Left(0).Right(0).Bottom(0),
},
}
_ = stack
}

func StackOf

func StackOf(children ...core.Widget) Stack

StackOf creates a stack with the given children. This is a convenience helper for the common case of creating a Stack with children. Children are layered with the first child at the bottom and last child on top.

func (Stack) ChildrenWidgets

func (s Stack) ChildrenWidgets() []core.Widget

ChildrenWidgets returns the child widgets.

func (Stack) CreateRenderObject

func (s Stack) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

CreateRenderObject creates the RenderStack.

func (Stack) UpdateRenderObject

func (s Stack) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

UpdateRenderObject updates the RenderStack.

type StackFit

StackFit determines how children are sized within a Stack.

type StackFit int
const (
// StackFitLoose allows children to size themselves.
StackFitLoose StackFit = iota
// StackFitExpand forces children to fill the stack.
StackFitExpand
)

func (StackFit) String

func (f StackFit) String() string

String returns a human-readable representation of the stack fit.

type SvgIcon

SvgIcon renders an SVG icon within a square bounding box. For rectangular bounds or more control over sizing, use SvgImage instead.

The icon scales to fit within the square bounds while preserving aspect ratio (contain behavior). If the SVG's viewBox is not square, it will be centered within the square bounds.

See SvgImage documentation for lifetime rules and CSS styling limitations.

Example:

widgets.SvgIcon{
Source: icon,
Size: 24,
}
type SvgIcon struct {
core.StatelessBase
// Source is the pre-loaded SVG icon to render.
Source *svg.Icon
// Size is the width and height for the icon.
// If zero, uses the SVG's intrinsic viewBox size.
Size float64
// TintColor replaces all SVG colors with this color while preserving alpha.
// Zero (ColorTransparent) means no tinting - original colors are preserved.
// Note: Tinting affects ALL content including gradients and embedded images.
TintColor graphics.Color
// SemanticLabel provides an accessibility description.
SemanticLabel string
// ExcludeFromSemantics excludes from the semantics tree when true.
ExcludeFromSemantics bool
}

func (SvgIcon) Build

func (s SvgIcon) Build(_ core.BuildContext) core.Widget

type SvgImage

SvgImage renders an SVG from a loaded [svg.Icon].

The SVG is rendered using Skia's native SVG DOM, which supports gradients, filters, transforms, and most SVG features.

Creation Pattern

Use struct literal:

icon, _ := svg.LoadFile("icon.svg")
widgets.SvgImage{
Source: icon,
Width: 100,
Height: 50,
SemanticLabel: "Logo",
}

Sizing Behavior

The SVG always scales to fill the widget bounds, respecting preserveAspectRatio (default: contain, centered). When Width and/or Height are specified, the SVG scales to fit. When both are zero, the widget uses the SVG's viewBox dimensions, and the SVG scales to match (visually equivalent to intrinsic sizing).

CSS Styling Limitation

Skia's SVG DOM does not fully support CSS class-based styling. SVGs that use <style> blocks with class selectors (e.g., .st0{fill:#FF0000}) will render with default colors (typically black). This is common in SVGs exported from Adobe Illustrator.

To fix, convert CSS classes to inline presentation attributes using a tool like svgo (https://github.com/nicolo-ribaudo/svgo-browser or npm svgo):

svgo --config '{"plugins":[{"name":"inlineStyles","params":{"onlyMatchedOnce":false}}]}' input.svg -o output.svg

Lifetime

The Source must remain valid for as long as any widget or display list references it. Do not call [svg.Icon.Destroy] while widgets may still render the icon.

type SvgImage struct {
core.RenderObjectBase
// Source is the pre-loaded SVG to render.
Source *svg.Icon

// Width is the desired width. If zero and Height is set, calculated from aspect ratio.
// If both zero, uses the SVG's intrinsic viewBox width.
Width float64

// Height is the desired height. If zero and Width is set, calculated from aspect ratio.
// If both zero, uses the SVG's intrinsic viewBox height.
Height float64

// PreserveAspectRatio controls how the SVG scales within its bounds.
// If nil, uses the SVG's intrinsic preserveAspectRatio (default: contain, centered).
// Note: Setting this mutates the Source Icon.
PreserveAspectRatio *svg.PreserveAspectRatio

// TintColor replaces all SVG colors with this color while preserving alpha.
// Zero (ColorTransparent) means no tinting - original colors are preserved.
// Note: Tinting affects ALL content including gradients and embedded images.
TintColor graphics.Color

// SemanticLabel provides an accessibility description.
SemanticLabel string

// ExcludeFromSemantics excludes from the semantics tree when true.
ExcludeFromSemantics bool
}

func (SvgImage) Child

func (s SvgImage) Child() core.Widget

func (SvgImage) CreateRenderObject

func (s SvgImage) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (SvgImage) UpdateRenderObject

func (s SvgImage) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type Switch

Switch is a toggle control that uses native platform components (UISwitch on iOS, SwitchCompat on Android).

Switch is a controlled component - it displays the Value you provide and calls OnChanged when toggled. To change the switch state, update Value in your state in response to OnChanged.

Creation Pattern

Use struct literal (no themed constructor exists for native Switch):

widgets.Switch{
Value: s.notificationsEnabled,
OnChanged: func(enabled bool) {
s.SetState(func() { s.notificationsEnabled = enabled })
},
OnTintColor: colors.Primary, // optional
}

The native implementation provides platform-appropriate animations and haptic feedback automatically.

For a Drift-rendered toggle with full styling control, use Toggle instead.

type Switch struct {
core.StatefulBase

// Value indicates the current on/off state.
Value bool
// OnChanged is called when the switch toggles.
OnChanged func(bool)
// Disabled disables interaction when true.
Disabled bool
// OnTintColor is the track color when on (optional).
OnTintColor graphics.Color
// ThumbColor is the thumb color (optional).
ThumbColor graphics.Color
}

Example:

This example shows a toggle switch.

package main

import (
"fmt"

"github.com/go-drift/drift/pkg/widgets"
)

func main() {
var isEnabled bool

toggle := widgets.Switch{
Value: isEnabled,
OnChanged: func(value bool) {
isEnabled = value
fmt.Printf("Switch is now: %v\n", isEnabled)
},
}
_ = toggle
}

func (Switch) CreateState

func (s Switch) CreateState() core.State

type TabBar

TabBar displays a row of tabs.

Styling Model

TabBar is explicit by default — all visual properties use their struct field values directly. A zero value means zero, not "use theme default." For example:

  • BackgroundColor: 0 means transparent background
  • Height: 0 means zero height (not rendered)
  • IndicatorHeight: 0 means no indicator

For theme-styled tab bars, use [theme.TabBarOf] which pre-fills visual properties from the current theme's [theme.TabBarThemeData].

Creation Patterns

Explicit with struct literal (full control):

widgets.TabBar{
Items: []widgets.TabItem{{Label: "Home"}, {Label: "Search"}},
CurrentIndex: 0,
OnTap: func(i int) { s.SetState(func() { s.currentIndex = i }) },
BackgroundColor: graphics.RGB(33, 33, 33),
ActiveColor: graphics.ColorWhite,
InactiveColor: graphics.RGBA(255, 255, 255, 0.6),
IndicatorColor: graphics.RGB(33, 150, 243),
Height: 56,
}

Themed (reads from current theme):

theme.TabBarOf(ctx, items, currentIndex, onTap)
// Pre-filled with theme colors, height, padding, indicator
type TabBar struct {
core.StatelessBase

// Items are the tab entries.
Items []TabItem
// CurrentIndex is the selected tab index.
CurrentIndex int
// OnTap is called when a tab is tapped.
OnTap func(index int)
// BackgroundColor is the bar background. Zero means transparent.
BackgroundColor graphics.Color
// ActiveColor is the selected tab text/icon color. Zero means transparent.
ActiveColor graphics.Color
// InactiveColor is the unselected tab text/icon color. Zero means transparent.
InactiveColor graphics.Color
// IndicatorColor is the selected tab indicator color. Zero means transparent.
IndicatorColor graphics.Color
// IndicatorHeight is the indicator bar height. Zero means no indicator.
IndicatorHeight float64
// Padding is the internal padding. Zero means no padding.
Padding layout.EdgeInsets
// Height is the bar height. Zero means zero height (not rendered).
Height float64
// LabelStyle is the text style for labels.
LabelStyle graphics.TextStyle
}

func (TabBar) Build

func (t TabBar) Build(ctx core.BuildContext) core.Widget

type TabItem

TabItem describes a single tab entry.

type TabItem struct {
Label string
Icon core.Widget
}

type Text

Text displays a string with a single style.

Creation Patterns

Struct literal (full control):

widgets.Text{
Content: "Hello, Drift",
Style: graphics.TextStyle{Color: colors.OnSurface, FontSize: 16},
}

Using text styles from theme:

_, _, textTheme := theme.UseTheme(ctx)
widgets.Text{Content: "Title", Style: textTheme.HeadlineLarge}

Themed (using [theme.TextOf]):

theme.TextOf(ctx, "Welcome", textTheme.HeadlineMedium)

Text Wrapping, Line Limits, and Alignment

The Wrap, MaxLines, and Align fields control how text flows, truncates, and aligns:

  • Wrap=TextWrapWrap (default zero value): Text wraps at the constraint width, creating multiple lines. Use for paragraphs, descriptions, and content that should fit a container.

  • Wrap=TextWrapNoWrap: Text renders on a single line, extending beyond the constraint width. Use for labels, buttons, and short text.

  • MaxLines: Limits the number of visible lines. When text wraps and exceeds MaxLines, it truncates. When MaxLines=0 (default), no limit applies.

  • Align: Controls horizontal alignment of lines within the paragraph. Alignment only takes effect when text wraps, because unwrapped text has no paragraph width to align within. Use Text.WithAlign for chaining.

Common patterns:

// Wrapping paragraph (default)
Text{Content: longText}

// Single line, may overflow
Text{Content: "Label", Wrap: graphics.TextWrapNoWrap}

// Preview text limited to 2 lines
Text{Content: description, MaxLines: 2}

// Centered wrapping text
Text{Content: longText, Align: graphics.TextAlignCenter}
type Text struct {
core.RenderObjectBase
// Content is the text string to display.
Content string
// Style controls the font, size, color, and other text properties.
Style graphics.TextStyle
// Align controls paragraph-level horizontal text alignment.
// Zero value is left-aligned. Only takes effect when text wraps;
// unwrapped text has no paragraph width to align within.
Align graphics.TextAlign
// MaxLines limits the number of visible lines (0 = unlimited).
// Lines beyond this limit are not rendered.
MaxLines int
// Wrap controls text wrapping behavior. The zero value
// ([graphics.TextWrapWrap]) wraps text at the constraint width.
// Set to [graphics.TextWrapNoWrap] for single-line text.
Wrap graphics.TextWrap
}

Example:

This example shows how to display styled text.

package main

import (
"github.com/go-drift/drift/pkg/graphics"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
text := widgets.Text{
Content: "Hello, Drift!",
Style: graphics.TextStyle{
FontSize: 24,
Color: graphics.RGB(33, 33, 33),
},
MaxLines: 2,
}
_ = text
}

func (Text) CreateRenderObject

func (t Text) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (Text) UpdateRenderObject

func (t Text) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

func (Text) WithAlign

func (t Text) WithAlign(align graphics.TextAlign) Text

WithAlign returns a copy of the text with the specified alignment. Alignment only takes effect when text wraps. See [graphics.TextAlign] for the available alignment options.

func (Text) WithMaxLines

func (t Text) WithMaxLines(maxLines int) Text

WithMaxLines returns a copy of the text with the specified max lines limit.

func (Text) WithStyle

func (t Text) WithStyle(style graphics.TextStyle) Text

WithStyle returns a copy of the text with the specified style.

func (Text) WithWrap

func (t Text) WithWrap(wrap graphics.TextWrap) Text

WithWrap returns a copy of the text with the specified wrap mode.

type TextField

TextField wraps TextInput and adds support for labels, helper text, and error display.

Styling Model

TextField is explicit by default — all visual properties use their struct field values directly. A zero value means zero, not "use theme default." For example:

  • BackgroundColor: 0 means transparent
  • BorderColor: 0 means no border
  • Height: 0 means zero height (invisible)
  • Style.FontSize: 0 means no text rendered

For theme-styled text fields, use [theme.TextFieldOf] which pre-fills visual properties from the current theme's [theme.TextFieldThemeData].

Creation Patterns

Struct literal (full control):

widgets.TextField{
Controller: controller,
Label: "Email",
Placeholder: "you@example.com",
BackgroundColor: graphics.ColorWhite,
BorderColor: graphics.RGB(200, 200, 200),
FocusColor: graphics.RGB(0, 122, 255),
Height: 48,
Padding: layout.EdgeInsetsSymmetric(12, 10),
BorderWidth: 1,
Style: graphics.TextStyle{FontSize: 16, Color: graphics.ColorBlack},
PlaceholderColor: graphics.RGB(150, 150, 150),
}

Themed (reads from current theme):

theme.TextFieldOf(ctx, controller).
WithLabel("Email").
WithPlaceholder("you@example.com")

For form validation support, use TextFormField instead, which wraps TextField and integrates with Form for validation, save, and reset operations.

type TextField struct {
core.StatelessBase

// Controller manages the text content and selection.
Controller *platform.TextEditingController
// Label is shown above the field.
Label string
// Placeholder text shown when empty.
Placeholder string
// HelperText is shown below the field when no error.
HelperText string
// ErrorText is shown below the field when non-empty.
ErrorText string
// KeyboardType specifies the keyboard to show.
KeyboardType platform.KeyboardType
// InputAction specifies the keyboard action button.
InputAction platform.TextInputAction
// Obscure hides the text (for passwords).
Obscure bool
// Autocorrect enables auto-correction.
Autocorrect bool
// OnChanged is called when the text changes.
OnChanged func(string)
// OnSubmitted is called when the user submits.
OnSubmitted func(string)
// OnEditingComplete is called with the current text when editing is complete.
OnEditingComplete func(string)
// Disabled controls whether the field rejects input.
Disabled bool
// Width of the text field. Zero expands to fill available width.
Width float64
// Height of the text field. Zero means zero height (invisible).
Height float64
// Padding inside the text field. Zero means no padding.
Padding layout.EdgeInsets
// BackgroundColor of the text field. Zero means transparent.
BackgroundColor graphics.Color
// BorderColor of the text field. Zero means no border.
BorderColor graphics.Color
// FocusColor of the text field outline when focused. Zero means no focus highlight.
FocusColor graphics.Color
// BorderRadius for rounded corners. Zero means sharp corners.
BorderRadius float64
// BorderWidth for the border stroke. Zero means no border.
BorderWidth float64
// Style for the text content. Zero FontSize means no text rendered.
Style graphics.TextStyle
// PlaceholderColor for the placeholder text. Zero means transparent.
PlaceholderColor graphics.Color
// LabelStyle for the label text above the field.
LabelStyle graphics.TextStyle
// HelperStyle for helper/error text below the field.
HelperStyle graphics.TextStyle
// ErrorColor for error text and border when ErrorText is set. Zero means no error styling.
ErrorColor graphics.Color

// Input is an optional escape hatch for accessing TextInput fields not
// exposed by TextField. TextField's own fields ALWAYS overwrite the
// corresponding Input fields (even with zero values), so Input is only
// useful for fields that TextField does not expose (e.g., future fields).
// To set Controller, Placeholder, etc., use TextField's fields directly.
Input *TextInput
}

Example:

This example shows a styled text input.

package main

import (
"github.com/go-drift/drift/pkg/platform"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
controller := platform.NewTextEditingController("")

textField := widgets.TextField{
Controller: controller,
Label: "Email",
Placeholder: "you@example.com",
HelperText: "We'll never share your email",
KeyboardType: platform.KeyboardTypeEmail,
}
_ = textField
}

func (TextField) Build

func (t TextField) Build(ctx core.BuildContext) core.Widget

func (TextField) WithAutocorrect

func (t TextField) WithAutocorrect(autocorrect bool) TextField

WithAutocorrect returns a copy with the specified auto-correction setting.

func (TextField) WithBackgroundColor

func (t TextField) WithBackgroundColor(c graphics.Color) TextField

WithBackgroundColor returns a copy with the specified background color.

func (TextField) WithBorderColor

func (t TextField) WithBorderColor(c graphics.Color) TextField

WithBorderColor returns a copy with the specified border color.

func (TextField) WithBorderRadius

func (t TextField) WithBorderRadius(radius float64) TextField

WithBorderRadius returns a copy with the specified corner radius.

func (TextField) WithBorderWidth

func (t TextField) WithBorderWidth(width float64) TextField

WithBorderWidth returns a copy with the specified border stroke width.

func (TextField) WithDisabled

func (t TextField) WithDisabled(disabled bool) TextField

WithDisabled returns a copy with the specified disabled state.

func (TextField) WithFocusColor

func (t TextField) WithFocusColor(c graphics.Color) TextField

WithFocusColor returns a copy with the specified focus outline color.

func (TextField) WithHeight

func (t TextField) WithHeight(height float64) TextField

WithHeight returns a copy with the specified height.

func (TextField) WithHelperText

func (t TextField) WithHelperText(helper string) TextField

WithHelperText returns a copy with the specified helper text.

func (TextField) WithInputAction

func (t TextField) WithInputAction(action platform.TextInputAction) TextField

WithInputAction returns a copy with the specified input action button.

func (TextField) WithKeyboardType

func (t TextField) WithKeyboardType(kt platform.KeyboardType) TextField

WithKeyboardType returns a copy with the specified keyboard type.

func (TextField) WithLabel

func (t TextField) WithLabel(label string) TextField

WithLabel returns a copy with the specified label text.

func (TextField) WithObscure

func (t TextField) WithObscure(obscure bool) TextField

WithObscure returns a copy with the specified obscure setting.

func (TextField) WithOnChanged

func (t TextField) WithOnChanged(fn func(string)) TextField

WithOnChanged returns a copy with the specified text-change callback.

func (TextField) WithOnEditingComplete

func (t TextField) WithOnEditingComplete(fn func(string)) TextField

WithOnEditingComplete returns a copy with the specified editing-complete callback.

func (TextField) WithOnSubmitted

func (t TextField) WithOnSubmitted(fn func(string)) TextField

WithOnSubmitted returns a copy with the specified submit callback.

func (TextField) WithPadding

func (t TextField) WithPadding(padding layout.EdgeInsets) TextField

WithPadding returns a copy with the specified internal padding.

func (TextField) WithPlaceholder

func (t TextField) WithPlaceholder(placeholder string) TextField

WithPlaceholder returns a copy with the specified placeholder text.

func (TextField) WithPlaceholderColor

func (t TextField) WithPlaceholderColor(c graphics.Color) TextField

WithPlaceholderColor returns a copy with the specified placeholder text color.

type TextFormField

TextFormField is a form-aware text input that wraps TextField and integrates with Form for validation, save, and reset operations.

TextFormField automatically registers with the nearest ancestor Form widget and participates in form-wide validation, save, and reset operations. It manages its own internal controller if none is provided.

Validation behavior:

  • When Autovalidate is true on the field, or on the parent Form, the Validator function is called whenever the field value changes after user interaction.
  • Disabled fields skip validation entirely.
  • Call FormState.Validate() to validate all fields at once (e.g., on submit).

Controller vs InitialValue:

  • If Controller is provided, it is the source of truth and InitialValue is ignored.
  • If no Controller is provided, TextFormField creates an internal controller initialized with InitialValue.

Example:

Form{
Child: Column{
Children: []core.Widget{
TextFormField{
Label: "Username",
Placeholder: "Enter username",
Validator: func(value string) string {
if len(value) < 3 {
return "Username must be at least 3 characters"
}
return ""
},
OnSaved: func(value string) {
// Called when FormState.Save() is invoked
},
},
},
},
}
type TextFormField struct {
core.StatefulBase

// TextField provides styling defaults. If set, its styling properties are used
// as a base, with individual properties below taking precedence if non-zero.
// This enables: TextFormField{TextField: theme.TextFieldOf(ctx, nil), ...}
TextField TextField

// Controller manages the text content and selection.
// If provided, it is the source of truth and InitialValue is ignored.
Controller *platform.TextEditingController

// InitialValue is the field's starting value when no Controller is provided.
InitialValue string

// Validator returns an error message or empty string if valid.
Validator func(string) string

// OnSaved is called when the form is saved.
OnSaved func(string)

// OnChanged is called when the field value changes.
OnChanged func(string)

// Autovalidate enables validation when the value changes.
Autovalidate bool

// Label is shown above the field.
Label string

// Placeholder text shown when empty.
Placeholder string

// HelperText is shown below the field when no error.
HelperText string

// KeyboardType specifies the keyboard to show.
KeyboardType platform.KeyboardType

// InputAction specifies the keyboard action button.
InputAction platform.TextInputAction

// Obscure hides the text (for passwords).
Obscure bool

// Autocorrect enables auto-correction.
Autocorrect bool

// OnSubmitted is called when the user submits.
OnSubmitted func(string)

// OnEditingComplete is called with the current text when editing is complete.
OnEditingComplete func(string)

// Disabled controls whether the field rejects input and validation.
Disabled bool

// Width of the text field (0 = expand to fill).
Width float64

// Height of the text field.
Height float64

// Padding inside the text field.
Padding layout.EdgeInsets

// BackgroundColor of the text field.
BackgroundColor graphics.Color

// BorderColor of the text field.
BorderColor graphics.Color

// FocusColor of the text field outline.
FocusColor graphics.Color

// BorderRadius for rounded corners.
BorderRadius float64

// Style for the text.
Style graphics.TextStyle

// PlaceholderColor for the placeholder text.
PlaceholderColor graphics.Color

// LabelStyle for the label text above the field.
LabelStyle graphics.TextStyle

// HelperStyle for helper/error text below the field.
HelperStyle graphics.TextStyle

// ErrorColor for error text and border when validation fails.
ErrorColor graphics.Color
}

Example:

This example shows a form field with validation.

package main

import (
"fmt"

"github.com/go-drift/drift/pkg/widgets"
)

func main() {
textFormField := widgets.TextFormField{
Label: "Username",
Placeholder: "Enter username",
Validator: func(value string) string {
if len(value) < 3 {
return "Username must be at least 3 characters"
}
return ""
},
OnSaved: func(value string) {
fmt.Printf("Saved username: %s\n", value)
},
}
_ = textFormField
}

func (TextFormField) CreateState

func (t TextFormField) CreateState() core.State

CreateState creates the state for this widget.

func (TextFormField) WithAutocorrect

func (t TextFormField) WithAutocorrect(autocorrect bool) TextFormField

WithAutocorrect sets whether auto-correction is enabled.

func (TextFormField) WithAutovalidate

func (t TextFormField) WithAutovalidate(autovalidate bool) TextFormField

WithAutovalidate enables validation on every value change.

func (TextFormField) WithController

func (t TextFormField) WithController(controller *platform.TextEditingController) TextFormField

WithController sets the text editing controller.

func (TextFormField) WithDisabled

func (t TextFormField) WithDisabled(disabled bool) TextFormField

WithDisabled sets whether the field is disabled.

func (TextFormField) WithHelperText

func (t TextFormField) WithHelperText(helper string) TextFormField

WithHelperText sets the helper text shown below the field.

func (TextFormField) WithInitialValue

func (t TextFormField) WithInitialValue(value string) TextFormField

WithInitialValue sets the initial value when no controller is provided.

func (TextFormField) WithInputAction

func (t TextFormField) WithInputAction(action platform.TextInputAction) TextFormField

WithInputAction sets the keyboard action button.

func (TextFormField) WithKeyboardType

func (t TextFormField) WithKeyboardType(kt platform.KeyboardType) TextFormField

WithKeyboardType sets the keyboard type.

func (TextFormField) WithLabel

func (t TextFormField) WithLabel(label string) TextFormField

WithLabel sets the label text shown above the field.

func (TextFormField) WithObscure

func (t TextFormField) WithObscure(obscure bool) TextFormField

WithObscure sets whether the text is obscured (for passwords).

func (TextFormField) WithOnChanged

func (t TextFormField) WithOnChanged(onChanged func(string)) TextFormField

WithOnChanged sets the callback invoked when the field value changes.

func (TextFormField) WithOnEditingComplete

func (t TextFormField) WithOnEditingComplete(fn func(string)) TextFormField

WithOnEditingComplete sets the callback invoked when editing is complete.

func (TextFormField) WithOnSaved

func (t TextFormField) WithOnSaved(onSaved func(string)) TextFormField

WithOnSaved sets the callback invoked when the form is saved.

func (TextFormField) WithOnSubmitted

func (t TextFormField) WithOnSubmitted(fn func(string)) TextFormField

WithOnSubmitted sets the callback invoked when the user submits.

func (TextFormField) WithPlaceholder

func (t TextFormField) WithPlaceholder(placeholder string) TextFormField

WithPlaceholder sets the placeholder text shown when the field is empty.

func (TextFormField) WithValidator

func (t TextFormField) WithValidator(validator func(string) string) TextFormField

WithValidator sets the validation function.

type TextInput

TextInput is the lowest-level text input widget that embeds a native platform text field with Skia-rendered chrome (background, border, focus styling).

Styling Model

TextInput is fully explicit — all visual properties use their struct field values directly. A zero value means zero (transparent, no border, no height). TextInput provides NO defaults; callers must supply all visual properties.

  • BackgroundColor: 0 means transparent
  • BorderColor: 0 means no border
  • Height: 0 means zero height (field won't be visible)
  • Style.FontSize: 0 means zero (text won't render)
  • Style.Color: 0 means transparent text

For most use cases, prefer TextField which provides sensible defaults and adds labels, helper text, and error display. Use [theme.TextFieldOf] for theme-styled text fields, or TextFormField for form validation support.

TextInput is intended as a building block for higher-level widgets, not for direct use in application code.

Direct Usage \(when needed\)

If you need TextInput directly, you must provide all visual properties:

widgets.TextInput{
Controller: controller,
Placeholder: "Enter text",
BackgroundColor: graphics.ColorWhite,
BorderColor: graphics.RGB(200, 200, 200),
FocusColor: graphics.RGB(33, 150, 243),
BorderRadius: 8,
BorderWidth: 1,
Height: 44,
Padding: layout.EdgeInsetsSymmetric(12, 8),
Style: graphics.TextStyle{FontSize: 16, Color: graphics.ColorBlack},
PlaceholderColor: graphics.RGB(150, 150, 150),
}

The native text field handles all text editing, selection, and IME composition. Skia renders the visual chrome (background, borders) while the platform view handles the actual text rendering and cursor.

type TextInput struct {
core.StatefulBase

// Controller manages the text content and selection.
Controller *platform.TextEditingController

// Style for the text.
Style graphics.TextStyle

// Placeholder text shown when empty.
Placeholder string

// KeyboardType specifies the keyboard to show.
KeyboardType platform.KeyboardType

// InputAction specifies the keyboard action button.
InputAction platform.TextInputAction

// Capitalization specifies text capitalization behavior.
// Defaults to None. Set to TextCapitalizationSentences for standard text input.
Capitalization platform.TextCapitalization

// Obscure hides the text (for passwords).
Obscure bool

// Autocorrect enables auto-correction.
Autocorrect bool

// Multiline enables multiline text input.
Multiline bool

// MaxLines limits the number of lines (multiline only).
MaxLines int

// OnChanged is called when the text changes.
OnChanged func(string)

// OnSubmitted is called when the user submits (presses done/return).
OnSubmitted func(string)

// OnEditingComplete is called with the current text when editing is complete.
OnEditingComplete func(string)

// OnFocusChange is called when focus changes.
OnFocusChange func(bool)

// Disabled controls whether the field rejects input.
Disabled bool

// Width of the text field. Zero expands to fill available width.
Width float64

// Height of the text field. Zero means zero height (invisible).
Height float64

// Padding inside the text field. Zero means no padding.
Padding layout.EdgeInsets

// BackgroundColor of the text field. Zero means transparent.
BackgroundColor graphics.Color

// BorderColor of the text field. Zero means no border.
BorderColor graphics.Color

// FocusColor of the text field outline when focused. Zero means no focus highlight.
FocusColor graphics.Color

// BorderRadius for rounded corners. Zero means sharp corners.
BorderRadius float64

// BorderWidth for the border stroke. Zero means no border.
BorderWidth float64

// PlaceholderColor is the color for placeholder text. Zero means transparent.
PlaceholderColor graphics.Color
}

Example:

This example shows a low-level text input.

package main

import (
"fmt"

"github.com/go-drift/drift/pkg/platform"
"github.com/go-drift/drift/pkg/widgets"
)

func main() {
controller := platform.NewTextEditingController("")

textInput := widgets.TextInput{
Controller: controller,
Placeholder: "Enter text",
KeyboardType: platform.KeyboardTypeText,
OnChanged: func(text string) {
fmt.Printf("Text changed: %s\n", text)
},
OnSubmitted: func(text string) {
fmt.Printf("Submitted: %s\n", text)
},
}
_ = textInput
}

func (TextInput) CreateState

func (n TextInput) CreateState() core.State

CreateState creates the state for this widget.

func (TextInput) WithBackgroundColor

func (n TextInput) WithBackgroundColor(c graphics.Color) TextInput

WithBackgroundColor returns a copy with the specified background color.

func (TextInput) WithBorderColor

func (n TextInput) WithBorderColor(c graphics.Color) TextInput

WithBorderColor returns a copy with the specified border color.

func (TextInput) WithBorderRadius

func (n TextInput) WithBorderRadius(radius float64) TextInput

WithBorderRadius returns a copy with the specified corner radius.

func (TextInput) WithBorderWidth

func (n TextInput) WithBorderWidth(width float64) TextInput

WithBorderWidth returns a copy with the specified border stroke width.

func (TextInput) WithFocusColor

func (n TextInput) WithFocusColor(c graphics.Color) TextInput

WithFocusColor returns a copy with the specified focus outline color.

func (TextInput) WithHeight

func (n TextInput) WithHeight(height float64) TextInput

WithHeight returns a copy with the specified height.

func (TextInput) WithPadding

func (n TextInput) WithPadding(padding layout.EdgeInsets) TextInput

WithPadding returns a copy with the specified internal padding.

func (TextInput) WithPlaceholderColor

func (n TextInput) WithPlaceholderColor(c graphics.Color) TextInput

WithPlaceholderColor returns a copy with the specified placeholder text color.

type TimePicker

TimePicker displays a time selection field that opens a native time picker modal.

type TimePicker struct {
core.StatefulBase

// Hour is the current selected hour (0-23).
Hour int

// Minute is the current selected minute (0-59).
Minute int

// OnChanged is called when the user selects a time.
OnChanged func(hour, minute int)

// Disabled disables interaction when true.
Disabled bool

// Is24Hour determines whether to use 24-hour format.
// If nil, uses system default.
Is24Hour *bool

// Format is the time format string (Go time format). Default: "3:04 PM"
// Set to "15:04" for 24-hour format.
Format string

// Placeholder is shown when no time is selected (Hour and Minute are both 0 and ShowPlaceholder is true).
Placeholder string

// ShowPlaceholder controls whether to show placeholder when hour/minute are 0.
ShowPlaceholder bool

// Decoration provides styling (label, hint, border, icons, etc.).
Decoration *InputDecoration

// TextStyle for the value text.
TextStyle graphics.TextStyle

// Child overrides the default rendering for full customization.
Child core.Widget
}

func (TimePicker) CreateState

func (t TimePicker) CreateState() core.State

type Toggle

Toggle is a Skia-rendered toggle switch for on/off states.

Styling Model

Toggle is explicit by default — all visual properties use their struct field values directly. A zero value means zero, not "use theme default." For example:

  • ActiveColor: 0 means transparent track when on
  • Width: 0 means zero width (not rendered)
  • Height: 0 means zero height (not rendered)

For theme-styled toggles, use [theme.ToggleOf] which pre-fills visual properties from the current theme's [theme.SwitchThemeData].

Creation Patterns

Struct literal (full control):

widgets.Toggle{
Value: isEnabled,
OnChanged: func(v bool) { s.SetState(func() { s.isEnabled = v }) },
ActiveColor: graphics.RGB(52, 199, 89),
InactiveColor: graphics.RGB(229, 229, 234),
ThumbColor: graphics.ColorWhite,
Width: 51,
Height: 31,
}

Themed (reads from current theme):

theme.ToggleOf(ctx, isEnabled, onChanged)
// Pre-filled with theme colors and dimensions

Toggle is a controlled component - it displays the Value you provide and calls OnChanged when toggled. To change the toggle state, update Value in your state in response to OnChanged.

For native platform toggles (UISwitch on iOS, SwitchCompat on Android), use Switch instead.

type Toggle struct {
core.StatelessBase

// Value indicates the current on/off state.
Value bool
// OnChanged is called when the toggle switches.
OnChanged func(bool)
// Disabled disables interaction when true.
Disabled bool
// Width controls the overall width. Zero means zero width (not rendered).
Width float64
// Height controls the overall height. Zero means zero height (not rendered).
Height float64
// ActiveColor is the track color when on. Zero means transparent.
ActiveColor graphics.Color
// InactiveColor is the track color when off. Zero means transparent.
InactiveColor graphics.Color
// ThumbColor is the thumb fill color. Zero means transparent.
ThumbColor graphics.Color

// DisabledActiveColor is the track color when on and disabled.
// If zero, falls back to 0.5 opacity on the normal colors.
DisabledActiveColor graphics.Color

// DisabledInactiveColor is the track color when off and disabled.
// If zero, falls back to 0.5 opacity on the normal colors.
DisabledInactiveColor graphics.Color

// DisabledThumbColor is the thumb color when disabled.
// If zero, falls back to 0.5 opacity on the normal colors.
DisabledThumbColor graphics.Color
}

func (Toggle) Build

func (s Toggle) Build(ctx core.BuildContext) core.Widget

type VerticalDivider

VerticalDivider renders a thin vertical line with optional insets and spacing.

VerticalDivider is typically used as a child of a Row or any horizontally-stacked layout. It expands to fill the available height and occupies Width pixels of horizontal space, drawing a centered line of the given Thickness.

Styling Model

VerticalDivider is explicit by default: all visual properties use their struct field values directly. A zero value means zero, not "use theme default." For example:

  • Width: 0 means zero horizontal space (not rendered)
  • Thickness: 0 means no visible line
  • Color: 0 means transparent (invisible)

For theme-styled vertical dividers, use [theme.VerticalDividerOf] which pre-fills values from the current theme's [DividerThemeData] (color from OutlineVariant, 16px space, 1px thickness).

Creation Patterns

Struct literal (full control):

widgets.VerticalDivider{
Width: 16,
Thickness: 1,
Color: graphics.RGB(200, 200, 200),
Indent: 8, // 8px top inset
}

Themed (reads from current theme):

theme.VerticalDividerOf(ctx)
type VerticalDivider struct {
core.RenderObjectBase
// Width is the total horizontal space the divider occupies.
Width float64
// Thickness is the thickness of the drawn line.
Thickness float64
// Color is the line color. Zero means transparent.
Color graphics.Color
// Indent is the top inset.
Indent float64
// EndIndent is the bottom inset.
EndIndent float64
}

func (VerticalDivider) CreateRenderObject

func (d VerticalDivider) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (VerticalDivider) UpdateRenderObject

func (d VerticalDivider) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type VideoPlayer

VideoPlayer embeds a native video player view with built-in platform controls.

The native player provides standard controls (play/pause, seek bar, time display) on both platforms. No Drift overlay is needed.

Create a [platform.VideoPlayerController] with [core.UseDisposable] and pass it to this widget:

s.video = platform.NewVideoPlayerController()
core.UseDisposable(s, s.video)
s.video.OnPlaybackStateChanged = func(state platform.PlaybackState) { ... }
s.video.Load(url)

// in Build:
widgets.VideoPlayer{Controller: s.video, Height: 225}

Width and Height set explicit dimensions. Use layout widgets such as Expanded to fill available space.

type VideoPlayer struct {
core.RenderObjectBase
// Controller provides the native video player surface and playback control.
Controller *platform.VideoPlayerController

// Width of the video player in logical pixels.
Width float64

// Height of the video player in logical pixels.
Height float64

// HideControls hides the native transport controls (play/pause, seek bar,
// time display). Use this when building custom Drift widget controls on
// top of the video surface.
HideControls bool
}

func (VideoPlayer) CreateRenderObject

func (v VideoPlayer) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

CreateRenderObject creates the render object for this widget.

func (VideoPlayer) UpdateRenderObject

func (v VideoPlayer) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

UpdateRenderObject updates the render object with new widget properties.

type View

View is the root widget that hosts the render tree.

type View struct {
core.RenderObjectBase
Child core.Widget
}

func Root

func Root(child core.Widget) View

Root creates a top-level view widget with the given child.

func (View) ChildWidget

func (v View) ChildWidget() core.Widget

ChildWidget returns the single child for render object wiring.

func (View) CreateRenderObject

func (v View) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

CreateRenderObject builds the root render view.

func (View) UpdateRenderObject

func (v View) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

UpdateRenderObject updates the root render view.

type Wrap

Wrap lays out children in runs, wrapping to the next line when space runs out.

Wrap is similar to CSS flexbox with flex-wrap: wrap. Children are laid out along the main axis until they exceed the available space, at which point a new run is started.

Sizing Behavior

Wrap requires bounded constraints on the main axis (width for horizontal, height for vertical). The cross axis can be unbounded - Wrap will size to fit all runs. If the main axis is unbounded, Wrap will panic with guidance.

Spacing

Use Spacing to add gaps between children within each run. Use RunSpacing to add gaps between runs.

Alignment

Wrap provides three alignment properties:

  • Alignment: Controls main axis positioning within each run (Start, End, Center, SpaceBetween, SpaceAround, SpaceEvenly)
  • CrossAxisAlignment: Controls cross axis positioning within each run (Start, End, Center)
  • RunAlignment: Controls distribution of runs along the cross axis (Start, End, Center, SpaceBetween, SpaceAround, SpaceEvenly)

Direction

Direction defaults to WrapAxisHorizontal (the zero value for WrapAxis). For vertical wrapping, set Direction to WrapAxisVertical.

Example:

Wrap{
Direction: WrapAxisHorizontal,
Spacing: 8,
RunSpacing: 8,
Children: []core.Widget{
Chip{Label: "Go"},
Chip{Label: "Rust"},
Chip{Label: "TypeScript"},
Chip{Label: "Python"},
},
}

For non-wrapping horizontal layout, use Row. For non-wrapping vertical layout, use Column.

type Wrap struct {
core.RenderObjectBase
Children []core.Widget
Direction WrapAxis // WrapAxisHorizontal (zero value); set WrapAxisVertical for column-style wrapping
Alignment WrapAlignment // Main axis alignment within runs
CrossAxisAlignment WrapCrossAlignment // Cross axis alignment within runs
RunAlignment RunAlignment // Distribution of runs in cross axis
Spacing float64 // Gap between items in a run
RunSpacing float64 // Gap between runs
}

func WrapOf

func WrapOf(spacing, runSpacing float64, children ...core.Widget) Wrap

WrapOf creates a Wrap widget with the specified spacing and children. This is a convenience helper for the common horizontal wrap case.

func (Wrap) ChildrenWidgets

func (w Wrap) ChildrenWidgets() []core.Widget

func (Wrap) CreateRenderObject

func (w Wrap) CreateRenderObject(ctx core.BuildContext) layout.RenderObject

func (Wrap) UpdateRenderObject

func (w Wrap) UpdateRenderObject(ctx core.BuildContext, renderObject layout.RenderObject)

type WrapAlignment

WrapAlignment controls how children are positioned along the main axis within each run.

type WrapAlignment int
const (
// WrapAlignmentStart places children at the start of each run.
WrapAlignmentStart WrapAlignment = iota
// WrapAlignmentEnd places children at the end of each run.
WrapAlignmentEnd
// WrapAlignmentCenter centers children within each run.
WrapAlignmentCenter
// WrapAlignmentSpaceBetween distributes free space evenly between children.
// No space before the first or after the last child in each run.
WrapAlignmentSpaceBetween
// WrapAlignmentSpaceAround distributes free space evenly, with half-sized
// spaces at the start and end of each run.
WrapAlignmentSpaceAround
// WrapAlignmentSpaceEvenly distributes free space evenly, including
// equal space before the first and after the last child in each run.
WrapAlignmentSpaceEvenly
)

func (WrapAlignment) String

func (a WrapAlignment) String() string

String returns a human-readable representation of the wrap alignment.

type WrapAxis

WrapAxis controls the layout direction for Wrap.

WrapAxisHorizontal is the zero value to make horizontal wrapping the default. Use WrapAxisVertical for top-to-bottom flow that wraps into new columns.

type WrapAxis int
const (
WrapAxisHorizontal WrapAxis = iota
WrapAxisVertical
)

func (WrapAxis) String

func (a WrapAxis) String() string

String returns a human-readable representation of the wrap axis.

type WrapCrossAlignment

WrapCrossAlignment controls how children are positioned along the cross axis within each run.

type WrapCrossAlignment int
const (
// WrapCrossAlignmentStart places children at the start of the cross axis within each run.
WrapCrossAlignmentStart WrapCrossAlignment = iota
// WrapCrossAlignmentEnd places children at the end of the cross axis within each run.
WrapCrossAlignmentEnd
// WrapCrossAlignmentCenter centers children along the cross axis within each run.
WrapCrossAlignmentCenter
)

func (WrapCrossAlignment) String

func (a WrapCrossAlignment) String() string

String returns a human-readable representation of the wrap cross alignment.

Generated by gomarkdoc