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
TextField
com 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
intl
para 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)