flutter3d_editor_widgets

apps/flutter3d_modeler and apps/flutter3d_editor each drew a private copy of these controls. They live in one package now so the copies stop drifting (ui-27). Ten so far, plus the row they assemble into:

Widget What it is
SectionLabel One line of upper-case, letter-spaced text naming a panel section. style/padding override the default look, and they reach far enough to reproduce the level editor's denser heading, so it never needs a second copy of this widget
NumberField A labelled box that holds one number: a comma and a full stop both read as a decimal point, and it reports only when the person has finished, not on every keystroke
ColorField A swatch, a hex box and HSV sliders over a three- or four-channel colour, with an Alpha slider only when a fourth channel is there to hold one
RangeSliderField A slider between min and max, an optional internal label, a nullable step that snaps and rounds a drag's value (null keeps it bit for bit), and an editable text box for a value that may sit outside its own ends
EnumField A dropdown over options. A bound value is shown even when nothing in options names it, and a null value falls back to the first option instead of refusing to draw
TextureSlotRow A texture slot: a label, a name or "None", Choose…/Clear, and, once a caller has them, a thumbnail, a w×h · weight subtitle and a format badge
TexturePathField A TextureHint's "path on disk" row: a box to type a path into and, when PathOffers names any, a button opening FilePickerDialog over them. A path that does not fit the hint's extensions is flagged, not refused
ColorSwatchField A ColorHint's row: a swatch that opens ColorPaletteDialog (a twelve-hue, four-brightness grid, plus a row of greys), beside one number box per channel. It is separate from ColorField; see below
HintTextBox / NumbersRow FieldRow's fallback for a value nothing has hinted: one text box that reports on Enter or on losing focus, and a row of them for a vector. HintTextBox wraps itself in Focus(onKeyEvent: skipRemainingHandlers), so a keystroke typed into it does not also reach an ancestor that reads bare letters as a tool shortcut
FieldRow One field: its name, and whatever the six above draw when a MaterialHint calls for one of them, or, failing that, whatever HintTextBox/NumbersRow draws for the value's type. apps/flutter3d_editor's inspector panel and material panel are its two callers

Every widget reads Theme.of(context) for colour. Row-level sizing (how tall a row is, how wide a label column is, a field's corner radius, a texture thumbnail's edge) comes from EditorWidgetsTheme.of(context) instead. That falls back to rowHeight: 32, labelWidth: 96, fieldRadius: 6, thumbnailSize: 26 when the ambient ThemeData has not registered one, so a widget from this package dropped into an application that has not opted in still renders sensibly.

Not ColorField

ColorSwatchField and ColorField both edit a colour and neither is built from the other. ColorField is a full HSV picker with a hex box, meant for a caller that wants that whole surface inline over a three- or four-channel value. ColorSwatchField is the level editor's lighter row from before that picker existed: a swatch opens a flat grid of pre-mixed swatches to land roughly on a colour, and the exact numbers are typed beside it. A level's colour hint never asked for hue and saturation sliders, only for "pick roughly the right paint, then type the exact number". Unifying them would mean picking one interaction for both callers; keeping two lets each row stay the shape its document wants.

Two number formatters, kept apart

NumberField.show and numberText (used by FieldRow, HintTextBox and NumbersRow) both turn a number into short text, and neither calls the other. NumberField.show fixes an ordinary field to three decimal places and strips the trailing zeros: a position of 1 reads as 1, one of 0.3333… stops at 0.333, and the field decides how much precision a number this size is worth showing. numberText never rounds. 2 stays 2 and 2.5 stays 2.5 however many digits are in it, because a number a document already carries (a field's raw value, or a RangeHint's min/max printed beside an out-of-range value) has to be shown exactly as the document has it, not rounded to a slider's precision. Both were moved here as they already stood, one from each side. Forcing them through a shared signature would have meant taking a rounding policy as a parameter for one caller and ignoring it for the other.

Depends on flutter and flutter3d_core

RangeSliderField's step, EnumField's options, and the hints of TexturePathField/ColorSwatchField/FieldRow read RangeHint/EnumHintValue/TextureHint/ColorHint/MaterialHint off package:flutter3d_core/formats.dart. They never read from flutter3d_editor_core (the .fmat gate stays there) or from flutter3d_model_core (its ParamHint/EnumHint name a different type than anything here reads). A widget that seems to need either of those two needs a parameter or a callback added to it, not an import.

Libraries

flutter3d_editor_widgets
Editor controls shared between the modeller and the level editor — ui-27's own package. SectionLabel, NumberField, ColorField, RangeSliderField, EnumField, TextureSlotRow, TexturePathField, ColorSwatchField, HintTextBox/NumbersRow and FieldRow so far, all reading Theme.of(context) for colour and EditorWidgetsTheme for row-level sizing.