Mastering Floating-Point Precision in Flutter Web: Pitfalls and Solutions
If you are building cross-platform applications with Flutter, you already know the incredible power of a shared codebase. However, translating Dart code to run natively in a browser comes with a unique set of platform-specific quirks.
One of the most dangerous, yet easily overlooked, issues in Flutter Web development is floating-point inaccuracy. While it might seem like niche computer science trivia, it becomes a critical roadblock in applications that handle financial transactions, cryptographic IDs, or large data sets.
In this guide, we'll break down exactly why this precision loss happens, why Flutter Web makes the problem uniquely painful, and provide production-ready solutions including an advanced strategy for safely intercepting JSON responses.
Why Are Floating-Point Numbers Inaccurate?
Computers are incredibly efficient at handling vast amounts of data, but they have inherent limitations when representing real numbers. The root cause of floating-point inaccuracy lies in how computers physically store these values.
In the digital world, numbers are represented using a base-2 binary system, comprised entirely of 0s and 1s. This contrasts with the base-10 decimal system that humans use. As a result, representing certain decimal fractions in binary form leads to infinite approximations.
Think of the fraction 1/3 in base-10; it is 0.3333... repeating indefinitely. When you attempt to represent certain base-10 decimals (like 0.1) in binary, the result also becomes an infinitely repeating sequence. Because a computer has finite memory, it must eventually cut off this sequence, leading to a subtle discrepancy known as a rounding error.
When performing arithmetic operations, these small inaccuracies accumulate. This is why evaluating simple math in Dart (and most programming languages) yields unexpected results:

For financial data like balances, amounts, or fees, this is unacceptable. A user seeing 10.000000000000001 € instead of 10.00 € is a critical failure in both UI correctness and user trust.
What is a Double, Exactly?
A double-precision floating-point format (often called a double or float64) uses 64 bits (8 bytes) of memory to represent a number. To store a vast range of numbers in these bits, the double utilizes a concept derived from scientific notation, composed of three parts:
- The Sign: Indicates if the number is positive or negative.
- The Base: The underlying numeral system (base-2).
- The Exponent: Determines how far the decimal point is moved.
- The Coefficient (or Significand): The actual digits of the number.

In the IEEE 754 standard for a 64-bit double, the memory is strictly divided:
- 1 bit for the Sign.
- 11 bits for the Exponent.
- 52 bits for the Fraction (the significand).

Because you only have 52 bits dedicated to the actual sequence of digits, the maximum precision of a double caps out at about 15 to 17 decimal digits. Beyond that, trailing digits are lost to approximation.
Why Number Precision is Worse on Flutter Web
On native platforms (iOS, Android, macOS), the Dart Virtual Machine uses true 64-bit integers and 64-bit doubles. However, when you compile Flutter for the Web, Dart code is translated into JavaScript. JavaScript does not traditionally have a separate 64-bit integer type; it treats all numbers as IEEE 754 double-precision floating-point numbers. This architectural difference severely amplifies the precision problem for two reasons:
- Large integers lose precision entirely: The maximum safe integer JavaScript can process without losing precision is 9,007,199,254,740,991 (253 - 1). If your app handles large integers (e.g., Satoshi amounts, Snowflake IDs, database primary keys), JavaScript will silently round and corrupt them.
- JSON parsing is compromised early: JSON parsing in the browser goes through JavaScript's native
JSON.parse(), which forces all numbers into JS Numbers. Your data loses precision before your Dart code even sees the payload.
The Solution: Using the Dart decimal Package
To circumvent these browser limits, never use doubles for financial data. Instead, utilize the highly reliable decimal package from pub.dev. It provides arbitrary-precision decimal numbers, storing the exact base-10 representation so mathematical truths hold up:

Implementing Decimal in Domain Entities
To prevent accidental double usage, strictly type the financial fields in your Data Transfer Objects (DTOs) and models:

The JSON Trap: How to Safely Parse External Data
The most common place developers encounter the floating-point crash on Flutter Web is when parsing JSON from a backend API.
By default, standard HTTP clients automatically parse JSON responses using dart:convert's jsonDecode. This function converts JSON numbers directly into Dart doubles, meaning the precision is lost instantly:
- API response:
{"balance": 1234567.8901234502} - jsonDecode:
{"balance": 1234567.89012345}(Precision lost!) - Decimal.parse: Too late, the damage is already done.
To fix this, choose one of two approaches depending on your level of control over the backend API.
Approach 1: The API Contract (Ideal)
If you control the backend, the safest route is to configure your server to serialize large integers and precise decimals as Strings.
If the API sends {"amount": "1234.56"}, standard jsonDecode paired with Decimal.parse() works perfectly. No precision is lost because the data travels safely as text.
Approach 2: Custom Interception with Dio (Real-World Workaround)
If the API returns financial amounts as raw JSON numbers and you cannot change the backend, you must bypass your HTTP client's automatic parsing. Here is how to handle it manually using the standard dio package paired with jsontool.
Step 1: Get the raw JSON string
Force Dio to return the raw string without parsing it by setting ResponseType.plain:

Step 2: Parse with a Decimal-aware processor
Feed that raw string to a custom parser that walks the JSON tokens manually. By extending jsontool's JsonSinkProcessor, you can override the processNum method to intercept every number before it becomes a double:

Step 3: Consume safely in your DTO
The resulting map now contains true Decimal objects instead of compromised doubles, preserving the exact text directly from the API.

Deserialization Strategy Cheat Sheet
| Approach | When to use it | Example Scenario |
Custom JSON parsing (jsontool) | Numbers arrive as raw JSON numbers; precision must be preserved. | Vaults, Balances, Cryptocurrencies. |
| String-based parsing | The API contract already sends decimals as JSON strings. | Standard Payment Payloads. |
| Manual Parse | Simple, controlled internal conversions (Decimal.parse(val.toString())). | Local order creation UI logic. |
Final Thoughts
Building for the web with Flutter is a fantastic experience, but the abstraction of compiling Dart to JavaScript can sometimes hide browser-level limitations until they crash your app in production.
By utilizing the decimal package for all sensitive arithmetic and carefully controlling how your application intercepts network data before the JavaScript engine processes it, you can completely immunize your Flutter Web apps against the hidden dangers of floating-point inaccuracies.
Have you run into floating-point issues on your Flutter Web projects? Let me know if this article help you solve them!