Skip to content

Deck widget views

A deck widget is a tile on the deck drawn from your tree, served over a widget surface.

public IReadOnlyList<UiSurfaceDeclaration> Surfaces { get; } =
[
new UiSurfaceDeclaration { Kind = UiSurfaceKinds.Widget, SessionMode = UiSessionModes.Shared },
];
public Task<IUiSession?> CreateSessionAsync(UiSessionRequest request, CancellationToken cancellationToken)
{
if (request.Surface.Kind != UiSurfaceKinds.Widget)
{
return Task.FromResult<IUiSession?>(null);
}
var data = request.Surface.Attributes[UiWidgetSurfaceAttributes.Data];
var city = data.TryGetProperty("city", out var value) ? value.GetString() : null;
var view = new UiView(request.Surface, Weather(city ?? "Berlin"));
return Task.FromResult<IUiSession?>(new ViewSession(view));
}
private UiStack Weather(string city) => new()
{
Key = "weather",
Justify = UiComponentJustify.SpaceBetween,
Children =
[
new UiStack
{
Key = "current",
Direction = UiComponentDirections.Horizontal,
Justify = UiComponentJustify.SpaceBetween,
Children =
[
new UiStack
{
Key = "labels",
Children =
[
new UiTextRun { Key = "location", Text = city, Size = 0.09 },
new UiTextRun { Key = "temp", Text = UiText.From(() => $"{_forecast.Value.Temperature}°"), Size = 0.26 },
],
},
new UiImage { Key = "icon", Source = _sun, Size = 0.26 },
],
},
new UiTextRun
{
Key = "condition",
Text = UiText.From(() => _forecast.Value.Condition),
Size = 0.08,
Role = UiComponentTextRoles.Secondary,
},
new UiRangeBar { Key = "range", MainSize = 0.06, Start = 0.2, End = 0.6, Marker = 0.45 },
],
};

A two-by-two weather widget: Berlin, 21°, a sun icon, the caption Sunny and a temperature range bar

To offer a widget in the picker, register a widget type - that page covers registration, the picker card and configuration. The same vocabulary also draws a folder view and an action modal.

ViewSession, the adapter between a UiView and IUiSession used on every view page, is defined on Serving a view.

Attribute (UiWidgetSurfaceAttributes) Meaning
widgetId The widget being drawn.
widgetType Its qualified widget type id.
data Its stored configuration, as JSON.
cornerRadius The tile’s corner radius.
sample true when the picker asks for a representative sample.
ghost true when drawing the drag ghost of a widget that is also drawn live.

The surface carries no size: a widget is resized on the deck without asking you. To lay a 2x1 or 1x2 tile out differently from a 1x1 one, use UiResponsive, which the reader resolves against the tile.

The stored configuration travels with the request rather than being looked up, so a provider outside the host can serve a widget. A provider that ignores sample or ghost keeps behaving exactly as it did.

For a type that declares standard appearance, UiWidgetAppearance.Read(data) returns the background, label, colours and font the user or an action set.

var attributes = request.Surface.Attributes;
var isSample = attributes.TryGetValue(UiWidgetSurfaceAttributes.Sample, out var sample)
&& sample.ValueKind == JsonValueKind.True;
var isGhost = attributes.TryGetValue(UiWidgetSurfaceAttributes.Ghost, out var ghost)
&& ghost.ValueKind == JsonValueKind.True;

An editor preview arrives as a preview surface carrying draft configuration instead. A preview with sample: true is a picker card: draw it without reading anything live - nothing is connected, bound or configured while someone is choosing a widget type. ghost exists because two surfaces for one widget are otherwise identical, and without it the ghost and the tile would share a session.

_sun above is a resource handle. Get one for your own image from IIntegrationContext.UiResources and register a name again to change the picture; see Registering your own images.