PlMeter
A quantity inside a range, drawn as a bar. It looks like a progress bar and it is not one: progress is something advancing, and a meter is something already known.
import { PlMeter } from 'plass-ui';
<PlMeter
value={82}
label="Disk used"
showValue
thresholds={[
{ from: 75, color: 'warning' },
{ from: 90, color: 'danger' }
]}
/>;import 'package:plass_ui/plass_ui.dart';
PlMeter(
value: 82,
label: const Text('Disk used'),
showValue: true,
thresholds: const <PlMeterThreshold>[
PlMeterThreshold(from: 75, color: PlassColor.warning),
PlMeterThreshold(from: 90, color: PlassColor.danger),
],
);Props
| Prop | Type | Default | Description |
|---|---|---|---|
| value * | number | — | How much there is. Required, and that is the whole difference from a PlProgressLinear: a meter reports a quantity that is already known, so there is no indeterminate case |
| min | number | 0 | The bottom of the range |
| max | number | 100 | The top of it |
| label | ReactNode | — | A name for what is being measured. Read out with the value |
| showValue | boolean | false | Shows the value as text beside the bar. A percentage of the range unless format says otherwise |
| format | Intl.NumberFormatOptions | — | How the value is written — Intl.NumberFormat options, so bytes and currencies work too |
| thresholds | readonly PlMeterThreshold[] | — | Bands that change the bar's family as the value climbs. The highest from at or below the value wins, and order does not matter |
| sizeshared | 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'md' | Thickness of the groove. Nothing else on a meter has a size |
| colorshared | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | 'primary' | The family the bar takes where no threshold applies |
| Prop | Type | Default | Description |
|---|---|---|---|
| value * | double | — | How much there is. Required, and that is the whole difference from a PlProgressLinear: a meter reports a quantity that is already known, so there is no indeterminate case |
| min | double | 0 | The bottom of the range |
| max | double | 100 | The top of it |
| label | Widget? | — | A name for what is being measured. Read out with the value |
| showValue | bool | false | Shows the value as text beside the bar. A percentage of the range unless format says otherwise |
| formatValue | String Function(double value)? | — | How to write the value, as a function rather than React's options object: there is no Intl.NumberFormat in the framework, and pulling package:intl in to provide one would be a dependency decision made on the consumer's behalf |
| thresholds | List<PlMeterThreshold>? | — | Bands that change the bar's family as the value climbs. The highest from at or below the value wins, and order does not matter |
| sizeshared | PlassSize | PlassSize.md | Thickness of the groove. Nothing else on a meter has a size |
| colorshared | PlassColor | PlassColor.primary | The family the bar takes where no threshold applies |
PlMeterThreshold
| Prop | Type | Default | Description |
|---|---|---|---|
| from * | number | — | The value the band begins at, in the meter's own units rather than a percentage |
| color * | 'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info' | — | The family the bar takes while the value is in this band |
| Prop | Type | Default | Description |
|---|---|---|---|
| from * | double | — | The value the band begins at, in the meter's own units rather than a percentage |
| color * | PlassColor | — | The family the bar takes while the value is in this band |
Every native <div> attribute passes straight through. What the shared axes mean across the library is in prop conventions.
Meter or progress bar
The two are drawn out of the same groove and the same gradient, and they say different things.
PlProgressLinear | Something is advancing. An upload, an install, a step of four. It can be indeterminate, because nobody always knows how much is left. |
PlMeter | Something is already known. Disk used, seats taken, a password's strength, how full a battery is. It is not going anywhere on its own. |
Which follows into the API: value is required here and there is no sweep, because a meter with nothing to report is not a meter. It is a bar that should not have been drawn yet.
And into the semantics: the role is meter, not progressbar. A screen reader announces the two differently, and telling somebody a static figure is in progress is telling them to wait for something that will never finish.
Base UI's Meter owns all of that (the role, the range attributes, aria-valuetext, the formatting and the fill width) the same way its Progress owns a progress bar's. What is left here is the material.
The semantics are where the two builds genuinely differ. SemanticsRole has no meter, and claiming progressBar would announce the one thing this widget exists to say it is not, so the Flutter build reports a named node carrying a value and no role at all. That is what the platforms read out for either one in practice; what is given up is the role name itself.
thresholds
The prop it exists for. A quota bar that turns amber at three quarters and red at ninety percent says something a fixed colour cannot, and the colour is derived from the value rather than chosen by the caller at the moment they happened to be looking at it.
Three rules, and none of them has an order in it:
- The band with the highest
fromat or below the value wins. The list is read, not walked, so writing the bands in any order gives the same answer. fromis in the meter's own units, not a percentage. Unless the range happens to be one. A band outsidemin…maxis simply never reached.coloris what the bar is made of below every band.
Turn showValue on with them. A band is a second way of saying how full something is; it must never be the only one, because a reader who cannot tell amber from red is left with a bar and no number.
Examples
A range that is not a percentage
min and max are the units the figure is actually in, and format writes it out in them. Without format the value reads as a percentage of the range, which is the only formatting that holds for a range nobody described.
<PlMeter
value={18}
max={100}
label="Documents"
showValue
format={{ style: 'unit', unit: 'gigabyte' }}
/>formatValue is a callback rather than an options object, and that is deliberate. There is no Intl.NumberFormat in the framework, and a package that pulled package:intl in to provide one would be making a dependency decision on its consumer's behalf. Whatever formats numbers in the app already formats this one.
PlMeter(
value: 18,
label: const Text('Documents'),
showValue: true,
formatValue: (double value) => '${value.toStringAsFixed(0)} of 100 GB',
);Password strength
Four steps rather than a hundred, which is what min and max are for.
<PlMeter
value={score}
min={0}
max={4}
label="Password strength"
thresholds={[
{ from: 2, color: 'warning' },
{ from: 3, color: 'success' }
]}
color="danger"
/>Notes
- A value outside the range is clamped, and both halves of that agree: the bar is drawn at the edge of the range and the value announced is the clamped one, so what is read out and what is on screen never disagree.
valueusually arrives from a division somewhere, and a bar rendered 140% wide because one number was counted twice is a worse bug than a bar that sits full. - An empty range (
maxat or belowmin) leaves the bar at nothing. It is a caller's mistake rather than a state, and a full bar would report something untrue. - The groove is
--plass-track, the same neutral ink a slider's rail and a switch's off state are cut in, and the fill is the family's gradient. The travel is on the width, because a gradient cannot be transitioned and a length can. - No
variant, nodensity, noelevation. A meter is one material, it has nothing to pad, and it is cut into the surface it sits on the way a groove is.
Accessibility
- The value is announced as text rather than as a bare number,
aria-valuetextin React, the node's value in Flutter. "3" out of a range that is not 0–100 is a percentage the platform would guess wrong. labelnames the meter, and it is the same string a sighted reader sees. Without one the bar is an unnamed figure, which is a number with nothing attached to it.- With
showValuethe figure is drawn and carried on the node, and the drawn copy is hidden from the accessibility tree so it is heard once rather than twice. - Colour is never the only carrier of a band. Pair
thresholdswithshowValue.