zwallet/lib/dualmoneyinput.dart

276 lines
7.4 KiB
Dart

import 'package:YWallet/store.dart';
import 'package:flutter/material.dart';
import 'package:flutter_form_builder/flutter_form_builder.dart';
import 'package:mobx/mobx.dart';
import 'generated/l10n.dart';
import 'main.dart';
class DualMoneyInputWidget extends StatefulWidget {
late final String fiat;
final int? spendable;
final int? initialValue;
final bool max;
final Function(int)? onChange;
DualMoneyInputWidget(
{Key? key,
this.initialValue,
this.spendable,
String? fiat,
this.max = false,
this.onChange})
: super(key: key) {
this.fiat = fiat ?? settings.currency;
}
@override
DualMoneyInputState createState() => DualMoneyInputState();
}
class DualMoneyInputState extends State<DualMoneyInputWidget> {
var zero = "";
var inputInCoin = true;
var coinAmountController = TextEditingController();
var fiatAmountController = TextEditingController();
late String _fiat;
var _fxRate = 0.0;
var amount = 0;
double sliderValue = 0;
var _feeIncluded = false;
bool _useMillis = settings.useMillis;
ReactionDisposer? priceAutorunDispose;
bool get isMax => sliderValue == 100.0;
bool get feeIncluded => _feeIncluded || isMax;
@override
void initState() {
super.initState();
_fiat = widget.fiat;
zero = amountToString(0, precision(_useMillis));
final initialValue = widget.initialValue ?? 0;
final amount = amountToString(initialValue, precision(_useMillis));
coinAmountController.text = amount;
_updateFxRate();
_updateSlider();
}
void _updateFxRate() {
Future.microtask(() async {
final rate = await getFxRate(active.coinDef.currency, _fiat) ?? 0.0;
setState(() {
_fxRate = rate;
if (inputInCoin)
_updateFiatAmount();
else
_updateAmount();
});
});
}
@override
void dispose() {
priceAutorunDispose?.call();
super.dispose();
}
@override
Widget build(BuildContext context) {
final s = S.of(context);
return Column(children: <Widget>[
Row(children: [
Expanded(
child: TextFormField(
style: !inputInCoin
? TextStyle(fontWeight: FontWeight.w200)
: TextStyle(),
decoration: InputDecoration(
labelText: s.amountInSettingscurrency(active.coinDef.ticker)),
controller: coinAmountController,
keyboardType: TextInputType.number,
inputFormatters: [makeInputFormatter(_useMillis)],
onTap: () => setState(() {
inputInCoin = true;
}),
validator: _checkAmount,
onChanged: (_) {
_updateFiatAmount();
_updateSlider();
_onChanged();
},
onSaved: _onAmount,
)),
if (widget.max) TextButton(child: Text(s.max), onPressed: _onMax),
]),
Row(children: [
Expanded(
child: TextFormField(
style: inputInCoin
? TextStyle(fontWeight: FontWeight.w200)
: TextStyle(),
decoration: InputDecoration(
labelText: s.amountInSettingscurrency(_fiat)),
controller: fiatAmountController,
keyboardType: TextInputType.number,
inputFormatters: [makeInputFormatter(_useMillis)],
validator: (v) => _checkAmount(v, isFiat: true),
onTap: () => setState(() {
inputInCoin = false;
}),
onChanged: (_) {
_updateAmount();
_updateSlider();
}))
]),
if (widget.spendable != null)
Slider(
value: sliderValue,
onChanged: _onSliderChanged,
min: 0,
max: 100,
divisions: 20,
label: "${sliderValue.toInt()} %"),
if (widget.spendable != null)
FormBuilderCheckbox(
key: UniqueKey(),
name: 'fee',
title: Text(s.includeFeeInAmount),
initialValue: _feeIncluded || isMax,
onChanged: (v) {
setState(() {
_feeIncluded = v ?? false;
});
}),
]);
}
void clear() {
setState(() {
_fiat = settings.currency;
_feeIncluded = false;
coinAmountController.text = zero;
fiatAmountController.text = zero;
sliderValue = 0;
_useMillis = settings.useMillis;
_onChanged();
});
}
void setAmount(int amount) {
setState(() {
coinAmountController.text = amountToString(amount, MAX_PRECISION);
_updateFiatAmount();
_updateSlider();
_onChanged();
});
}
_onSliderChanged(double v) {
setState(() {
sliderValue = v;
if (sliderValue == 100) {
_onMax();
} else {
final a = (widget.spendable! * v / 100).round();
coinAmountController.text = amountToString(a, precision(_useMillis));
_updateFiatAmount();
_onChanged();
}
});
}
void _onMax() {
setState(() {
final s = widget.spendable;
if (s != null) {
sliderValue = 100;
setAmount(s);
}
});
}
String? _checkAmount(String? vs, {bool isFiat = false}) {
final s = S.of(context);
if (vs == null) return s.amountMustBeANumber;
if (!checkNumber(vs)) return s.amountMustBeANumber;
final v = parseNumber(vs);
if (v < 0.0) return s.amountMustBePositive;
if (v >= MAXMONEY) return s.amountTooHigh;
if (isFiat) return null;
final spendable = widget.spendable;
if (spendable != null && stringToAmount(vs) > spendable)
return s.notEnoughBalance;
return null;
}
void _updateAmount() {
final rate = 1.0 / _fxRate;
final amount = parseNumber(fiatAmountController.text);
final otherAmount = decimalFormat(amount * rate, precision(_useMillis));
setState(() {
coinAmountController.text = otherAmount;
_onChanged();
});
}
void _updateFiatAmount() {
final amount = parseNumber(coinAmountController.text);
final otherAmount = decimalFormat(amount * _fxRate, precision(_useMillis));
setState(() {
fiatAmountController.text = otherAmount;
});
}
void _updateSlider() {
final spendable = widget.spendable;
setState(() {
final amount = stringToAmount(coinAmountController.text);
if (spendable != null) {
sliderValue = (amount / spendable * 100).clamp(0, 100);
if (isMax) _useMillis = false;
}
});
}
void _onAmount(String? vs) {
amount = stringToAmount(vs);
}
Future<void> restore(
int amount, double amountInFiat, bool feeIncluded, String? fiat) async {
final rate =
await getFxRate(active.coinDef.currency, fiat ?? settings.currency) ??
0.0;
setState(() {
_fxRate = rate;
if (fiat != null) {
_fiat = fiat;
fiatAmountController.text =
decimalFormat(amountInFiat, precision(_useMillis));
inputInCoin = false;
_updateAmount();
} else {
coinAmountController.text =
amountToString(amount, precision(_useMillis));
inputInCoin = true;
_updateFiatAmount();
_onChanged();
}
_feeIncluded = feeIncluded;
_updateSlider();
});
}
_onChanged() {
final onChange = widget.onChange;
if (onChange != null) {
amount = stringToAmount(coinAmountController.text);
onChange(amount);
}
}
}