Skip to main content
iVentureTeam

float_toggle

The float_toggle widget turns a float into a button that cycles through a fixed list of values on each click. It declares five options, and one of them is read by nothing.

September 18, 2026Updated September 18, 20265 min read
Technical namefloat_toggle
Field typesfloat
Viewsform, list
Moduleweb, present in every Odoo database
Used in core0 uses in Odoo 19 Community or Enterprise. Third-party timesheet and planning modules are its usual home.
VersionsOdoo 19.0, Odoo 20.0
No-code setupNo. There is no Studio entry for this widget.
Alternativesfloat_factor, selection, boolean_toggle, float_time

What the float_toggle widget does

float_toggle replaces the number input with a button showing the formatted value. Clicking it advances to the next entry in range, and past the end it wraps around to the first:

onChange() {
    let currentIndex = this.props.range.indexOf(
        this.props.record.data[this.props.name] * this.factor
    );
    currentIndex++;
    if (currentIndex > this.props.range.length - 1) {
        currentIndex = 0;
    }
    this.props.record.update({
        [this.props.name]: this.props.range[currentIndex] / this.factor,
    });
}

Two things follow from that code. The current value is matched against the range with indexOf, so a stored value that is not exactly in the range returns -1 and the next click lands on the second entry, not the first. And the factor is applied on the way in and divided out on the way back, exactly like float_factor.

What this means for your team

This is a one-click field. It suits the case where a value is genuinely one of a few options and typing a number is overkill: a half day or full day, a confidence level of nothing, half or certain, a simple weighting.

The gain is speed in list views, where a user can set dozens of rows without opening anything. The cost is that a user cannot enter a value outside the range at all. If somebody eventually needs 0.75 and it is not in the range, they cannot type it, and the field looks broken to them rather than restricted.

Decide that up front: if the set of values is genuinely closed, this is a good widget, and if it is merely usually one of three, use a plain float.

Supported options in Odoo 19

Five options are declared. Four of them work. We list the fifth because Odoo declares it, and because a developer reading the source list will otherwise assume it does something.

OptionTypeWhat it does
rangelist of numbersThe values the button cycles through, in order. Clicking past the last entry wraps to the first. A stored value that is not exactly in this list does not match, and the next click lands on the second entry rather than the first.(default: [0.0, 0.5, 1.0])
factornumberMultiplier applied before matching against the range and divided back out when writing. Works exactly like the factor option on float_factor, so the range you write is expressed in displayed units rather than stored ones.(default: 1)
digitsdigitsDisplay precision as a [total, decimal] pair. If the field also carries a digits XML attribute, the attribute wins and this option is ignored, which a comment in the source flags as a historical accident.
force_buttonbooleanNamed for what it does, not for what it is called internally: it sets disableReadOnly, which keeps the button clickable even when the field is readonly. The Odoo option label is Disable readonly.(default: false)
typestring<strong>Declared but never read.</strong> It appears in supportedOptions, so Studio and any options helper will offer it, but extractProps ignores it and the component has no matching prop. Setting it has no effect in Odoo 19 or Odoo 20.

Two traps here, both verified in extractProps. First, type is declared as a supported option but is never read, so it is inert. Second, when both the digits attribute and the digits option are present, the attribute wins: the code checks attrs.digits first and only falls back to options.digits. A source comment calls this out as a historical accident that a future XML refactor could remove.

Working examples

Default behavior, cycling 0, 0.5 and 1:

<field name="unit_amount" widget="float_toggle"/>

A custom range with a factor, and the button kept clickable in readonly:

<field name="day_share" widget="float_toggle"
       options="{'range': [0, 0.25, 0.5, 0.75, 1], 'factor': 1, 'force_button': True}"/>

This option is accepted and ignored:

<!-- 'type' is declared by the widget but read by nothing -->
<field name="day_share" widget="float_toggle" options="{'type': 'anything'}"/>

The type option is declared and never used

The descriptor lists five supported options: digits, type, range, factor and force_button. Its extractProps returns four props:

extractProps: ({ attrs, options }) => {
    // Sadly, digits param was available as an option and an attr.
    let digits;
    if (attrs.digits) {
        digits = JSON.parse(attrs.digits);
    } else if (options.digits) {
        digits = options.digits;
    }

    return {
        digits,
        range: options.range,
        factor: options.factor,
        disableReadOnly: options.force_button || false,
    };
},

options.type appears nowhere, and the component's props declaration has no type entry either. Setting it changes nothing at all.

This matters beyond trivia. Anything that reads supportedOptions to build a configuration interface, including Odoo Studio and every third-party widget-options helper, will offer type as a real setting. A user will set it, see no change, and reasonably conclude the widget is broken.

The same block shows the digits precedence rule. If a view sets digits="[16, 2]" as an attribute and something inheriting that view later adds options="{'digits': [16, 4]}", the option loses silently. When a float_toggle refuses to change its precision, check for an attribute on the original view before changing the option again.

Version compatibility

VersionStatusNotes
Odoo 19.0VerifiedVerified against the shipped 19.0 source. Five options, one of them inert.
Odoo 20.0VerifiedAdds hide_trailing_zeros. Nothing removed.

One option added in Odoo 20, nothing removed.

What is changing in Odoo 20

Odoo 20 adds one option, hide_trailing_zeros, which trims zeros to the right of the last significant digit so a value like 1.20 renders as 1.2. We verified this by diffing the float_toggle registration between the 19.0 and 20.0 branches.

Nothing is removed and the registration is otherwise identical, so existing float_toggle fields carry over unchanged. The dead type option is still declared and still unread in Odoo 20.

The same option arrived on statinfo in Odoo 20, which suggests a deliberate pass over numeric formatting rather than a one-off.

Common problems and fixes

SymptomCause and fix
The type option has no effectIt is declared in supportedOptions but never read by extractProps. The option is inert. Remove it. If you need a formatting change, use digits, or hide_trailing_zeros on Odoo 20.
The first click jumps to the second value instead of the firstThe stored value is not exactly present in range, so indexOf returns -1 and the increment lands on index 1. Make sure stored values match the range exactly, allowing for the factor, or widen the range to include them.
The digits option is ignoredThe field also carries a digits attribute, which takes precedence in extractProps. Remove the attribute, or change the attribute instead of the option. Check inherited views, since the attribute may come from the parent.
Users cannot click the button on a readonly fieldDefault behavior. disableReadOnly is false unless you set it. Add options="{'force_button': True}".
A zero value still shows the button rather than an empty cellThe descriptor sets isEmpty to always return false, so the field is never treated as empty. Working as designed. Use a plain float widget if you want empty rendering.

Float_toggle widget vs the alternatives

WidgetBest forKey difference
float_toggleA float that is really one of a few fixed valuesOne-click cycling, with no way to type a value outside the range
float_factorA scaled float the user types freelySame factor idea, but a normal input instead of a button
selectionA genuinely closed set of choicesSearchable and groupable, and shows every option at once
boolean_toggleA two-state valueBoolean rather than float, so no arithmetic downstream
float_timeA float representing a durationRenders hours and minutes and is freely typed

If the value is genuinely a small closed set, compare this against a selection field holding the same choices. A selection is searchable, groupable and shows the user every option, where float_toggle hides the choices behind repeated clicking. Choose float_toggle when the value must stay numeric for arithmetic downstream, and a selection when it is really a category that happens to be stored as a number.

Frequently asked questions

How do I change the values an Odoo float_toggle cycles through?+
Set the range option, for example options="{'range': [0, 0.25, 0.5, 0.75, 1]}". The default is [0.0, 0.5, 1.0]. Clicking past the last value wraps back to the first.
Why does the type option on float_toggle do nothing?+
Because nothing reads it. It is declared in the widget's supportedOptions, so Studio and options helpers offer it, but extractProps never reads options.type and the component has no matching prop. This is true in both Odoo 19 and Odoo 20.
How do I make a readonly float_toggle still clickable?+
Set options="{'force_button': True}". The option maps internally to disableReadOnly, and Odoo labels it Disable readonly.
Does float_toggle change in Odoo 20?+
Yes, it gains a hide_trailing_zeros option that renders 1.20 as 1.2. Nothing is removed, so existing fields carry over unchanged.

Click-to-set fields that fight your users?

A toggle is excellent when the value set is truly closed and miserable when it is not, and the difference only shows up months in. We design the field before we pick the widget, then build it for Odoo 16 through 19 with an eye on what Odoo 20 changes.

Book a free consultation

How this page was produced

Verified by reading addons/web/static/src/views/fields/float_toggle/float_toggle_field.js on the 19.0 branch of a local clone of the official Odoo repository. The dead type option was confirmed by comparing the declared supportedOptions list against both extractProps and the component's props declaration, quoted above. Defaults are read from static defaultProps. The Odoo 20 change comes from diffing the registration against the 20.0 branch. The usage count comes from scanning every XML file in Community, Enterprise and odoo/addons/base. If Odoo wires up type in a later release we will update this page and say so. Corrections welcome via our contact page.