Quantify - Type-safe unit conversion
Quantify is a comprehensive, type-safe unit conversion library. Convert length, mass, temperature, speed, force, energy, and 20+ other physical quantities with compile-time safety, elegant syntax, and optimal performance. Perfect for scientific computing, engineering applications, fitness tracking, IoT projects, and any cross-platform app that works with measurements.
Why quantify?
quantify makes working with physical units in Dart and Flutter safer, more readable, and efficient:
- Type Safety: Prevents unit mismatch errors at compile-time - no more accidentally adding kilograms to meters.
- Elegant API: Intuitive syntax like
10.morlength.inKmthat reads naturally. - Cross-Platform: Works identically across Flutter and pure Dart projects.
- Rich Constants Library: Provides type-safe physical, astronomical, and engineering constants ready for calculations.
- Precise & Performant: Uses
doubleand direct conversion factors for speed and to minimize rounding errors. - Immutable:
Quantityobjects are immutable for safer, more predictable code. - Configurable Output: Highly flexible
toString()for customized formatting with locale support. - Lightweight: Minimal dependencies - just
intlandmeta.
Whether you're building a fitness tracker that converts between miles and kilometers, an IoT dashboard displaying sensor readings in multiple units, an engineering calculator, or a scientific application requiring precise measurements, quantify provides the robust foundation you need.
Quick Start
import 'package:quantify/quantify.dart';
// For locale-specific number formatting, add 'intl' to your pubspec.yaml
// and import 'package:intl/intl.dart';
void main() {
// Create quantities
final pathA = 1500.m;
final pathB = 2.5.km;
// Convert to value / a new Quantity object
double pathInKm = pathA.inKm;
final pathAsKm = pathA.asKm;
// Convert and print
print(pathA.toString(targetUnit: LengthUnit.kilometer, fractionDigits: 1)); // Output: "1.5 km"
print(pathB.toString(targetUnit: LengthUnit.mile, fractionDigits: 2)); // Output: "1.55 mi" (approx.)
// Arithmetic
final distance = pathA + pathB; // pathB is converted to meters
print(distance.toString(fractionDigits: 0)); // Output: "4000 m"
print(distance.toString(
targetUnit: LengthUnit.yard,
fractionDigits: 0,
)); // Output: "4374 yd" (approx., with non-breaking space)
// Locale-specific example (if 'intl' is used)
final distanceDE = 1234.567.m;
print(distanceDE.toString(
targetUnit: LengthUnit.kilometer,
fractionDigits: 2,
locale: 'de_DE',
)); // Output: "1,23 km"
}
Installation
Add to your pubspec.yaml:
dart pub add quantify
dart pub get
Supported Units
The library supports a comprehensive range of physical quantities, including all 7 SI base units and many derived units - ideal for scientific computing, engineering calculations, and any Flutter or Dart application requiring precise unit conversions:
| Quantity Type | Status | Units Available | Notes / SI Base Unit Ref. |
|---|---|---|---|
| Length | ✅ | m (meter), km, hm, dam, dm, cm, mm, μm, nm, pm, fm, in, ft, yd, mi, nmi, AU, ly, pc, Å |
SI Base: Meter (m) |
| Mass | ✅ | kg (kilogram), hg, dag, g, dg, cg, mg, μg, ng, t, lb, oz, st, slug, short ton, long ton, u, ct |
SI Base: Kilogram (kg) |
| Time | ✅ | s (second), μs, ns, ps, ms, min, h, d, wk, mo, yr |
SI Base: Second (s) |
| Electric Current | ✅ | A (ampere), mA, μA, nA, kA |
SI Base: Ampere (A) |
| Temperature | ✅ | K (kelvin), °C (celsius), °F (fahrenheit), °R (rankine) |
SI Base: Kelvin (K). Affine conversions. |
| TemperatureDelta | ✅ | K (kelvinDelta), °C (celsiusDelta), °F (fahrenheitDelta), °R (rankineDelta) |
Linear. Represents a temperature change, not a point. |
| Amount of Substance | ✅ | mol (mole), mmol, μmol, nmol, pmol, kmol |
SI Base: Mole (mol) |
| Luminous Intensity | ✅ | cd (candela), mcd, kcd |
SI Base: Candela (cd) |
| Derived | |||
| Angle | ✅ | rad (radian), ° (degree), grad, rev, arcmin ('), arcsec ("), mrad |
Derived SI: dimensionless |
| Angular Velocity | ✅ | rad/s, °/s, rpm, rps |
Derived SI: 1/s |
| Speed / Velocity | ✅ | m/s (meter per second), km/h, mph, kn (knot), ft/s |
Derived SI |
| Acceleration | ✅ | m/s², g (standard gravity), km/h/s, cm/s² (Galileo) |
Derived SI |
| Force | ✅ | N (Newton), lbf, dyn, kgf, kN, gf, pdl |
Derived SI: kg·m/s² |
| Pressure | ✅ | Pa (Pascal), atm, bar, psi, Torr, mmHg, inHg, kPa, hPa, mbar, cmH₂O, inH₂O |
Derived SI: N/m² |
| Area | ✅ | m², Mm², km², hm², dam², dm², cm², mm², µm², ha, mi², acre, yd², ft², in² |
Derived SI |
| Volume | ✅ | m³, L, mL, gal, fl-oz, ft³, in³, qt, pt, tbsp, tsp... |
Derived SI: L (Liter) |
| Frequency | ✅ | Hz, kHz, MHz, GHz, THz, rpm, bpm |
Derived SI: 1/s |
| Electric Charge | ✅ | C, mC, µC, nC, Ah, e, mAh, statC, abC |
Derived SI: A·s |
| Solid Angle | ✅ | sr, deg² (Square Degree), sp (Spat) |
Derived SI: dimensionless |
| Energy / Work | ✅ | J (Joule), kJ, MJ, kWh, cal, kcal, eV, Btu |
Derived SI: N·m |
| Power | ✅ | W (Watt), mW, kW, MW, GW, hp, PS (metric hp), Btu/h, erg/s |
Derived SI: J/s |
Detailed Usage
Creating Quantities
Use extension methods on num for readability:
final myLength = 25.5.m;
final anotherLength = 10.ft; // feet
final verySmall = 500.nm; // nanometers
final astronomical = 4.2.ly; // light years
Or use the constructor of the specific Quantity class:
final specificLength = Length(5.0, LengthUnit.yard);
Converting and Retrieving Values
-
Get Numerical Value: Use
in[UnitName]getters orgetValue(TargetUnit).final oneMile = 1.0.mi; double milesInKm = oneMile.inKm; // approx 1.609344 double milesInMeters = oneMile.getValue(LengthUnit.meter); // approx 1609.344 final smallDistance = 1.um; // micrometer double inNanometers = smallDistance.inNm; // 1000.0 -
Get New
QuantityObject: UseconvertTo(TargetUnit)oras[UnitName]getters.final tenMeters = 10.m; final tenMetersInFeetObj = tenMeters.convertTo(LengthUnit.foot); // tenMetersInFeetObj is Length(approx 32.8084, LengthUnit.foot) final tenMetersInKmObj = tenMeters.asKm; // tenMetersInKmObj is Length(0.01, LengthUnit.kilometer)
Using the Constants Library
quantify includes a comprehensive library of type-safe physical, astronomical, and engineering constants - perfect for scientific computing and engineering applications.
import 'package:quantify/quantify.dart';
import 'package:quantify/constants.dart'; // Import the constants library
void main() {
// Constants are already Quantity objects, ready for use.
final gravity = AstronomicalConstants.standardGravity; // An Acceleration object
final electron = PhysicalConstants.electronMass; // A Mass object
final speedOfLight = PhysicalConstants.speedOfLight; // A Speed object
final steelStiffness = EngineeringConstants.steelYoungsModulus; // A Pressure object
// Use them in calculations
final weightOfElectron = Force.from(electron, gravity);
print('Weight of an electron on Earth: ${weightOfElectron.inNewtons} N');
// Use convenience methods for common formulas
final photonEnergy = PhysicalConstants.photonEnergy(500.0.nm); // Returns an Energy object
print('Energy of a 500nm photon: ${photonEnergy.inElectronvolts.toStringAsFixed(2)} eV');
}
Formatting Output with toString()
The toString() method on Quantity objects is highly configurable with full locale support:
final myDistance = 1578.345.m;
// Default
print(myDistance.toString()); // "1578.345 m"
// Convert to kilometers, 2 fraction digits
print(myDistance.toString(targetUnit: LengthUnit.kilometer, fractionDigits: 2));
// Output: "1.58 km"
// Scientific notation with micrometers
final smallLength = 0.000523.m;
print(smallLength.toString(targetUnit: LengthUnit.micrometer, fractionDigits: 0));
// Output: "523 μm"
Arithmetic Operations
Standard arithmetic operators (+, -, *, / by a scalar) are overloaded. The result's unit is typically that of the left-hand operand.
final segment1 = 500.m;
final segment2 = 0.25.km; // 250 meters
final total = segment1 + segment2; // Result is in meters
print(total.toString()); // "750.0 m"
final scaled = segment1 * 3;
print(scaled.toString()); // "1500.0 m"
// Work with different scales
final bigMass = 5.t; // tonnes
final smallMass = 250.g; // grams
final combined = bigMass + smallMass; // Result: 5.00025 t
Temperature & TemperatureDelta
Temperature is an affine quantity—absolute temperatures are points on a scale, not linear vectors. This means you cannot add two temperatures (20 °C + 20 °C is not 40 °C), and ratios between Celsius or Fahrenheit values are physically meaningless.
TemperatureDelta is the companion linear quantity representing a temperature change. It behaves like every other quantity: factor-based conversion with no offsets, and full arithmetic support.
// 1. Creating temperature deltas
final rise = 20.celsiusDelta; // A change of 20 °C (equivalent to 20 K)
final drop = 18.fahrenheitDelta; // A change of 18 °F (equivalent to 10 K)
final step = 5.kelvinDelta;
// 2. Point − Point = Vector (Temperature subtraction yields a Delta)
final t1 = 10.celsius;
final t2 = 30.celsius;
final delta = t2 - t1; // Result is a TemperatureDelta object
print(delta.inCelsiusDelta); // 20.0
print(delta.inFahrenheitDelta); // 36.0 (Handles the 1.8x scaling automatically!)
// 3. Point + Vector = Point (Heating an object)
final heated = t1 + 20.celsiusDelta; // Temperature(30.0, celsius)
// 4. Point − Vector = Point (Cooling via named method)
final cooled = t2.subtract(5.kelvinDelta); // Temperature(25.0, celsius)
// 5. Vector Arithmetic & Scaling
final doubled = delta * 2.0; // TemperatureDelta(40.0, celsiusDelta)
final combined = rise + drop; // 20 + 10 = 30 celsiusDelta
// 6. Thermodynamic Ratios (Always calculated in Kelvin for physical validity)
final efficiency = 1.0 - 300.kelvin.ratioTo(600.kelvin); // Carnot: 0.5
// 7. Engineering constants now require TemperatureDelta (Prevents logic bugs)
final expansion = EngineeringConstants.thermalExpansion(
10.0.m,
11.7e-6,
20.celsiusDelta, // Correctly treated as 20 K
);
Important
Why a separate type? Without TemperatureDelta, a 20°C change passed to a formula would be incorrectly converted to $293.15,\text{K}$ (by adding the Celsius offset). This makes the result ~14× too large. The type system now makes this common scientific mistake impossible at compile time.
Comparisons & Sorting: Magnitude vs. Strict Equality
quantify provides a clear and intuitive way to compare quantities, distinguishing between checking for physical magnitude and strict equality.
Magnitude Comparison (using >, <, >=, <=)
All Quantity objects can be compared directly using standard relational operators. The library automatically handles unit conversions, so you can compare any two quantities of the same type (e.g., two Length objects) without worrying about their internal units.
final oneKm = 1.km;
final oneMile = 1.mi;
final thousandMeters = 1000.m;
print(oneMile > oneKm); // true
print(oneKm < 999.m); // false
print(oneKm >= thousandMeters); // true
Equality Checks (isEquivalentTo() vs. ==)
It's important to understand the two types of equality checks available:
- Magnitude Equality (
isEquivalentTo): Checks if two quantities represent the same physical amount. - Strict Equality (
==): Checks if two quantities have the exact same value AND unit.
final oneMeter = 1.m;
final hundredCm = 100.cm;
// Magnitude check: Do they represent the same distance?
print(oneMeter.isEquivalentTo(hundredCm)); // true
// Strict check: Are they represented in the same way?
print(oneMeter == hundredCm); // false (different units: m vs cm)
print(oneMeter == 1.m); // true (same value and unit)
This distinction is crucial when working with collections like Sets or Maps, where the strict equality of == is typically the desired behavior.
Sorting
final lengths = [1.mi, 2000.m, 1.km, 5000.ft]
..sort(); // Sorts by physical magnitude
print(lengths.map((l) => l.toString()).join(', '));
// Output: 1.0 km, 5000.0 ft, 1.0 mi, 2000.0 m
print(lengths.map((l) => l.asM).join(', '));
// Output: 1000.0 m, 1524.0 m, 1609.344 m, 2000.0 m
Managing Imports and Avoiding Conflicts
quantify is designed to be both convenient and robust. By default, importing package:quantify/quantify.dart gives you access to all quantities and their handy extensions (like 10.m or 5.s).
However, in large projects or when combining quantify with other libraries, this might lead to name conflicts with extension methods. To give you full control over what is imported into your project's namespace, quantify provides separate entry points for each quantity type.
If you encounter a name conflict or simply want to be more explicit about your dependencies, you can import only the quantities you need.
Example: Importing only Length and Time
Instead of the main package, you can import specific libraries:
// Import only the extensions you need
import 'package:quantify/length.dart';
import 'package:quantify/time.dart';
void main() {
// Now, only extensions for Length and Time are available.
final distance = 100.m; // Works
final duration = 30.s; // Works
// This will cause a compile-time error because Mass extensions were not imported.
// final weight = 70.kg; // ERROR: The getter 'kg' isn't defined for the type 'int'.
}
This granular approach ensures that quantify can be used safely and effectively in any project, no matter how complex.
Use Cases
quantify is ideal for a wide range of applications:
- Flutter Mobile & Desktop Apps: Build cross-platform fitness trackers, recipe converters, travel planners, or educational apps that work seamlessly across iOS, Android, web, Windows, macOS, and Linux.
- Scientific Computing: Perform precise calculations with physical constants and units in research applications, data analysis tools, or simulation software.
- Engineering Applications: Handle structural calculations, electrical circuit design, fluid dynamics, thermodynamics, and other engineering domains requiring robust unit management.
- IoT & Embedded Systems: Process sensor readings in various units, convert between measurement systems, and display data in user-preferred formats.
- Education & Training: Create interactive learning tools that demonstrate unit conversions and physical relationships.
- Health & Fitness: Track running distances, cycling speeds, body measurements, calorie calculations, and nutrition metrics with proper unit handling.
Goals & Roadmap
- V1.0 (Current): All 7 SI base units with comprehensive unit coverage and a rich constants library. Production-ready for Dart and Flutter applications.
- V2.0 and Beyond:
- High Precision: Support for
Decimaltypes for applications requiring arbitrary precision. - Serialization Support: Built-in JSON serialization/deserialization.
- High Precision: Support for
Contributing
Contributions are welcome! Please open an Issue or a Pull Request.
License
MIT License - see the LICENSE file for details.
Libraries
- acceleration
- Provides type-safe units for Acceleration.
- angle
- Provides type-safe units for Angle.
- angular_velocity
- Provides type-safe units for Angular Velocity.
- area
- Provides type-safe units for Area.
- constants
- Physical, astronomical, and engineering constants for the quantify package.
- current
- Provides type-safe units for Electric Current.
- density
- Exports the 'Density' quantity, its units, and convenience extensions.
- electric_charge
- Provides type-safe units for Electric Charge.
- energy
- Provides type-safe units for Energy.
- force
- Provides type-safe units for Force.
- frequency
- Provides type-safe units for Frequency.
- length
- Provides type-safe units for Length.
- luminous_intensity
- Provides type-safe units for Luminous Intensity.
- mass
- Provides type-safe units for Mass.
- molar
- Provides type-safe units for Amount of Substance (Molar Amount).
- power
- Provides type-safe units for Power.
- pressure
- Provides type-safe units for Pressure.
- quantify
- A type-safe library for units of measurement, providing elegant syntax for unit conversions.
- solid_angle
- Provides type-safe units for Solid Angle.
- specific_energy
- Exports the 'SpecificEnergy' quantity, its units, and convenience extensions.
- speed
- Provides type-safe units for Speed.
- temperature
- Provides type-safe units for
TemperatureandTemperatureDelta. - time
- Provides type-safe units for Time.
- volume
- Provides type-safe units for Volume.