DeviceWall class
The Device Wall — desktop, tablet and mobile rendered together.
The single-viewport modes answer "what does this look like at that width". This one answers a question they cannot: what changed between them. A reader switching between two modes compares one layout against a memory of the other; here both are on screen, reacting to the same knobs at the same time, and the difference is the thing being looked at rather than something being recalled.
Every frame is live, and that was decided against this ticket's own acceptance criteria. The ticket that asked for this wanted no pointer input, and for a test asserting a tap could not reach a row. It shipped the other way, on the maintainer's call, because the two reasons behind that criterion did not survive being measured.
The first reason was technical: a scaled frame would put drag selection's
viewport-local coordinate frame, the rubber-band geometry and the auto-scroll
Timer under a transform. Measured and withdrawn — Transform applies
the inverse to hit testing, so a drag inside a frame at half size selects
exactly the rows it crosses. That is why PreviewFrame's fit mode is
interactive at all; measured by a widget test, "interaction survives
the scale".
The second was that three frames side by side are too small to operate. That is true of one of them. The three do not share a scale: each viewport is fit into an equal column, so a wide viewport shrinks further than a narrow one, and the frame never scales up. Measured 2026-09-01 in the shell at 1800px — desktop 0.28× (a 40px row drawn 11px tall), tablet 0.48× (19px), mobile 1.0×, at its full 40px. The narrowest viewport is the one a reader most wants to poke at, and it is the one rendered at real size.
What that buys is the thing the single-viewport modes cannot do at all. The frames share one state, so a drag in the mobile frame paints its selection in the tablet and desktop frames at the same time: how this renders at three widths, at once, instead of held against a memory of the other mode.
Two consequences worth knowing rather than discovering. At 0.28× the desktop
frame really does draw that 40px target ~11px tall, and clicking one is
imprecise — the single-viewport modes are where precision lives. And an
IgnorePointer here would have been half a decision anyway: it vetoes hit
testing only, while focus traversal and Actions bypass it entirely, so the
subject's own ink wells and checkboxes stayed keyboard-activatable
throughout. Measured: 20 of 40 Tab presses landed inside a scaled frame.
Live in both routes is one rule; live in one and dead in the other was two.
Mutually exclusive with any scenario making a performance claim, and that
is now a property rather than a convention. Such a scenario measures one
subject under load; this draws three of it over the same data at once, so a
frame rate measured here would be measuring the wall. The rule lives on
StageDestination.allowsWall — the destination's call, not this
widget's — and ShellPage._select leaves the wall when a
destination that refuses it is opened, because hiding the segment alone is
silent, and asserts nothing about it.
- Inheritance
-
- Object
- DiagnosticableTree
- Widget
- StatelessWidget
- DeviceWall
Constructors
-
DeviceWall({Key? key, required WidgetBuilder stage, List<
ViewportSpec> specs = ViewportSpec.values, double spacing = 12}) -
const
Properties
- hashCode → int
-
The hash code for this object.
no setterinherited
- key → Key?
-
Controls how one widget replaces another widget in the tree.
finalinherited
- runtimeType → Type
-
A representation of the runtime type of the object.
no setterinherited
- spacing → double
-
final
-
specs
→ List<
ViewportSpec> -
final
- stage → WidgetBuilder
-
Builds the subtree each frame shows.
final
Methods
-
build(
BuildContext context) → Widget -
Describes the part of the user interface represented by this widget.
override
-
createElement(
) → StatelessElement -
Creates a StatelessElement to manage this widget's location in the tree.
inherited
-
debugDescribeChildren(
) → List< DiagnosticsNode> -
Returns a list of DiagnosticsNode objects describing this node's
children.
inherited
-
debugFillProperties(
DiagnosticPropertiesBuilder properties) → void -
Add additional properties associated with the node.
inherited
-
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a nonexistent method or property is accessed.
inherited
-
toDiagnosticsNode(
{String? name, DiagnosticsTreeStyle? style}) → DiagnosticsNode -
Returns a debug representation of the object that is used by debugging
tools and by DiagnosticsNode.toStringDeep.
inherited
-
toString(
{DiagnosticLevel minLevel = DiagnosticLevel.info}) → String -
A string representation of this object.
inherited
-
toStringDeep(
{String prefixLineOne = '', String? prefixOtherLines, DiagnosticLevel minLevel = DiagnosticLevel.debug, int wrapWidth = 65}) → String -
Returns a string representation of this node and its descendants.
inherited
-
toStringShallow(
{String joiner = ', ', DiagnosticLevel minLevel = DiagnosticLevel.debug}) → String -
Returns a one-line detailed description of the object.
inherited
-
toStringShort(
) → String -
A short, textual description of this widget.
inherited
Operators
-
operator ==(
Object other) → bool -
The equality operator.
inherited