Trabalhar com valores monetários em aplicativos é uma parte crítica do desenvolvimento, especialmente em soluções de e-commerce, fintechs e apps que lidam com finanças. É necessário cuidado adicional ao trabalhar com operações de moeda para evitar problemas como arredondamento incorreto, perda de precisão ou conversões inadequadas.
  
  
  Evitando o Uso de double para Valores Monetários
No Dart, o tipo double é frequentemente utilizado para representar números float. Porem, este não é o tipo adequado para valores monetários, pois números em ponto flutuante introduzem imprecisões. Por exemplo:
void main() {
  final double value = 0.1 + 0.2;
  print(value); // Output: 0.30000000000000004
}
Essa imprecisão pode parecer insignificante em alguns casos, mas pode ter relevancia quando se trata de valores financeiros.
Usar Inteiros para Representar Centavos
Uma prática é representar valores monetários em centavos utilizando int, evitando o uso de decimais e a imprecisão associada a double. Por exemplo, R$ 12,34 seria representado como 1234.
void main() {
  final int value = 1234;
  final double decimal = value / 100;
  print(decimal); // Output: 12.34
}
  
  
  Usando a Biblioteca intl para Formatação Monetária
A biblioteca intl é uma ferramenta boa para lidar com internacionalização e formatação de números, inclusive valores monetários. Ela permite formatar números com o símbolo de moeda correto e de acordo com as convenções locais.
Exemplo de Formatação de Valores Monetários
import 'package:intl/intl.dart';
void main() {
  final int value = 1234;
  final format = NumberFormat.simpleCurrency(locale: 'pt_BR').format;
  final double decimal = value / 100;
  print(format(decimal)); // Output: R$12,34
}
Aqui, usamos NumberFormat.simpleCurrency com a localidade brasileira (pt_BR) para formatar o valor corretamente em reais.
3. Operações Seguras com Moeda
Operações financeiras como adição, subtração, multiplicação e divisão precisam ser feitas com cautela para evitar erros de precisão. A melhor prática é realizar todas as operações em centavos e, somente para exibição, converter o valor para o formato apropriado.
Exemplo de Adição de Valores Monetários
void main() {
  final int value = 1234; // R$12,34
  final int value2 = 5678; // R$56,78
  final int sum = value + value2;
  print('Soma em centavos: $sum'); // Output: 6912
  final double decimal = sum / 100;
  print('Soma em reais: $decimal'); // Output: 69.12
}
Exemplo: Aplicando Desconto
void main() {
  final int original = 10000; // R$100,00
  final double discount = 0.15; // 15%
  final int total = (original * (1 - discount)).round();
  print('Valor com desconto: ${total / 100}'); // Output: 85.00
}
Aqui usamos a função round() para garantir que o valor seja arredondado corretamente após a aplicação do desconto, evitando problemas de precisão.
Exemplo de Arredondamento
import 'package:intl/intl.dart';
void main() {
  final double value = 1234.56789;
  final formatter = NumberFormat("#,##0.00", "pt_BR");
  print(formatter.format(value)); // Output: 1.234,57
}
Mascara de Valores
No Flutter, podemos utilizar o pacote extended_masked_text para aplicar uma máscara ao valor digitado, permitindo que ele seja formatado automaticamente enquanto o usuário insere a quantia.
- Exemplo de TextFieldcom máscara e validação:
import 'package:flutter/material.dart';
import 'package:extended_masked_text/extended_masked_text.dart';
import 'package:intl/intl.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Validação de Valores Monetários'),
        ),
        body: Padding(
          padding: const EdgeInsets.all(16.0),
          child: MonetaryTextField(),
        ),
      ),
    );
  }
}
class MonetaryTextField extends StatefulWidget {
  const MonetaryTextField({super.key});
  @override
  createState() => _MonetaryTextFieldState();
}
class _MonetaryTextFieldState extends State<MonetaryTextField> {
  final _controller = MoneyMaskedTextController(leftSymbol: 'R\$ ');
  String? _errorText;
  @override
  Widget build(BuildContext context) {
    return TextField(
      controller: _controller,
      keyboardType: TextInputType.number,
      decoration: InputDecoration(
        labelText: 'Valor',
        border: const OutlineInputBorder(),
        errorText: _errorText,
      ),
      inputFormatters: [
        FilteringTextInputFormatter.digitsOnly,
      ],
      onChanged: (value) {
        setState(() {
          _errorText = _validateInput(value);
        });
      },
    );
  }
  String? _validateInput(String value) {
    if (value.isEmpty) {
      return 'Por favor, insira um valor';
    }
    final numericValue = _controller.numberValue;
    if (numericValue <= 0) {
      return 'O valor deve ser maior que zero';
    }
    return null;
  }
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}
Conversão de Valores para Centavos
final int value = (_controller.numberValue * 100).round()
Exemplo: se o usuário inserir 1,234.56, o valor será convertido para 123456 centavos, garantindo que o dado seja armazenado corretamente.
Armazenando a Moeda
Além de salvar o valor em centavos, é essencial armazenar a moeda associada a esse valor. Aplicativos frequentemente lidam com múltiplas moedas e associar cada valor à sua moeda permite o tratamento correto nas operações e na exibição.
Por exemplo, para um produto com preço em dólares:
- Valor em centavos (int): 4567(representando $45.67)
- Moeda (string): USD
Isso garante que, ao exibir o valor ao usuário, o aplicativo possa formatá-lo corretamente como $45.67, preservando a moeda adequada.
Extra: packages
Aqui estão alguns pacotes que ajudam a lidar com valores monetários em Dart, fornecendo soluções mais robustas para cálculos financeiros, formatação e manipulação de moedas:
1. money2
O pacote money2 oferece uma API completa para lidar com valores monetários em qualquer moeda. Ele ajuda a resolver problemas comuns de precisão e formatação ao trabalhar com valores financeiros.
Recursos principais:
- Representação de valores em diferentes moedas.
- Manipulação segura de operações financeiras.
- Arredondamento configurável e suporte para múltiplas casas decimais.
- Integração com intlpara formatação.
Exemplo:
import 'package:money2/money2.dart';
void main() {
  final currency = Currency.create('BRL', 2, symbol: 'R\$');
  final value = Money.fromInt(12345, currency); // R$123,45
  print(value.format('S0.00')); // Output: R$123.45
}
2. decimal
O pacote decimal é ideal para quem precisa de precisão exata em operações numéricas, evitando as limitações do tipo double. É muito útil em cálculos financeiros onde a precisão é crítica.
Recursos principais:
- Suporta números com precisão arbitrária.
- Ideal para cálculos que envolvem valores monetários grandes ou complexos.
Exemplo:
import 'package:decimal/decimal.dart';
void main() {
  final value1 = Decimal.parse('10.25');
  final value2 = Decimal.parse('20.35');
  final result = value1 + value2;
  print(result); // Output: 30.60
}
3. money_formatter
O money_formatter é um pacote simples e eficiente para formatação de valores monetários, fornecendo diferentes opções de exibição e manipulação. Ele é útil se você precisa apenas de formatação, sem operações matemáticas complexas.
Recursos principais:
- Formatação flexível e suporte para múltiplas moedas.
- Suporte para arredondamento e exibição de valores em diferentes formatos.
Exemplo:
import 'package:money_formatter/money_formatter.dart';
void main() {
  final fmf = MoneyFormatter(amount: 1234567.89);
  print(fmf.output.symbolOnLeft); // Output: $1,234,567.89
}
4. currency_formatter
O currency_formatter é outra alternativa para lidar com a formatação de valores monetários, mas ele também inclui opções de conversão entre moedas, o que pode ser útil se o seu aplicativo precisa exibir múltiplas moedas.
Recursos principais:
- Conversão de moedas usando taxas de câmbio.
- Formatação de moedas com suporte para diferentes locais.
Exemplo:
import 'package:currency_formatter/currency_formatter.dart';
void main() {
  final formatter = CurrencyFormatter();
  final formattedValue = formatter.format(123456.78, CurrencyFormatterSettings(symbol: 'R\$', symbolSide: SymbolSide.left));
  print(formattedValue); // Output: R$123,456.78
}
É isso! :)
Foto de Eric Prouzet na Unsplash
 
 
              
 
    
Top comments (0)