CAKE-49 | refactored send page

OleksandrSobol committed Sep 21, 2020 at 22:01 UTC 45c8b74bf14578ffaa2b3499fe43512014317893
3 files changed +636 -611
lib/di.dart
+2 -2
@@ -257,8 +257,8 @@ Future setup(
257 getIt.registerFactory(
258 () => SendPage(sendViewModel: getIt.get<SendViewModel>()));
259
260 - // getIt.registerFactory(
261 - // () => SendTemplatePage(sendViewModel: getIt.get<SendViewModel>()));
260 + getIt.registerFactory(
261 + () => SendTemplatePage(sendViewModel: getIt.get<SendViewModel>()));
262
263 getIt.registerFactory(() => WalletListViewModel(
264 walletInfoSource, getIt.get<AppStore>(), getIt.get<KeyService>()));
lib/src/screens/send/send_page.dart
+622 -590
@@ -1,4 +1,7 @@
1 import 'dart:ui';
2 +import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
3 +import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
4 +import 'package:cake_wallet/src/widgets/picker.dart';
5 import 'package:flutter/cupertino.dart';
6 import 'package:flutter/material.dart';
7 import 'package:flutter/services.dart';
@@ -19,22 +22,22 @@ import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
22 import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
23 import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
24 import 'package:cake_wallet/src/widgets/trail_button.dart';
22 -
23 -import 'package:flutter/cupertino.dart';
24 -import 'package:flutter/material.dart';
25 -import 'package:cake_wallet/src/screens/base_page.dart';
26 -import 'package:cake_wallet/src/screens/send/widgets/base_send_widget.dart';
27 -
28 -// FIXME: Refactor this screen.
25 +import 'package:dotted_border/dotted_border.dart';
26
27 class SendPage extends BasePage {
28 SendPage({@required this.sendViewModel});
29
30 final SendViewModel sendViewModel;
31 + final _addressController = TextEditingController();
32 + final _cryptoAmountController = TextEditingController();
33 + final _fiatAmountController = TextEditingController();
34 + final _focusNode = FocusNode();
35 + final _formKey = GlobalKey<FormState>();
36 +
37 + bool _effectsInstalled = false;
38
35 - // ???
39 @override
37 - String get title => 'SEND';
40 + String get title => S.current.send;
41
42 @override
43 Color get titleColor => Colors.white;
@@ -48,64 +51,9 @@ class SendPage extends BasePage {
51 @override
52 Widget trailing(context) => TrailButton(
53 caption: S.of(context).clear, onPressed: () => sendViewModel.reset());
51 - Color get backgroundDarkColor => Colors.transparent;
52 -
53 - // @override
54 - // State<StatefulWidget> createState() => SendFormState();
55 -
56 - final _addressController = TextEditingController();
57 - final _cryptoAmountController = TextEditingController();
58 - final _fiatAmountController = TextEditingController();
59 -
60 - final _focusNode = FocusNode();
61 -
62 - bool _effectsInstalled = false;
63 -
64 - final _formKey = GlobalKey<FormState>();
65 -
66 - // @override
67 - // void initState() {
68 - // _focusNode.addListener(() {
69 - // if (!_focusNode.hasFocus && _addressController.text.isNotEmpty) {
70 - // getOpenaliasRecord(context);
71 - // }
72 - // });
73 -
74 - // super.initState();
75 - // }
76 -
77 - Future<void> getOpenaliasRecord(BuildContext context) async {
78 -// final sendStore = Provider.of<SendStore>(context);
79 -// final isOpenalias =
80 -// await sendStore.isOpenaliasRecord(_addressController.text);
81 -//
82 -// if (isOpenalias) {
83 -// _addressController.text = sendStore.recordAddress;
84 -//
85 -// await showDialog<void>(
86 -// context: context,
87 -// builder: (BuildContext context) {
88 -// return AlertWithOneAction(
89 -// alertTitle: S.of(context).openalias_alert_title,
90 -// alertContent:
91 -// S.of(context).openalias_alert_content(sendStore.recordName),
92 -// buttonText: S.of(context).ok,
93 -// buttonAction: () => Navigator.of(context).pop());
94 -// });
95 -// }
96 - }
97 -
98 - // @override
99 - // Widget body(BuildContext context) {
100 - // return super.build(context);
101 - // }
54
55 @override
104 - Widget body(BuildContext context) => BaseSendWidget(
105 - sendViewModel: sendViewModel,
106 - leading: leading(context),
107 - middle: middle(context),
108 - );
56 + Color get backgroundDarkColor => Colors.transparent;
57
58 @override
59 Widget build(BuildContext context) {
@@ -115,529 +63,613 @@ class SendPage extends BasePage {
63 color: Theme.of(context).backgroundColor, child: body(context)));
64 }
65
118 -// @override
119 -// Widget build(BuildContext context) {
120 -// _setEffects(context);
121 -
122 -// return Container(
123 -// color: Theme.of(context).backgroundColor,
124 -// child: ScrollableWithBottomSection(
125 -// contentPadding: EdgeInsets.only(bottom: 24),
126 -// content: Column(
127 -// children: <Widget>[
128 -// TopPanel(
129 -// color: Theme.of(context).accentTextTheme.title.backgroundColor,
130 -// widget: Form(
131 -// key: _formKey,
132 -// child: Column(children: <Widget>[
133 -// AddressTextField(
134 -// controller: _addressController,
135 -// placeholder: 'Address',
136 -// //S.of(context).send_monero_address, FIXME: placeholder for btc and xmr address text field.
137 -// focusNode: _focusNode,
138 -// onURIScanned: (uri) {
139 -// var address = '';
140 -// var amount = '';
141 -
142 -// if (uri != null) {
143 -// address = uri.path;
144 -// amount = uri.queryParameters['tx_amount'];
145 -// } else {
146 -// address = uri.toString();
147 -// }
148 -
149 -// _addressController.text = address;
150 -// _cryptoAmountController.text = amount;
151 -// },
152 -// options: [
153 -// AddressTextFieldOption.qrCode,
154 -// AddressTextFieldOption.addressBook
155 -// ],
156 -// buttonColor: Theme.of(context).accentTextTheme.title.color,
157 -// validator: widget.sendViewModel.addressValidator,
158 -// ),
159 -// Padding(
160 -// padding: const EdgeInsets.only(top: 20),
161 -// child: TextFormField(
162 -// onChanged: (value) =>
163 -// widget.sendViewModel.setCryptoAmount(value),
164 -// style: TextStyle(
165 -// fontSize: 16.0,
166 -// color:
167 -// Theme.of(context).primaryTextTheme.title.color),
168 -// controller: _cryptoAmountController,
169 -// keyboardType: TextInputType.numberWithOptions(
170 -// signed: false, decimal: true),
171 -// // inputFormatters: [
172 -// // BlacklistingTextInputFormatter(
173 -// // RegExp('[\\-|\\ |\\,]'))
174 -// // ],
175 -// decoration: InputDecoration(
176 -// prefixIcon: Padding(
177 -// padding: EdgeInsets.only(top: 12),
178 -// child: Text(
179 -// '${widget.sendViewModel.currency.toString()}:',
180 -// style: TextStyle(
181 -// fontSize: 16,
182 -// fontWeight: FontWeight.w500,
183 -// color: Theme.of(context)
184 -// .primaryTextTheme
185 -// .title
186 -// .color,
187 -// )),
188 -// ),
189 -// suffixIcon: Padding(
190 -// padding: EdgeInsets.only(bottom: 5),
191 -// child: Container(
192 -// height: 32,
193 -// width: 32,
194 -// margin: EdgeInsets.only(
195 -// left: 12, bottom: 7, top: 4),
196 -// decoration: BoxDecoration(
197 -// color: Theme.of(context)
198 -// .accentTextTheme
199 -// .title
200 -// .color,
201 -// borderRadius:
202 -// BorderRadius.all(Radius.circular(6))),
203 -// child: InkWell(
204 -// onTap: () => widget.sendViewModel.setAll(),
205 -// child: Center(
206 -// child: Text(S.of(context).all,
207 -// textAlign: TextAlign.center,
208 -// style: TextStyle(
209 -// fontSize: 9,
210 -// fontWeight: FontWeight.bold,
211 -// color: Theme.of(context)
212 -// .primaryTextTheme
213 -// .caption
214 -// .color)),
215 -// ),
216 -// ),
217 -// )),
218 -// hintStyle: TextStyle(
219 -// fontSize: 16.0,
220 -// color: Theme.of(context)
221 -// .primaryTextTheme
222 -// .title
223 -// .color),
224 -// hintText: '0.0000',
225 -// focusedBorder: UnderlineInputBorder(
226 -// borderSide: BorderSide(
227 -// color: Theme.of(context).dividerColor,
228 -// width: 1.0)),
229 -// enabledBorder: UnderlineInputBorder(
230 -// borderSide: BorderSide(
231 -// color: Theme.of(context).dividerColor,
232 -// width: 1.0))),
233 -// validator: (String value) {
234 -// if (widget.sendViewModel.all) {
235 -// return null;
236 -// }
237 -
238 -// return widget.sendViewModel.amountValidator
239 -// .call(value);
240 -// }),
241 -// ),
242 -// Padding(
243 -// padding: const EdgeInsets.only(top: 20),
244 -// child: TextFormField(
245 -// onChanged: (value) =>
246 -// widget.sendViewModel.setFiatAmount(value),
247 -// style: TextStyle(
248 -// fontSize: 16.0,
249 -// color:
250 -// Theme.of(context).primaryTextTheme.title.color),
251 -// controller: _fiatAmountController,
252 -// keyboardType: TextInputType.numberWithOptions(
253 -// signed: false, decimal: true),
254 -// // inputFormatters: [
255 -// // BlacklistingTextInputFormatter(
256 -// // RegExp('[\\-|\\ |\\,]'))
257 -// // ],
258 -// decoration: InputDecoration(
259 -// prefixIcon: Padding(
260 -// padding: EdgeInsets.only(top: 12),
261 -// child: Text(
262 -// '${widget.sendViewModel.fiat.toString()}:',
263 -// style: TextStyle(
264 -// fontSize: 16,
265 -// fontWeight: FontWeight.w500,
266 -// color: Theme.of(context)
267 -// .primaryTextTheme
268 -// .title
269 -// .color,
270 -// )),
271 -// ),
272 -// hintStyle: TextStyle(
273 -// fontSize: 16.0,
274 -// color: Theme.of(context)
275 -// .primaryTextTheme
276 -// .caption
277 -// .color),
278 -// hintText: '0.00',
279 -// focusedBorder: UnderlineInputBorder(
280 -// borderSide: BorderSide(
281 -// color: Theme.of(context).dividerColor,
282 -// width: 1.0)),
283 -// enabledBorder: UnderlineInputBorder(
284 -// borderSide: BorderSide(
285 -// color: Theme.of(context).dividerColor,
286 -// width: 1.0)))),
287 -// ),
288 -// Padding(
289 -// padding: const EdgeInsets.only(top: 20),
290 -// child: Row(
291 -// mainAxisAlignment: MainAxisAlignment.spaceBetween,
292 -// children: <Widget>[
293 -// Text(S.of(context).send_estimated_fee,
294 -// style: TextStyle(
295 -// fontSize: 12,
296 -// fontWeight: FontWeight.w600,
297 -// color: Theme.of(context)
298 -// .primaryTextTheme
299 -// .title
300 -// .color,
301 -// )),
302 -// Text(
303 -// '${widget.sendViewModel.estimatedFee} ${widget.sendViewModel.currency.toString()}',
304 -// style: TextStyle(
305 -// fontSize: 12,
306 -// fontWeight: FontWeight.w600,
307 -// color: Theme.of(context)
308 -// .primaryTextTheme
309 -// .title
310 -// .color,
311 -// ))
312 -// ],
313 -// ),
314 -// )
315 -// ]),
316 -// ),
317 -// ),
318 -// // Padding(
319 -// // padding: EdgeInsets.only(top: 32, left: 24, bottom: 24),
320 -// // child: Row(
321 -// // mainAxisAlignment: MainAxisAlignment.start,
322 -// // children: <Widget>[
323 -// // Text(
324 -// // S.of(context).send_templates,
325 -// // style: TextStyle(
326 -// // fontSize: 18,
327 -// // fontWeight: FontWeight.w600,
328 -// // color:
329 -// // Theme.of(context).primaryTextTheme.caption.color),
330 -// // )
331 -// // ],
332 -// // ),
333 -// // ),
334 -// // Container(
335 -// // height: 40,
336 -// // width: double.infinity,
337 -// // padding: EdgeInsets.only(left: 24),
338 -// // child: Observer(builder: (_) {
339 -// // final itemCount = sendTemplateStore.templates.length + 1;
340 -// //
341 -// // return ListView.builder(
342 -// // scrollDirection: Axis.horizontal,
343 -// // itemCount: itemCount,
344 -// // itemBuilder: (context, index) {
345 -// // if (index == 0) {
346 -// // return GestureDetector(
347 -// // onTap: () => Navigator.of(context)
348 -// // .pushNamed(Routes.sendTemplate),
349 -// // child: Container(
350 -// // padding: EdgeInsets.only(right: 10),
351 -// // child: DottedBorder(
352 -// // borderType: BorderType.RRect,
353 -// // dashPattern: [8, 4],
354 -// // color: Theme.of(context)
355 -// // .accentTextTheme
356 -// // .title
357 -// // .backgroundColor,
358 -// // strokeWidth: 2,
359 -// // radius: Radius.circular(20),
360 -// // child: Container(
361 -// // height: 40,
362 -// // width: 75,
363 -// // padding: EdgeInsets.only(left: 10, right: 10),
364 -// // alignment: Alignment.center,
365 -// // decoration: BoxDecoration(
366 -// // borderRadius:
367 -// // BorderRadius.all(Radius.circular(20)),
368 -// // color: Colors.transparent,
369 -// // ),
370 -// // child: Text(
371 -// // S.of(context).send_new,
372 -// // style: TextStyle(
373 -// // fontSize: 14,
374 -// // fontWeight: FontWeight.w600,
375 -// // color: Theme.of(context)
376 -// // .primaryTextTheme
377 -// // .caption
378 -// // .color),
379 -// // ),
380 -// // )),
381 -// // ),
382 -// // );
383 -// // }
384 -// //
385 -// // index -= 1;
386 -// //
387 -// // final template = sendTemplateStore.templates[index];
388 -// //
389 -// // return TemplateTile(
390 -// // to: template.name,
391 -// // amount: template.amount,
392 -// // from: template.cryptoCurrency,
393 -// // onTap: () {
394 -// // _addressController.text = template.address;
395 -// // _cryptoAmountController.text = template.amount;
396 -// // getOpenaliasRecord(context);
397 -// // });
398 -// // });
399 -// // }),
400 -// // )
401 -// ],
402 -// ),
403 -// bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
404 -// bottomSection: Observer(builder: (_) {
405 -// return LoadingPrimaryButton(
406 -// onPressed: () async {
407 -// FocusScope.of(context).requestFocus(FocusNode());
408 -
409 -// if (!_formKey.currentState.validate()) {
410 -// return;
411 -// }
412 -
413 -// await showDialog<void>(
414 -// context: context,
415 -// builder: (dialogContext) {
416 -// return AlertWithTwoActions(
417 -// alertTitle: S.of(context).send_creating_transaction,
418 -// alertContent: S.of(context).confirm_sending,
419 -// leftButtonText: S.of(context).send,
420 -// rightButtonText: S.of(context).cancel,
421 -// actionLeftButton: () async {
422 -// await Navigator.of(dialogContext)
423 -// .popAndPushNamed(Routes.auth, arguments:
424 -// (bool isAuthenticatedSuccessfully,
425 -// AuthPageState auth) {
426 -// if (!isAuthenticatedSuccessfully) {
427 -// return;
428 -// }
429 -
430 -// Navigator.of(auth.context).pop();
431 -// widget.sendViewModel.createTransaction();
432 -// });
433 -// },
434 -// actionRightButton: () => Navigator.of(context).pop());
435 -// });
436 -// },
437 -// text: S.of(context).send,
438 -// color: Colors.blue,
439 -// textColor: Colors.white,
440 -// isLoading: widget.sendViewModel.state is TransactionIsCreating ||
441 -// widget.sendViewModel.state is TransactionCommitting,
442 -// isDisabled: !widget.sendViewModel.isReadyForSend);
443 -// }),
444 -// ),
445 -// );
446 -// }
447 -
448 -// void _setEffects(BuildContext context) {
449 -// if (_effectsInstalled) {
450 -// return;
451 -// }
452 -
453 -// reaction((_) => widget.sendViewModel.all, (bool all) {
454 -// if (all) {
455 -// _cryptoAmountController.text = S.current.all;
456 -// _fiatAmountController.text = null;
457 -// }
458 -// });
459 -
460 -// reaction((_) => widget.sendViewModel.fiatAmount, (String amount) {
461 -// if (amount != _fiatAmountController.text) {
462 -// _fiatAmountController.text = amount;
463 -// }
464 -// });
465 -
466 -// reaction((_) => widget.sendViewModel.cryptoAmount, (String amount) {
467 -// if (widget.sendViewModel.all && amount != S.current.all) {
468 -// widget.sendViewModel.all = false;
469 -// }
470 -
471 -// if (amount != _cryptoAmountController.text) {
472 -// _cryptoAmountController.text = amount;
473 -// }
474 -// });
475 -
476 -// reaction((_) => widget.sendViewModel.address, (String address) {
477 -// if (address != _addressController.text) {
478 -// _addressController.text = address;
479 -// }
480 -// });
481 -
482 -// _addressController.addListener(() {
483 -// final address = _addressController.text;
484 -
485 -// if (widget.sendViewModel.address != address) {
486 -// widget.sendViewModel.address = address;
487 -// }
488 -// });
489 -
490 -// reaction((_) => widget.sendViewModel.state, (SendViewModelState state) {
491 -// if (state is SendingFailed) {
492 -// WidgetsBinding.instance.addPostFrameCallback((_) {
493 -// showDialog<void>(
494 -// context: context,
495 -// builder: (BuildContext context) {
496 -// return AlertWithOneAction(
497 -// alertTitle: S.of(context).error,
498 -// alertContent: state.error,
499 -// buttonText: S.of(context).ok,
500 -// buttonAction: () => Navigator.of(context).pop());
501 -// });
502 -// });
503 -// }
504 -
505 -// if (state is TransactionCreatedSuccessfully) {
506 -// WidgetsBinding.instance.addPostFrameCallback((_) {
507 -// showDialog<void>(
508 -// context: context,
509 -// builder: (BuildContext context) {
510 -// return ConfirmSendingAlert(
511 -// alertTitle: S.of(context).confirm_sending,
512 -// amount: S.of(context).send_amount,
513 -// amountValue:
514 -// widget.sendViewModel.pendingTransaction.amountFormatted,
515 -// fee: S.of(context).send_fee,
516 -// feeValue:
517 -// widget.sendViewModel.pendingTransaction.feeFormatted,
518 -// leftButtonText: S.of(context).ok,
519 -// rightButtonText: S.of(context).cancel,
520 -// actionLeftButton: () {
521 -// Navigator.of(context).pop();
522 -// widget.sendViewModel.commitTransaction();
523 -// showDialog<void>(
524 -// context: context,
525 -// builder: (BuildContext context) {
526 -// return Observer(builder: (_) {
527 -// final state = widget.sendViewModel.state;
528 -
529 -// if (state is TransactionCommitted) {
530 -// return Stack(
531 -// children: <Widget>[
532 -// Container(
533 -// color: Theme.of(context).backgroundColor,
534 -// child: Center(
535 -// child: Image.asset(
536 -// 'assets/images/birthday_cake.png'),
537 -// ),
538 -// ),
539 -// Center(
540 -// child: Padding(
541 -// padding: EdgeInsets.only(
542 -// top: 220, left: 24, right: 24),
543 -// child: Text(
544 -// S.of(context).send_success,
545 -// textAlign: TextAlign.center,
546 -// style: TextStyle(
547 -// fontSize: 22,
548 -// fontWeight: FontWeight.bold,
549 -// color: Theme.of(context)
550 -// .primaryTextTheme
551 -// .title
552 -// .color,
553 -// decoration: TextDecoration.none,
554 -// ),
555 -// ),
556 -// ),
557 -// ),
558 -// Positioned(
559 -// left: 24,
560 -// right: 24,
561 -// bottom: 24,
562 -// child: PrimaryButton(
563 -// onPressed: () =>
564 -// Navigator.of(context).pop(),
565 -// text: S.of(context).send_got_it,
566 -// color: Colors.blue,
567 -// textColor: Colors.white))
568 -// ],
569 -// );
570 -// }
571 -
572 -// return Stack(
573 -// children: <Widget>[
574 -// Container(
575 -// color: Theme.of(context).backgroundColor,
576 -// child: Center(
577 -// child: Image.asset(
578 -// 'assets/images/birthday_cake.png'),
579 -// ),
580 -// ),
581 -// BackdropFilter(
582 -// filter: ImageFilter.blur(
583 -// sigmaX: 3.0, sigmaY: 3.0),
584 -// child: Container(
585 -// decoration: BoxDecoration(
586 -// color: Theme.of(context)
587 -// .backgroundColor
588 -// .withOpacity(0.25)),
589 -// child: Center(
590 -// child: Padding(
591 -// padding: EdgeInsets.only(top: 220),
592 -// child: Text(
593 -// S.of(context).send_sending,
594 -// textAlign: TextAlign.center,
595 -// style: TextStyle(
596 -// fontSize: 22,
597 -// fontWeight: FontWeight.bold,
598 -// color: Theme.of(context)
599 -// .primaryTextTheme
600 -// .title
601 -// .color,
602 -// decoration: TextDecoration.none,
603 -// ),
604 -// ),
605 -// ),
606 -// ),
607 -// ),
608 -// )
609 -// ],
610 -// );
611 -// });
612 -// });
613 -// },
614 -// actionRightButton: () => Navigator.of(context).pop());
615 -// });
616 -// });
617 -// }
618 -
619 -// if (state is TransactionCommitted) {
620 -// WidgetsBinding.instance.addPostFrameCallback((_) {
621 -// _addressController.text = '';
622 -// _cryptoAmountController.text = '';
623 -// });
624 -// }
625 -// });
626 -
627 -// _effectsInstalled = true;
628 -// }
629 -
630 -// Widget body(BuildContext context) => BaseSendWidget(
631 -// sendViewModel: sendViewModel,
632 -// leading: leading(context),
633 -// middle: middle(context),
634 -// );
635 -
636 -// @override
637 -// Widget build(BuildContext context) {
638 -// return Scaffold(
639 -// resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
640 -// body: Container(
641 -// color: Theme.of(context).backgroundColor, child: body(context)));
642 -// }
66 + @override
67 + Widget body(BuildContext context) {
68 + _setEffects(context);
69 +
70 + return ScrollableWithBottomSection(
71 + contentPadding: EdgeInsets.only(bottom: 24),
72 + content: Column(
73 + children: <Widget>[
74 + TopPanel(
75 + edgeInsets: EdgeInsets.all(0),
76 + gradient: LinearGradient(colors: [
77 + Theme.of(context).primaryTextTheme.subhead.color,
78 + Theme.of(context).primaryTextTheme.subhead.decorationColor,
79 + ], begin: Alignment.topLeft, end: Alignment.bottomRight),
80 + widget: Form(
81 + key: _formKey,
82 + child: Column(children: <Widget>[
83 + CupertinoNavigationBar(
84 + leading: leading(context),
85 + middle: middle(context),
86 + trailing: trailing(context),
87 + backgroundColor: Colors.transparent,
88 + border: null,
89 + ),
90 + Padding(
91 + padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
92 + child: Column(
93 + children: <Widget>[
94 + AddressTextField(
95 + controller: _addressController,
96 + focusNode: _focusNode,
97 + onURIScanned: (uri) {
98 + var address = '';
99 + var amount = '';
100 +
101 + if (uri != null) {
102 + address = uri.path;
103 + amount = uri.queryParameters['tx_amount'];
104 + } else {
105 + address = uri.toString();
106 + }
107 +
108 + _addressController.text = address;
109 + _cryptoAmountController.text = amount;
110 + },
111 + options: [
112 + AddressTextFieldOption.paste,
113 + AddressTextFieldOption.qrCode,
114 + AddressTextFieldOption.addressBook
115 + ],
116 + buttonColor:
117 + Theme.of(context).primaryTextTheme.display1.color,
118 + borderColor:
119 + Theme.of(context).primaryTextTheme.headline.color,
120 + textStyle: TextStyle(
121 + fontSize: 14,
122 + fontWeight: FontWeight.w500,
123 + color: Colors.white),
124 + hintStyle: TextStyle(
125 + fontSize: 14,
126 + fontWeight: FontWeight.w500,
127 + color: Theme.of(context)
128 + .primaryTextTheme
129 + .headline
130 + .decorationColor),
131 + validator: sendViewModel.addressValidator,
132 + ),
133 + Observer(builder: (_) {
134 + return Padding(
135 + padding: const EdgeInsets.only(top: 20),
136 + child: BaseTextFormField(
137 + controller: _cryptoAmountController,
138 + keyboardType: TextInputType.numberWithOptions(
139 + signed: false, decimal: true),
140 + inputFormatters: [
141 + BlacklistingTextInputFormatter(
142 + RegExp('[\\-|\\ |\\,]'))
143 + ],
144 + prefixIcon: Padding(
145 + padding: EdgeInsets.only(top: 9),
146 + child:
147 + Text(sendViewModel.currency.title + ':',
148 + style: TextStyle(
149 + fontSize: 16,
150 + fontWeight: FontWeight.w600,
151 + color: Colors.white,
152 + )),
153 + ),
154 + suffixIcon: Container(
155 + height: 32,
156 + width: 32,
157 + margin: EdgeInsets.only(
158 + left: 14, top: 4, bottom: 10),
159 + decoration: BoxDecoration(
160 + color: Theme.of(context)
161 + .primaryTextTheme
162 + .display1
163 + .color,
164 + borderRadius: BorderRadius.all(
165 + Radius.circular(6))),
166 + child: InkWell(
167 + onTap: () =>
168 + sendViewModel.setSendAll(),
169 + child: Center(
170 + child: Text(S.of(context).all,
171 + textAlign: TextAlign.center,
172 + style: TextStyle(
173 + fontSize: 12,
174 + fontWeight: FontWeight.bold,
175 + color: Theme.of(context)
176 + .primaryTextTheme
177 + .display1
178 + .decorationColor)),
179 + ),
180 + ),
181 + ),
182 + hintText: '0.0000',
183 + borderColor: Theme.of(context)
184 + .primaryTextTheme
185 + .headline
186 + .color,
187 + textStyle: TextStyle(
188 + fontSize: 14,
189 + fontWeight: FontWeight.w500,
190 + color: Colors.white),
191 + placeholderTextStyle: TextStyle(
192 + color: Theme.of(context)
193 + .primaryTextTheme
194 + .headline
195 + .decorationColor,
196 + fontWeight: FontWeight.w500,
197 + fontSize: 14),
198 + validator: sendViewModel.amountValidator));
199 + }),
200 + Observer(
201 + builder: (_) => Padding(
202 + padding: EdgeInsets.only(top: 10),
203 + child: Row(
204 + mainAxisSize: MainAxisSize.max,
205 + mainAxisAlignment:
206 + MainAxisAlignment.spaceBetween,
207 + children: <Widget>[
208 + Expanded(
209 + child: Text(
210 + S.of(context).available_balance + ':',
211 + style: TextStyle(
212 + fontSize: 12,
213 + fontWeight: FontWeight.w600,
214 + color: Theme.of(context)
215 + .primaryTextTheme
216 + .headline
217 + .decorationColor),
218 + )),
219 + Text(
220 + sendViewModel.balance,
221 + style: TextStyle(
222 + fontSize: 12,
223 + fontWeight: FontWeight.w600,
224 + color: Theme.of(context)
225 + .primaryTextTheme
226 + .headline
227 + .decorationColor),
228 + )
229 + ],
230 + ),
231 + )),
232 + Padding(
233 + padding: const EdgeInsets.only(top: 20),
234 + child: BaseTextFormField(
235 + controller: _fiatAmountController,
236 + keyboardType: TextInputType.numberWithOptions(
237 + signed: false, decimal: true),
238 + inputFormatters: [
239 + BlacklistingTextInputFormatter(
240 + RegExp('[\\-|\\ |\\,]'))
241 + ],
242 + prefixIcon: Padding(
243 + padding: EdgeInsets.only(top: 9),
244 + child: Text(sendViewModel.fiat.title + ':',
245 + style: TextStyle(
246 + fontSize: 16,
247 + fontWeight: FontWeight.w600,
248 + color: Colors.white,
249 + )),
250 + ),
251 + hintText: '0.00',
252 + borderColor: Theme.of(context)
253 + .primaryTextTheme
254 + .headline
255 + .color,
256 + textStyle: TextStyle(
257 + fontSize: 14,
258 + fontWeight: FontWeight.w500,
259 + color: Colors.white),
260 + placeholderTextStyle: TextStyle(
261 + color: Theme.of(context)
262 + .primaryTextTheme
263 + .headline
264 + .decorationColor,
265 + fontWeight: FontWeight.w500,
266 + fontSize: 14),
267 + )),
268 + Observer(
269 + builder: (_) => GestureDetector(
270 + onTap: () =>
271 + _setTransactionPriority(context),
272 + child: Container(
273 + padding: EdgeInsets.only(top: 24),
274 + child: Row(
275 + mainAxisAlignment:
276 + MainAxisAlignment.spaceBetween,
277 + children: <Widget>[
278 + Text(S.of(context).send_estimated_fee,
279 + style: TextStyle(
280 + fontSize: 12,
281 + fontWeight: FontWeight.w500,
282 + //color: Theme.of(context).primaryTextTheme.display2.color,
283 + color: Colors.white)),
284 + Container(
285 + child: Row(
286 + children: <Widget>[
287 + Text(
288 + sendViewModel.estimatedFee
289 + .toString() +
290 + ' ' +
291 + sendViewModel
292 + .currency.title,
293 + style: TextStyle(
294 + fontSize: 12,
295 + fontWeight:
296 + FontWeight.w600,
297 + //color: Theme.of(context).primaryTextTheme.display2.color,
298 + color: Colors.white)),
299 + Padding(
300 + padding:
301 + EdgeInsets.only(left: 5),
302 + child: Icon(
303 + Icons.arrow_forward_ios,
304 + size: 12,
305 + color: Colors.white,
306 + ),
307 + )
308 + ],
309 + ),
310 + )
311 + ],
312 + ),
313 + ),
314 + ))
315 + ],
316 + ),
317 + )
318 + ]),
319 + ),
320 + ),
321 + Padding(
322 + padding: EdgeInsets.only(top: 30, left: 24, bottom: 24),
323 + child: Row(
324 + mainAxisAlignment: MainAxisAlignment.start,
325 + children: <Widget>[
326 + Text(
327 + S.of(context).send_templates,
328 + style: TextStyle(
329 + fontSize: 18,
330 + fontWeight: FontWeight.w600,
331 + color: Theme.of(context)
332 + .primaryTextTheme
333 + .display4
334 + .color),
335 + )
336 + ],
337 + ),
338 + ),
339 + Container(
340 + height: 40,
341 + width: double.infinity,
342 + padding: EdgeInsets.only(left: 24),
343 + child: SingleChildScrollView(
344 + scrollDirection: Axis.horizontal,
345 + child: Row(
346 + children: <Widget>[
347 + GestureDetector(
348 + onTap: () => Navigator.of(context)
349 + .pushNamed(Routes.sendTemplate),
350 + child: Container(
351 + padding: EdgeInsets.only(left: 1, right: 10),
352 + child: DottedBorder(
353 + borderType: BorderType.RRect,
354 + dashPattern: [6, 4],
355 + color: Theme.of(context)
356 + .primaryTextTheme
357 + .display2
358 + .decorationColor,
359 + strokeWidth: 2,
360 + radius: Radius.circular(20),
361 + child: Container(
362 + height: 34,
363 + width: 75,
364 + padding: EdgeInsets.only(left: 10, right: 10),
365 + alignment: Alignment.center,
366 + decoration: BoxDecoration(
367 + borderRadius:
368 + BorderRadius.all(Radius.circular(20)),
369 + color: Colors.transparent,
370 + ),
371 + child: Text(
372 + S.of(context).send_new,
373 + style: TextStyle(
374 + fontSize: 14,
375 + fontWeight: FontWeight.w600,
376 + color: Theme.of(context)
377 + .primaryTextTheme
378 + .display3
379 + .color),
380 + ),
381 + )),
382 + ),
383 + ),
384 + // Observer(
385 + // builder: (_) {
386 + // final templates = sendViewModel.templates;
387 + // final itemCount = templates.length;
388 +
389 + // return ListView.builder(
390 + // scrollDirection: Axis.horizontal,
391 + // shrinkWrap: true,
392 + // physics: NeverScrollableScrollPhysics(),
393 + // itemCount: itemCount,
394 + // itemBuilder: (context, index) {
395 + // final template = templates[index];
396 +
397 + // return TemplateTile(
398 + // key: UniqueKey(),
399 + // to: template.name,
400 + // amount: template.amount,
401 + // from: template.cryptoCurrency,
402 + // onTap: () {
403 + // _addressController.text = template.address;
404 + // _cryptoAmountController.text = template.amount;
405 + // getOpenaliasRecord(context);
406 + // },
407 + // onRemove: () {
408 + // showDialog<void>(
409 + // context: context,
410 + // builder: (dialogContext) {
411 + // return AlertWithTwoActions(
412 + // alertTitle: S.of(context).template,
413 + // alertContent: S.of(context).confirm_delete_template,
414 + // leftButtonText: S.of(context).delete,
415 + // rightButtonText: S.of(context).cancel,
416 + // actionLeftButton: () {
417 + // Navigator.of(dialogContext).pop();
418 + // sendViewModel.sendTemplateStore.remove(template: template);
419 + // sendViewModel.sendTemplateStore.update();
420 + // },
421 + // actionRightButton: () => Navigator.of(dialogContext).pop()
422 + // );
423 + // }
424 + // );
425 + // },
426 + // );
427 + // }
428 + // );
429 + // }
430 + // )
431 + ],
432 + ),
433 + ),
434 + )
435 + ],
436 + ),
437 + bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
438 + bottomSection: Observer(builder: (_) {
439 + return LoadingPrimaryButton(
440 + onPressed: () {
441 + if (_formKey.currentState.validate()) {
442 + print('SENT!!!');
443 + }
444 + },
445 + text: S.of(context).send,
446 + color: Palette.blueCraiola,
447 + textColor: Colors.white,
448 + isLoading: sendViewModel.state is TransactionIsCreating ||
449 + sendViewModel.state is TransactionCommitting,
450 + isDisabled:
451 + false // FIXME !(syncStore.status is SyncedSyncStatus),
452 + );
453 + })
454 + );
455 + }
456 +
457 + void _setEffects(BuildContext context) {
458 + if (_effectsInstalled) {
459 + return;
460 + }
461 +
462 + reaction((_) => sendViewModel.sendAll, (bool all) {
463 + if (all) {
464 + _cryptoAmountController.text = S.current.all;
465 + _fiatAmountController.text = null;
466 + }
467 + });
468 +
469 + reaction((_) => sendViewModel.fiatAmount, (String amount) {
470 + if (amount != _fiatAmountController.text) {
471 + _fiatAmountController.text = amount;
472 + }
473 + });
474 +
475 + reaction((_) => sendViewModel.cryptoAmount, (String amount) {
476 + if (sendViewModel.sendAll && amount != S.current.all) {
477 + sendViewModel.sendAll = false;
478 + }
479 +
480 + if (amount != _cryptoAmountController.text) {
481 + _cryptoAmountController.text = amount;
482 + }
483 + });
484 +
485 + reaction((_) => sendViewModel.address, (String address) {
486 + if (address != _addressController.text) {
487 + _addressController.text = address;
488 + }
489 + });
490 +
491 + _addressController.addListener(() {
492 + final address = _addressController.text;
493 +
494 + if (sendViewModel.address != address) {
495 + sendViewModel.address = address;
496 + }
497 + });
498 +
499 + reaction((_) => sendViewModel.state, (SendViewModelState state) {
500 + if (state is SendingFailed) {
501 + WidgetsBinding.instance.addPostFrameCallback((_) {
502 + showDialog<void>(
503 + context: context,
504 + builder: (BuildContext context) {
505 + return AlertWithOneAction(
506 + alertTitle: S.of(context).error,
507 + alertContent: state.error,
508 + buttonText: S.of(context).ok,
509 + buttonAction: () => Navigator.of(context).pop());
510 + });
511 + });
512 + }
513 +
514 + if (state is TransactionCreatedSuccessfully) {
515 + WidgetsBinding.instance.addPostFrameCallback((_) {
516 + showDialog<void>(
517 + context: context,
518 + builder: (BuildContext context) {
519 + return ConfirmSendingAlert(
520 + alertTitle: S.of(context).confirm_sending,
521 + amount: S.of(context).send_amount,
522 + amountValue:
523 + sendViewModel.pendingTransaction.amountFormatted,
524 + fee: S.of(context).send_fee,
525 + feeValue: sendViewModel.pendingTransaction.feeFormatted,
526 + leftButtonText: S.of(context).ok,
527 + rightButtonText: S.of(context).cancel,
528 + actionLeftButton: () {
529 + Navigator.of(context).pop();
530 + sendViewModel.commitTransaction();
531 + showDialog<void>(
532 + context: context,
533 + builder: (BuildContext context) {
534 + return Observer(builder: (_) {
535 + final state = sendViewModel.state;
536 +
537 + if (state is TransactionCommitted) {
538 + return Stack(
539 + children: <Widget>[
540 + Container(
541 + color: Theme.of(context).backgroundColor,
542 + child: Center(
543 + child: Image.asset(
544 + 'assets/images/birthday_cake.png'),
545 + ),
546 + ),
547 + Center(
548 + child: Padding(
549 + padding: EdgeInsets.only(
550 + top: 220, left: 24, right: 24),
551 + child: Text(
552 + S.of(context).send_success,
553 + textAlign: TextAlign.center,
554 + style: TextStyle(
555 + fontSize: 22,
556 + fontWeight: FontWeight.bold,
557 + color: Theme.of(context)
558 + .primaryTextTheme
559 + .title
560 + .color,
561 + decoration: TextDecoration.none,
562 + ),
563 + ),
564 + ),
565 + ),
566 + Positioned(
567 + left: 24,
568 + right: 24,
569 + bottom: 24,
570 + child: PrimaryButton(
571 + onPressed: () =>
572 + Navigator.of(context).pop(),
573 + text: S.of(context).send_got_it,
574 + color: Colors.blue,
575 + textColor: Colors.white))
576 + ],
577 + );
578 + }
579 +
580 + return Stack(
581 + children: <Widget>[
582 + Container(
583 + color: Theme.of(context).backgroundColor,
584 + child: Center(
585 + child: Image.asset(
586 + 'assets/images/birthday_cake.png'),
587 + ),
588 + ),
589 + BackdropFilter(
590 + filter: ImageFilter.blur(
591 + sigmaX: 3.0, sigmaY: 3.0),
592 + child: Container(
593 + decoration: BoxDecoration(
594 + color: Theme.of(context)
595 + .backgroundColor
596 + .withOpacity(0.25)),
597 + child: Center(
598 + child: Padding(
599 + padding: EdgeInsets.only(top: 220),
600 + child: Text(
601 + S.of(context).send_sending,
602 + textAlign: TextAlign.center,
603 + style: TextStyle(
604 + fontSize: 22,
605 + fontWeight: FontWeight.bold,
606 + color: Theme.of(context)
607 + .primaryTextTheme
608 + .title
609 + .color,
610 + decoration: TextDecoration.none,
611 + ),
612 + ),
613 + ),
614 + ),
615 + ),
616 + )
617 + ],
618 + );
619 + });
620 + });
621 + },
622 + actionRightButton: () => Navigator.of(context).pop());
623 + });
624 + });
625 + }
626 +
627 + if (state is TransactionCommitted) {
628 + WidgetsBinding.instance.addPostFrameCallback((_) {
629 + _addressController.text = '';
630 + _cryptoAmountController.text = '';
631 + });
632 + }
633 + });
634 +
635 + _effectsInstalled = true;
636 + }
637 +
638 + Future<void> getOpenaliasRecord(BuildContext context) async {
639 + // final isOpenalias =
640 + // await sendViewModel.isOpenaliasRecord(_addressController.text);
641 +
642 + // if (isOpenalias) {
643 + // _addressController.text = sendViewModel.recordAddress;
644 +
645 + // await showDialog<void>(
646 + // context: context,
647 + // builder: (BuildContext context) {
648 + // return AlertWithOneAction(
649 + // alertTitle: S.of(context).openalias_alert_title,
650 + // alertContent: S
651 + // .of(context)
652 + // .openalias_alert_content(sendViewModel.recordName),
653 + // buttonText: S.of(context).ok,
654 + // buttonAction: () => Navigator.of(context).pop());
655 + // });
656 + // }
657 + }
658 +
659 + Future<void> _setTransactionPriority(BuildContext context) async {
660 + final items = TransactionPriority.all;
661 + final selectedItem = items.indexOf(sendViewModel.transactionPriority);
662 +
663 + await showDialog<void>(
664 + builder: (_) => Picker(
665 + items: items,
666 + selectedAtIndex: selectedItem,
667 + title: S.of(context).please_select,
668 + mainAxisAlignment: MainAxisAlignment.center,
669 + onItemSelected: (TransactionPriority priority) => null,
670 + // sendViewModel.setTransactionPriority(priority),
671 + isAlwaysShowScrollThumb: true,
672 + ),
673 + context: context);
674 + }
675 }
pubspec.lock
+12 -19
@@ -42,7 +42,7 @@ packages:
42 name: async
43 url: "https://pub.dartlang.org"
44 source: hosted
45 - version: "2.4.2"
45 + version: "2.4.1"
46 auto_size_text:
47 dependency: "direct main"
48 description:
@@ -210,7 +210,7 @@ packages:
210 name: collection
211 url: "https://pub.dartlang.org"
212 source: hosted
213 - version: "1.14.13"
213 + version: "1.14.12"
214 convert:
215 dependency: transitive
216 description:
@@ -224,7 +224,7 @@ packages:
224 name: crypto
225 url: "https://pub.dartlang.org"
226 source: hosted
227 - version: "2.1.5"
227 + version: "2.1.4"
228 csslib:
229 dependency: transitive
230 description:
@@ -302,13 +302,6 @@ packages:
302 url: "https://pub.dartlang.org"
303 source: hosted
304 version: "1.0.2"
305 - fake_async:
306 - dependency: transitive
307 - description:
308 - name: fake_async
309 - url: "https://pub.dartlang.org"
310 - source: hosted
311 - version: "1.1.0"
305 ffi:
306 dependency: transitive
307 description:
@@ -475,7 +468,7 @@ packages:
468 name: image
469 url: "https://pub.dartlang.org"
470 source: hosted
478 - version: "2.1.14"
471 + version: "2.1.12"
472 intl:
473 dependency: "direct main"
474 description:
@@ -524,7 +517,7 @@ packages:
517 name: matcher
518 url: "https://pub.dartlang.org"
519 source: hosted
527 - version: "0.12.8"
520 + version: "0.12.6"
521 meta:
522 dependency: transitive
523 description:
@@ -594,7 +587,7 @@ packages:
587 name: path
588 url: "https://pub.dartlang.org"
589 source: hosted
597 - version: "1.7.0"
590 + version: "1.6.4"
591 path_drawing:
592 dependency: transitive
593 description:
@@ -650,7 +643,7 @@ packages:
643 name: petitparser
644 url: "https://pub.dartlang.org"
645 source: hosted
653 - version: "3.0.4"
646 + version: "2.4.0"
647 platform:
648 dependency: transitive
649 description:
@@ -830,7 +823,7 @@ packages:
823 name: stack_trace
824 url: "https://pub.dartlang.org"
825 source: hosted
833 - version: "1.9.5"
826 + version: "1.9.3"
827 stream_channel:
828 dependency: transitive
829 description:
@@ -865,7 +858,7 @@ packages:
858 name: test_api
859 url: "https://pub.dartlang.org"
860 source: hosted
868 - version: "0.2.17"
861 + version: "0.2.15"
862 time:
863 dependency: transitive
864 description:
@@ -886,7 +879,7 @@ packages:
879 name: typed_data
880 url: "https://pub.dartlang.org"
881 source: hosted
889 - version: "1.2.0"
882 + version: "1.1.6"
883 url_launcher:
884 dependency: "direct main"
885 description:
@@ -963,7 +956,7 @@ packages:
956 name: xml
957 url: "https://pub.dartlang.org"
958 source: hosted
966 - version: "4.2.0"
959 + version: "3.6.1"
960 yaml:
961 dependency: "direct main"
962 description:
@@ -972,5 +965,5 @@ packages:
965 source: hosted
966 version: "2.2.1"
967 sdks:
975 - dart: ">=2.9.0-14.0.dev <3.0.0"
968 + dart: ">=2.7.0 <3.0.0"
969 flutter: ">=1.12.13+hotfix.5 <2.0.0"