Standard plotting widgets for Controls Flutter applications. Available plotting widgets:
TODO
BarChartWidget acquires the latest scalar reading for each configured device.
A BarChartController can also add, replace, or remove client-managed segments
such as setpoints, limits, or composition values.
The public bar-chart API is renderer-neutral: clients use BarChartModel,
BarSegment, BarChartStyle, and BarChartController; they do not supply
fl_chart types. BarChartAdapter is the renderer boundary and the current
FlChartBarAdapter is an internal implementation detail. This keeps the data
contract, acquisition, and controller state reusable if the renderer changes.
BarChartLayout.grouped preserves the original behavior. Each segment becomes
an independent side-by-side rod within the device's category.
final controller = BarChartController(deviceNames: ['MAGNET:A']);
controller.setSegment(
device: 'MAGNET:A',
key: 'reading',
label: 'Reading',
value: 12.5,
);
controller.setSegment(
device: 'MAGNET:A',
key: 'setpoint',
label: 'Setpoint',
value: 15,
);Use BarChartLayout.stacked when a device's segments are components of one
bar. Each device may have a different number of segments. The order in which
segments are supplied is their bottom-to-top stack order.
final controller = BarChartController(
deviceNames: ['SOURCE:A'],
style: const BarChartStyle(layout: BarChartLayout.stacked),
);
// Additive segments contribute from the previous segment's end.
controller.setSegment(
device: 'SOURCE:A',
key: 'beam',
label: 'Beam',
value: 2.5,
);
// Explicit ranges are supported when the client owns the cumulative bounds.
controller.setRangeSegment(
device: 'SOURCE:A',
key: 'loss',
label: 'Loss',
start: 2.5,
end: 3.0,
);In stacked layout, positive and negative additive segments start independently
at zero: positive contributions accumulate upward and negative contributions
accumulate downward. Explicit ranges must remain on one side of zero and start
at the preceding segment's resolved end on that same signed side. Gaps,
overlaps, ranges crossing zero, ranges reversing toward zero, and non-finite
values throw ArgumentError; the controller retains its previous model state
when that happens. A small floating-point tolerance is applied when checking
contiguous explicit bounds.
Explicit ranges store fixed coordinates and are not automatically rebased when an earlier contribution changes or is removed. In the example above, changing the beam contribution from 2.5 to 2.0 would leave a gap before the loss range, so the update is rejected. Streamed replies are rejected atomically: if any device's proposed stack is invalid, no segments, units, timestamps, or channel errors from that reply are published. Invalid removals likewise preserve the entire previous snapshot, and rejected controller operations do not notify listeners.
To remove an earlier contribution, remove its dependent ranges first (starting with the outermost range if several depend on one another), or clear all segments on that device in one call. To represent a fixed-size contribution that should move with a live reading, use an additive segment instead of an explicit range.
Configure axis divisions, Y labels, and device-label rotation through
BarChartStyle without exposing renderer types:
final style = BarChartStyle(
minY: -1,
maxY: 1,
yAxisDivisions: 4,
xAxisLabelRotation: 45,
yAxisLabelFormatter: (value) => '${value.toStringAsFixed(2)} A',
);yAxisDivisions is the number of equal intervals across the displayed Y range;
null preserves automatic renderer ticks. When no formatter is supplied, the
adapter derives enough decimal precision from the tick interval to distinguish
fractional values. Fixed Y limits clip the rendered rod geometry to the visible
range, while touch tooltips retain the original raw values and stacked ranges.
xAxisLabelRotation is expressed in degrees and is mapped to fl_chart's native
axis-title rotation.
Grouped layout remains the default for source compatibility. Existing calls to
setSegment remain additive values and therefore continue to render as
independent rods unless the style opts into stacked layout.
- Categorical device groups and stable device ordering.
- Grouped rods and contiguous stacked rods.
- Additive contributions and explicit segment intervals.
- Per-device units and error state.
- Automatic or fixed Y-axis bounds with clipped render geometry and raw-value tooltips.
- Configurable Y-axis division count, precision-aware or custom Y labels, and degree-based X-label rotation.
- Segment width, segment/group spacing, corner radius, and colors.
- Axis-title visibility, device labels, error indicators, and touch tooltips.
The fl_chart adapter uses space-around category alignment, fixed axis-title layout and typography (except division count, Y label formatting, and rotation), fixed tooltip styling, and a 10% auto-scale padding policy. These choices are intentionally not public configuration yet.
The bar abstraction does not currently expose fl_chart gradients, rod borders, background rods, error indicators, grid/border styling, annotations or extra lines, touch callbacks or programmatic highlights, group alignment, or animation. No fl_chart configuration classes are part of the package's public bar-chart API.
The bar chart has a clean data-to-renderer boundary: BarChartWidget owns
stream lifecycle, BarChartController and BarChartReducer own neutral state,
and BarChartAdapter renders a BarChartModel. Unlike PlotWidget, which can
select among several renderer adapters, the bar widget currently constructs the
fl_chart adapter directly. Replacing the bar renderer would therefore require a
small widget-level selection or injection change, but would not require
changing the bar data contract.
Run the focused bar-chart tests with:
flutter test test/widget/bar_chart_data_test.dart \
test/widget/fl_chart_bar_adapter_test.dart \
test/widget/bar_chart_widget_test.dart