> ## Documentation Index
> Fetch the complete documentation index at: https://neo.tvk.company/llms.txt
> Use this file to discover all available pages before exploring further.

# Fluid Counter

> Animated numeric display where each digit rolls like an odometer when its value changes.

## Examples

<Tabs>
  <Tab title="Basic Usage">
    <CodeGroup>
      ```dart Basic Counter lines theme={null}
      NeoFluidCounter(
        count.value,
        style: theme.textStyles.header2,
      ),
      ```
    </CodeGroup>
  </Tab>

  <Tab title="With Formatter">
    <CodeGroup>
      ```dart With Formatter lines theme={null}
      // Define the formatter at top level or as a static so it has a stable identity.
      String _formatCurrency(num n) => '€${n.toStringAsFixed(2)}';

      NeoFluidCounter(
        price.value,
        formatter: _formatCurrency,
        style: theme.textStyles.header3,
      ),
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Face-Value Mode">
    <CodeGroup>
      ```dart Face-Value Mode lines theme={null}
      NeoFluidCounter(
        score.value,
        continuous: false,
        style: theme.textStyles.header1,
      ),
      ```
    </CodeGroup>
  </Tab>
</Tabs>

## Properties

### Required

<ParamField path="value" type="num" required>
  The numeric value to display. When this changes, each digit rolls through intermediate values (in `continuous` mode) or directly to the new face value.
</ParamField>

### Content

<ParamField path="formatter" type="String Function(num value)">
  Converts `value` to a display string. Supports locale-aware separators (both `.` and `,` as decimal points). Pass a top-level or `static` function to keep its identity stable across builds and avoid discarding cached transitions.

  Defaults to `value.toString()`.
</ParamField>

<ParamField path="style" type="TextStyle">
  Text style applied to all digits and decorators. Merges with `DefaultTextStyle`.
</ParamField>

<ParamField path="textAlign" type="TextAlign">
  Horizontal alignment of the counter within its bounding box.
</ParamField>

<ParamField path="textDirection" type="TextDirection">
  Reading direction. Defaults to the ambient `Directionality`.
</ParamField>

### Styling

<ParamField path="duration" type="Duration">
  Override the animation duration. Defaults to `theme.durations.long`.
</ParamField>

<ParamField path="continuous" type="bool" default="true">
  When `true` (default), digits roll through every intermediate step like an odometer — e.g. going from 100 to 200 spins the tens and units through all values. When `false`, only digits whose face value actually changes animate, jumping directly to the target digit.
</ParamField>

<ParamField path="maskHeightFraction" type="double" default="0.25">
  Fraction of the line height used as vertical padding on each edge for the fade-out gradient. Higher values give a taller fade zone above and below the rolling digits. Defaults to `0.25`.
</ParamField>

## Best Practices

* **Stable formatter**: The `formatter` function is used as a `useMemoized` dependency. Passing an inline lambda on every build forces a new transition on every rebuild even when `value` hasn't changed. Always use a top-level, `static`, or `useMemoized`-wrapped function.
* **Integer vs decimal**: `NeoFluidCounter` auto-detects decimal separators (`.` and `,`) in the formatted string and handles the odometer independently for integer and fractional digit groups.
* **Continuous vs face-value**: Use `continuous: true` (default) for counters like scores or progress values where the rolling motion reinforces the sense of change. Use `continuous: false` for values that jump arbitrarily (e.g. a live price feed) where rolling through every intermediate step would be distracting.

## Integration Notes

* `NeoFluidCounter` uses tabular-figures (`FontFeature.tabularFigures()`) automatically so digit columns don't shift width during the roll animation.
* Like [`NeoFluidText`](/widgets/utilities/fluid-text), the widget uses a custom `RenderObject` for intrinsic size measurement, so it sizes itself exactly like a `Text` widget would for the displayed value.
* The semantic label is set to the formatted string so screen readers announce the current value correctly.
