Inputs
CDX UI ships purpose-built input controls for the cases Material's defaults don't cover well: a labeled slider, a live-formatting rich text field, and a consistent form-field label primitive.
CdxSlider
Canonical labeled slider for the design system. Wraps Material's Slider with a primary-coloured value label, optional leading icon, and configurable min/max end labels — replaces the ad-hoc "duration / days / count" sliders that each rolled their own chrome.
// Top-centered value with calendar icon, days formatting
CdxSlider(
value: days.toDouble(),
min: 0,
max: 365,
onChanged: (v) => setDays(v.round()),
format: (v) => '${v.round()} days',
icon: FluentIcons.calendar_24_regular,
)
// Compact row: trailing value, no end labels
CdxSlider(
value: rows.toDouble(),
min: 10, max: 30, divisions: 2,
onChanged: (v) => setRows(v.toInt()),
format: (v) => v.toInt().toString(),
labelPosition: CdxSliderLabelPosition.trailing,
showEndLabels: false,
)Parameters
| Param | Type | Default | Notes |
|---|---|---|---|
value | double | required | |
min / max | double | required | |
divisions | int? | null | |
onChanged | ValueChanged<double> | required | |
onChangeEnd | ValueChanged<double>? | null | Forwarded to Slider.onChangeEnd |
format | String Function(double) | required | Drives the prominent label + Material drag tooltip |
endLabelFormat | String Function(double)? | falls back to format | Min/max end labels |
icon | IconData? | null | Leading icon next to the value label |
labelPosition | CdxSliderLabelPosition | top | top / trailing / none |
showEndLabels | bool | true | Auto-suppressed on right when labelPosition is trailing |
compact | bool | false | Smaller value label + icon for tight surfaces |
RemarksField
Rich-text input with live inline formatting. As the user types markers like **bold** or ==highlight==, they render formatted in-place. When the cursor leaves a span, the markers hide (MarkerVisibility.whenActive).
RemarksField(
controller: _remarksController,
labelText: 'Comments',
isRequired: true,
hintText: 'Why was this rejected?',
helperText: 'Visible to approvers and the submitter.',
maxLines: 4,
showToolbar: true,
errorText: form.errors['comments'],
)Parameters
| Param | Type | Default |
|---|---|---|
controller | TextEditingController? | null (creates internal TextfEditingController) |
labelText | String? | null |
isRequired | bool | false (adds red * next to label) |
hintText / helperText / errorText | String? | null |
maxLines | int | 4 |
maxLength | int? | null |
showToolbar | bool | true |
prefixIcon | Widget? | null |
autofocus / focusNode / onChanged | standard |
When the caller passes a plain TextEditingController, RemarksField syncs back to it whenever the internal TextfEditingController changes — so existing form-binding patterns work unchanged.
RemarksText
Read-only renderer for textf-formatted strings. Drop-in replacement for Text that parses the same markup as RemarksField:
RemarksText(
text: comment.body,
maxLines: 5,
overflow: TextOverflow.ellipsis,
)Markup
| Markup | Rendered |
|---|---|
**bold** / __bold__ | bold |
*italic* / _italic_ | italic |
~~strikethrough~~ | strike |
++underline++ | underline |
==highlight== | highlight (tertiary container background) |
`code` | inline code (monospace + surfaceContainerHighest) |
^superscript^ | superscript |
~subscript~ | subscript |
FormattingToolbar
The bare toolbar embedded inside RemarksField. Use it directly when building a custom rich-text container.
FormattingToolbar(
controller: myTextfController,
focusNode: myFocusNode,
)Renders a row of icon buttons for bold / italic / highlight / underline / strikethrough / code / superscript / subscript. Each wraps the current selection (or inserts empty markers at the cursor).
CdxFieldLabel
Standard form-field label — mirrors the fieldLabel(context) extension in vyuh_feature_forms. Use it for non-form widgets that still need to present the same visual contract (label in labelMedium, required * in error color, focus / error coloring).
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
CdxFieldLabel(
label: 'Operator',
required: true,
hasFocus: _hasFocus,
hasError: _hasError,
trailing: Button.text(label: 'Create new', onPressed: _createOperator),
),
MyCustomInput(...),
])| Param | Default |
|---|---|
required | false (red * next to label) |
hasFocus | false (drives label color to primary) |
hasError | false (drives label color to error) |
padding | EdgeInsets.only(bottom: 6) |
trailing | null (optional same-row affordance such as help or "Create new") |