CAKE-345 | fixed add receiver button; applied dotted borders to primary button; applied localization to add receiver button; added scrollbar to confirm_sending_alert.dart

OleksandrSobol committed Aug 12, 2021 at 17:56 UTC 30a32ab07163d207d13f0b163c10aa4c8cb0ee0e
19 files changed +314 -182
lib/src/screens/send/send_page.dart
+13 -2
@@ -264,10 +264,21 @@ class SendPage extends BasePage {
264 child: PrimaryButton(
265 onPressed: () {
266 sendViewModel.addOutput();
267 + Future.delayed(const Duration(milliseconds: 250), () {
268 + controller.jumpToPage(sendViewModel.outputs.length - 1);
269 + });
270 },
271 text: S.of(context).add_receiver,
269 - color: Colors.green,
270 - textColor: Colors.white,
272 + color: Colors.transparent,
273 + textColor: Theme.of(context)
274 + .accentTextTheme
275 + .display2
276 + .decorationColor,
277 + isDottedBorder: true,
278 + borderColor: Theme.of(context)
279 + .primaryTextTheme
280 + .display2
281 + .decorationColor,
282 )
283 ),
284 Observer(builder: (_) {
lib/src/screens/send/widgets/confirm_sending_alert.dart
+262 -162
@@ -3,6 +3,7 @@ import 'package:cake_wallet/view_model/send/output.dart';
3 import 'package:flutter/material.dart';
4 import 'package:cake_wallet/src/widgets/base_alert_dialog.dart';
5 import 'package:cake_wallet/generated/i18n.dart';
6 +import 'package:cake_wallet/src/widgets/cake_scrollbar.dart';
7
8 class ConfirmSendingAlert extends BaseAlertDialog {
9 ConfirmSendingAlert({
@@ -18,13 +19,7 @@ class ConfirmSendingAlert extends BaseAlertDialog {
19 @required this.rightButtonText,
20 @required this.actionLeftButton,
21 @required this.actionRightButton,
21 - this.alertBarrierDismissible = true
22 - }) {
23 - itemCount = outputs.length;
24 - recipientTitle = itemCount > 1
25 - ? S.current.transaction_details_recipient_address
26 - : S.current.recipient_address;
27 - }
22 + this.alertBarrierDismissible = true});
23
24 final String alertTitle;
25 final String amount;
@@ -40,9 +35,6 @@ class ConfirmSendingAlert extends BaseAlertDialog {
35 final VoidCallback actionRightButton;
36 final bool alertBarrierDismissible;
37
43 - String recipientTitle;
44 - int itemCount;
45 -
38 @override
39 String get titleText => alertTitle;
40
@@ -65,131 +57,184 @@ class ConfirmSendingAlert extends BaseAlertDialog {
57 bool get barrierDismissible => alertBarrierDismissible;
58
59 @override
68 - Widget content(BuildContext context) {
69 - return Container(
70 - height: 200,
71 - child: SingleChildScrollView(
72 - child: Column(
73 - children: <Widget>[
74 - Row(
75 - mainAxisSize: MainAxisSize.max,
76 - mainAxisAlignment: MainAxisAlignment.spaceBetween,
77 - crossAxisAlignment: CrossAxisAlignment.start,
78 - children: <Widget>[
79 - Text(
80 - amount,
81 - style: TextStyle(
82 - fontSize: 16,
83 - fontWeight: FontWeight.normal,
84 - fontFamily: 'Lato',
85 - color: Theme.of(context).primaryTextTheme.title.color,
86 - decoration: TextDecoration.none,
87 - ),
88 - ),
89 - Column(
90 - crossAxisAlignment: CrossAxisAlignment.end,
91 - children: [
92 - Text(
93 - amountValue,
94 - style: TextStyle(
95 - fontSize: 18,
96 - fontWeight: FontWeight.w600,
97 - fontFamily: 'Lato',
98 - color: Theme.of(context).primaryTextTheme.title.color,
99 - decoration: TextDecoration.none,
100 - ),
101 - ),
102 - Text(
103 - fiatAmountValue,
104 - style: TextStyle(
105 - fontSize: 12,
106 - fontWeight: FontWeight.w600,
107 - fontFamily: 'Lato',
108 - color: PaletteDark.pigeonBlue,
109 - decoration: TextDecoration.none,
110 - ),
111 - )
112 - ],
113 - )
114 - ],
115 - ),
116 - Padding(
117 - padding: EdgeInsets.only(top: 16),
118 - child: Row(
119 - mainAxisSize: MainAxisSize.max,
120 - mainAxisAlignment: MainAxisAlignment.spaceBetween,
121 - crossAxisAlignment: CrossAxisAlignment.start,
60 + Widget content(BuildContext context) => ConfirmSendingAlertContent(
61 + amount: amount,
62 + amountValue: amountValue,
63 + fiatAmountValue: fiatAmountValue,
64 + fee: fee,
65 + feeValue: feeValue,
66 + feeFiatAmount: feeFiatAmount,
67 + outputs: outputs
68 + );
69 +}
70 +
71 +class ConfirmSendingAlertContent extends StatefulWidget {
72 + ConfirmSendingAlertContent({
73 + @required this.amount,
74 + @required this.amountValue,
75 + @required this.fiatAmountValue,
76 + @required this.fee,
77 + @required this.feeValue,
78 + @required this.feeFiatAmount,
79 + @required this.outputs});
80 +
81 + final String amount;
82 + final String amountValue;
83 + final String fiatAmountValue;
84 + final String fee;
85 + final String feeValue;
86 + final String feeFiatAmount;
87 + final List<Output> outputs;
88 +
89 + @override
90 + ConfirmSendingAlertContentState createState() => ConfirmSendingAlertContentState(
91 + amount: amount,
92 + amountValue: amountValue,
93 + fiatAmountValue: fiatAmountValue,
94 + fee: fee,
95 + feeValue: feeValue,
96 + feeFiatAmount: feeFiatAmount,
97 + outputs: outputs
98 + );
99 +}
100 +
101 +class ConfirmSendingAlertContentState extends State<ConfirmSendingAlertContent> {
102 + ConfirmSendingAlertContentState({
103 + @required this.amount,
104 + @required this.amountValue,
105 + @required this.fiatAmountValue,
106 + @required this.fee,
107 + @required this.feeValue,
108 + @required this.feeFiatAmount,
109 + @required this.outputs}) {
110 +
111 + itemCount = outputs.length;
112 + recipientTitle = itemCount > 1
113 + ? S.current.transaction_details_recipient_address
114 + : S.current.recipient_address;
115 + }
116 +
117 + final String amount;
118 + final String amountValue;
119 + final String fiatAmountValue;
120 + final String fee;
121 + final String feeValue;
122 + final String feeFiatAmount;
123 + final List<Output> outputs;
124 +
125 + final double backgroundHeight = 160;
126 + final double thumbHeight = 72;
127 + ScrollController controller = ScrollController();
128 + double fromTop = 0;
129 + String recipientTitle;
130 + int itemCount;
131 +
132 + @override
133 + Widget build(BuildContext context) {
134 + controller.addListener(() {
135 + fromTop = controller.hasClients
136 + ? (controller.offset / controller.position.maxScrollExtent *
137 + (backgroundHeight - thumbHeight))
138 + : 0;
139 + setState(() {});
140 + });
141 +
142 + return Stack(
143 + alignment: Alignment.center,
144 + clipBehavior: Clip.none,
145 + children: [
146 + Container(
147 + height: 200,
148 + child: SingleChildScrollView(
149 + controller: controller,
150 + child: Column(
151 children: <Widget>[
123 - Text(
124 - fee,
125 - style: TextStyle(
126 - fontSize: 16,
127 - fontWeight: FontWeight.normal,
128 - fontFamily: 'Lato',
129 - color: Theme.of(context).primaryTextTheme.title.color,
130 - decoration: TextDecoration.none,
131 - ),
132 - ),
133 - Column(
134 - crossAxisAlignment: CrossAxisAlignment.end,
135 - children: [
152 + Row(
153 + mainAxisSize: MainAxisSize.max,
154 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
155 + crossAxisAlignment: CrossAxisAlignment.start,
156 + children: <Widget>[
157 Text(
137 - feeValue,
158 + amount,
159 style: TextStyle(
139 - fontSize: 18,
140 - fontWeight: FontWeight.w600,
160 + fontSize: 16,
161 + fontWeight: FontWeight.normal,
162 fontFamily: 'Lato',
142 - color: Theme.of(context).primaryTextTheme.title.color,
163 + color: Theme.of(context)
164 + .primaryTextTheme
165 + .title
166 + .color,
167 decoration: TextDecoration.none,
168 ),
169 ),
146 - Text(
147 - feeFiatAmount,
148 - style: TextStyle(
149 - fontSize: 12,
150 - fontWeight: FontWeight.w600,
151 - fontFamily: 'Lato',
152 - color: PaletteDark.pigeonBlue,
153 - decoration: TextDecoration.none,
154 - ),
170 + Column(
171 + crossAxisAlignment: CrossAxisAlignment.end,
172 + children: [
173 + Text(
174 + amountValue,
175 + style: TextStyle(
176 + fontSize: 18,
177 + fontWeight: FontWeight.w600,
178 + fontFamily: 'Lato',
179 + color: Theme.of(context)
180 + .primaryTextTheme
181 + .title
182 + .color,
183 + decoration: TextDecoration.none,
184 + ),
185 + ),
186 + Text(
187 + fiatAmountValue,
188 + style: TextStyle(
189 + fontSize: 12,
190 + fontWeight: FontWeight.w600,
191 + fontFamily: 'Lato',
192 + color: PaletteDark.pigeonBlue,
193 + decoration: TextDecoration.none,
194 + ),
195 + )
196 + ],
197 )
198 ],
157 - )
158 - ],
159 - )
160 - ),
161 - Padding(
162 - padding: EdgeInsets.only(top: 16),
163 - child: Column(
164 - children: [
165 - Text(
166 - '$recipientTitle:',
167 - style: TextStyle(
168 - fontSize: 16,
169 - fontWeight: FontWeight.normal,
170 - fontFamily: 'Lato',
171 - color: Theme.of(context).primaryTextTheme.title.color,
172 - decoration: TextDecoration.none,
199 ),
174 - ),
175 - itemCount > 1
176 - ? ListView.builder(
177 - padding: EdgeInsets.only(top: 0),
178 - shrinkWrap: true,
179 - physics: NeverScrollableScrollPhysics(),
180 - itemCount: itemCount,
181 - itemBuilder: (context, index) {
182 - final item = outputs[index];
183 - final _address = item.address;
184 - final _amount =
185 - item.cryptoAmount.replaceAll(',', '.');
186 -
187 - return Column(
188 - children: [
189 - Padding(
190 - padding: EdgeInsets.only(top: 8),
191 - child: Text(
192 - _address,
200 + Padding(
201 + padding: EdgeInsets.only(top: 16),
202 + child: Row(
203 + mainAxisSize: MainAxisSize.max,
204 + mainAxisAlignment: MainAxisAlignment.spaceBetween,
205 + crossAxisAlignment: CrossAxisAlignment.start,
206 + children: <Widget>[
207 + Text(
208 + fee,
209 + style: TextStyle(
210 + fontSize: 16,
211 + fontWeight: FontWeight.normal,
212 + fontFamily: 'Lato',
213 + color: Theme.of(context)
214 + .primaryTextTheme
215 + .title
216 + .color,
217 + decoration: TextDecoration.none,
218 + ),
219 + ),
220 + Column(
221 + crossAxisAlignment: CrossAxisAlignment.end,
222 + children: [
223 + Text(
224 + feeValue,
225 + style: TextStyle(
226 + fontSize: 18,
227 + fontWeight: FontWeight.w600,
228 + fontFamily: 'Lato',
229 + color: Theme.of(context)
230 + .primaryTextTheme
231 + .title
232 + .color,
233 + decoration: TextDecoration.none,
234 + ),
235 + ),
236 + Text(
237 + feeFiatAmount,
238 style: TextStyle(
239 fontSize: 12,
240 fontWeight: FontWeight.w600,
@@ -198,49 +243,104 @@ class ConfirmSendingAlert extends BaseAlertDialog {
243 decoration: TextDecoration.none,
244 ),
245 )
246 + ],
247 + )
248 + ],
249 + )
250 + ),
251 + Padding(
252 + padding: EdgeInsets.only(top: 16),
253 + child: Column(
254 + children: [
255 + Text(
256 + '$recipientTitle:',
257 + style: TextStyle(
258 + fontSize: 16,
259 + fontWeight: FontWeight.normal,
260 + fontFamily: 'Lato',
261 + color: Theme.of(context)
262 + .primaryTextTheme
263 + .title
264 + .color,
265 + decoration: TextDecoration.none,
266 ),
202 - Padding(
203 - padding: EdgeInsets.only(top: 8),
204 - child: Row(
205 - mainAxisSize: MainAxisSize.max,
206 - mainAxisAlignment: MainAxisAlignment.end,
267 + ),
268 + itemCount > 1
269 + ? ListView.builder(
270 + padding: EdgeInsets.only(top: 0),
271 + shrinkWrap: true,
272 + physics: NeverScrollableScrollPhysics(),
273 + itemCount: itemCount,
274 + itemBuilder: (context, index) {
275 + final item = outputs[index];
276 + final _address = item.address;
277 + final _amount =
278 + item.cryptoAmount.replaceAll(',', '.');
279 +
280 + return Column(
281 children: [
208 - Text(
209 - _amount,
210 - style: TextStyle(
211 - fontSize: 12,
212 - fontWeight: FontWeight.w600,
213 - fontFamily: 'Lato',
214 - color: PaletteDark.pigeonBlue,
215 - decoration: TextDecoration.none,
216 - ),
282 + Padding(
283 + padding: EdgeInsets.only(top: 8),
284 + child: Text(
285 + _address,
286 + style: TextStyle(
287 + fontSize: 12,
288 + fontWeight: FontWeight.w600,
289 + fontFamily: 'Lato',
290 + color: PaletteDark.pigeonBlue,
291 + decoration: TextDecoration.none,
292 + ),
293 + )
294 + ),
295 + Padding(
296 + padding: EdgeInsets.only(top: 8),
297 + child: Row(
298 + mainAxisSize: MainAxisSize.max,
299 + mainAxisAlignment: MainAxisAlignment.end,
300 + children: [
301 + Text(
302 + _amount,
303 + style: TextStyle(
304 + fontSize: 12,
305 + fontWeight: FontWeight.w600,
306 + fontFamily: 'Lato',
307 + color: PaletteDark.pigeonBlue,
308 + decoration: TextDecoration.none,
309 + ),
310 + )
311 + ],
312 + )
313 )
314 ],
219 - )
220 - )
221 - ],
222 - );
223 - }
224 - )
225 - : Padding(
226 - padding: EdgeInsets.only(top: 8),
227 - child: Text(
228 - outputs.first.address,
229 - style: TextStyle(
230 - fontSize: 12,
231 - fontWeight: FontWeight.w600,
232 - fontFamily: 'Lato',
233 - color: PaletteDark.pigeonBlue,
234 - decoration: TextDecoration.none,
315 + );
316 + })
317 + : Padding(
318 + padding: EdgeInsets.only(top: 8),
319 + child: Text(
320 + outputs.first.address,
321 + style: TextStyle(
322 + fontSize: 12,
323 + fontWeight: FontWeight.w600,
324 + fontFamily: 'Lato',
325 + color: PaletteDark.pigeonBlue,
326 + decoration: TextDecoration.none,
327 + ),
328 + ),
329 + )
330 + ],
331 ),
236 - ),
237 - )
238 - ],
239 - ),
332 + )
333 + ],
334 + )
335 )
241 - ],
242 - )
243 - )
336 + ),
337 + if (itemCount > 1) CakeScrollbar(
338 + backgroundHeight: backgroundHeight,
339 + thumbHeight: thumbHeight,
340 + fromTop: fromTop,
341 + rightOffset: -15
342 + )
343 + ]
344 );
345 }
346 }
\ No newline at end of file
lib/src/widgets/cake_scrollbar.dart
+4 -2
@@ -4,17 +4,19 @@ class CakeScrollbar extends StatelessWidget {
4 CakeScrollbar({
5 @required this.backgroundHeight,
6 @required this.thumbHeight,
7 - @required this.fromTop
7 + @required this.fromTop,
8 + this.rightOffset = 6
9 });
10
11 final double backgroundHeight;
12 final double thumbHeight;
13 final double fromTop;
14 + final double rightOffset;
15
16 @override
17 Widget build(BuildContext context) {
18 return Positioned(
17 - right: 6,
19 + right: rightOffset,
20 child: Container(
21 height: backgroundHeight,
22 width: 6,
lib/src/widgets/primary_button.dart
+18 -1
@@ -1,3 +1,4 @@
1 +import 'package:dotted_border/dotted_border.dart';
2 import 'package:flutter/cupertino.dart';
3 import 'package:flutter/material.dart';
4
@@ -8,18 +9,22 @@ class PrimaryButton extends StatelessWidget {
9 @required this.color,
10 @required this.textColor,
11 this.isDisabled = false,
12 + this.isDottedBorder = false,
13 + this.borderColor = Colors.black,
14 this.onDisabledPressed});
15
16 final VoidCallback onPressed;
17 final VoidCallback onDisabledPressed;
18 final Color color;
19 final Color textColor;
20 + final Color borderColor;
21 final String text;
22 final bool isDisabled;
23 + final bool isDottedBorder;
24
25 @override
26 Widget build(BuildContext context) {
22 - return ButtonTheme(
27 + final content = ButtonTheme(
28 minWidth: double.infinity,
29 height: 52.0,
30 child: FlatButton(
@@ -27,6 +32,8 @@ class PrimaryButton extends StatelessWidget {
32 ? (onDisabledPressed != null ? onDisabledPressed : null)
33 : onPressed,
34 color: isDisabled ? color.withOpacity(0.5) : color,
35 + splashColor: Colors.transparent,
36 + highlightColor: Colors.transparent,
37 disabledColor: color.withOpacity(0.5),
38 shape: RoundedRectangleBorder(
39 borderRadius: BorderRadius.circular(26.0),
@@ -40,6 +47,16 @@ class PrimaryButton extends StatelessWidget {
47 ? textColor.withOpacity(0.5)
48 : textColor)),
49 ));
50 +
51 + return isDottedBorder
52 + ? DottedBorder(
53 + borderType: BorderType.RRect,
54 + dashPattern: [6, 4],
55 + color: borderColor,
56 + strokeWidth: 2,
57 + radius: Radius.circular(26),
58 + child: content)
59 + : content;
60 }
61 }
62
lib/view_model/send/send_view_model.dart
+3 -1
@@ -58,7 +58,9 @@ abstract class SendViewModelBase with Store {
58
59 @action
60 void removeOutput(Output output) {
61 - outputs.remove(output);
61 + if (isBatchSending) {
62 + outputs.remove(output);
63 + }
64 }
65
66 @action
res/values/strings_de.arb
+1 -1
@@ -494,5 +494,5 @@
494 "address_detected" : "Adresse erkannt",
495 "address_from_domain" : "Sie haben die Adresse von der unaufhaltsamen Domain ${domain} erhalten",
496
497 - "add_receiver" : "Empfänger hinzufügen"
497 + "add_receiver" : "Fügen Sie einen weiteren Empfänger hinzu (optional)"
498 }
res/values/strings_en.arb
+1 -1
@@ -494,5 +494,5 @@
494 "address_detected" : "Address detected",
495 "address_from_domain" : "You got address from unstoppable domain ${domain}",
496
497 - "add_receiver" : "Add receiver"
497 + "add_receiver" : "Add another receiver (optional)"
498 }
\ No newline at end of file
res/values/strings_es.arb
+1 -1
@@ -494,5 +494,5 @@
494 "address_detected" : "Dirección detectada",
495 "address_from_domain" : "Tienes la dirección de unstoppable domain ${domain}",
496
497 - "add_receiver" : "Agregar receptor"
497 + "add_receiver" : "Agregar otro receptor (opcional)"
498 }
\ No newline at end of file
res/values/strings_hi.arb
+1 -1
@@ -494,5 +494,5 @@
494 "address_detected" : "पता लग गया",
495 "address_from_domain" : "आपको अजेय डोमेन ${domain} से पता मिला है",
496
497 - "add_receiver" : "रिसीवर जोड़ें"
497 + "add_receiver" : "एक और रिसीवर जोड़ें (वैकल्पिक)"
498 }
\ No newline at end of file
res/values/strings_hr.arb
+1 -1
@@ -494,5 +494,5 @@
494 "address_detected" : "Adresa je otkrivena",
495 "address_from_domain" : "Dobili ste adresu od unstoppable domain ${domain}",
496
497 - "add_receiver" : "Dodajte prijamnik"
497 + "add_receiver" : "Dodajte drugi prijemnik (izborno)"
498 }
\ No newline at end of file
res/values/strings_it.arb
+1 -1
@@ -494,5 +494,5 @@
494 "address_detected" : "Indirizzo rilevato",
495 "address_from_domain" : "Hai l'indirizzo da unstoppable domain ${domain}",
496
497 - "add_receiver" : "Aggiungi ricevitore"
497 + "add_receiver" : "Aggiungi un altro ricevitore (opzionale)"
498 }
\ No newline at end of file
res/values/strings_ja.arb
+1 -1
@@ -494,5 +494,5 @@
494 "address_detected" : "アドレスが検出されました",
495 "address_from_domain" : "あなたはからアドレスを得ました unstoppable domain ${domain}",
496
497 - "add_receiver" : "レシーバーを追加"
497 + "add_receiver" : "別のレシーバーを追加します(オプション)"
498 }
\ No newline at end of file
res/values/strings_ko.arb
+1 -1
@@ -494,5 +494,5 @@
494 "address_detected" : "주소 감지",
495 "address_from_domain" : "주소는 unstoppable domain ${domain}",
496
497 - "add_receiver" : "수신기 추가"
497 + "add_receiver" : "다른 수신기 추가(선택 사항)"
498 }
\ No newline at end of file
res/values/strings_nl.arb
+1 -1
@@ -494,5 +494,5 @@
494 "address_detected" : "Adres gedetecteerd",
495 "address_from_domain" : "Je adres is van unstoppable domain ${domain}",
496
497 - "add_receiver" : "Ontvanger toevoegen"
497 + "add_receiver" : "Nog een ontvanger toevoegen (optioneel)"
498 }
\ No newline at end of file
res/values/strings_pl.arb
+1 -1
@@ -494,5 +494,5 @@
494 "address_detected" : "Wykryto adres",
495 "address_from_domain" : "Dostałeś adres od unstoppable domain ${domain}",
496
497 - "add_receiver" : "Dodaj odbiorcę"
497 + "add_receiver" : "Dodaj kolejny odbiornik (opcjonalnie)"
498 }
\ No newline at end of file
res/values/strings_pt.arb
+1 -1
@@ -494,5 +494,5 @@
494 "address_detected" : "Endereço detectado",
495 "address_from_domain" : "Você obteve o endereço de unstoppable domain ${domain}",
496
497 - "add_receiver" : "Adicionar receptor"
497 + "add_receiver" : "Adicione outro receptor (opcional)"
498 }
\ No newline at end of file
res/values/strings_ru.arb
+1 -1
@@ -494,5 +494,5 @@
494 "address_detected" : "Обнаружен адрес",
495 "address_from_domain" : "Вы получили адрес из unstoppable domain ${domain}",
496
497 - "add_receiver" : "Добавить получателя"
497 + "add_receiver" : "Добавить получателя (необязательно)"
498 }
\ No newline at end of file
res/values/strings_uk.arb
+1 -1
@@ -494,5 +494,5 @@
494 "address_detected" : "Виявлено адресу",
495 "address_from_domain" : "Ви отримали адресу від unstoppable domain ${domain}",
496
497 - "add_receiver" : "Додати одержувача"
497 + "add_receiver" : "Додати одержувача (необов'язково)"
498 }
\ No newline at end of file
res/values/strings_zh.arb
+1 -1
@@ -494,5 +494,5 @@
494 "address_detected" : "檢測到地址",
495 "address_from_domain" : "您有以下地址 unstoppable domain ${domain}",
496
497 - "add_receiver" : "添加接收器"
497 + "add_receiver" : "添加另一個接收器(可選)"
498 }
\ No newline at end of file