dev
dart 256 lines 10 KB
Raw
1 import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
2 import 'package:cake_wallet/src/screens/receive/widgets/currency_input_field.dart';
3 import 'package:cake_wallet/utils/payment_request.dart';
4 import 'package:cake_wallet/utils/show_pop_up.dart';
5 import 'package:cake_wallet/view_model/send/template_view_model.dart';
6 import 'package:cw_core/crypto_currency.dart';
7 import 'package:cw_core/currency.dart';
8 import 'package:flutter_mobx/flutter_mobx.dart';
9 import 'package:flutter/material.dart';
10 import 'package:cake_wallet/generated/i18n.dart';
11 import 'package:cake_wallet/view_model/send/send_template_view_model.dart';
12 import 'package:cake_wallet/src/widgets/address_text_field.dart';
13 import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
14 import 'package:mobx/mobx.dart';
15
16 class SendTemplateCard extends StatelessWidget {
17 SendTemplateCard({
18 super.key,
19 required this.template,
20 required this.index,
21 required this.sendTemplateViewModel,
22 });
23
24 final TemplateViewModel template;
25 final int index;
26 final SendTemplateViewModel sendTemplateViewModel;
27
28 final _addressController = TextEditingController();
29 final _cryptoAmountController = TextEditingController();
30 final _fiatAmountController = TextEditingController();
31 final _nameController = TextEditingController();
32 final FocusNode _cryptoAmountFocus = FocusNode();
33 final FocusNode _fiatAmountFocus = FocusNode();
34
35 bool _effectsInstalled = false;
36
37 @override
38 Widget build(BuildContext context) {
39 _setEffects(context);
40
41 return Container(
42 decoration: BoxDecoration(
43 borderRadius: BorderRadius.only(
44 bottomLeft: Radius.circular(24),
45 bottomRight: Radius.circular(24),
46 ),
47 color: Theme.of(context).colorScheme.surfaceContainer,
48 ),
49 child: Column(
50 children: <Widget>[
51 Padding(
52 padding: EdgeInsets.fromLTRB(24, 90, 24, 32),
53 child: Column(
54 children: <Widget>[
55 SizedBox(height: 32),
56 if (index == 0)
57 BaseTextFormField(
58 contentPadding: EdgeInsets.symmetric(vertical: 8),
59 hasUnderlineBorder: true,
60 controller: _nameController,
61 hintText: sendTemplateViewModel.recipients.length > 1
62 ? S.of(context).template_name
63 : S.of(context).send_name,
64 textStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
65 fontWeight: FontWeight.w500,
66 ),
67 placeholderTextStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
68 color: Theme.of(context).colorScheme.onSurfaceVariant,
69 fontWeight: FontWeight.w500,
70 ),
71 validator: sendTemplateViewModel.templateValidator,
72 fillColor: Theme.of(context).colorScheme.surfaceContainerHighest,
73 ),
74 Padding(
75 padding: EdgeInsets.only(top: 20),
76 child: Observer(
77 builder: (context) {
78 return AddressTextField(
79 hasUnderlineBorder: true,
80 contentPadding: EdgeInsets.symmetric(vertical: 8),
81 selectedCurrency: template.selectedCurrency,
82 controller: _addressController,
83 onURIScanned: (uri) {
84 final paymentRequest = PaymentRequest.fromUri(uri);
85 _addressController.text = paymentRequest.address;
86 _cryptoAmountController.text = paymentRequest.amount;
87 },
88 options: [
89 AddressTextFieldOption.paste,
90 AddressTextFieldOption.qrCode,
91 AddressTextFieldOption.addressBook
92 ],
93 onPushPasteButton: (context) async {
94 template.output.resetParsedAddress();
95 },
96 onPushAddressBookButton: (context) async {
97 template.output.resetParsedAddress();
98 },
99 buttonColor: Theme.of(context).colorScheme.surfaceContainerHighest,
100 textStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
101 fontWeight: FontWeight.w500,
102 ),
103 hintStyle: Theme.of(context).textTheme.bodyMedium!.copyWith(
104 fontWeight: FontWeight.w500,
105 color: Theme.of(context).colorScheme.onSurfaceVariant,
106 ),
107 validator: sendTemplateViewModel.addressValidator,
108 fillColor: Theme.of(context).colorScheme.surfaceContainerHighest,
109 );
110 },
111 ),
112 ),
113 Focus(
114 onFocusChange: (hasFocus) {
115 if (hasFocus) template.setCryptoCurrency(true);
116 },
117 child: Column(
118 children: [
119 Observer(
120 builder: (context) => CurrencyAmountTextField(
121 hasUnderlineBorder: true,
122 borderWidth: 0.0,
123 selectedCurrency: template.selectedCurrency.title,
124 selectedCurrencyDecimals: template.selectedCurrency.decimals,
125 amountFocusNode: _cryptoAmountFocus,
126 amountController: _cryptoAmountController,
127 isSelected: template.isCryptoSelected,
128 tag: template.selectedCurrency.tag,
129 isPickerEnable: sendTemplateViewModel.hasMultipleTokens,
130 onTapPicker: () => _presentPicker(context),
131 currencyValueValidator: sendTemplateViewModel.amountValidator,
132 isAmountEditable: true,
133 fillColor: Theme.of(context).colorScheme.surfaceContainerHighest,
134 ),
135 ),
136 Divider(height: 1, color: Theme.of(context).colorScheme.outlineVariant),
137 ],
138 ),
139 ),
140 Focus(
141 onFocusChange: (hasFocus) {
142 if (hasFocus) template.setCryptoCurrency(false);
143 },
144 child: Column(
145 children: [
146 Observer(
147 builder: (context) => CurrencyAmountTextField(
148 hasUnderlineBorder: true,
149 borderWidth: 0.0,
150 selectedCurrency: sendTemplateViewModel.fiatCurrency,
151 selectedCurrencyDecimals: sendTemplateViewModel.fiatCurrencyDecimals,
152 amountFocusNode: _fiatAmountFocus,
153 amountController: _fiatAmountController,
154 isSelected: !template.isCryptoSelected,
155 hintText: '0.00',
156 isAmountEditable: true,
157 fillColor: Theme.of(context).colorScheme.surfaceContainerHighest,
158 ),
159 ),
160 Divider(height: 1, color: Theme.of(context).colorScheme.outlineVariant),
161 ],
162 ),
163 ),
164 ],
165 ),
166 )
167 ],
168 ),
169 );
170 }
171
172 void _setEffects(BuildContext context) {
173 if (_effectsInstalled) {
174 return;
175 }
176
177 final output = template.output;
178
179 if (template.address.isNotEmpty) {
180 _addressController.text = template.address;
181 }
182 if (template.name.isNotEmpty) {
183 _nameController.text = template.name;
184 }
185 if (template.output.cryptoAmount.isNotEmpty) {
186 _cryptoAmountController.text = template.output.cryptoAmount;
187 }
188 if (template.output.fiatAmount.isNotEmpty) {
189 _fiatAmountController.text = template.output.fiatAmount;
190 }
191
192 _addressController.addListener(() {
193 final address = _addressController.text;
194
195 if (template.address != address) {
196 template.address = address;
197 }
198 });
199 _cryptoAmountController.addListener(() {
200 final amount = _cryptoAmountController.text;
201
202 if (amount != output.cryptoAmount) {
203 output.setCryptoAmount(amount);
204 }
205 });
206 _fiatAmountController.addListener(() {
207 final amount = _fiatAmountController.text;
208
209 if (amount != output.fiatAmount) {
210 output.setFiatAmount(amount);
211 }
212 });
213 _nameController.addListener(() {
214 final name = _nameController.text;
215
216 if (name != template.name) {
217 template.name = name;
218 }
219 });
220
221 reaction((_) => template.address, (String address) {
222 if (address != _addressController.text) {
223 _addressController.text = address;
224 }
225 });
226 reaction((_) => output.cryptoAmount, (String amount) {
227 if (amount != _cryptoAmountController.text) {
228 _cryptoAmountController.text = amount;
229 }
230 });
231 reaction((_) => output.fiatAmount, (String amount) {
232 if (amount != _fiatAmountController.text) {
233 _fiatAmountController.text = amount;
234 }
235 });
236 reaction((_) => template.name, (String name) {
237 if (name != _nameController.text) {
238 _nameController.text = name;
239 }
240 });
241
242 _effectsInstalled = true;
243 }
244
245 void _presentPicker(BuildContext context) {
246 showPopUp<void>(
247 context: context,
248 builder: (_) => CurrencyPicker(
249 selectedAtIndex: sendTemplateViewModel.walletCurrencies.indexOf(template.selectedCurrency),
250 items: sendTemplateViewModel.walletCurrencies,
251 hintText: S.of(context).search_currency,
252 onItemSelected: (Currency cur) => template.changeSelectedCurrency(cur as CryptoCurrency),
253 ),
254 );
255 }
256 }