-
Notifications
You must be signed in to change notification settings - Fork 0
Widgets Reference
All widgets are in the Gui.Widgets namespace unless noted.
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
}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)
)
);
}A widget with mutable state. CreateState() is called once; the State<T> object persists
across rebuilds. See State Management.
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. -
UpdateShouldNotifycontrols whether dependents are notified on update. -
State.DidChangeDependencies()is called on dependents when inherited data changes.
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).
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.
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.
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 |
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()),
])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.
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.
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 RowAdds 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("..."))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.
Shorthand for Align(Alignment.Center).
new Center(child: new Text("Centered"))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")
)Vector4 components map to corners: X = top-right, Y = bottom-right, Z = top-left,
W = bottom-left. Use new Vector4(r) for uniform radius.
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 CSSoverflow: hiddenbehavior — outer shadows will be clipped.
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.
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)
)| 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 |
-
VtmlConverter (
Gui.Vtml) — convertsVtmlToken[]from the game'sVtmlParser.Tokenize()into a flat list ofVtmlInlineElementobjects (text runs, icons, item stacks, hotkeys, etc.). -
VtmlSpanBuilder (
Gui.Vtml) — convertsVtmlInlineElementlist into anInlineSpantree (TextSpan,WidgetSpan,ClearSpan) suitable forRichText. -
VtmlText (
Gui.Widgets) —StatefulWidgetthat parses VTML, builds anInlineSpantree, and renders viaRichTextcomposition (delegates toRenderRichText).
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
)| 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 |
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)
)Renders its child at a specified alpha.
new Opacity(0.5f, child: new Text("Half-transparent"))- Values ≤
0.001skip painting entirely - Values ≥
0.999skipSaveLayerfor performance - Values in between use
SKCanvas.SaveLayer
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())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
})
])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).
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
)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.
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.
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.
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)
)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.
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"
)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)
)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 |
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 |
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.
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
)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.
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.
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
)
)| 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.
Non-virtualized scrollable container.
new SingleChildScrollView(
child: new Column(children: manyWidgets)
)Accepts reverse: true to anchor scroll origin at the bottom.
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— sharedScrollController(required) -
child— the scrollable child widget -
position—ScrollbarPosition.Right(default) orScrollbarPosition.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.
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.
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 |
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
)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)
)
)ItemSlotHoverData — InheritedWidget carrying the CurvedAnimation for hover.
Read from any descendant with ItemSlotHoverData.Of(context)?.HoverAnimation.
ItemSlotOverlay — StatelessWidget 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, callsActivateSlot, sends packet -
WheelSlot(slot, wheelDir)— transfers one item at a time via mouse wheel -
WatchInventory(inv)/UnwatchInventory(inv)— subscribes toSlotModifiedfor automatic widget rebuilds on external inventory changes
ItemTooltipContent is a StatelessWidget shown inside the tooltip bubble,
displaying item name, description, durability, and quantity.
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)
)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()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.
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.
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.
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
}
)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)
))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;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);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.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)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