Skip to content

Widgets Reference

Kirill Smirnov edited this page May 10, 2026 · 1 revision

Widgets Reference

All widgets are in the Gui.Widgets namespace unless noted.


Base Classes

Widget

All widgets extend Widget. It is immutable — widgets describe UI, they hold no state and create no GPU resources.

public abstract class Widget
{
    public Key? Key { get; }
    public abstract Element CreateElement();
    public static bool CanUpdate(Widget old, Widget @new); // type + key equality
}

StatelessWidget

A widget whose appearance is fully determined by its constructor arguments. Override Build to describe the UI.

class MyButton : StatelessWidget
{
    public string Label { get; }
    public Action? OnPressed { get; }

    public MyButton(string label, Action? onPressed = null)
    {
        Label = label;
        OnPressed = onPressed;
    }

    public override Widget Build(BuildContext ctx) =>
        new GestureDetector(
            onTap: _ => OnPressed?.Invoke(),
            child: new Container(
                style: new BoxStyle { Padding = EdgeInsets.All(8) },
                child: new Text(Label)
            )
        );
}

StatefulWidget + State<T>

A widget with mutable state. CreateState() is called once; the State<T> object persists across rebuilds. See State Management.

InheritedWidget

A widget that propagates data down the tree efficiently. Descendants look up the nearest ancestor InheritedWidget of a given type and are automatically rebuilt when the inherited data changes.

class MyConfig : InheritedWidget
{
    public string Locale { get; }

    public MyConfig(string locale, Widget child) : base(child)
    {
        Locale = locale;
    }

    public override bool UpdateShouldNotify(InheritedWidget oldWidget)
    {
        return Locale != ((MyConfig)oldWidget).Locale;
    }

    public static string Of(BuildContext context)
    {
        var w = context.DependOnInheritedWidgetOfExactType<MyConfig>();
        return w?.Locale ?? "en";
    }
}

Key points:

  • DependOnInheritedWidgetOfExactType<T>() registers a dependency — the caller rebuilds when data changes.
  • UpdateShouldNotify controls whether dependents are notified on update.
  • State.DidChangeDependencies() is called on dependents when inherited data changes.

Theme

An InheritedWidget that provides ThemeData (colors + typography) to a subtree. Use Theme.Of(context) to read the current theme.

// Provide a theme at the top of the tree:
new Theme(
    data: new ThemeData(
        colorScheme: new ColorScheme { Primary = new Vector4(0.2f, 0.6f, 1f, 1f), ... },
        textTheme: new TextTheme { Body = new TextStyle { FontSize = 16 }, ... }
    ),
    child: myAppContent
)

// Read the theme inside any widget's Build():
var theme = Theme.Of(context);
var primary = theme.ColorScheme.Primary;
var bodyStyle = theme.TextTheme.Body;

ThemeData contains:

  • ColorScheme — 9 semantic colors: Primary, OnPrimary, Surface, OnSurface, Background, OnBackground, Border, Error, OnError
  • TextTheme — 3 text styles: Headline, Body, Label

If no Theme ancestor exists, Theme.Of() returns ThemeData.Default (dark theme).


Layout Widgets

Row

Horizontal flexbox. Children are arranged left-to-right.

new Row(
    spacing: 8,                                    // gap between children
    mainAxisAlignment: MainAxisAlignment.Start,    // default
    crossAxisAlignment: CrossAxisAlignment.Start,  // default
    mainAxisSize: MainAxisSize.Max,                // default: fill available width
    children: [ ... ]
)

Use MainAxisSize.Min to shrink-wrap the row to its children's total width.

Column

Vertical flexbox. Children are arranged top-to-bottom.

new Column(
    spacing: 12,
    mainAxisAlignment: MainAxisAlignment.Center,
    crossAxisAlignment: CrossAxisAlignment.Stretch,
    mainAxisSize: MainAxisSize.Min,                // shrink to content height
    children: [ ... ]
)

Use MainAxisSize.Max (default) to fill all available height.

Wrap

Lays out children in runs, wrapping to the next line when a child would overflow the available main-axis space. Similar to CSS flex-wrap. Unlike Row/Column, Wrap handles overflow by creating new runs instead of clipping or overflowing.

new Wrap(
    direction: FlexDirection.Horizontal,  // or Vertical
    spacing: 8,                           // gap between children in a run
    runSpacing: 4,                        // gap between runs
    mainAxisAlignment: MainAxisAlignment.Start,
    crossAxisAlignment: CrossAxisAlignment.Start,
    runAlignment: MainAxisAlignment.Start, // how runs are distributed
    children: [
        new Chip("Tag1"),
        new Chip("Tag2"),
        new Chip("LongTag3"),
        // wraps to next line if no room
    ]
)
Parameter Type Default Description
direction FlexDirection Horizontal Primary axis direction
spacing float 0 Space between children within a run
runSpacing float 0 Space between runs
mainAxisAlignment MainAxisAlignment Start Child alignment within a run
crossAxisAlignment CrossAxisAlignment Start Child cross-axis alignment within a run
runAlignment MainAxisAlignment Start Run distribution across the cross axis

Stack

Layers children on top of each other. Sizes to unpositioned children; fills parent if all children are positioned.

new Stack(
[
    new Container(style: new BoxStyle { Color = Colors.Background }),
    new Positioned(right: 8, top: 8, child: new CloseButton()),
])

Positioned

Places a child at absolute coordinates within a Stack. Only valid as a direct child of Stack.

new Positioned(
    left: 10,           // offset from left edge (optional)
    top: 20,            // offset from top edge (optional)
    right: null,        // offset from right edge (optional)
    bottom: null,       // offset from bottom edge (optional)
    width: 200,         // explicit width (optional)
    height: 100,        // explicit height (optional)
    child: new MyWidget()
)

At least one of left/right and at least one of top/bottom (or explicit width/height) should be set. Conflicting constraints (e.g., both left and right) define an exact size.

Expanded

Tells its Row/Column parent to give it all remaining main-axis space. Must be a direct child of Row or Column.

new Row(children:
[
    new Text("Name:"),
    new Expanded(flex: 1, child: new TextField()),   // gets remaining space
])

Multiple Expanded children share remaining space by their flex ratio.

SizedBox

Imposes explicit size constraints. With no child, acts as a spacer.

new SizedBox(width: 200, height: 100, child: new Text("Fixed size"))
new SizedBox(height: 16)    // 16px vertical spacer in Column
new SizedBox(width: 8)      // 8px horizontal spacer in Row

Padding

Adds inset space around its child.

new Padding(EdgeInsets.All(16), child: new Text("Padded"))
new Padding(EdgeInsets.Only(left: 12, top: 8), child: new Text("..."))

Align

Positions a child at a point within the parent using a normalized (-1,-1) to (1,1) space.

new Align(alignment: Alignment.BottomRight, child: new Text("BR"))
new Align(alignment: new Alignment(0, -0.5f), child: new Text("Custom"))

Predefined constants: TopLeft, TopCenter, TopRight, CenterLeft, Center, CenterRight, BottomLeft, BottomCenter, BottomRight.

Center

Shorthand for Align(Alignment.Center).

new Center(child: new Text("Centered"))

Visual Widgets

Container

A visual box with background, border, corner radii, optional fixed size, and optional child.

new Container(
    style: new BoxStyle
    {
        Color = new Vector4(0.1f, 0.1f, 0.1f, 0.95f),
        Color2 = new Vector4(0.15f, 0.15f, 0.15f, 0.95f),  // gradient end (optional)
        CornerRadius = new Vector4(8),     // uniform radius; or per-corner Vector4
        BorderThickness = 1f,
        BorderColor = new Vector4(1, 1, 1, 0.3f),
        Padding = EdgeInsets.All(12),
        Width = 300f,                      // optional fixed width
        Height = 200f,                     // optional fixed height
        Texture = myBitmap,                // optional SKBitmap background
        ClipBehavior = ClipBehavior.AntiAlias,
        HitTestBehavior = HitTestBehavior.Defer
    },
    child: new Text("Content")
)

BoxStyle.CornerRadius convention

Vector4 components map to corners: X = top-right, Y = bottom-right, Z = top-left, W = bottom-left. Use new Vector4(r) for uniform radius.

BoxStyle.BoxShadows

CSS-like box shadows — outer (drop shadow) and inner (inset). Multiple shadows are supported and painted in array order (first = bottommost).

new Container(
    style: new BoxStyle
    {
        Color = new Vector4(0.15f, 0.15f, 0.2f, 1f),
        CornerRadius = new Vector4(12),
        BoxShadows = new[]
        {
            // Outer drop shadow
            new BoxShadow(
                Color: new Vector4(0, 0, 0, 0.6f),
                Offset: new Vector2(0, 4f),
                BlurRadius: 16f,
                SpreadRadius: 2f),
            // Subtle outer glow
            new BoxShadow(
                Color: new Vector4(0.3f, 0.5f, 1f, 0.15f),
                Offset: Vector2.Zero,
                BlurRadius: 24f),
            // Inner shadow (inset) for depth
            new BoxShadow(
                Color: new Vector4(0, 0, 0, 0.4f),
                Offset: new Vector2(0, 2f),
                BlurRadius: 8f,
                SpreadRadius: 1f,
                Inset: true),
        }
    },
    child: new Text("Shadowed card")
)

Each BoxShadow has:

Parameter Type Default Description
Color Vector4 required RGBA shadow color
Offset Vector2 required Displacement (X=right, Y=down)
BlurRadius float 0 Gaussian blur sigma; 0 = hard edge
SpreadRadius float 0 Expands (+) or contracts (−) the shadow shape
Inset bool false true = inner shadow, false = outer shadow

Outer shadows are drawn before the fill (behind the box). Inner shadows are drawn after the fill but before children. Outer shadows are clipped out of the box area so they don't bleed through transparent backgrounds.

Note: When ClipBehavior != None, the clip wraps the entire box including outer shadows. This matches CSS overflow: hidden behavior — outer shadows will be clipped.

Text

Renders a string with configurable style.

new Text("Hello, World!", new TextStyle
{
    FontFamily = "sans-serif",
    FontSize = 14f,
    Color = Vector4.One,
    Weight = FontWeight.Normal,      // Normal, Bold, Italic
    Align = TextAlignment.Left,      // Left, Center, Right
    Overflow = TextOverflow.Clip,    // Clip, Ellipsis
    Boldness = 0.1f,                 // -0.5 (thin) to 0.5 (bold)
    OutlineWidth = 0.03f,            // fraction of font size
    OutlineColor = new Vector4(0, 0, 0, 1),
    GlowWidth = 0.2f,                // blur radius fraction of font size
    GlowColor = new Vector4(0.5f, 0.8f, 1, 0.5f)
})

Multi-line text is supported via \n in the content string.

VtmlText

Renders VTML (Vintage Story Text Markup Language) content with full support for styled text, links, icons, item stacks (with float layout), and hotkey display.

new VtmlText(
    vtml: "Hello <strong>bold</strong> and <i>italic</i><br>"
        + "<font color=\"#ff0000\" size=\"20\">Red big text</font><br>"
        + "<a href=\"handbook://page\">Click me</a><br>"
        + "<icon name=\"dice\"></icon> inline icon<br>"
        + "<itemstack type=\"block\" code=\"plank-oak\" floattype=\"left\"></itemstack>"
        + "Text wraps around the floated item stack.",
    baseStyle: new TextStyle { FontSize = 14f, Color = Vector4.One },
    onLinkClick: href => HandleLink(href)
)

Supported VTML tags

Tag Description
<br> Line break
<strong>, <b> Bold text
<i> Italic text
<font size/color/weight/opacity/family/align> Styled text span
<code> Monospace text
<a href="..."> Clickable link (handbook://, http://, chattype:///, etc.)
<icon name="..." path="..."> Inline SVG icon
<itemstack type="block|item" code="..." floattype="inline|left|right" rsize="1" offx="0" offy="0"> Item/block rendering
<hk>, <hotkey> Display current key binding
<clear> Clear floated elements

Architecture

  • VtmlConverter (Gui.Vtml) — converts VtmlToken[] from the game's VtmlParser.Tokenize() into a flat list of VtmlInlineElement objects (text runs, icons, item stacks, hotkeys, etc.).
  • VtmlSpanBuilder (Gui.Vtml) — converts VtmlInlineElement list into an InlineSpan tree (TextSpan, WidgetSpan, ClearSpan) suitable for RichText.
  • VtmlText (Gui.Widgets) — StatefulWidget that parses VTML, builds an InlineSpan tree, and renders via RichText composition (delegates to RenderRichText).

Image

Displays a game asset bitmap with configurable fit and alignment.

new Image(
    domain: "game",                  // asset domain
    path: "textures/gui/icon.png",   // path within domain
    fit: BoxFit.Contain,
    alignment: Alignment.Center,     // optional, defaults to Center
    width: 64f,                      // optional explicit width
    height: 64f                      // optional explicit height
)

BoxFit values

Value Behavior
Fill Stretch to fill; distorts aspect ratio
Contain Scale uniformly to fit within bounds; may letterbox
Cover Scale uniformly to fill bounds; may crop edges
FitWidth Scale to match width; may overflow vertically
FitHeight Scale to match height; may overflow horizontally
None Natural pixel size, positioned by alignment
ScaleDown Like Contain but never upscales

Clip

Clips its child to a rounded rectangle. Useful for rounded images.

new Clip(
    borderRadius: new Vector4(12),   // same Vector4 convention as CornerRadius
    child: new Image("game", "textures/banner.png", fit: BoxFit.Cover, width: 200, height: 100)
)

Opacity

Renders its child at a specified alpha.

new Opacity(0.5f, child: new Text("Half-transparent"))
  • Values ≤ 0.001 skip painting entirely
  • Values ≥ 0.999 skip SaveLayer for performance
  • Values in between use SKCanvas.SaveLayer

RepaintBoundary

Caches its entire subtree as an SKPicture. Prevents repaints from propagating into the cached subtree when siblings change. See Rendering.

new RepaintBoundary(child: new StaticPanel())

ShaderMask

Paints its child normally, then overlays an SKShader across the child's bounds using a configurable SKBlendMode. The shader is not owned by the widget — the caller manages its lifecycle (create / dispose).

Parameter Type Default Description
shader SKShader? null The shader to overlay
blendMode SKBlendMode SrcOver Blend mode for the overlay
child Widget? null Child widget painted underneath
offsetX float 0 Horizontal shader scroll offset
offsetY float 0 Vertical shader scroll offset
// Perlin noise overlay on a colored box
new ShaderMask(
    shader: SKShader.CreatePerlinNoiseTurbulence(0.015f, 0.03f, 4, 0f),
    blendMode: SKBlendMode.Multiply,
    child: new Container(
        style: new BoxStyle { Color = new Vector4(0.8f, 0.2f, 0.1f, 0.9f) }
    )
)

Blend mode pitfall: With Multiply, every pixel of the child is multiplied by the shader. Decorative elements like borders or shadows inside the child will be distorted. Place borders and shadows in a separate layer outside the ShaderMask:

// Wrong — border gets multiplied by noise and disappears
new ShaderMask(
    shader: noise, blendMode: SKBlendMode.Multiply,
    child: new Container(style: new BoxStyle {
        Color = fillColor, BorderThickness = 2f, BorderColor = gray
    })
)

// Correct — border is a separate layer on top
new Stack([
    new ShaderMask(
        shader: noise, blendMode: SKBlendMode.Multiply,
        child: new Container(style: new BoxStyle { Color = fillColor })
    ),
    new Container(style: new BoxStyle {
        BorderThickness = 2f, BorderColor = gray
    })
])

CompositedTransformTarget / CompositedTransformFollower

Links a trigger widget to an overlay widget so the overlay tracks the trigger's position during layout — even inside scrollable containers. Used internally by Tooltip and Dropdown.

var link = new LayerLink();

// In the main widget tree (e.g., inside a scroll view):
new CompositedTransformTarget(link: link, child: triggerWidget)

// In the overlay entry:
new CompositedTransformFollower(
    link: link,
    offset: new Vector2(0, 40), // below the trigger
    child: overlayContent
)
Parameter Type Default Description
link LayerLink required Shared link between target and follower
offset Vector2? (0, 0) Offset from the target's top-left corner

The follower computes its position in the layout phase (not build), so it reads the target's post-scroll global position. It writes StackParentData and must be placed inside a Stack (typically the Overlay root stack).

FractionalTranslation

Translates the child's paint position by a fraction of the child's own size. Useful for centering a widget of unknown size relative to an anchor point.

new FractionalTranslation(
    translation: new Vector2(-0.5f, -1f), // center horizontally, shift up by full height
    child: tooltipContent
)

Interactive Widgets

GestureDetector

Wraps a child with pointer event callbacks.

new GestureDetector(
    onTap:     e => { },    // mouse click (down + up on same element)
    onPress:   e => { },    // mouse down
    onRelease: e => { },    // mouse up
    onEnter:   e => { },    // pointer enters bounds
    onExit:    e => { },    // pointer leaves bounds
    onMove:    e => { },    // pointer moves within bounds
    onWheel:   e => { },    // scroll wheel; e.Delta > 0 = scroll down
    child: new Container(...)
)

All callbacks are optional. See Event Handling.

MouseRegion

Detects pointer enter/exit/hover and optionally changes the cursor shape. Nested regions are supported — the innermost cursor wins.

new MouseRegion(
    cursor:  MouseCursor.LinkSelect,  // VS "linkselect" cursor
    onEnter: e => { },                // pointer enters bounds
    onExit:  e => { },                // pointer leaves bounds
    onHover: e => { },                // pointer moves within bounds
    child: new Container(...)
)

// Custom VS cursor name:
new MouseRegion(
    cursor: MouseCursor.Custom("mycursor"),
    child: someWidget
)

Built-in cursors: MouseCursor.LinkSelect, MouseCursor.TextSelect. Custom names via MouseCursor.Custom("name"). Uses VS's GuiDialog.MouseOverCursor.

All callbacks and cursor are optional. A MouseRegion with only cursor set (no callbacks) still participates in hit-testing.

TextField

Single-line text input with cursor, caret scrolling, and keyboard handling.

new TextField(
    controller: new TextEditingController("initial text"),
    focusNode: new FocusNode(),    // optional; one is created automatically
    style: new BoxStyle
    {
        Height = 35,
        Color = new Vector4(0, 0, 0, 0.3f),
        BorderThickness = 1
    },
    textStyle: new TextStyle { FontSize = 14 }
)

Without an external controller, the text value is inaccessible from the parent.

NumericField

A row of button + TextField + + button. Validates input as a float.

new NumericField(
    initialValue: 25f,
    step: 5f,                             // increment/decrement step
    onChanged: v => SetState(() => _value = v)
)

Checkbox

A parchment-textured checkbox with an animated check mark.

new Checkbox(
    value: _checked,
    onChanged: v => SetState(() => _checked = v),
    label: "Enable feature",
    size: 24f                             // checkbox size in pixels
)

Use the controlled pattern: the parent holds _checked, passes it as value, and updates it in onChanged.

RadioButton<T>

A radio button. Selected when value == groupValue.

new RadioButton<int>(
    value: 1,                             // this button's value
    groupValue: _selectedMode,            // currently selected group value
    onChanged: v => SetState(() => _selectedMode = v),
    label: "Survival"
)

Dropdown<T>

A dropdown menu that opens an overlay panel when clicked.

new Dropdown<int>(
    _selectedValue,
    [
        new DropdownItem<int> { Value = 1, Label = "Option A" },
        new DropdownItem<int> { Value = 2, Label = "Option B" },
        new DropdownItem<int> { Value = 3, Label = "Option C" },
    ],
    v => SetState(() => _selectedValue = v)
)

Slider

A horizontal slider for selecting a numeric value from a range. Supports continuous and discrete (stepped) modes, mouse wheel, and an optional value tooltip on hover/drag. The value tooltip uses the Tooltip widget internally.

// Continuous slider (0.0 – 1.0)
new Slider(
    value: _volume,
    onChanged: v => SetState(() => _volume = v)
)

// Discrete slider with label (0 – 100, 10 steps)
new Slider(
    value: _brightness,
    onChanged: v => SetState(() => _brightness = v),
    onChangeEnd: v => SaveSetting(v),
    min: 0, max: 100, divisions: 10,
    label: "Brightness"
)

// Custom value tooltip content
new Slider(
    value: _val,
    onChanged: v => SetState(() => _val = v),
    valueLabelBuilder: v => new Text($"Vol: {v:F0}")
)

// No value tooltip
new Slider(
    value: _val,
    onChanged: v => SetState(() => _val = v),
    showValueLabel: false
)
Parameter Type Default Description
value float required Current value (clamped to min..max)
onChanged Action<float>? null Called on every drag/click/wheel change
onChangeEnd Action<float>? null Called once on pointer release
min float 0 Minimum value
max float 1 Maximum value
divisions int? null Number of discrete steps; null = continuous
label string? null Text label displayed to the left
trackHeight float 8 Track thickness in pixels
thumbWidth float 12 Thumb width in pixels
thumbHeight float 24 Thumb height in pixels
thumbRadius float 2 Thumb corner radius (2 = rectangular VS style)
activeColor Vector4? theme Filled portion of track
inactiveColor Vector4? theme Unfilled portion of track
thumbColor Vector4? theme Thumb color
showValueLabel bool true Whether to show a tooltip with the current value on hover/drag
valueLabelBuilder Func<float, Widget>? null Custom builder for tooltip content; receives current value, returns widget. When null, a default Text with the formatted value is used

ProgressBar

A horizontal progress bar that displays a value between 0 and 1. Renders a background track with a filled portion and an optional border, using rounded corners. Colors default to the current theme's ProgressBarStyle.

// Basic usage
new ProgressBar(value: 0.6f)

// Custom style override
new ProgressBar(
    value: _progress,
    style: new ProgressBarStyle
    {
        Height = 16,
        CornerRadius = 8,
        BorderThickness = 2,
        FillColor = new Vector4(0.2f, 0.8f, 0.2f, 1f),
        TrackColor = new Vector4(0.1f, 0.1f, 0.1f, 1f),
        BorderColor = new Vector4(0.4f, 0.4f, 0.4f, 1f),
    }
)
Parameter Type Default Description
value float required Progress (clamped to 0..1)
style ProgressBarStyle? theme Optional style override

ProgressBarStyle tokens:

Property Type Default Description
Height float 10 Track height in pixels
CornerRadius float 5 Corner radius for track and fill
BorderThickness float 1 Border thickness around the track
FillColor Vector4 Primary Color of the filled portion
TrackColor Vector4 Surface Background color of the track
BorderColor Vector4 Border Border color

Tooltip

Wraps a child and shows a floating tooltip widget above it on hover after a configurable delay. Uses the Overlay system for rendering above other content and fades in/out with an AnimationController.

new Tooltip(
    content: new Text("Save the document"),
    child: myButton
)

// With custom settings:
new Tooltip(
    content: new Row(children: [new Icon("info"), new Text("Details here")]),
    child: myWidget,
    waitDuration: TimeSpan.FromMilliseconds(300),
    fadeDuration: TimeSpan.FromMilliseconds(200),
    verticalGap: 12f
)
Parameter Type Default Description
child Widget required The trigger widget
content Widget required Arbitrary widget shown inside the tooltip bubble
waitDuration TimeSpan? 500 ms Hover delay before showing
fadeDuration TimeSpan? 150 ms Fade-in/out animation duration
verticalGap float 8 Pixel gap between trigger top and tooltip bottom
anchorX float? null Horizontal anchor X within the child (pixels from left edge). When null, centers on child

The tooltip positions itself centered above the child using CompositedTransformTarget/CompositedTransformFollower + LayerLink, so it correctly tracks the trigger even inside scrollable containers. The content is wrapped in a styled Container with a dark semi-transparent background, rounded corners, and a thin border.

IconButton

A circular button (40×40) with hover animation.

new IconButton(
    color: new Vector4(0.8f, 0.3f, 0.3f, 1),
    onTap: _ => Console.WriteLine("clicked"),
    child: new Text("X")    // optional icon
)

Scrolling Widgets

ListView

Virtualized scrollable list. Only visible items are rendered.

Fixed height — builder:

new ListView(
    itemCount: 1000,
    itemHeight: 60f,
    itemBuilder: (ctx, i) => new Text($"Item {i}")
)

Fixed height — static children (short lists):

new ListView(
    children: myWidgets,
    itemHeight: 40f
)

Variable height (items can have different heights):

new ListView(
    itemBuilder: (ctx, i) => new IntrinsicHeight(
        child: new MyVariableHeightWidget(items[i])
    ),
    itemCount: items.Count,
    estimatedItemHeight: 44f,
    variableHeight: true,
    stickToBottom: true // auto-scroll to bottom on append
)

Parameters:

  • estimatedItemHeight — initial height estimate for unmeasured items (affects scrollbar accuracy)
  • variableHeight: true — enables per-item measurement and height caching
  • stickToBottom: true — auto-scrolls to bottom when new items are appended (useful for chat)
  • reverse: true — anchor at bottom; offset 0 shows last items

Items are measured on first layout and heights are cached. Scroll offset is corrected automatically when measured heights differ from estimates.

See Scrolling.

IntrinsicHeight / IntrinsicWidth

Sizes itself to the child's intrinsic height (or width). Useful for items in a variable-height ListView where the child needs to determine its own natural size.

new IntrinsicHeight(
    child: new Column(children: [ new Text("Hello"), new Text("World") ])
)

See Layout.

GridView

Virtualized scrollable grid with built-in scroll, two layout delegates, and two construction modes.

Builder mode (lazy, virtualized — preferred for large data):

GridView.Builder(
    itemCount: 200,
    gridDelegate: new SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 4,
        crossAxisSpacing: 8,
        mainAxisSpacing: 8,
        childAspectRatio: 1.0f     // width / height ratio per cell
    ),
    itemBuilder: (ctx, i) => new Container(
        style: new BoxStyle { Color = new Vector4(0.2f, 0.2f, 0.2f, 1) },
        child: new Center(child: new Text($"{i}"))
    )
)

Static mode (all children provided up-front):

new GridView(
    children: myWidgets,
    gridDelegate: new SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 120,   // max cell width; column count auto-computed
        crossAxisSpacing: 4,
        mainAxisSpacing: 4,
        fixedItemHeight: 80        // optional explicit cell height
    )
)

Grid delegates

Class Key parameter Column count
SliverGridDelegateWithFixedCrossAxisCount crossAxisCount Fixed
SliverGridDelegateWithMaxCrossAxisExtent maxCrossAxisExtent Auto: floor(width / maxExtent)

Both delegates accept mainAxisSpacing, crossAxisSpacing, childAspectRatio, and fixedItemHeight (overrides aspect ratio). Both construction modes accept reverse: true.

See Scrolling.

SingleChildScrollView

Non-virtualized scrollable container.

new SingleChildScrollView(
    child: new Column(children: manyWidgets)
)

Accepts reverse: true to anchor scroll origin at the bottom.

Scrollbar

Standalone wrapper that adds a scrollbar alongside any scrollable child. The scrollbar and the scrollable widget must share the same ScrollController.

var controller = new ScrollController();

new Scrollbar(
    controller: controller,
    child: new ListView(
        itemCount: 1000,
        itemHeight: 60,
        controller: controller,
        itemBuilder: (ctx, i) => new Text($"Item {i}")
    )
)

Parameters:

  • controller — shared ScrollController (required)
  • child — the scrollable child widget
  • positionScrollbarPosition.Right (default) or ScrollbarPosition.Left
  • width — track width in pixels (default 12)
  • trackColor / thumbColor — optional color overrides (theme-based by default)
  • thumbRadius — corner radius of the thumb (default 4)

Supports thumb dragging and click-to-jump on the track. See Scrolling.


Complex Widgets

TabView

A tabbed panel with a header row and content area.

new TabView(
    tabs:
    [
        new TabItem { Label = "Overview", Content = new OverviewPanel() },
        new TabItem { Label = "Settings", Content = new SettingsPanel() },
    ],
    position: TabPosition.Top,    // Top (default) or Left
    initialIndex: 0
)

TabView manages its own active tab index via SetState — no external state needed.

FlatItemSlot

Inventory slot with a solid-color background and animated glowing border on hover. All visual style tokens come from ItemSlotStyle; unset tokens fall back to the theme.

var controller = new SlotController(capi);
controller.WatchInventory(hotbarInv);    // auto-update on external inventory changes

// Minimal — all colors from theme:
new FlatItemSlot(slot: inventory[0], controller: controller)

// With explicit style:
new FlatItemSlot(
    slot: inventory[0],
    controller: controller,
    style: new ItemSlotStyle
    {
        Size = 48f,
        BackgroundColor = new Vector4(0, 0, 0, 0.5f),
        BorderColor = new Vector4(0.5f, 0.5f, 0.5f, 1f),
        BorderHoverColor = new Vector4(1f, 0.8f, 0f, 1f)
    }
)

ItemSlotStyle tokens (all nullable — null defers to theme):

Token Default Description
Size 48f Slot side length in pixels
BackgroundColor theme Solid fill color
BorderColor theme Border color at rest
BorderHoverColor theme Border color when hovered
HoverColor theme Hover highlight overlay color
Padding theme Inner padding around item icon

NineSliceItemSlot

Inventory slot rendered with a nine-slice bitmap background instead of a flat box. Same gesture handling, hover animation, and item display as FlatItemSlot.

// Load bitmap once (e.g., in GuiBase.OnScreenOpen):
var slotTex = SKBitmap.Decode(asset.Data);

new NineSliceItemSlot(
    texture: slotTex,
    slice: EdgeInsets.All(16),    // fixed corner insets for nine-slice
    size: 48f,
    slot: inventory[0],
    controller: controller
)

Slot building blocks

These public components can be composed to build custom slot variants:

ItemSlotGestureLayer — stateful wrapper that owns hover animation, GestureDetector, Tooltip, and all SlotController forwarding (click, wheel, drag). Publishes ItemSlotHoverData to descendants via InheritedWidget.

// FlatItemSlot equivalent built manually:
new ItemSlotGestureLayer(
    slot: inventory[0],
    controller: controller,
    childBuilder: () => new Container(
        new BoxStyle { Width = 48, Height = 48, Color = new Vector4(0, 0, 0, 0.4f) },
        new ItemSlotOverlay(inventory[0], size: 48f)
    )
)

ItemSlotHoverDataInheritedWidget carrying the CurvedAnimation for hover. Read from any descendant with ItemSlotHoverData.Of(context)?.HoverAnimation.

ItemSlotOverlayStatelessWidget that renders hover highlight, item icon (ItemStackDisplay), stack-size text, and durability bar. Reads hover animation from ItemSlotHoverData automatically.

new ItemSlotOverlay(slot: inventory[0], size: 48f)
// Optional overrides:
new ItemSlotOverlay(slot, size: 48f, hoverColor: new Vector4(1), padding: EdgeInsets.All(4))

SlotController is a ChangeNotifier encapsulating all interaction logic:

  • ClickSlot(slot, button) — reads modifier keys, calls ActivateSlot, sends packet
  • WheelSlot(slot, wheelDir) — transfers one item at a time via mouse wheel
  • WatchInventory(inv) / UnwatchInventory(inv) — subscribes to SlotModified for automatic widget rebuilds on external inventory changes

ItemTooltipContent is a StatelessWidget shown inside the tooltip bubble, displaying item name, description, durability, and quantity.

SlotGrid

A grid of inventory slots for game inventory UIs. Uses NineSliceItemSlot when slotBackground is provided; otherwise FlatItemSlot.

new SlotGrid(
    slots: [slot1, slot2, null, slot3],    // null = empty slot
    controller: controller,                 // optional SlotController
    columns: 10,
    slotSize: 48f,
    spacing: 4f,
    slotBackground: slotTextureBitmap,      // optional nine-slice background
    slotBackgroundSlice: EdgeInsets.All(16)
)

Overlay

Manages a dynamic stack of layers (for dropdowns, tooltips, dialogs). GuiBase.TryOpen() automatically wraps your Build() result in an Overlay.

// Insert an entry dynamically from within a widget:
var entry = new OverlayEntry(
    new Positioned(left: 100, top: 100,
        child: new Container(style: new BoxStyle { Width = 200, Height = 100 },
                             child: new Text("Tooltip")))
);
Overlay.Of(context)!.Insert(entry);

// Remove it:
entry.Remove();   // or entry.Dispose()

Window Widgets

WindowTitleBar

A title bar with title text, minimize (collapse), and close buttons. Typically the first child in a Column.

new WindowTitleBar(
    title: "My Window",
    height: 28f,
    onClose: () => TryClose(),
    onMinimizeChanged: minimized => SetState(() => _minimized = minimized),
    showMinimize: true,
    showClose: true,
    fontSize: 14f,
    backgroundColor: new Vector4(0.15f, 0.15f, 0.18f, 1f),
    textColor: new Vector4(0.85f, 0.85f, 0.85f, 1f),
    buttonHoverColor: new Vector4(0.3f, 0.3f, 0.35f, 1f),
    closeHoverColor: new Vector4(0.8f, 0.2f, 0.2f, 1f)
)

The minimize button toggles internal state and fires onMinimizeChanged. The close button fires onClose. Both buttons show hover highlighting.

WindowFrame

Convenience widget that wraps WindowTitleBar + content in a Column and handles minimize state automatically. When minimized, only the title bar is visible.

new WindowFrame(
    title: "My Window",
    onClose: () => TryClose(),
    child: new Padding(
        EdgeInsets.All(12),
        child: new Text("Content")
    )
)

See Windowing for full details.


Animation Widgets

AnimatedBuilder

Rebuilds a subtree every animation frame.

new AnimatedBuilder(
    animation: _controller,
    builder: ctx =>
    {
        float size = new FloatTween(12f, 20f).Evaluate(_curved);
        return new Text("Animated", new TextStyle { FontSize = size });
    }
)

See Animations.

AnimatedContainer

A Container that smoothly interpolates its BoxStyle when properties change.

new AnimatedContainer(
    duration: TimeSpan.FromMilliseconds(200),
    curve: Curves.EaseOut,
    style: new BoxStyle
    {
        Color = _active ? activeColor : inactiveColor,
        CornerRadius = new Vector4(_active ? 8 : 2),
        Width = 120,
        Height = 40
    }
)

Key Types

Key and ValueKey<T>

Keys provide stable element identity across rebuilds. Use them when list items can be reordered:

new Column(children: items.Select(item =>
    new ItemRow(key: new ValueKey<int>(item.Id), item: item)
))

GlobalKey

A key that is unique across the entire widget tree, providing cross-subtree access to the element and its render object. Used by Scrollable.EnsureVisible and other imperative APIs.

// Allocate once (e.g. in InitState), reuse across rebuilds
private GlobalKey _targetKey = new GlobalKey();

// Attach to a widget
new MyRow(key: _targetKey, ...)

// Access the element and RenderObject
Element? el = _targetKey.CurrentElement;
RenderObject? ro = _targetKey.CurrentRenderObject;

Scrollable.EnsureVisible

Scrolls the nearest scrollable ancestor (SingleChildScrollView or ListView) so that the target element is fully visible. Supports instant jump and animated scroll.

// Instant scroll
Scrollable.EnsureVisible(globalKey.CurrentElement!);

// Animated scroll
Scrollable.EnsureVisible(
    globalKey.CurrentElement!,
    duration: TimeSpan.FromMilliseconds(150),
    curve: Curves.EaseOut);

ScrollController.AnimateTo

Smoothly animates the scroll offset to a target value over a given duration.

controller.AnimateTo(
    offset: 500f,
    duration: TimeSpan.FromMilliseconds(300),
    curve: Curves.EaseOut,
    minScroll: 0f,
    maxScroll: 1000f);

EdgeInsets

EdgeInsets.Zero
EdgeInsets.All(16)
EdgeInsets.Only(left: 8, top: 4, right: 8, bottom: 4)
EdgeInsets.Symmetric(vertical: 12, horizontal: 20)
EdgeInsets.LTRB(left, top, right, bottom)

Alignment

Normalized 2D position: (-1, -1) = top-left, (0, 0) = center, (1, 1) = bottom-right.

Alignment.Center
Alignment.TopLeft
new Alignment(0.5f, -1f)    // top edge, 75% from left

Clone this wiki locally