CAKE-49 | deleted base send widget, base exchange widget and top panel; added extendBodyBehindAppBar property and transparent AppBar style to base page; refactored send page, send template page, exchange page and exchange template page

OleksandrSobol committed Sep 22, 2020 at 21:32 UTC 585f90b2fb263e7ea3c7839d6c21b020b0c47a42
8 files changed +1527 -1789
lib/src/screens/base_page.dart
+18 -7
@@ -6,7 +6,7 @@ import 'package:cake_wallet/themes.dart';
6 import 'package:cake_wallet/theme_changer.dart';
7 import 'package:cake_wallet/palette.dart';
8
9 -enum AppBarStyle { regular, withShadow }
9 +enum AppBarStyle { regular, withShadow, transparent }
10
11 abstract class BasePage extends StatelessWidget {
12 String get title => null;
@@ -21,6 +21,8 @@ abstract class BasePage extends StatelessWidget {
21
22 bool get resizeToAvoidBottomPadding => true;
23
24 + bool get extendBodyBehindAppBar => false;
25 +
26 Widget get endDrawer => null;
27
28 AppBarStyle get appBarStyle => AppBarStyle.regular;
@@ -85,6 +87,8 @@ abstract class BasePage extends StatelessWidget {
87 ObstructingPreferredSizeWidget appBar(BuildContext context) {
88 final _themeChanger = Provider.of<ThemeChanger>(context);
89 final _isDarkTheme = _themeChanger.getTheme() == Themes.darkTheme;
90 + final appBarColor = _isDarkTheme
91 + ? backgroundDarkColor : backgroundLightColor;
92
93 switch (appBarStyle) {
94 case AppBarStyle.regular:
@@ -93,8 +97,7 @@ abstract class BasePage extends StatelessWidget {
97 leading: leading(context),
98 middle: middle(context),
99 trailing: trailing(context),
96 - backgroundColor:
97 - _isDarkTheme ? backgroundDarkColor : backgroundLightColor);
100 + backgroundColor: appBarColor);
101
102 case AppBarStyle.withShadow:
103 return NavBar.withShadow(
@@ -102,8 +105,16 @@ abstract class BasePage extends StatelessWidget {
105 leading: leading(context),
106 middle: middle(context),
107 trailing: trailing(context),
105 - backgroundColor:
106 - _isDarkTheme ? backgroundDarkColor : backgroundLightColor);
108 + backgroundColor: appBarColor);
109 +
110 + case AppBarStyle.transparent:
111 + return CupertinoNavigationBar(
112 + leading: leading(context),
113 + middle: middle(context),
114 + trailing: trailing(context),
115 + backgroundColor: Colors.transparent,
116 + border: null,
117 + );
118
119 default:
120 return NavBar(
@@ -111,8 +122,7 @@ abstract class BasePage extends StatelessWidget {
122 leading: leading(context),
123 middle: middle(context),
124 trailing: trailing(context),
114 - backgroundColor:
115 - _isDarkTheme ? backgroundDarkColor : backgroundLightColor);
125 + backgroundColor: appBarColor);
126 }
127 }
128
@@ -128,6 +138,7 @@ abstract class BasePage extends StatelessWidget {
138 backgroundColor:
139 _isDarkTheme ? backgroundDarkColor : backgroundLightColor,
140 resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
141 + extendBodyBehindAppBar: extendBodyBehindAppBar,
142 endDrawer: endDrawer,
143 appBar: appBar(context),
144 body: body(context), //SafeArea(child: ),
lib/src/screens/exchange/exchange_page.dart
+549 -18
@@ -1,18 +1,38 @@
1 import 'dart:ui';
2 +import 'package:cake_wallet/core/address_validator.dart';
3 +import 'package:cake_wallet/core/amount_validator.dart';
4 +import 'package:cake_wallet/routes.dart';
5 +import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
6 +import 'package:cake_wallet/src/domain/exchange/exchange_template.dart';
7 +import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
8 +import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
9 +import 'package:cake_wallet/src/stores/exchange/exchange_trade_state.dart';
10 +import 'package:cake_wallet/src/stores/exchange/limits_state.dart';
11 +import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
12 +import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
13 +import 'package:cake_wallet/src/widgets/primary_button.dart';
14 +import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
15 +import 'package:cake_wallet/src/widgets/template_tile.dart';
16 +import 'package:dotted_border/dotted_border.dart';
17 import 'package:flutter/cupertino.dart';
18 import 'package:flutter/material.dart';
19 import 'package:cake_wallet/palette.dart';
20 import 'package:cake_wallet/generated/i18n.dart';
21 import 'package:cake_wallet/src/screens/base_page.dart';
22 import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
8 -import 'package:cake_wallet/src/screens/exchange/widgets/base_exchange_widget.dart';
23 import 'package:cake_wallet/src/widgets/trail_button.dart';
24 import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
25 +import 'package:flutter_mobx/flutter_mobx.dart';
26 +import 'package:mobx/mobx.dart';
27
28 class ExchangePage extends BasePage {
29 ExchangePage(this.exchangeViewModel);
30
31 final ExchangeViewModel exchangeViewModel;
32 + final depositKey = GlobalKey<ExchangeCardState>();
33 + final receiveKey = GlobalKey<ExchangeCardState>();
34 + final _formKey = GlobalKey<FormState>();
35 + var _isReactionsSet = false;
36
37 @override
38 String get title => S.current.exchange;
@@ -21,10 +41,13 @@ class ExchangePage extends BasePage {
41 Color get titleColor => Colors.white;
42
43 @override
24 - Color get backgroundLightColor => Colors.transparent;
44 + bool get resizeToAvoidBottomPadding => false;
45
46 @override
27 - Color get backgroundDarkColor => Colors.transparent;
47 + bool get extendBodyBehindAppBar => true;
48 +
49 + @override
50 + AppBarStyle get appBarStyle => AppBarStyle.transparent;
51
52 @override
53 Widget middle(BuildContext context) =>
@@ -38,22 +61,530 @@ class ExchangePage extends BasePage {
61 );
62
63 @override
41 - Widget body(BuildContext context) =>
42 - BaseExchangeWidget(
43 - exchangeViewModel: exchangeViewModel,
44 - leading: leading(context),
45 - middle: middle(context),
46 - trailing: trailing(context),
47 - );
64 + Widget body(BuildContext context) {
65 + final arrowBottomPurple = Image.asset(
66 + 'assets/images/arrow_bottom_purple_icon.png',
67 + color: Colors.white,
68 + height: 8,
69 + );
70 + final arrowBottomCakeGreen = Image.asset(
71 + 'assets/images/arrow_bottom_cake_green.png',
72 + color: Colors.white,
73 + height: 8,
74 + );
75
49 - @override
50 - Widget build(BuildContext context) {
51 - return Scaffold(
52 - resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
53 - body: Container(
54 - color: Theme.of(context).backgroundColor,
55 - child: body(context)
56 - )
76 + final depositWalletName =
77 + exchangeViewModel.depositCurrency == CryptoCurrency.xmr
78 + ? exchangeViewModel.wallet.name
79 + : null;
80 + final receiveWalletName =
81 + exchangeViewModel.receiveCurrency == CryptoCurrency.xmr
82 + ? exchangeViewModel.wallet.name
83 + : null;
84 +
85 + WidgetsBinding.instance
86 + .addPostFrameCallback((_) => _setReactions(context, exchangeViewModel));
87 +
88 + return Container(
89 + color: Theme.of(context).backgroundColor,
90 + child: Form(
91 + key: _formKey,
92 + child: ScrollableWithBottomSection(
93 + contentPadding: EdgeInsets.only(bottom: 24),
94 + content: Column(
95 + children: <Widget>[
96 + Container(
97 + padding: EdgeInsets.only(bottom: 32),
98 + decoration: BoxDecoration(
99 + borderRadius: BorderRadius.only(
100 + bottomLeft: Radius.circular(24),
101 + bottomRight: Radius.circular(24)
102 + ),
103 + gradient: LinearGradient(
104 + colors: [
105 + Theme.of(context).primaryTextTheme.body1.color,
106 + Theme.of(context).primaryTextTheme.body1.decorationColor,
107 + ],
108 + stops: [0.35, 1.0],
109 + begin: Alignment.topLeft,
110 + end: Alignment.bottomRight),
111 + ),
112 + child: Column(
113 + children: <Widget>[
114 + Container(
115 + decoration: BoxDecoration(
116 + borderRadius: BorderRadius.only(
117 + bottomLeft: Radius.circular(24),
118 + bottomRight: Radius.circular(24)
119 + ),
120 + gradient: LinearGradient(
121 + colors: [
122 + Theme.of(context)
123 + .primaryTextTheme
124 + .subtitle
125 + .color,
126 + Theme.of(context)
127 + .primaryTextTheme
128 + .subtitle
129 + .decorationColor,
130 + ],
131 + begin: Alignment.topLeft,
132 + end: Alignment.bottomRight),
133 + ),
134 + padding: EdgeInsets.fromLTRB(24, 90, 24, 32),
135 + child: Observer(
136 + builder: (_) => ExchangeCard(
137 + key: depositKey,
138 + title: S.of(context).you_will_send,
139 + initialCurrency:
140 + exchangeViewModel.depositCurrency,
141 + initialWalletName: depositWalletName,
142 + initialAddress: exchangeViewModel
143 + .depositCurrency ==
144 + exchangeViewModel.wallet.currency
145 + ? exchangeViewModel.wallet.address
146 + : exchangeViewModel.depositAddress,
147 + initialIsAmountEditable: true,
148 + initialIsAddressEditable: exchangeViewModel
149 + .isDepositAddressEnabled,
150 + isAmountEstimated: false,
151 + currencies: CryptoCurrency.all,
152 + onCurrencySelected: (currency) =>
153 + exchangeViewModel.changeDepositCurrency(
154 + currency: currency),
155 + imageArrow: arrowBottomPurple,
156 + currencyButtonColor: Colors.transparent,
157 + addressButtonsColor:
158 + Theme.of(context).focusColor,
159 + borderColor: Theme.of(context)
160 + .primaryTextTheme
161 + .body2
162 + .color,
163 + currencyValueValidator: AmountValidator(
164 + type: exchangeViewModel.wallet.type),
165 + addressTextFieldValidator: AddressValidator(
166 + type:
167 + exchangeViewModel.depositCurrency),
168 + ),
169 + ),
170 + ),
171 + Padding(
172 + padding: EdgeInsets.only(top: 29, left: 24, right: 24),
173 + child: Observer(
174 + builder: (_) => ExchangeCard(
175 + key: receiveKey,
176 + title: S.of(context).you_will_get,
177 + initialCurrency:
178 + exchangeViewModel.receiveCurrency,
179 + initialWalletName: receiveWalletName,
180 + initialAddress:
181 + exchangeViewModel.receiveCurrency ==
182 + exchangeViewModel.wallet.currency
183 + ? exchangeViewModel.wallet.address
184 + : exchangeViewModel.receiveAddress,
185 + initialIsAmountEditable: false,
186 + initialIsAddressEditable:
187 + exchangeViewModel.isReceiveAddressEnabled,
188 + isAmountEstimated: true,
189 + currencies: CryptoCurrency.all,
190 + onCurrencySelected: (currency) =>
191 + exchangeViewModel.changeReceiveCurrency(
192 + currency: currency),
193 + imageArrow: arrowBottomCakeGreen,
194 + currencyButtonColor: Colors.transparent,
195 + addressButtonsColor:
196 + Theme.of(context).focusColor,
197 + borderColor: Theme.of(context)
198 + .primaryTextTheme
199 + .body2
200 + .decorationColor,
201 + currencyValueValidator: AmountValidator(
202 + type: exchangeViewModel.wallet.type),
203 + addressTextFieldValidator: AddressValidator(
204 + type: exchangeViewModel.receiveCurrency),
205 + )),
206 + )
207 + ],
208 + ),
209 + ),
210 + Padding(
211 + padding: EdgeInsets.only(top: 30, left: 24, bottom: 24),
212 + child: Row(
213 + mainAxisAlignment: MainAxisAlignment.start,
214 + children: <Widget>[
215 + Text(
216 + S.of(context).send_templates,
217 + style: TextStyle(
218 + fontSize: 18,
219 + fontWeight: FontWeight.w600,
220 + color: Theme.of(context)
221 + .primaryTextTheme
222 + .display4
223 + .color),
224 + )
225 + ],
226 + ),
227 + ),
228 + Container(
229 + height: 40,
230 + width: double.infinity,
231 + padding: EdgeInsets.only(left: 24),
232 + child: SingleChildScrollView(
233 + scrollDirection: Axis.horizontal,
234 + child: Row(
235 + children: <Widget>[
236 + GestureDetector(
237 + onTap: () => Navigator.of(context)
238 + .pushNamed(Routes.exchangeTemplate),
239 + child: Container(
240 + padding: EdgeInsets.only(left: 1, right: 10),
241 + child: DottedBorder(
242 + borderType: BorderType.RRect,
243 + dashPattern: [6, 4],
244 + color: Theme.of(context)
245 + .primaryTextTheme
246 + .display2
247 + .decorationColor,
248 + strokeWidth: 2,
249 + radius: Radius.circular(20),
250 + child: Container(
251 + height: 34,
252 + width: 75,
253 + padding: EdgeInsets.only(
254 + left: 10, right: 10),
255 + alignment: Alignment.center,
256 + decoration: BoxDecoration(
257 + borderRadius: BorderRadius.all(
258 + Radius.circular(20)),
259 + color: Colors.transparent,
260 + ),
261 + child: Text(
262 + S.of(context).send_new,
263 + style: TextStyle(
264 + fontSize: 14,
265 + fontWeight: FontWeight.w600,
266 + color: Theme.of(context)
267 + .primaryTextTheme
268 + .display3
269 + .color),
270 + ),
271 + )),
272 + ),
273 + ),
274 + Observer(builder: (_) {
275 + final templates = exchangeViewModel.templates;
276 + final itemCount =
277 + exchangeViewModel.templates.length;
278 +
279 + return ListView.builder(
280 + scrollDirection: Axis.horizontal,
281 + shrinkWrap: true,
282 + physics: NeverScrollableScrollPhysics(),
283 + itemCount: itemCount,
284 + itemBuilder: (context, index) {
285 + final template = templates[index];
286 +
287 + return TemplateTile(
288 + key: UniqueKey(),
289 + amount: template.amount,
290 + from: template.depositCurrency,
291 + to: template.receiveCurrency,
292 + onTap: () {
293 + applyTemplate(
294 + exchangeViewModel, template);
295 + },
296 + onRemove: () {
297 + showDialog<void>(
298 + context: context,
299 + builder: (dialogContext) {
300 + return AlertWithTwoActions(
301 + alertTitle:
302 + S.of(context).template,
303 + alertContent: S
304 + .of(context)
305 + .confirm_delete_template,
306 + leftButtonText:
307 + S.of(context).delete,
308 + rightButtonText:
309 + S.of(context).cancel,
310 + actionLeftButton: () {
311 + Navigator.of(
312 + dialogContext)
313 + .pop();
314 + exchangeViewModel
315 + .exchangeTemplateStore
316 + .remove(
317 + template:
318 + template);
319 + exchangeViewModel
320 + .exchangeTemplateStore
321 + .update();
322 + },
323 + actionRightButton: () =>
324 + Navigator.of(
325 + dialogContext)
326 + .pop());
327 + });
328 + },
329 + );
330 + });
331 + }),
332 + ],
333 + )))
334 + ],
335 + ),
336 + bottomSectionPadding:
337 + EdgeInsets.only(left: 24, right: 24, bottom: 24),
338 + bottomSection: Column(children: <Widget>[
339 + Padding(
340 + padding: EdgeInsets.only(bottom: 15),
341 + child: Observer(builder: (_) {
342 + final description =
343 + exchangeViewModel.provider is XMRTOExchangeProvider
344 + ? S.of(context).amount_is_guaranteed
345 + : S.of(context).amount_is_estimate;
346 + return Center(
347 + child: Text(
348 + description,
349 + style: TextStyle(
350 + color: Theme.of(context)
351 + .primaryTextTheme
352 + .display4
353 + .decorationColor,
354 + fontWeight: FontWeight.w500,
355 + fontSize: 12),
356 + ),
357 + );
358 + }),
359 + ),
360 + Observer(
361 + builder: (_) => LoadingPrimaryButton(
362 + text: S.of(context).exchange,
363 + onPressed: () {
364 + if (_formKey.currentState.validate()) {
365 + exchangeViewModel.createTrade();
366 + }
367 + },
368 + color: Palette.blueCraiola,
369 + textColor: Colors.white,
370 + isLoading:
371 + exchangeViewModel.tradeState is TradeIsCreating,
372 + )),
373 + ]),
374 + )),
375 );
376 }
377 +
378 + void applyTemplate(
379 + ExchangeViewModel exchangeViewModel, ExchangeTemplate template) {
380 + exchangeViewModel.changeDepositCurrency(
381 + currency: CryptoCurrency.fromString(template.depositCurrency));
382 + exchangeViewModel.changeReceiveCurrency(
383 + currency: CryptoCurrency.fromString(template.receiveCurrency));
384 +
385 + switch (template.provider) {
386 + case 'XMR.TO':
387 + exchangeViewModel.changeProvider(
388 + provider: exchangeViewModel.providerList[0]);
389 + break;
390 + case 'ChangeNOW':
391 + exchangeViewModel.changeProvider(
392 + provider: exchangeViewModel.providerList[1]);
393 + break;
394 + case 'MorphToken':
395 + exchangeViewModel.changeProvider(
396 + provider: exchangeViewModel.providerList[2]);
397 + break;
398 + }
399 +
400 + exchangeViewModel.changeDepositAmount(amount: template.amount);
401 + exchangeViewModel.depositAddress = template.depositAddress;
402 + exchangeViewModel.receiveAddress = template.receiveAddress;
403 + }
404 +
405 + void _setReactions(
406 + BuildContext context, ExchangeViewModel exchangeViewModel) {
407 + if (_isReactionsSet) {
408 + return;
409 + }
410 +
411 + final depositAddressController = depositKey.currentState.addressController;
412 + final depositAmountController = depositKey.currentState.amountController;
413 + final receiveAddressController = receiveKey.currentState.addressController;
414 + final receiveAmountController = receiveKey.currentState.amountController;
415 + final limitsState = exchangeViewModel.limitsState;
416 +
417 + if (limitsState is LimitsLoadedSuccessfully) {
418 + final min = limitsState.limits.min != null
419 + ? limitsState.limits.min.toString()
420 + : null;
421 + final max = limitsState.limits.max != null
422 + ? limitsState.limits.max.toString()
423 + : null;
424 + final key = depositKey;
425 + key.currentState.changeLimits(min: min, max: max);
426 + }
427 +
428 + _onCurrencyChange(
429 + exchangeViewModel.receiveCurrency, exchangeViewModel, receiveKey);
430 + _onCurrencyChange(
431 + exchangeViewModel.depositCurrency, exchangeViewModel, depositKey);
432 +
433 + reaction(
434 + (_) => exchangeViewModel.wallet.name,
435 + (String _) => _onWalletNameChange(
436 + exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey));
437 +
438 + reaction(
439 + (_) => exchangeViewModel.wallet.name,
440 + (String _) => _onWalletNameChange(
441 + exchangeViewModel, exchangeViewModel.depositCurrency, depositKey));
442 +
443 + reaction(
444 + (_) => exchangeViewModel.receiveCurrency,
445 + (CryptoCurrency currency) =>
446 + _onCurrencyChange(currency, exchangeViewModel, receiveKey));
447 +
448 + reaction(
449 + (_) => exchangeViewModel.depositCurrency,
450 + (CryptoCurrency currency) =>
451 + _onCurrencyChange(currency, exchangeViewModel, depositKey));
452 +
453 + reaction((_) => exchangeViewModel.depositAmount, (String amount) {
454 + if (depositKey.currentState.amountController.text != amount) {
455 + depositKey.currentState.amountController.text = amount;
456 + }
457 + });
458 +
459 + reaction((_) => exchangeViewModel.depositAddress, (String address) {
460 + if (depositKey.currentState.addressController.text != address) {
461 + depositKey.currentState.addressController.text = address;
462 + }
463 + });
464 +
465 + reaction((_) => exchangeViewModel.isDepositAddressEnabled,
466 + (bool isEnabled) {
467 + depositKey.currentState.isAddressEditable(isEditable: isEnabled);
468 + });
469 +
470 + reaction((_) => exchangeViewModel.receiveAmount, (String amount) {
471 + if (receiveKey.currentState.amountController.text != amount) {
472 + receiveKey.currentState.amountController.text = amount;
473 + }
474 + });
475 +
476 + reaction((_) => exchangeViewModel.receiveAddress, (String address) {
477 + if (receiveKey.currentState.addressController.text != address) {
478 + receiveKey.currentState.addressController.text = address;
479 + }
480 + });
481 +
482 + reaction((_) => exchangeViewModel.isReceiveAddressEnabled,
483 + (bool isEnabled) {
484 + receiveKey.currentState.isAddressEditable(isEditable: isEnabled);
485 + });
486 +
487 + reaction((_) => exchangeViewModel.tradeState, (ExchangeTradeState state) {
488 + if (state is TradeIsCreatedFailure) {
489 + WidgetsBinding.instance.addPostFrameCallback((_) {
490 + showDialog<void>(
491 + context: context,
492 + builder: (BuildContext context) {
493 + return AlertWithOneAction(
494 + alertTitle: S.of(context).error,
495 + alertContent: state.error,
496 + buttonText: S.of(context).ok,
497 + buttonAction: () => Navigator.of(context).pop());
498 + });
499 + });
500 + }
501 + if (state is TradeIsCreatedSuccessfully) {
502 + Navigator.of(context).pushNamed(Routes.exchangeConfirm);
503 + }
504 + });
505 +
506 + reaction((_) => exchangeViewModel.limitsState, (LimitsState state) {
507 + String min;
508 + String max;
509 +
510 + if (state is LimitsLoadedSuccessfully) {
511 + min = state.limits.min != null ? state.limits.min.toString() : null;
512 + max = state.limits.max != null ? state.limits.max.toString() : null;
513 + }
514 +
515 + if (state is LimitsLoadedFailure) {
516 + min = '0';
517 + max = '0';
518 + }
519 +
520 + if (state is LimitsIsLoading) {
521 + min = '...';
522 + max = '...';
523 + }
524 +
525 + depositKey.currentState.changeLimits(min: min, max: max);
526 + receiveKey.currentState.changeLimits(min: null, max: null);
527 + });
528 +
529 + depositAddressController.addListener(
530 + () => exchangeViewModel.depositAddress = depositAddressController.text);
531 +
532 + depositAmountController.addListener(() {
533 + if (depositAmountController.text != exchangeViewModel.depositAmount) {
534 + exchangeViewModel.changeDepositAmount(
535 + amount: depositAmountController.text);
536 + }
537 + });
538 +
539 + receiveAddressController.addListener(
540 + () => exchangeViewModel.receiveAddress = receiveAddressController.text);
541 +
542 + receiveAmountController.addListener(() {
543 + if (receiveAmountController.text != exchangeViewModel.receiveAmount) {
544 + exchangeViewModel.changeReceiveAmount(
545 + amount: receiveAmountController.text);
546 + }
547 + });
548 +
549 + reaction((_) => exchangeViewModel.wallet.address, (String address) {
550 + if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) {
551 + depositKey.currentState.changeAddress(address: address);
552 + }
553 +
554 + if (exchangeViewModel.receiveCurrency == CryptoCurrency.xmr) {
555 + receiveKey.currentState.changeAddress(address: address);
556 + }
557 + });
558 +
559 + _isReactionsSet = true;
560 + }
561 +
562 + void _onCurrencyChange(CryptoCurrency currency,
563 + ExchangeViewModel exchangeViewModel, GlobalKey<ExchangeCardState> key) {
564 + final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
565 +
566 + key.currentState.changeSelectedCurrency(currency);
567 + key.currentState.changeWalletName(
568 + isCurrentTypeWallet ? exchangeViewModel.wallet.name : null);
569 +
570 + key.currentState.changeAddress(
571 + address: isCurrentTypeWallet ? exchangeViewModel.wallet.address : '');
572 +
573 + key.currentState.changeAmount(amount: '');
574 + }
575 +
576 + void _onWalletNameChange(ExchangeViewModel exchangeViewModel,
577 + CryptoCurrency currency, GlobalKey<ExchangeCardState> key) {
578 + final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
579 +
580 + if (isCurrentTypeWallet) {
581 + key.currentState.changeWalletName(exchangeViewModel.wallet.name);
582 + key.currentState.addressController.text =
583 + exchangeViewModel.wallet.address;
584 + } else if (key.currentState.addressController.text ==
585 + exchangeViewModel.wallet.address) {
586 + key.currentState.changeWalletName(null);
587 + key.currentState.addressController.text = null;
588 + }
589 + }
590 }
lib/src/screens/exchange/exchange_template_page.dart
+374 -19
@@ -1,16 +1,29 @@
1 import 'dart:ui';
2 +import 'package:cake_wallet/core/address_validator.dart';
3 +import 'package:cake_wallet/core/amount_validator.dart';
4 +import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
5 +import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
6 +import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
7 +import 'package:cake_wallet/src/stores/exchange/limits_state.dart';
8 +import 'package:cake_wallet/src/widgets/primary_button.dart';
9 +import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
10 import 'package:flutter/cupertino.dart';
11 import 'package:flutter/material.dart';
12 import 'package:cake_wallet/src/screens/base_page.dart';
13 import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
6 -import 'package:cake_wallet/src/screens/exchange/widgets/base_exchange_widget.dart';
14 import 'package:cake_wallet/generated/i18n.dart';
15 import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
16 +import 'package:flutter_mobx/flutter_mobx.dart';
17 +import 'package:mobx/mobx.dart';
18
19 class ExchangeTemplatePage extends BasePage {
20 ExchangeTemplatePage(this.exchangeViewModel);
21
22 final ExchangeViewModel exchangeViewModel;
23 + final depositKey = GlobalKey<ExchangeCardState>();
24 + final receiveKey = GlobalKey<ExchangeCardState>();
25 + final _formKey = GlobalKey<FormState>();
26 + var _isReactionsSet = false;
27
28 @override
29 String get title => S.current.exchange_new_template;
@@ -19,33 +32,375 @@ class ExchangeTemplatePage extends BasePage {
32 Color get titleColor => Colors.white;
33
34 @override
22 - Color get backgroundLightColor => Colors.transparent;
35 + bool get resizeToAvoidBottomPadding => false;
36
37 @override
25 - Color get backgroundDarkColor => Colors.transparent;
38 + bool get extendBodyBehindAppBar => true;
39 +
40 + @override
41 + AppBarStyle get appBarStyle => AppBarStyle.transparent;
42
43 @override
44 Widget trailing(BuildContext context) =>
45 PresentProviderPicker(exchangeViewModel: exchangeViewModel);
46
47 @override
32 - Widget body(BuildContext context) =>
33 - BaseExchangeWidget(
34 - exchangeViewModel: exchangeViewModel,
35 - leading: leading(context),
36 - middle: middle(context),
37 - trailing: trailing(context),
38 - isTemplate: true
39 - );
48 + Widget body(BuildContext context) {
49 + final arrowBottomPurple = Image.asset(
50 + 'assets/images/arrow_bottom_purple_icon.png',
51 + color: Colors.white,
52 + height: 8,
53 + );
54 + final arrowBottomCakeGreen = Image.asset(
55 + 'assets/images/arrow_bottom_cake_green.png',
56 + color: Colors.white,
57 + height: 8,
58 + );
59
41 - @override
42 - Widget build(BuildContext context) {
43 - return Scaffold(
44 - resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
45 - body: Container(
46 - color: Theme.of(context).backgroundColor,
47 - child: body(context)
48 - )
60 + final depositWalletName =
61 + exchangeViewModel.depositCurrency == CryptoCurrency.xmr
62 + ? exchangeViewModel.wallet.name
63 + : null;
64 + final receiveWalletName =
65 + exchangeViewModel.receiveCurrency == CryptoCurrency.xmr
66 + ? exchangeViewModel.wallet.name
67 + : null;
68 +
69 + WidgetsBinding.instance
70 + .addPostFrameCallback((_) => _setReactions(context, exchangeViewModel));
71 +
72 + return Container(
73 + color: Theme.of(context).backgroundColor,
74 + child: Form(
75 + key: _formKey,
76 + child: ScrollableWithBottomSection(
77 + contentPadding: EdgeInsets.only(bottom: 24),
78 + content: Container(
79 + padding: EdgeInsets.only(bottom: 32),
80 + decoration: BoxDecoration(
81 + borderRadius: BorderRadius.only(
82 + bottomLeft: Radius.circular(24),
83 + bottomRight: Radius.circular(24)
84 + ),
85 + gradient: LinearGradient(
86 + colors: [
87 + Theme.of(context).primaryTextTheme.body1.color,
88 + Theme.of(context).primaryTextTheme.body1.decorationColor,
89 + ],
90 + stops: [0.35, 1.0],
91 + begin: Alignment.topLeft,
92 + end: Alignment.bottomRight),
93 + ),
94 + child: Column(
95 + children: <Widget>[
96 + Container(
97 + decoration: BoxDecoration(
98 + borderRadius: BorderRadius.only(
99 + bottomLeft: Radius.circular(24),
100 + bottomRight: Radius.circular(24)
101 + ),
102 + gradient: LinearGradient(
103 + colors: [
104 + Theme.of(context)
105 + .primaryTextTheme
106 + .subtitle
107 + .color,
108 + Theme.of(context)
109 + .primaryTextTheme
110 + .subtitle
111 + .decorationColor,
112 + ],
113 + begin: Alignment.topLeft,
114 + end: Alignment.bottomRight),
115 + ),
116 + padding: EdgeInsets.fromLTRB(24, 90, 24, 32),
117 + child: Observer(
118 + builder: (_) => ExchangeCard(
119 + key: depositKey,
120 + title: S.of(context).you_will_send,
121 + initialCurrency:
122 + exchangeViewModel.depositCurrency,
123 + initialWalletName: depositWalletName,
124 + initialAddress: exchangeViewModel
125 + .depositCurrency ==
126 + exchangeViewModel.wallet.currency
127 + ? exchangeViewModel.wallet.address
128 + : exchangeViewModel.depositAddress,
129 + initialIsAmountEditable: true,
130 + initialIsAddressEditable: exchangeViewModel
131 + .isDepositAddressEnabled,
132 + isAmountEstimated: false,
133 + currencies: CryptoCurrency.all,
134 + onCurrencySelected: (currency) =>
135 + exchangeViewModel.changeDepositCurrency(
136 + currency: currency),
137 + imageArrow: arrowBottomPurple,
138 + currencyButtonColor: Colors.transparent,
139 + addressButtonsColor:
140 + Theme.of(context).focusColor,
141 + borderColor: Theme.of(context)
142 + .primaryTextTheme
143 + .body2
144 + .color,
145 + currencyValueValidator: AmountValidator(
146 + type: exchangeViewModel.wallet.type),
147 + addressTextFieldValidator: AddressValidator(
148 + type:
149 + exchangeViewModel.depositCurrency),
150 + ),
151 + ),
152 + ),
153 + Padding(
154 + padding: EdgeInsets.only(top: 29, left: 24, right: 24),
155 + child: Observer(
156 + builder: (_) => ExchangeCard(
157 + key: receiveKey,
158 + title: S.of(context).you_will_get,
159 + initialCurrency:
160 + exchangeViewModel.receiveCurrency,
161 + initialWalletName: receiveWalletName,
162 + initialAddress:
163 + exchangeViewModel.receiveCurrency ==
164 + exchangeViewModel.wallet.currency
165 + ? exchangeViewModel.wallet.address
166 + : exchangeViewModel.receiveAddress,
167 + initialIsAmountEditable: false,
168 + initialIsAddressEditable:
169 + exchangeViewModel.isReceiveAddressEnabled,
170 + isAmountEstimated: true,
171 + currencies: CryptoCurrency.all,
172 + onCurrencySelected: (currency) =>
173 + exchangeViewModel.changeReceiveCurrency(
174 + currency: currency),
175 + imageArrow: arrowBottomCakeGreen,
176 + currencyButtonColor: Colors.transparent,
177 + addressButtonsColor:
178 + Theme.of(context).focusColor,
179 + borderColor: Theme.of(context)
180 + .primaryTextTheme
181 + .body2
182 + .decorationColor,
183 + currencyValueValidator: AmountValidator(
184 + type: exchangeViewModel.wallet.type),
185 + addressTextFieldValidator: AddressValidator(
186 + type: exchangeViewModel.receiveCurrency),
187 + )),
188 + )
189 + ],
190 + ),
191 + ),
192 + bottomSectionPadding:
193 + EdgeInsets.only(left: 24, right: 24, bottom: 24),
194 + bottomSection: Column(children: <Widget>[
195 + Padding(
196 + padding: EdgeInsets.only(bottom: 15),
197 + child: Observer(builder: (_) {
198 + final description =
199 + exchangeViewModel.provider is XMRTOExchangeProvider
200 + ? S.of(context).amount_is_guaranteed
201 + : S.of(context).amount_is_estimate;
202 + return Center(
203 + child: Text(
204 + description,
205 + style: TextStyle(
206 + color: Theme.of(context)
207 + .primaryTextTheme
208 + .display4
209 + .decorationColor,
210 + fontWeight: FontWeight.w500,
211 + fontSize: 12),
212 + ),
213 + );
214 + }),
215 + ),
216 + PrimaryButton(
217 + onPressed: () {
218 + if (_formKey.currentState.validate()) {
219 + exchangeViewModel.exchangeTemplateStore.addTemplate(
220 + amount: exchangeViewModel.depositAmount,
221 + depositCurrency:
222 + exchangeViewModel.depositCurrency.toString(),
223 + receiveCurrency:
224 + exchangeViewModel.receiveCurrency.toString(),
225 + provider: exchangeViewModel.provider.toString(),
226 + depositAddress: exchangeViewModel.depositAddress,
227 + receiveAddress: exchangeViewModel.receiveAddress);
228 + exchangeViewModel.exchangeTemplateStore.update();
229 + Navigator.of(context).pop();
230 + }
231 + },
232 + text: S.of(context).save,
233 + color: Colors.green,
234 + textColor: Colors.white),
235 + ]),
236 + ))
237 );
238 }
239 +
240 + void _setReactions(
241 + BuildContext context, ExchangeViewModel exchangeViewModel) {
242 + if (_isReactionsSet) {
243 + return;
244 + }
245 +
246 + final depositAddressController = depositKey.currentState.addressController;
247 + final depositAmountController = depositKey.currentState.amountController;
248 + final receiveAddressController = receiveKey.currentState.addressController;
249 + final receiveAmountController = receiveKey.currentState.amountController;
250 + final limitsState = exchangeViewModel.limitsState;
251 +
252 + if (limitsState is LimitsLoadedSuccessfully) {
253 + final min = limitsState.limits.min != null
254 + ? limitsState.limits.min.toString()
255 + : null;
256 + final max = limitsState.limits.max != null
257 + ? limitsState.limits.max.toString()
258 + : null;
259 + final key = depositKey;
260 + key.currentState.changeLimits(min: min, max: max);
261 + }
262 +
263 + _onCurrencyChange(
264 + exchangeViewModel.receiveCurrency, exchangeViewModel, receiveKey);
265 + _onCurrencyChange(
266 + exchangeViewModel.depositCurrency, exchangeViewModel, depositKey);
267 +
268 + reaction(
269 + (_) => exchangeViewModel.wallet.name,
270 + (String _) => _onWalletNameChange(
271 + exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey));
272 +
273 + reaction(
274 + (_) => exchangeViewModel.wallet.name,
275 + (String _) => _onWalletNameChange(
276 + exchangeViewModel, exchangeViewModel.depositCurrency, depositKey));
277 +
278 + reaction(
279 + (_) => exchangeViewModel.receiveCurrency,
280 + (CryptoCurrency currency) =>
281 + _onCurrencyChange(currency, exchangeViewModel, receiveKey));
282 +
283 + reaction(
284 + (_) => exchangeViewModel.depositCurrency,
285 + (CryptoCurrency currency) =>
286 + _onCurrencyChange(currency, exchangeViewModel, depositKey));
287 +
288 + reaction((_) => exchangeViewModel.depositAmount, (String amount) {
289 + if (depositKey.currentState.amountController.text != amount) {
290 + depositKey.currentState.amountController.text = amount;
291 + }
292 + });
293 +
294 + reaction((_) => exchangeViewModel.depositAddress, (String address) {
295 + if (depositKey.currentState.addressController.text != address) {
296 + depositKey.currentState.addressController.text = address;
297 + }
298 + });
299 +
300 + reaction((_) => exchangeViewModel.isDepositAddressEnabled,
301 + (bool isEnabled) {
302 + depositKey.currentState.isAddressEditable(isEditable: isEnabled);
303 + });
304 +
305 + reaction((_) => exchangeViewModel.receiveAmount, (String amount) {
306 + if (receiveKey.currentState.amountController.text != amount) {
307 + receiveKey.currentState.amountController.text = amount;
308 + }
309 + });
310 +
311 + reaction((_) => exchangeViewModel.receiveAddress, (String address) {
312 + if (receiveKey.currentState.addressController.text != address) {
313 + receiveKey.currentState.addressController.text = address;
314 + }
315 + });
316 +
317 + reaction((_) => exchangeViewModel.isReceiveAddressEnabled,
318 + (bool isEnabled) {
319 + receiveKey.currentState.isAddressEditable(isEditable: isEnabled);
320 + });
321 +
322 + reaction((_) => exchangeViewModel.limitsState, (LimitsState state) {
323 + String min;
324 + String max;
325 +
326 + if (state is LimitsLoadedSuccessfully) {
327 + min = state.limits.min != null ? state.limits.min.toString() : null;
328 + max = state.limits.max != null ? state.limits.max.toString() : null;
329 + }
330 +
331 + if (state is LimitsLoadedFailure) {
332 + min = '0';
333 + max = '0';
334 + }
335 +
336 + if (state is LimitsIsLoading) {
337 + min = '...';
338 + max = '...';
339 + }
340 +
341 + depositKey.currentState.changeLimits(min: min, max: max);
342 + receiveKey.currentState.changeLimits(min: null, max: null);
343 + });
344 +
345 + depositAddressController.addListener(
346 + () => exchangeViewModel.depositAddress = depositAddressController.text);
347 +
348 + depositAmountController.addListener(() {
349 + if (depositAmountController.text != exchangeViewModel.depositAmount) {
350 + exchangeViewModel.changeDepositAmount(
351 + amount: depositAmountController.text);
352 + }
353 + });
354 +
355 + receiveAddressController.addListener(
356 + () => exchangeViewModel.receiveAddress = receiveAddressController.text);
357 +
358 + receiveAmountController.addListener(() {
359 + if (receiveAmountController.text != exchangeViewModel.receiveAmount) {
360 + exchangeViewModel.changeReceiveAmount(
361 + amount: receiveAmountController.text);
362 + }
363 + });
364 +
365 + reaction((_) => exchangeViewModel.wallet.address, (String address) {
366 + if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) {
367 + depositKey.currentState.changeAddress(address: address);
368 + }
369 +
370 + if (exchangeViewModel.receiveCurrency == CryptoCurrency.xmr) {
371 + receiveKey.currentState.changeAddress(address: address);
372 + }
373 + });
374 +
375 + _isReactionsSet = true;
376 + }
377 +
378 + void _onCurrencyChange(CryptoCurrency currency,
379 + ExchangeViewModel exchangeViewModel, GlobalKey<ExchangeCardState> key) {
380 + final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
381 +
382 + key.currentState.changeSelectedCurrency(currency);
383 + key.currentState.changeWalletName(
384 + isCurrentTypeWallet ? exchangeViewModel.wallet.name : null);
385 +
386 + key.currentState.changeAddress(
387 + address: isCurrentTypeWallet ? exchangeViewModel.wallet.address : '');
388 +
389 + key.currentState.changeAmount(amount: '');
390 + }
391 +
392 + void _onWalletNameChange(ExchangeViewModel exchangeViewModel,
393 + CryptoCurrency currency, GlobalKey<ExchangeCardState> key) {
394 + final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
395 +
396 + if (isCurrentTypeWallet) {
397 + key.currentState.changeWalletName(exchangeViewModel.wallet.name);
398 + key.currentState.addressController.text =
399 + exchangeViewModel.wallet.address;
400 + } else if (key.currentState.addressController.text ==
401 + exchangeViewModel.wallet.address) {
402 + key.currentState.changeWalletName(null);
403 + key.currentState.addressController.text = null;
404 + }
405 + }
406 }
\ No newline at end of file
lib/src/screens/exchange/widgets/base_exchange_widget.dart deleted
-618
@@ -1,618 +0,0 @@
1 -import 'dart:ui';
2 -import 'package:cake_wallet/palette.dart';
3 -import 'package:cake_wallet/src/domain/exchange/exchange_template.dart';
4 -import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
5 -import 'package:cake_wallet/src/widgets/template_tile.dart';
6 -import 'package:dotted_border/dotted_border.dart';
7 -import 'package:flutter/cupertino.dart';
8 -import 'package:flutter/material.dart';
9 -import 'package:flutter_mobx/flutter_mobx.dart';
10 -import 'package:mobx/mobx.dart';
11 -import 'package:cake_wallet/routes.dart';
12 -import 'package:cake_wallet/generated/i18n.dart';
13 -import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
14 -import 'package:cake_wallet/src/domain/exchange/xmrto/xmrto_exchange_provider.dart';
15 -import 'package:cake_wallet/src/stores/exchange/exchange_trade_state.dart';
16 -import 'package:cake_wallet/src/stores/exchange/limits_state.dart';
17 -import 'package:cake_wallet/src/screens/exchange/widgets/exchange_card.dart';
18 -import 'package:cake_wallet/src/widgets/primary_button.dart';
19 -import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
20 -import 'package:cake_wallet/src/widgets/top_panel.dart';
21 -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
22 -import 'package:cake_wallet/view_model/exchange/exchange_view_model.dart';
23 -import 'package:cake_wallet/core/address_validator.dart';
24 -import 'package:cake_wallet/core/amount_validator.dart';
25 -
26 -class BaseExchangeWidget extends StatefulWidget {
27 - BaseExchangeWidget({
28 - @required this.exchangeViewModel,
29 - this.leading,
30 - this.middle,
31 - this.trailing,
32 - this.isTemplate = false,
33 - });
34 -
35 - final ExchangeViewModel exchangeViewModel;
36 - final Widget leading;
37 - final Widget middle;
38 - final Widget trailing;
39 - final bool isTemplate;
40 -
41 - @override
42 - BaseExchangeWidgetState createState() => BaseExchangeWidgetState(
43 - exchangeViewModel: exchangeViewModel,
44 - leading: leading,
45 - middle: middle,
46 - trailing: trailing,
47 - isTemplate: isTemplate);
48 -}
49 -
50 -class BaseExchangeWidgetState extends State<BaseExchangeWidget> {
51 - BaseExchangeWidgetState({
52 - @required this.exchangeViewModel,
53 - @required this.leading,
54 - @required this.middle,
55 - @required this.trailing,
56 - @required this.isTemplate,
57 - });
58 -
59 - final ExchangeViewModel exchangeViewModel;
60 - final Widget leading;
61 - final Widget middle;
62 - final Widget trailing;
63 - final bool isTemplate;
64 - final double topPanelHeight = 290;
65 -
66 - final depositKey = GlobalKey<ExchangeCardState>();
67 - final receiveKey = GlobalKey<ExchangeCardState>();
68 - final _formKey = GlobalKey<FormState>();
69 - var _isReactionsSet = false;
70 -
71 - @override
72 - Widget build(BuildContext context) {
73 - final arrowBottomPurple = Image.asset(
74 - 'assets/images/arrow_bottom_purple_icon.png',
75 - color: Colors.white,
76 - height: 8,
77 - );
78 - final arrowBottomCakeGreen = Image.asset(
79 - 'assets/images/arrow_bottom_cake_green.png',
80 - color: Colors.white,
81 - height: 8,
82 - );
83 -
84 - final depositWalletName =
85 - exchangeViewModel.depositCurrency == CryptoCurrency.xmr
86 - ? exchangeViewModel.wallet.name
87 - : null;
88 - final receiveWalletName =
89 - exchangeViewModel.receiveCurrency == CryptoCurrency.xmr
90 - ? exchangeViewModel.wallet.name
91 - : null;
92 -
93 - WidgetsBinding.instance
94 - .addPostFrameCallback((_) => _setReactions(context, exchangeViewModel));
95 -
96 - return Form(
97 - key: _formKey,
98 - child: ScrollableWithBottomSection(
99 - contentPadding: EdgeInsets.only(bottom: 24),
100 - content: Column(
101 - children: <Widget>[
102 - TopPanel(
103 - gradient: LinearGradient(colors: [
104 - Theme.of(context).primaryTextTheme.body1.color,
105 - Theme.of(context).primaryTextTheme.body1.decorationColor,
106 - ], stops: [
107 - 0.35,
108 - 1.0
109 - ], begin: Alignment.topLeft, end: Alignment.bottomRight),
110 - edgeInsets: EdgeInsets.only(bottom: 32),
111 - widget: Column(
112 - children: <Widget>[
113 - TopPanel(
114 - edgeInsets: EdgeInsets.all(0),
115 - gradient: LinearGradient(
116 - colors: [
117 - Theme.of(context)
118 - .primaryTextTheme
119 - .subtitle
120 - .color,
121 - Theme.of(context)
122 - .primaryTextTheme
123 - .subtitle
124 - .decorationColor,
125 - ],
126 - begin: Alignment.topLeft,
127 - end: Alignment.bottomRight),
128 - widget: Column(
129 - children: <Widget>[
130 - CupertinoNavigationBar(
131 - leading: leading,
132 - middle: middle,
133 - trailing: trailing,
134 - backgroundColor: Colors.transparent,
135 - border: null,
136 - ),
137 - Padding(
138 - padding: EdgeInsets.fromLTRB(24, 29, 24, 32),
139 - child: Observer(
140 - builder: (_) => ExchangeCard(
141 - key: depositKey,
142 - title: S.of(context).you_will_send,
143 - initialCurrency:
144 - exchangeViewModel.depositCurrency,
145 - initialWalletName: depositWalletName,
146 - initialAddress: exchangeViewModel
147 - .depositCurrency ==
148 - exchangeViewModel.wallet.currency
149 - ? exchangeViewModel.wallet.address
150 - : exchangeViewModel.depositAddress,
151 - initialIsAmountEditable: true,
152 - initialIsAddressEditable: exchangeViewModel
153 - .isDepositAddressEnabled,
154 - isAmountEstimated: false,
155 - currencies: CryptoCurrency.all,
156 - onCurrencySelected: (currency) =>
157 - exchangeViewModel.changeDepositCurrency(
158 - currency: currency),
159 - imageArrow: arrowBottomPurple,
160 - currencyButtonColor: Colors.transparent,
161 - addressButtonsColor:
162 - Theme.of(context).focusColor,
163 - borderColor: Theme.of(context)
164 - .primaryTextTheme
165 - .body2
166 - .color,
167 - currencyValueValidator: AmountValidator(
168 - type: exchangeViewModel.wallet.type),
169 - addressTextFieldValidator: AddressValidator(
170 - type:
171 - exchangeViewModel.depositCurrency),
172 - ),
173 - ),
174 - )
175 - ],
176 - )),
177 - Padding(
178 - padding: EdgeInsets.only(top: 29, left: 24, right: 24),
179 - child: Observer(
180 - builder: (_) => ExchangeCard(
181 - key: receiveKey,
182 - title: S.of(context).you_will_get,
183 - initialCurrency:
184 - exchangeViewModel.receiveCurrency,
185 - initialWalletName: receiveWalletName,
186 - initialAddress:
187 - exchangeViewModel.receiveCurrency ==
188 - exchangeViewModel.wallet.currency
189 - ? exchangeViewModel.wallet.address
190 - : exchangeViewModel.receiveAddress,
191 - initialIsAmountEditable: false,
192 - initialIsAddressEditable:
193 - exchangeViewModel.isReceiveAddressEnabled,
194 - isAmountEstimated: true,
195 - currencies: CryptoCurrency.all,
196 - onCurrencySelected: (currency) =>
197 - exchangeViewModel.changeReceiveCurrency(
198 - currency: currency),
199 - imageArrow: arrowBottomCakeGreen,
200 - currencyButtonColor: Colors.transparent,
201 - addressButtonsColor:
202 - Theme.of(context).focusColor,
203 - borderColor: Theme.of(context)
204 - .primaryTextTheme
205 - .body2
206 - .decorationColor,
207 - currencyValueValidator: AmountValidator(
208 - type: exchangeViewModel.wallet.type),
209 - addressTextFieldValidator: AddressValidator(
210 - type: exchangeViewModel.receiveCurrency),
211 - )),
212 - )
213 - ],
214 - )),
215 - isTemplate
216 - ? Offstage()
217 - : Padding(
218 - padding: EdgeInsets.only(top: 30, left: 24, bottom: 24),
219 - child: Row(
220 - mainAxisAlignment: MainAxisAlignment.start,
221 - children: <Widget>[
222 - Text(
223 - S.of(context).send_templates,
224 - style: TextStyle(
225 - fontSize: 18,
226 - fontWeight: FontWeight.w600,
227 - color: Theme.of(context)
228 - .primaryTextTheme
229 - .display4
230 - .color),
231 - )
232 - ],
233 - ),
234 - ),
235 - isTemplate
236 - ? Offstage()
237 - : Container(
238 - height: 40,
239 - width: double.infinity,
240 - padding: EdgeInsets.only(left: 24),
241 - child: SingleChildScrollView(
242 - scrollDirection: Axis.horizontal,
243 - child: Row(
244 - children: <Widget>[
245 - GestureDetector(
246 - onTap: () => Navigator.of(context)
247 - .pushNamed(Routes.exchangeTemplate),
248 - child: Container(
249 - padding: EdgeInsets.only(left: 1, right: 10),
250 - child: DottedBorder(
251 - borderType: BorderType.RRect,
252 - dashPattern: [6, 4],
253 - color: Theme.of(context)
254 - .primaryTextTheme
255 - .display2
256 - .decorationColor,
257 - strokeWidth: 2,
258 - radius: Radius.circular(20),
259 - child: Container(
260 - height: 34,
261 - width: 75,
262 - padding: EdgeInsets.only(
263 - left: 10, right: 10),
264 - alignment: Alignment.center,
265 - decoration: BoxDecoration(
266 - borderRadius: BorderRadius.all(
267 - Radius.circular(20)),
268 - color: Colors.transparent,
269 - ),
270 - child: Text(
271 - S.of(context).send_new,
272 - style: TextStyle(
273 - fontSize: 14,
274 - fontWeight: FontWeight.w600,
275 - color: Theme.of(context)
276 - .primaryTextTheme
277 - .display3
278 - .color),
279 - ),
280 - )),
281 - ),
282 - ),
283 - Observer(builder: (_) {
284 - final templates = exchangeViewModel.templates;
285 - final itemCount =
286 - exchangeViewModel.templates.length;
287 -
288 - return ListView.builder(
289 - scrollDirection: Axis.horizontal,
290 - shrinkWrap: true,
291 - physics: NeverScrollableScrollPhysics(),
292 - itemCount: itemCount,
293 - itemBuilder: (context, index) {
294 - final template = templates[index];
295 -
296 - return TemplateTile(
297 - key: UniqueKey(),
298 - amount: template.amount,
299 - from: template.depositCurrency,
300 - to: template.receiveCurrency,
301 - onTap: () {
302 - applyTemplate(
303 - exchangeViewModel, template);
304 - },
305 - onRemove: () {
306 - showDialog<void>(
307 - context: context,
308 - builder: (dialogContext) {
309 - return AlertWithTwoActions(
310 - alertTitle:
311 - S.of(context).template,
312 - alertContent: S
313 - .of(context)
314 - .confirm_delete_template,
315 - leftButtonText:
316 - S.of(context).delete,
317 - rightButtonText:
318 - S.of(context).cancel,
319 - actionLeftButton: () {
320 - Navigator.of(
321 - dialogContext)
322 - .pop();
323 - exchangeViewModel
324 - .exchangeTemplateStore
325 - .remove(
326 - template:
327 - template);
328 - exchangeViewModel
329 - .exchangeTemplateStore
330 - .update();
331 - },
332 - actionRightButton: () =>
333 - Navigator.of(
334 - dialogContext)
335 - .pop());
336 - });
337 - },
338 - );
339 - });
340 - }),
341 - ],
342 - )))
343 - ],
344 - ),
345 - bottomSectionPadding:
346 - EdgeInsets.only(left: 24, right: 24, bottom: 24),
347 - bottomSection: Column(children: <Widget>[
348 - Padding(
349 - padding: EdgeInsets.only(bottom: 15),
350 - child: Observer(builder: (_) {
351 - final description =
352 - exchangeViewModel.provider is XMRTOExchangeProvider
353 - ? S.of(context).amount_is_guaranteed
354 - : S.of(context).amount_is_estimate;
355 - return Center(
356 - child: Text(
357 - description,
358 - style: TextStyle(
359 - color: Theme.of(context)
360 - .primaryTextTheme
361 - .display4
362 - .decorationColor,
363 - fontWeight: FontWeight.w500,
364 - fontSize: 12),
365 - ),
366 - );
367 - }),
368 - ),
369 - isTemplate
370 - ? PrimaryButton(
371 - onPressed: () {
372 - if (_formKey.currentState.validate()) {
373 - exchangeViewModel.exchangeTemplateStore.addTemplate(
374 - amount: exchangeViewModel.depositAmount,
375 - depositCurrency:
376 - exchangeViewModel.depositCurrency.toString(),
377 - receiveCurrency:
378 - exchangeViewModel.receiveCurrency.toString(),
379 - provider: exchangeViewModel.provider.toString(),
380 - depositAddress: exchangeViewModel.depositAddress,
381 - receiveAddress: exchangeViewModel.receiveAddress);
382 - exchangeViewModel.exchangeTemplateStore.update();
383 - Navigator.of(context).pop();
384 - }
385 - },
386 - text: S.of(context).save,
387 - color: Colors.green,
388 - textColor: Colors.white)
389 - : Observer(
390 - builder: (_) => LoadingPrimaryButton(
391 - text: S.of(context).exchange,
392 - onPressed: () {
393 - if (_formKey.currentState.validate()) {
394 - exchangeViewModel.createTrade();
395 - }
396 - },
397 - color: Palette.blueCraiola,
398 - textColor: Colors.white,
399 - isLoading:
400 - exchangeViewModel.tradeState is TradeIsCreating,
401 - )),
402 - ]),
403 - ));
404 - }
405 -
406 - void applyTemplate(
407 - ExchangeViewModel exchangeViewModel, ExchangeTemplate template) {
408 - exchangeViewModel.changeDepositCurrency(
409 - currency: CryptoCurrency.fromString(template.depositCurrency));
410 - exchangeViewModel.changeReceiveCurrency(
411 - currency: CryptoCurrency.fromString(template.receiveCurrency));
412 -
413 - switch (template.provider) {
414 - case 'XMR.TO':
415 - exchangeViewModel.changeProvider(
416 - provider: exchangeViewModel.providerList[0]);
417 - break;
418 - case 'ChangeNOW':
419 - exchangeViewModel.changeProvider(
420 - provider: exchangeViewModel.providerList[1]);
421 - break;
422 - case 'MorphToken':
423 - exchangeViewModel.changeProvider(
424 - provider: exchangeViewModel.providerList[2]);
425 - break;
426 - }
427 -
428 - exchangeViewModel.changeDepositAmount(amount: template.amount);
429 - exchangeViewModel.depositAddress = template.depositAddress;
430 - exchangeViewModel.receiveAddress = template.receiveAddress;
431 - }
432 -
433 - void _setReactions(
434 - BuildContext context, ExchangeViewModel exchangeViewModel) {
435 - if (_isReactionsSet) {
436 - return;
437 - }
438 -
439 - final depositAddressController = depositKey.currentState.addressController;
440 - final depositAmountController = depositKey.currentState.amountController;
441 - final receiveAddressController = receiveKey.currentState.addressController;
442 - final receiveAmountController = receiveKey.currentState.amountController;
443 - final limitsState = exchangeViewModel.limitsState;
444 -
445 - if (limitsState is LimitsLoadedSuccessfully) {
446 - final min = limitsState.limits.min != null
447 - ? limitsState.limits.min.toString()
448 - : null;
449 - final max = limitsState.limits.max != null
450 - ? limitsState.limits.max.toString()
451 - : null;
452 - final key = depositKey;
453 - key.currentState.changeLimits(min: min, max: max);
454 - }
455 -
456 - _onCurrencyChange(
457 - exchangeViewModel.receiveCurrency, exchangeViewModel, receiveKey);
458 - _onCurrencyChange(
459 - exchangeViewModel.depositCurrency, exchangeViewModel, depositKey);
460 -
461 - reaction(
462 - (_) => exchangeViewModel.wallet.name,
463 - (String _) => _onWalletNameChange(
464 - exchangeViewModel, exchangeViewModel.receiveCurrency, receiveKey));
465 -
466 - reaction(
467 - (_) => exchangeViewModel.wallet.name,
468 - (String _) => _onWalletNameChange(
469 - exchangeViewModel, exchangeViewModel.depositCurrency, depositKey));
470 -
471 - reaction(
472 - (_) => exchangeViewModel.receiveCurrency,
473 - (CryptoCurrency currency) =>
474 - _onCurrencyChange(currency, exchangeViewModel, receiveKey));
475 -
476 - reaction(
477 - (_) => exchangeViewModel.depositCurrency,
478 - (CryptoCurrency currency) =>
479 - _onCurrencyChange(currency, exchangeViewModel, depositKey));
480 -
481 - reaction((_) => exchangeViewModel.depositAmount, (String amount) {
482 - if (depositKey.currentState.amountController.text != amount) {
483 - depositKey.currentState.amountController.text = amount;
484 - }
485 - });
486 -
487 - reaction((_) => exchangeViewModel.depositAddress, (String address) {
488 - if (depositKey.currentState.addressController.text != address) {
489 - depositKey.currentState.addressController.text = address;
490 - }
491 - });
492 -
493 - reaction((_) => exchangeViewModel.isDepositAddressEnabled,
494 - (bool isEnabled) {
495 - depositKey.currentState.isAddressEditable(isEditable: isEnabled);
496 - });
497 -
498 - reaction((_) => exchangeViewModel.receiveAmount, (String amount) {
499 - if (receiveKey.currentState.amountController.text != amount) {
500 - receiveKey.currentState.amountController.text = amount;
501 - }
502 - });
503 -
504 - reaction((_) => exchangeViewModel.receiveAddress, (String address) {
505 - if (receiveKey.currentState.addressController.text != address) {
506 - receiveKey.currentState.addressController.text = address;
507 - }
508 - });
509 -
510 - reaction((_) => exchangeViewModel.isReceiveAddressEnabled,
511 - (bool isEnabled) {
512 - receiveKey.currentState.isAddressEditable(isEditable: isEnabled);
513 - });
514 -
515 - reaction((_) => exchangeViewModel.tradeState, (ExchangeTradeState state) {
516 - if (state is TradeIsCreatedFailure) {
517 - WidgetsBinding.instance.addPostFrameCallback((_) {
518 - showDialog<void>(
519 - context: context,
520 - builder: (BuildContext context) {
521 - return AlertWithOneAction(
522 - alertTitle: S.of(context).error,
523 - alertContent: state.error,
524 - buttonText: S.of(context).ok,
525 - buttonAction: () => Navigator.of(context).pop());
526 - });
527 - });
528 - }
529 - if (state is TradeIsCreatedSuccessfully) {
530 - Navigator.of(context).pushNamed(Routes.exchangeConfirm);
531 - }
532 - });
533 -
534 - reaction((_) => exchangeViewModel.limitsState, (LimitsState state) {
535 - String min;
536 - String max;
537 -
538 - if (state is LimitsLoadedSuccessfully) {
539 - min = state.limits.min != null ? state.limits.min.toString() : null;
540 - max = state.limits.max != null ? state.limits.max.toString() : null;
541 - }
542 -
543 - if (state is LimitsLoadedFailure) {
544 - min = '0';
545 - max = '0';
546 - }
547 -
548 - if (state is LimitsIsLoading) {
549 - min = '...';
550 - max = '...';
551 - }
552 -
553 - depositKey.currentState.changeLimits(min: min, max: max);
554 - receiveKey.currentState.changeLimits(min: null, max: null);
555 - });
556 -
557 - depositAddressController.addListener(
558 - () => exchangeViewModel.depositAddress = depositAddressController.text);
559 -
560 - depositAmountController.addListener(() {
561 - if (depositAmountController.text != exchangeViewModel.depositAmount) {
562 - exchangeViewModel.changeDepositAmount(
563 - amount: depositAmountController.text);
564 - }
565 - });
566 -
567 - receiveAddressController.addListener(
568 - () => exchangeViewModel.receiveAddress = receiveAddressController.text);
569 -
570 - receiveAmountController.addListener(() {
571 - if (receiveAmountController.text != exchangeViewModel.receiveAmount) {
572 - exchangeViewModel.changeReceiveAmount(
573 - amount: receiveAmountController.text);
574 - }
575 - });
576 -
577 - reaction((_) => exchangeViewModel.wallet.address, (String address) {
578 - if (exchangeViewModel.depositCurrency == CryptoCurrency.xmr) {
579 - depositKey.currentState.changeAddress(address: address);
580 - }
581 -
582 - if (exchangeViewModel.receiveCurrency == CryptoCurrency.xmr) {
583 - receiveKey.currentState.changeAddress(address: address);
584 - }
585 - });
586 -
587 - _isReactionsSet = true;
588 - }
589 -
590 - void _onCurrencyChange(CryptoCurrency currency,
591 - ExchangeViewModel exchangeViewModel, GlobalKey<ExchangeCardState> key) {
592 - final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
593 -
594 - key.currentState.changeSelectedCurrency(currency);
595 - key.currentState.changeWalletName(
596 - isCurrentTypeWallet ? exchangeViewModel.wallet.name : null);
597 -
598 - key.currentState.changeAddress(
599 - address: isCurrentTypeWallet ? exchangeViewModel.wallet.address : '');
600 -
601 - key.currentState.changeAmount(amount: '');
602 - }
603 -
604 - void _onWalletNameChange(ExchangeViewModel exchangeViewModel,
605 - CryptoCurrency currency, GlobalKey<ExchangeCardState> key) {
606 - final isCurrentTypeWallet = currency == exchangeViewModel.wallet.currency;
607 -
608 - if (isCurrentTypeWallet) {
609 - key.currentState.changeWalletName(exchangeViewModel.wallet.name);
610 - key.currentState.addressController.text =
611 - exchangeViewModel.wallet.address;
612 - } else if (key.currentState.addressController.text ==
613 - exchangeViewModel.wallet.address) {
614 - key.currentState.changeWalletName(null);
615 - key.currentState.addressController.text = null;
616 - }
617 - }
618 -}
lib/src/screens/send/send_page.dart
+351 -359
@@ -16,7 +16,6 @@ import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
16 import 'package:cake_wallet/view_model/send/send_view_model.dart';
17 import 'package:cake_wallet/src/screens/base_page.dart';
18 import 'package:cake_wallet/generated/i18n.dart';
19 -import 'package:cake_wallet/src/widgets/top_panel.dart';
19 import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
20 import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
21 import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
@@ -42,99 +41,189 @@ class SendPage extends BasePage {
41 @override
42 Color get titleColor => Colors.white;
43
45 - @override
46 - Color get backgroundLightColor => Colors.transparent;
47 -
44 @override
45 bool get resizeToAvoidBottomPadding => false;
46
47 @override
52 - Widget trailing(context) => TrailButton(
53 - caption: S.of(context).clear, onPressed: () => sendViewModel.reset());
48 + bool get extendBodyBehindAppBar => true;
49
50 @override
56 - Color get backgroundDarkColor => Colors.transparent;
51 + AppBarStyle get appBarStyle => AppBarStyle.transparent;
52
53 @override
59 - Widget build(BuildContext context) {
60 - return Scaffold(
61 - resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
62 - body: Container(
63 - color: Theme.of(context).backgroundColor, child: body(context)));
64 - }
54 + Widget trailing(context) => TrailButton(
55 + caption: S.of(context).clear, onPressed: () => sendViewModel.reset());
56
57 @override
58 Widget body(BuildContext context) {
59 _setEffects(context);
60
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,
61 + return Container(
62 + color: Theme.of(context).backgroundColor,
63 + child: ScrollableWithBottomSection(
64 + contentPadding: EdgeInsets.only(bottom: 24),
65 + content: Column(
66 + children: <Widget>[
67 + Container(
68 + decoration: BoxDecoration(
69 + borderRadius: BorderRadius.only(
70 + bottomLeft: Radius.circular(24),
71 + bottomRight: Radius.circular(24)
72 + ),
73 + gradient: LinearGradient(colors: [
74 + Theme.of(context).primaryTextTheme.subhead.color,
75 + Theme.of(context).primaryTextTheme.subhead.decorationColor,
76 + ], begin: Alignment.topLeft, end: Alignment.bottomRight),
77 ),
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,
78 + child: Form(
79 + key: _formKey,
80 + child: Column(children: <Widget>[
81 + Padding(
82 + padding: EdgeInsets.fromLTRB(24, 90, 24, 32),
83 + child: Column(
84 + children: <Widget>[
85 + AddressTextField(
86 + controller: _addressController,
87 + focusNode: _focusNode,
88 + onURIScanned: (uri) {
89 + var address = '';
90 + var amount = '';
91 +
92 + if (uri != null) {
93 + address = uri.path;
94 + amount = uri.queryParameters['tx_amount'];
95 + } else {
96 + address = uri.toString();
97 + }
98 +
99 + _addressController.text = address;
100 + _cryptoAmountController.text = amount;
101 + },
102 + options: [
103 + AddressTextFieldOption.paste,
104 + AddressTextFieldOption.qrCode,
105 + AddressTextFieldOption.addressBook
106 + ],
107 + buttonColor:
108 + Theme.of(context).primaryTextTheme.display1.color,
109 + borderColor:
110 + Theme.of(context).primaryTextTheme.headline.color,
111 + textStyle: TextStyle(
112 + fontSize: 14,
113 + fontWeight: FontWeight.w500,
114 + color: Colors.white),
115 + hintStyle: TextStyle(
116 + fontSize: 14,
117 + fontWeight: FontWeight.w500,
118 + color: Theme.of(context)
119 + .primaryTextTheme
120 + .headline
121 + .decorationColor),
122 + validator: sendViewModel.addressValidator,
123 + ),
124 + Observer(builder: (_) {
125 + return Padding(
126 + padding: const EdgeInsets.only(top: 20),
127 + child: BaseTextFormField(
128 + controller: _cryptoAmountController,
129 + keyboardType: TextInputType.numberWithOptions(
130 + signed: false, decimal: true),
131 + inputFormatters: [
132 + BlacklistingTextInputFormatter(
133 + RegExp('[\\-|\\ |\\,]'))
134 + ],
135 + prefixIcon: Padding(
136 + padding: EdgeInsets.only(top: 9),
137 + child:
138 + Text(sendViewModel.currency.title + ':',
139 + style: TextStyle(
140 + fontSize: 16,
141 + fontWeight: FontWeight.w600,
142 + color: Colors.white,
143 + )),
144 + ),
145 + suffixIcon: Container(
146 + height: 32,
147 + width: 32,
148 + margin: EdgeInsets.only(
149 + left: 14, top: 4, bottom: 10),
150 + decoration: BoxDecoration(
151 + color: Theme.of(context)
152 + .primaryTextTheme
153 + .display1
154 + .color,
155 + borderRadius: BorderRadius.all(
156 + Radius.circular(6))),
157 + child: InkWell(
158 + onTap: () =>
159 + sendViewModel.setSendAll(),
160 + child: Center(
161 + child: Text(S.of(context).all,
162 + textAlign: TextAlign.center,
163 + style: TextStyle(
164 + fontSize: 12,
165 + fontWeight: FontWeight.bold,
166 + color: Theme.of(context)
167 + .primaryTextTheme
168 + .display1
169 + .decorationColor)),
170 + ),
171 + ),
172 + ),
173 + hintText: '0.0000',
174 + borderColor: Theme.of(context)
175 + .primaryTextTheme
176 + .headline
177 + .color,
178 + textStyle: TextStyle(
179 + fontSize: 14,
180 + fontWeight: FontWeight.w500,
181 + color: Colors.white),
182 + placeholderTextStyle: TextStyle(
183 + color: Theme.of(context)
184 + .primaryTextTheme
185 + .headline
186 + .decorationColor,
187 + fontWeight: FontWeight.w500,
188 + fontSize: 14),
189 + validator: sendViewModel.amountValidator));
190 + }),
191 + Observer(
192 + builder: (_) => Padding(
193 + padding: EdgeInsets.only(top: 10),
194 + child: Row(
195 + mainAxisSize: MainAxisSize.max,
196 + mainAxisAlignment:
197 + MainAxisAlignment.spaceBetween,
198 + children: <Widget>[
199 + Expanded(
200 + child: Text(
201 + S.of(context).available_balance + ':',
202 + style: TextStyle(
203 + fontSize: 12,
204 + fontWeight: FontWeight.w600,
205 + color: Theme.of(context)
206 + .primaryTextTheme
207 + .headline
208 + .decorationColor),
209 + )),
210 + Text(
211 + sendViewModel.balance,
212 + style: TextStyle(
213 + fontSize: 12,
214 + fontWeight: FontWeight.w600,
215 + color: Theme.of(context)
216 + .primaryTextTheme
217 + .headline
218 + .decorationColor),
219 + )
220 + ],
221 + ),
222 + )),
223 + Padding(
224 + padding: const EdgeInsets.only(top: 20),
225 + child: BaseTextFormField(
226 + controller: _fiatAmountController,
227 keyboardType: TextInputType.numberWithOptions(
228 signed: false, decimal: true),
229 inputFormatters: [
@@ -143,43 +232,14 @@ class SendPage extends BasePage {
232 ],
233 prefixIcon: Padding(
234 padding: EdgeInsets.only(top: 9),
146 - child:
147 - Text(sendViewModel.currency.title + ':',
235 + child: Text(sendViewModel.fiat.title + ':',
236 style: TextStyle(
237 fontSize: 16,
238 fontWeight: FontWeight.w600,
239 color: Colors.white,
240 )),
241 ),
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',
242 + hintText: '0.00',
243 borderColor: Theme.of(context)
244 .primaryTextTheme
245 .headline
@@ -195,262 +255,194 @@ class SendPage extends BasePage {
255 .decorationColor,
256 fontWeight: FontWeight.w500,
257 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,
258 + )),
259 + Observer(
260 + builder: (_) => GestureDetector(
261 + onTap: () =>
262 + _setTransactionPriority(context),
263 + child: Container(
264 + padding: EdgeInsets.only(top: 24),
265 + child: Row(
266 + mainAxisAlignment:
267 + MainAxisAlignment.spaceBetween,
268 + children: <Widget>[
269 + Text(S.of(context).send_estimated_fee,
270 + style: TextStyle(
271 + fontSize: 12,
272 + fontWeight: FontWeight.w500,
273 + //color: Theme.of(context).primaryTextTheme.display2.color,
274 + color: Colors.white)),
275 + Container(
276 + child: Row(
277 + children: <Widget>[
278 + Text(
279 + sendViewModel.estimatedFee
280 + .toString() +
281 + ' ' +
282 + sendViewModel
283 + .currency.title,
284 + style: TextStyle(
285 + fontSize: 12,
286 + fontWeight:
287 + FontWeight.w600,
288 + //color: Theme.of(context).primaryTextTheme.display2.color,
289 + color: Colors.white)),
290 + Padding(
291 + padding:
292 + EdgeInsets.only(left: 5),
293 + child: Icon(
294 + Icons.arrow_forward_ios,
295 + size: 12,
296 + color: Colors.white,
297 + ),
298 + )
299 + ],
300 + ),
301 + )
302 + ],
303 + ),
304 + ),
305 + ))
306 + ],
307 + ),
308 + )
309 + ]),
310 + ),
311 + ),
312 + Padding(
313 + padding: EdgeInsets.only(top: 30, left: 24, bottom: 24),
314 + child: Row(
315 + mainAxisAlignment: MainAxisAlignment.start,
316 + children: <Widget>[
317 + Text(
318 + S.of(context).send_templates,
319 + style: TextStyle(
320 + fontSize: 18,
321 + fontWeight: FontWeight.w600,
322 + color: Theme.of(context)
323 + .primaryTextTheme
324 + .display4
325 + .color),
326 + )
327 + ],
328 + ),
329 + ),
330 + Container(
331 + height: 40,
332 + width: double.infinity,
333 + padding: EdgeInsets.only(left: 24),
334 + child: SingleChildScrollView(
335 + scrollDirection: Axis.horizontal,
336 + child: Row(
337 + children: <Widget>[
338 + GestureDetector(
339 + onTap: () => Navigator.of(context)
340 + .pushNamed(Routes.sendTemplate),
341 + child: Container(
342 + padding: EdgeInsets.only(left: 1, right: 10),
343 + child: DottedBorder(
344 + borderType: BorderType.RRect,
345 + dashPattern: [6, 4],
346 + color: Theme.of(context)
347 + .primaryTextTheme
348 + .display2
349 + .decorationColor,
350 + strokeWidth: 2,
351 + radius: Radius.circular(20),
352 + child: Container(
353 + height: 34,
354 + width: 75,
355 + padding: EdgeInsets.only(left: 10, right: 10),
356 + alignment: Alignment.center,
357 + decoration: BoxDecoration(
358 + borderRadius:
359 + BorderRadius.all(Radius.circular(20)),
360 + color: Colors.transparent,
361 + ),
362 + child: Text(
363 + S.of(context).send_new,
364 style: TextStyle(
222 - fontSize: 12,
365 + fontSize: 14,
366 fontWeight: FontWeight.w600,
367 color: Theme.of(context)
368 .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 - ))
369 + .display3
370 + .color),
371 + ),
372 + )),
373 + ),
374 + ),
375 + // Observer(
376 + // builder: (_) {
377 + // final templates = sendViewModel.templates;
378 + // final itemCount = templates.length;
379 +
380 + // return ListView.builder(
381 + // scrollDirection: Axis.horizontal,
382 + // shrinkWrap: true,
383 + // physics: NeverScrollableScrollPhysics(),
384 + // itemCount: itemCount,
385 + // itemBuilder: (context, index) {
386 + // final template = templates[index];
387 +
388 + // return TemplateTile(
389 + // key: UniqueKey(),
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 + // onRemove: () {
399 + // showDialog<void>(
400 + // context: context,
401 + // builder: (dialogContext) {
402 + // return AlertWithTwoActions(
403 + // alertTitle: S.of(context).template,
404 + // alertContent: S.of(context).confirm_delete_template,
405 + // leftButtonText: S.of(context).delete,
406 + // rightButtonText: S.of(context).cancel,
407 + // actionLeftButton: () {
408 + // Navigator.of(dialogContext).pop();
409 + // sendViewModel.sendTemplateStore.remove(template: template);
410 + // sendViewModel.sendTemplateStore.update();
411 + // },
412 + // actionRightButton: () => Navigator.of(dialogContext).pop()
413 + // );
414 + // }
415 + // );
416 + // },
417 + // );
418 + // }
419 + // );
420 + // }
421 + // )
422 ],
423 ),
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 - ),
424 + ),
425 + )
426 + ],
427 ),
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 - ],
428 + bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
429 + bottomSection: Observer(builder: (_) {
430 + return LoadingPrimaryButton(
431 + onPressed: () {
432 + if (_formKey.currentState.validate()) {
433 + print('SENT!!!');
434 + }
435 + },
436 + text: S.of(context).send,
437 + color: Palette.blueCraiola,
438 + textColor: Colors.white,
439 + isLoading: sendViewModel.state is TransactionIsCreating ||
440 + sendViewModel.state is TransactionCommitting,
441 + isDisabled:
442 + false // FIXME !(syncStore.status is SyncedSyncStatus),
443 + );
444 + })
445 ),
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 - })
446 );
447 }
448
lib/src/screens/send/send_template_page.dart
+235 -15
@@ -1,14 +1,27 @@
1 +import 'package:cake_wallet/src/widgets/address_text_field.dart';
2 +import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
3 +import 'package:cake_wallet/src/widgets/primary_button.dart';
4 +import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
5 import 'package:flutter/cupertino.dart';
6 import 'package:flutter/material.dart';
7 import 'package:cake_wallet/src/screens/base_page.dart';
8 import 'package:cake_wallet/generated/i18n.dart';
9 import 'package:cake_wallet/view_model/send/send_view_model.dart';
6 -import 'package:cake_wallet/src/screens/send/widgets/base_send_widget.dart';
10 +import 'package:flutter/services.dart';
11 +import 'package:flutter_mobx/flutter_mobx.dart';
12 +import 'package:mobx/mobx.dart';
13
14 class SendTemplatePage extends BasePage {
15 SendTemplatePage({@required this.sendViewModel});
16
17 final SendViewModel sendViewModel;
18 + final _addressController = TextEditingController();
19 + final _cryptoAmountController = TextEditingController();
20 + final _fiatAmountController = TextEditingController();
21 + final _nameController = TextEditingController();
22 + final _formKey = GlobalKey<FormState>();
23 +
24 + bool _effectsInstalled = false;
25
26 @override
27 String get title => S.current.exchange_new_template;
@@ -17,26 +30,233 @@ class SendTemplatePage extends BasePage {
30 Color get titleColor => Colors.white;
31
32 @override
20 - Color get backgroundLightColor => Colors.transparent;
33 + bool get resizeToAvoidBottomPadding => false;
34
35 @override
23 - Color get backgroundDarkColor => Colors.transparent;
36 + bool get extendBodyBehindAppBar => true;
37
38 @override
26 - bool get resizeToAvoidBottomPadding => false;
39 + AppBarStyle get appBarStyle => AppBarStyle.transparent;
40
41 @override
29 - Widget body(BuildContext context) => BaseSendWidget(
30 - sendViewModel: sendViewModel,
31 - leading: leading(context),
32 - middle: middle(context),
33 - isTemplate: true);
42 + Widget body(BuildContext context) {
43 + _setEffects(context);
44
35 - @override
36 - Widget build(BuildContext context) {
37 - return Scaffold(
38 - resizeToAvoidBottomPadding: resizeToAvoidBottomPadding,
39 - body: Container(
40 - color: Theme.of(context).backgroundColor, child: body(context)));
45 + return Container(
46 + color: Theme.of(context).backgroundColor,
47 + child: ScrollableWithBottomSection(
48 + contentPadding: EdgeInsets.only(bottom: 24),
49 + content: Container(
50 + decoration: BoxDecoration(
51 + borderRadius: BorderRadius.only(
52 + bottomLeft: Radius.circular(24),
53 + bottomRight: Radius.circular(24)
54 + ),
55 + gradient: LinearGradient(colors: [
56 + Theme.of(context).primaryTextTheme.subhead.color,
57 + Theme.of(context).primaryTextTheme.subhead.decorationColor,
58 + ], begin: Alignment.topLeft, end: Alignment.bottomRight),
59 + ),
60 + child: Form(
61 + key: _formKey,
62 + child: Column(children: <Widget>[
63 + Padding(
64 + padding: EdgeInsets.fromLTRB(24, 90, 24, 32),
65 + child: Column(
66 + children: <Widget>[
67 + BaseTextFormField(
68 + controller: _nameController,
69 + hintText: S.of(context).send_name,
70 + borderColor: Theme.of(context)
71 + .primaryTextTheme
72 + .headline
73 + .color,
74 + textStyle: TextStyle(
75 + fontSize: 14,
76 + fontWeight: FontWeight.w500,
77 + color: Colors.white),
78 + placeholderTextStyle: TextStyle(
79 + color: Theme.of(context)
80 + .primaryTextTheme
81 + .headline
82 + .decorationColor,
83 + fontWeight: FontWeight.w500,
84 + fontSize: 14),
85 + validator: sendViewModel.templateValidator,
86 + ),
87 + Padding(
88 + padding: EdgeInsets.only(top: 20),
89 + child: AddressTextField(
90 + controller: _addressController,
91 + onURIScanned: (uri) {
92 + var address = '';
93 + var amount = '';
94 +
95 + if (uri != null) {
96 + address = uri.path;
97 + amount = uri.queryParameters['tx_amount'];
98 + } else {
99 + address = uri.toString();
100 + }
101 +
102 + _addressController.text = address;
103 + _cryptoAmountController.text = amount;
104 + },
105 + options: [
106 + AddressTextFieldOption.paste,
107 + AddressTextFieldOption.qrCode,
108 + AddressTextFieldOption.addressBook
109 + ],
110 + buttonColor:
111 + Theme.of(context).primaryTextTheme.display1.color,
112 + borderColor:
113 + Theme.of(context).primaryTextTheme.headline.color,
114 + textStyle: TextStyle(
115 + fontSize: 14,
116 + fontWeight: FontWeight.w500,
117 + color: Colors.white),
118 + hintStyle: TextStyle(
119 + fontSize: 14,
120 + fontWeight: FontWeight.w500,
121 + color: Theme.of(context)
122 + .primaryTextTheme
123 + .headline
124 + .decorationColor),
125 + validator: sendViewModel.addressValidator,
126 + ),
127 + ),
128 + Observer(builder: (_) {
129 + return Padding(
130 + padding: const EdgeInsets.only(top: 20),
131 + child: BaseTextFormField(
132 + controller: _cryptoAmountController,
133 + keyboardType: TextInputType.numberWithOptions(
134 + signed: false, decimal: true),
135 + inputFormatters: [
136 + BlacklistingTextInputFormatter(
137 + RegExp('[\\-|\\ |\\,]'))
138 + ],
139 + prefixIcon: Padding(
140 + padding: EdgeInsets.only(top: 9),
141 + child:
142 + Text(sendViewModel.currency.title + ':',
143 + style: TextStyle(
144 + fontSize: 16,
145 + fontWeight: FontWeight.w600,
146 + color: Colors.white,
147 + )),
148 + ),
149 + hintText: '0.0000',
150 + borderColor: Theme.of(context)
151 + .primaryTextTheme
152 + .headline
153 + .color,
154 + textStyle: TextStyle(
155 + fontSize: 14,
156 + fontWeight: FontWeight.w500,
157 + color: Colors.white),
158 + placeholderTextStyle: TextStyle(
159 + color: Theme.of(context)
160 + .primaryTextTheme
161 + .headline
162 + .decorationColor,
163 + fontWeight: FontWeight.w500,
164 + fontSize: 14),
165 + validator: sendViewModel.amountValidator));
166 + }),
167 + Padding(
168 + padding: const EdgeInsets.only(top: 20),
169 + child: BaseTextFormField(
170 + controller: _fiatAmountController,
171 + keyboardType: TextInputType.numberWithOptions(
172 + signed: false, decimal: true),
173 + inputFormatters: [
174 + BlacklistingTextInputFormatter(
175 + RegExp('[\\-|\\ |\\,]'))
176 + ],
177 + prefixIcon: Padding(
178 + padding: EdgeInsets.only(top: 9),
179 + child: Text(sendViewModel.fiat.title + ':',
180 + style: TextStyle(
181 + fontSize: 16,
182 + fontWeight: FontWeight.w600,
183 + color: Colors.white,
184 + )),
185 + ),
186 + hintText: '0.00',
187 + borderColor: Theme.of(context)
188 + .primaryTextTheme
189 + .headline
190 + .color,
191 + textStyle: TextStyle(
192 + fontSize: 14,
193 + fontWeight: FontWeight.w500,
194 + color: Colors.white),
195 + placeholderTextStyle: TextStyle(
196 + color: Theme.of(context)
197 + .primaryTextTheme
198 + .headline
199 + .decorationColor,
200 + fontWeight: FontWeight.w500,
201 + fontSize: 14),
202 + )),
203 + ],
204 + ),
205 + )
206 + ]),
207 + ),
208 + ),
209 + bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
210 + bottomSection: PrimaryButton(
211 + onPressed: () {
212 + if (_formKey.currentState.validate()) {
213 + // sendViewModel.sendTemplateStore.addTemplate(
214 + // name: _nameController.text,
215 + // address: _addressController.text,
216 + // cryptoCurrency: sendViewModel.currency.title,
217 + // amount: _cryptoAmountController.text);
218 + // sendViewModel.sendTemplateStore.update();
219 + Navigator.of(context).pop();
220 + }
221 + },
222 + text: S.of(context).save,
223 + color: Colors.green,
224 + textColor: Colors.white),
225 + ),
226 + );
227 + }
228 +
229 + void _setEffects(BuildContext context) {
230 + if (_effectsInstalled) {
231 + return;
232 + }
233 +
234 + reaction((_) => sendViewModel.fiatAmount, (String amount) {
235 + if (amount != _fiatAmountController.text) {
236 + _fiatAmountController.text = amount;
237 + }
238 + });
239 +
240 + reaction((_) => sendViewModel.cryptoAmount, (String amount) {
241 + if (amount != _cryptoAmountController.text) {
242 + _cryptoAmountController.text = amount;
243 + }
244 + });
245 +
246 + reaction((_) => sendViewModel.address, (String address) {
247 + if (address != _addressController.text) {
248 + _addressController.text = address;
249 + }
250 + });
251 +
252 + _addressController.addListener(() {
253 + final address = _addressController.text;
254 +
255 + if (sendViewModel.address != address) {
256 + sendViewModel.address = address;
257 + }
258 + });
259 +
260 + _effectsInstalled = true;
261 }
262 }
lib/src/screens/send/widgets/base_send_widget.dart deleted
-709
@@ -1,709 +0,0 @@
1 -import 'dart:ui';
2 -import 'package:cake_wallet/src/domain/common/transaction_priority.dart';
3 -import 'package:cake_wallet/src/widgets/picker.dart';
4 -import 'package:cake_wallet/src/widgets/primary_button.dart';
5 -import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
6 -import 'package:cake_wallet/view_model/send/send_view_model_state.dart';
7 -import 'package:flutter/cupertino.dart';
8 -import 'package:flutter/material.dart';
9 -import 'package:cake_wallet/view_model/send/send_view_model.dart';
10 -import 'package:flutter/services.dart';
11 -import 'package:flutter_mobx/flutter_mobx.dart';
12 -import 'package:mobx/mobx.dart';
13 -import 'package:cake_wallet/palette.dart';
14 -import 'package:cake_wallet/src/widgets/address_text_field.dart';
15 -import 'package:cake_wallet/generated/i18n.dart';
16 -import 'package:cake_wallet/src/widgets/top_panel.dart';
17 -import 'package:dotted_border/dotted_border.dart';
18 -import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
19 -import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
20 -import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart';
21 -import 'package:cake_wallet/src/screens/send/widgets/sending_alert.dart';
22 -import 'package:cake_wallet/src/widgets/template_tile.dart';
23 -import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
24 -import 'package:cake_wallet/routes.dart';
25 -
26 -class BaseSendWidget extends StatelessWidget {
27 - BaseSendWidget(
28 - {@required this.sendViewModel,
29 - @required this.leading,
30 - @required this.middle,
31 - this.isTemplate = false});
32 -
33 - final SendViewModel sendViewModel;
34 - final bool isTemplate;
35 - final Widget leading;
36 - final Widget middle;
37 -
38 - final _addressController = TextEditingController();
39 - final _cryptoAmountController = TextEditingController();
40 - final _fiatAmountController = TextEditingController();
41 - final _nameController = TextEditingController();
42 - final _focusNode = FocusNode();
43 - final _formKey = GlobalKey<FormState>();
44 -
45 - bool _effectsInstalled = false;
46 -
47 - @override
48 - Widget build(BuildContext context) {
49 - _setEffects(context);
50 -
51 - return ScrollableWithBottomSection(
52 - contentPadding: EdgeInsets.only(bottom: 24),
53 - content: Column(
54 - children: <Widget>[
55 - TopPanel(
56 - edgeInsets: EdgeInsets.all(0),
57 - gradient: LinearGradient(colors: [
58 - Theme.of(context).primaryTextTheme.subhead.color,
59 - Theme.of(context).primaryTextTheme.subhead.decorationColor,
60 - ], begin: Alignment.topLeft, end: Alignment.bottomRight),
61 - widget: Form(
62 - key: _formKey,
63 - child: Column(children: <Widget>[
64 - CupertinoNavigationBar(
65 - leading: leading,
66 - middle: middle,
67 - backgroundColor: Colors.transparent,
68 - border: null,
69 - ),
70 - Padding(
71 - padding: EdgeInsets.fromLTRB(24, 24, 24, 32),
72 - child: Column(
73 - children: <Widget>[
74 - isTemplate
75 - ? BaseTextFormField(
76 - controller: _nameController,
77 - hintText: S.of(context).send_name,
78 - borderColor: Theme.of(context)
79 - .primaryTextTheme
80 - .headline
81 - .color,
82 - textStyle: TextStyle(
83 - fontSize: 14,
84 - fontWeight: FontWeight.w500,
85 - color: Colors.white),
86 - placeholderTextStyle: TextStyle(
87 - color: Theme.of(context)
88 - .primaryTextTheme
89 - .headline
90 - .decorationColor,
91 - fontWeight: FontWeight.w500,
92 - fontSize: 14),
93 - validator: sendViewModel.templateValidator,
94 - )
95 - : Offstage(),
96 - Padding(
97 - padding: EdgeInsets.only(top: isTemplate ? 20 : 0),
98 - child: AddressTextField(
99 - controller: _addressController,
100 - // placeholder: S
101 - // .of(context)
102 - // .send_address(sendViewModel.cryptoCurrencyTitle),
103 - focusNode: _focusNode,
104 - onURIScanned: (uri) {
105 - var address = '';
106 - var amount = '';
107 -
108 - if (uri != null) {
109 - address = uri.path;
110 - amount = uri.queryParameters['tx_amount'];
111 - } else {
112 - address = uri.toString();
113 - }
114 -
115 - _addressController.text = address;
116 - _cryptoAmountController.text = amount;
117 - },
118 - options: [
119 - AddressTextFieldOption.paste,
120 - AddressTextFieldOption.qrCode,
121 - AddressTextFieldOption.addressBook
122 - ],
123 - buttonColor:
124 - Theme.of(context).primaryTextTheme.display1.color,
125 - borderColor:
126 - Theme.of(context).primaryTextTheme.headline.color,
127 - textStyle: TextStyle(
128 - fontSize: 14,
129 - fontWeight: FontWeight.w500,
130 - color: Colors.white),
131 - hintStyle: TextStyle(
132 - fontSize: 14,
133 - fontWeight: FontWeight.w500,
134 - color: Theme.of(context)
135 - .primaryTextTheme
136 - .headline
137 - .decorationColor),
138 - validator: sendViewModel.addressValidator,
139 - ),
140 - ),
141 - Observer(builder: (_) {
142 - return Padding(
143 - padding: const EdgeInsets.only(top: 20),
144 - child: BaseTextFormField(
145 - controller: _cryptoAmountController,
146 - keyboardType: TextInputType.numberWithOptions(
147 - signed: false, decimal: true),
148 - inputFormatters: [
149 - BlacklistingTextInputFormatter(
150 - RegExp('[\\-|\\ |\\,]'))
151 - ],
152 - prefixIcon: Padding(
153 - padding: EdgeInsets.only(top: 9),
154 - child:
155 - Text(sendViewModel.currency.title + ':',
156 - style: TextStyle(
157 - fontSize: 16,
158 - fontWeight: FontWeight.w600,
159 - color: Colors.white,
160 - )),
161 - ),
162 - suffixIcon: isTemplate
163 - ? Offstage()
164 - : Container(
165 - height: 32,
166 - width: 32,
167 - margin: EdgeInsets.only(
168 - left: 14, top: 4, bottom: 10),
169 - decoration: BoxDecoration(
170 - color: Theme.of(context)
171 - .primaryTextTheme
172 - .display1
173 - .color,
174 - borderRadius: BorderRadius.all(
175 - Radius.circular(6))),
176 - child: InkWell(
177 - onTap: () =>
178 - sendViewModel.setSendAll(),
179 - child: Center(
180 - child: Text(S.of(context).all,
181 - textAlign: TextAlign.center,
182 - style: TextStyle(
183 - fontSize: 12,
184 - fontWeight: FontWeight.bold,
185 - color: Theme.of(context)
186 - .primaryTextTheme
187 - .display1
188 - .decorationColor)),
189 - ),
190 - ),
191 - ),
192 - hintText: '0.0000',
193 - borderColor: Theme.of(context)
194 - .primaryTextTheme
195 - .headline
196 - .color,
197 - textStyle: TextStyle(
198 - fontSize: 14,
199 - fontWeight: FontWeight.w500,
200 - color: Colors.white),
201 - placeholderTextStyle: TextStyle(
202 - color: Theme.of(context)
203 - .primaryTextTheme
204 - .headline
205 - .decorationColor,
206 - fontWeight: FontWeight.w500,
207 - fontSize: 14),
208 - validator: sendViewModel.amountValidator));
209 - }),
210 - isTemplate
211 - ? Offstage()
212 - : Observer(
213 - builder: (_) => Padding(
214 - padding: EdgeInsets.only(top: 10),
215 - child: Row(
216 - mainAxisSize: MainAxisSize.max,
217 - mainAxisAlignment:
218 - MainAxisAlignment.spaceBetween,
219 - children: <Widget>[
220 - Expanded(
221 - child: Text(
222 - S.of(context).available_balance + ':',
223 - style: TextStyle(
224 - fontSize: 12,
225 - fontWeight: FontWeight.w600,
226 - color: Theme.of(context)
227 - .primaryTextTheme
228 - .headline
229 - .decorationColor),
230 - )),
231 - Text(
232 - sendViewModel.balance,
233 - style: TextStyle(
234 - fontSize: 12,
235 - fontWeight: FontWeight.w600,
236 - color: Theme.of(context)
237 - .primaryTextTheme
238 - .headline
239 - .decorationColor),
240 - )
241 - ],
242 - ),
243 - )),
244 - Padding(
245 - padding: const EdgeInsets.only(top: 20),
246 - child: BaseTextFormField(
247 - controller: _fiatAmountController,
248 - keyboardType: TextInputType.numberWithOptions(
249 - signed: false, decimal: true),
250 - inputFormatters: [
251 - BlacklistingTextInputFormatter(
252 - RegExp('[\\-|\\ |\\,]'))
253 - ],
254 - prefixIcon: Padding(
255 - padding: EdgeInsets.only(top: 9),
256 - child: Text(sendViewModel.fiat.title + ':',
257 - style: TextStyle(
258 - fontSize: 16,
259 - fontWeight: FontWeight.w600,
260 - color: Colors.white,
261 - )),
262 - ),
263 - hintText: '0.00',
264 - borderColor: Theme.of(context)
265 - .primaryTextTheme
266 - .headline
267 - .color,
268 - textStyle: TextStyle(
269 - fontSize: 14,
270 - fontWeight: FontWeight.w500,
271 - color: Colors.white),
272 - placeholderTextStyle: TextStyle(
273 - color: Theme.of(context)
274 - .primaryTextTheme
275 - .headline
276 - .decorationColor,
277 - fontWeight: FontWeight.w500,
278 - fontSize: 14),
279 - )),
280 - isTemplate
281 - ? Offstage()
282 - : Observer(
283 - builder: (_) => GestureDetector(
284 - onTap: () =>
285 - _setTransactionPriority(context),
286 - child: Container(
287 - padding: EdgeInsets.only(top: 24),
288 - child: Row(
289 - mainAxisAlignment:
290 - MainAxisAlignment.spaceBetween,
291 - children: <Widget>[
292 - Text(S.of(context).send_estimated_fee,
293 - style: TextStyle(
294 - fontSize: 12,
295 - fontWeight: FontWeight.w500,
296 - //color: Theme.of(context).primaryTextTheme.display2.color,
297 - color: Colors.white)),
298 - Container(
299 - child: Row(
300 - children: <Widget>[
301 - Text(
302 - sendViewModel.estimatedFee
303 - .toString() +
304 - ' ' +
305 - sendViewModel
306 - .currency.title,
307 - style: TextStyle(
308 - fontSize: 12,
309 - fontWeight:
310 - FontWeight.w600,
311 - //color: Theme.of(context).primaryTextTheme.display2.color,
312 - color: Colors.white)),
313 - Padding(
314 - padding:
315 - EdgeInsets.only(left: 5),
316 - child: Icon(
317 - Icons.arrow_forward_ios,
318 - size: 12,
319 - color: Colors.white,
320 - ),
321 - )
322 - ],
323 - ),
324 - )
325 - ],
326 - ),
327 - ),
328 - ))
329 - ],
330 - ),
331 - )
332 - ]),
333 - ),
334 - ),
335 - isTemplate
336 - ? Offstage()
337 - : Padding(
338 - padding: EdgeInsets.only(top: 30, left: 24, bottom: 24),
339 - child: Row(
340 - mainAxisAlignment: MainAxisAlignment.start,
341 - children: <Widget>[
342 - Text(
343 - S.of(context).send_templates,
344 - style: TextStyle(
345 - fontSize: 18,
346 - fontWeight: FontWeight.w600,
347 - color: Theme.of(context)
348 - .primaryTextTheme
349 - .display4
350 - .color),
351 - )
352 - ],
353 - ),
354 - ),
355 - isTemplate
356 - ? Offstage()
357 - : Container(
358 - height: 40,
359 - width: double.infinity,
360 - padding: EdgeInsets.only(left: 24),
361 - child: SingleChildScrollView(
362 - scrollDirection: Axis.horizontal,
363 - child: Row(
364 - children: <Widget>[
365 - GestureDetector(
366 - onTap: () => Navigator.of(context)
367 - .pushNamed(Routes.sendTemplate),
368 - child: Container(
369 - padding: EdgeInsets.only(left: 1, right: 10),
370 - child: DottedBorder(
371 - borderType: BorderType.RRect,
372 - dashPattern: [6, 4],
373 - color: Theme.of(context)
374 - .primaryTextTheme
375 - .display2
376 - .decorationColor,
377 - strokeWidth: 2,
378 - radius: Radius.circular(20),
379 - child: Container(
380 - height: 34,
381 - width: 75,
382 - padding: EdgeInsets.only(left: 10, right: 10),
383 - alignment: Alignment.center,
384 - decoration: BoxDecoration(
385 - borderRadius:
386 - BorderRadius.all(Radius.circular(20)),
387 - color: Colors.transparent,
388 - ),
389 - child: Text(
390 - S.of(context).send_new,
391 - style: TextStyle(
392 - fontSize: 14,
393 - fontWeight: FontWeight.w600,
394 - color: Theme.of(context)
395 - .primaryTextTheme
396 - .display3
397 - .color),
398 - ),
399 - )),
400 - ),
401 - ),
402 - // Observer(
403 - // builder: (_) {
404 - // final templates = sendViewModel.templates;
405 - // final itemCount = templates.length;
406 -
407 - // return ListView.builder(
408 - // scrollDirection: Axis.horizontal,
409 - // shrinkWrap: true,
410 - // physics: NeverScrollableScrollPhysics(),
411 - // itemCount: itemCount,
412 - // itemBuilder: (context, index) {
413 - // final template = templates[index];
414 -
415 - // return TemplateTile(
416 - // key: UniqueKey(),
417 - // to: template.name,
418 - // amount: template.amount,
419 - // from: template.cryptoCurrency,
420 - // onTap: () {
421 - // _addressController.text = template.address;
422 - // _cryptoAmountController.text = template.amount;
423 - // getOpenaliasRecord(context);
424 - // },
425 - // onRemove: () {
426 - // showDialog<void>(
427 - // context: context,
428 - // builder: (dialogContext) {
429 - // return AlertWithTwoActions(
430 - // alertTitle: S.of(context).template,
431 - // alertContent: S.of(context).confirm_delete_template,
432 - // leftButtonText: S.of(context).delete,
433 - // rightButtonText: S.of(context).cancel,
434 - // actionLeftButton: () {
435 - // Navigator.of(dialogContext).pop();
436 - // sendViewModel.sendTemplateStore.remove(template: template);
437 - // sendViewModel.sendTemplateStore.update();
438 - // },
439 - // actionRightButton: () => Navigator.of(dialogContext).pop()
440 - // );
441 - // }
442 - // );
443 - // },
444 - // );
445 - // }
446 - // );
447 - // }
448 - // )
449 - ],
450 - ),
451 - ),
452 - )
453 - ],
454 - ),
455 - bottomSectionPadding: EdgeInsets.only(left: 24, right: 24, bottom: 24),
456 - bottomSection: isTemplate
457 - ? PrimaryButton(
458 - onPressed: () {
459 - if (_formKey.currentState.validate()) {
460 - // sendViewModel.sendTemplateStore.addTemplate(
461 - // name: _nameController.text,
462 - // address: _addressController.text,
463 - // cryptoCurrency: sendViewModel.currency.title,
464 - // amount: _cryptoAmountController.text);
465 - // sendViewModel.sendTemplateStore.update();
466 - Navigator.of(context).pop();
467 - }
468 - },
469 - text: S.of(context).save,
470 - color: Colors.green,
471 - textColor: Colors.white)
472 - : Observer(builder: (_) {
473 - return LoadingPrimaryButton(
474 - onPressed: () {
475 - if (_formKey.currentState.validate()) {
476 - print('SENT!!!');
477 - }
478 - },
479 - text: S.of(context).send,
480 - color: Palette.blueCraiola,
481 - textColor: Colors.white,
482 - isLoading: sendViewModel.state is TransactionIsCreating ||
483 - sendViewModel.state is TransactionCommitting,
484 - isDisabled:
485 - false // FIXME !(syncStore.status is SyncedSyncStatus),
486 - );
487 - }),
488 - );
489 - }
490 -
491 - void _setEffects(BuildContext context) {
492 - if (_effectsInstalled) {
493 - return;
494 - }
495 -
496 - reaction((_) => sendViewModel.sendAll, (bool all) {
497 - if (all) {
498 - _cryptoAmountController.text = S.current.all;
499 - _fiatAmountController.text = null;
500 - }
501 - });
502 -
503 - reaction((_) => sendViewModel.fiatAmount, (String amount) {
504 - if (amount != _fiatAmountController.text) {
505 - _fiatAmountController.text = amount;
506 - }
507 - });
508 -
509 - reaction((_) => sendViewModel.cryptoAmount, (String amount) {
510 - if (sendViewModel.sendAll && amount != S.current.all) {
511 - sendViewModel.sendAll = false;
512 - }
513 -
514 - if (amount != _cryptoAmountController.text) {
515 - _cryptoAmountController.text = amount;
516 - }
517 - });
518 -
519 - reaction((_) => sendViewModel.address, (String address) {
520 - if (address != _addressController.text) {
521 - _addressController.text = address;
522 - }
523 - });
524 -
525 - _addressController.addListener(() {
526 - final address = _addressController.text;
527 -
528 - if (sendViewModel.address != address) {
529 - sendViewModel.address = address;
530 - }
531 - });
532 -
533 - reaction((_) => sendViewModel.state, (SendViewModelState state) {
534 - if (state is SendingFailed) {
535 - WidgetsBinding.instance.addPostFrameCallback((_) {
536 - showDialog<void>(
537 - context: context,
538 - builder: (BuildContext context) {
539 - return AlertWithOneAction(
540 - alertTitle: S.of(context).error,
541 - alertContent: state.error,
542 - buttonText: S.of(context).ok,
543 - buttonAction: () => Navigator.of(context).pop());
544 - });
545 - });
546 - }
547 -
548 - if (state is TransactionCreatedSuccessfully) {
549 - WidgetsBinding.instance.addPostFrameCallback((_) {
550 - showDialog<void>(
551 - context: context,
552 - builder: (BuildContext context) {
553 - return ConfirmSendingAlert(
554 - alertTitle: S.of(context).confirm_sending,
555 - amount: S.of(context).send_amount,
556 - amountValue:
557 - sendViewModel.pendingTransaction.amountFormatted,
558 - fee: S.of(context).send_fee,
559 - feeValue: sendViewModel.pendingTransaction.feeFormatted,
560 - leftButtonText: S.of(context).ok,
561 - rightButtonText: S.of(context).cancel,
562 - actionLeftButton: () {
563 - Navigator.of(context).pop();
564 - sendViewModel.commitTransaction();
565 - showDialog<void>(
566 - context: context,
567 - builder: (BuildContext context) {
568 - return Observer(builder: (_) {
569 - final state = sendViewModel.state;
570 -
571 - if (state is TransactionCommitted) {
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 - Center(
582 - child: Padding(
583 - padding: EdgeInsets.only(
584 - top: 220, left: 24, right: 24),
585 - child: Text(
586 - S.of(context).send_success,
587 - textAlign: TextAlign.center,
588 - style: TextStyle(
589 - fontSize: 22,
590 - fontWeight: FontWeight.bold,
591 - color: Theme.of(context)
592 - .primaryTextTheme
593 - .title
594 - .color,
595 - decoration: TextDecoration.none,
596 - ),
597 - ),
598 - ),
599 - ),
600 - Positioned(
601 - left: 24,
602 - right: 24,
603 - bottom: 24,
604 - child: PrimaryButton(
605 - onPressed: () =>
606 - Navigator.of(context).pop(),
607 - text: S.of(context).send_got_it,
608 - color: Colors.blue,
609 - textColor: Colors.white))
610 - ],
611 - );
612 - }
613 -
614 - return Stack(
615 - children: <Widget>[
616 - Container(
617 - color: Theme.of(context).backgroundColor,
618 - child: Center(
619 - child: Image.asset(
620 - 'assets/images/birthday_cake.png'),
621 - ),
622 - ),
623 - BackdropFilter(
624 - filter: ImageFilter.blur(
625 - sigmaX: 3.0, sigmaY: 3.0),
626 - child: Container(
627 - decoration: BoxDecoration(
628 - color: Theme.of(context)
629 - .backgroundColor
630 - .withOpacity(0.25)),
631 - child: Center(
632 - child: Padding(
633 - padding: EdgeInsets.only(top: 220),
634 - child: Text(
635 - S.of(context).send_sending,
636 - textAlign: TextAlign.center,
637 - style: TextStyle(
638 - fontSize: 22,
639 - fontWeight: FontWeight.bold,
640 - color: Theme.of(context)
641 - .primaryTextTheme
642 - .title
643 - .color,
644 - decoration: TextDecoration.none,
645 - ),
646 - ),
647 - ),
648 - ),
649 - ),
650 - )
651 - ],
652 - );
653 - });
654 - });
655 - },
656 - actionRightButton: () => Navigator.of(context).pop());
657 - });
658 - });
659 - }
660 -
661 - if (state is TransactionCommitted) {
662 - WidgetsBinding.instance.addPostFrameCallback((_) {
663 - _addressController.text = '';
664 - _cryptoAmountController.text = '';
665 - });
666 - }
667 - });
668 -
669 - _effectsInstalled = true;
670 - }
671 -
672 - Future<void> getOpenaliasRecord(BuildContext context) async {
673 - // final isOpenalias =
674 - // await sendViewModel.isOpenaliasRecord(_addressController.text);
675 -
676 - // if (isOpenalias) {
677 - // _addressController.text = sendViewModel.recordAddress;
678 -
679 - // await showDialog<void>(
680 - // context: context,
681 - // builder: (BuildContext context) {
682 - // return AlertWithOneAction(
683 - // alertTitle: S.of(context).openalias_alert_title,
684 - // alertContent: S
685 - // .of(context)
686 - // .openalias_alert_content(sendViewModel.recordName),
687 - // buttonText: S.of(context).ok,
688 - // buttonAction: () => Navigator.of(context).pop());
689 - // });
690 - // }
691 - }
692 -
693 - Future<void> _setTransactionPriority(BuildContext context) async {
694 - final items = TransactionPriority.all;
695 - final selectedItem = items.indexOf(sendViewModel.transactionPriority);
696 -
697 - await showDialog<void>(
698 - builder: (_) => Picker(
699 - items: items,
700 - selectedAtIndex: selectedItem,
701 - title: S.of(context).please_select,
702 - mainAxisAlignment: MainAxisAlignment.center,
703 - onItemSelected: (TransactionPriority priority) => null,
704 - // sendViewModel.setTransactionPriority(priority),
705 - isAlwaysShowScrollThumb: true,
706 - ),
707 - context: context);
708 - }
709 -}
lib/src/widgets/top_panel.dart deleted
-44
@@ -1,44 +0,0 @@
1 -import 'package:flutter/material.dart';
2 -
3 -class TopPanel extends StatefulWidget {
4 - TopPanel({
5 - @required this.widget,
6 - this.edgeInsets = const EdgeInsets.all(24),
7 - this.color,
8 - this.gradient
9 - });
10 -
11 - final Color color;
12 - final Widget widget;
13 - final EdgeInsets edgeInsets;
14 - final Gradient gradient;
15 -
16 - @override
17 - TopPanelState createState() => TopPanelState(widget, edgeInsets, color, gradient);
18 -}
19 -
20 -class TopPanelState extends State<TopPanel> {
21 - TopPanelState(this._widget, this._edgeInsets, this._color, this._gradient);
22 -
23 - final Color _color;
24 - final Widget _widget;
25 - final EdgeInsets _edgeInsets;
26 - final Gradient _gradient;
27 -
28 - @override
29 - Widget build(BuildContext context) {
30 - return Container(
31 - width: double.infinity,
32 - padding: _edgeInsets,
33 - decoration: BoxDecoration(
34 - borderRadius: BorderRadius.only(
35 - bottomLeft: Radius.circular(24),
36 - bottomRight: Radius.circular(24)
37 - ),
38 - color: _color,
39 - gradient: _gradient
40 - ),
41 - child: _widget,
42 - );
43 - }
44 -}
\ No newline at end of file