CWA-215 | added cryptocurrency icons to address book; applied new design to contact page; added suffixIcon parameter to base text form field; fixed seed widget
Oleksandr Sobol committed
May 20, 2020 at 20:28 UTC
3a8bae74e4d3f8146829a5a2710859053ae1bb8e
16 files changed
+243
-241
assets/images/ada.png
Binary files /dev/null and b/assets/images/ada.png differ
assets/images/bch.png
Binary files /dev/null and b/assets/images/bch.png differ
assets/images/bnb.png
Binary files /dev/null and b/assets/images/bnb.png differ
assets/images/dash.png
Binary files /dev/null and b/assets/images/dash.png differ
assets/images/eos.png
Binary files /dev/null and b/assets/images/eos.png differ
assets/images/eth.png
Binary files /dev/null and b/assets/images/eth.png differ
assets/images/nano.png
Binary files /dev/null and b/assets/images/nano.png differ
assets/images/trx.png
Binary files /dev/null and b/assets/images/trx.png differ
assets/images/usdt.png
Binary files /dev/null and b/assets/images/usdt.png differ
assets/images/xlm.png
Binary files /dev/null and b/assets/images/xlm.png differ
assets/images/xrp.png
Binary files /dev/null and b/assets/images/xrp.png differ
lib/src/screens/address_book/address_book_page.dart
+11
-11
@@ -216,43 +216,43 @@ class AddressBookPage extends BasePage {
216
image = Image.asset('assets/images/monero.png', height: 24, width: 24);
217
break;
218
case CryptoCurrency.ada:
219
- image = null;
219
+ image = Image.asset('assets/images/ada.png', height: 24, width: 24);
220
break;
221
case CryptoCurrency.bch:
222
- image = null;
222
+ image = Image.asset('assets/images/bch.png', height: 24, width: 24);
223
break;
224
case CryptoCurrency.bnb:
225
- image = null;
225
+ image = Image.asset('assets/images/bnb.png', height: 24, width: 24);
226
break;
227
case CryptoCurrency.btc:
228
image = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
229
break;
230
case CryptoCurrency.dash:
231
- image = null;
231
+ image = Image.asset('assets/images/dash.png', height: 24, width: 24);
232
break;
233
case CryptoCurrency.eos:
234
- image = null;
234
+ image = Image.asset('assets/images/eos.png', height: 24, width: 24);
235
break;
236
case CryptoCurrency.eth:
237
- image = null;
237
+ image = Image.asset('assets/images/eth.png', height: 24, width: 24);
238
break;
239
case CryptoCurrency.ltc:
240
image = Image.asset('assets/images/litecoin.png', height: 24, width: 24);
241
break;
242
case CryptoCurrency.nano:
243
- image = null;
243
+ image = Image.asset('assets/images/nano.png', height: 24, width: 24);
244
break;
245
case CryptoCurrency.trx:
246
- image = null;
246
+ image = Image.asset('assets/images/trx.png', height: 24, width: 24);
247
break;
248
case CryptoCurrency.usdt:
249
- image = null;
249
+ image = Image.asset('assets/images/usdt.png', height: 24, width: 24);
250
break;
251
case CryptoCurrency.xlm:
252
- image = null;
252
+ image = Image.asset('assets/images/xlm.png', height: 24, width: 24);
253
break;
254
case CryptoCurrency.xrp:
255
- image = null;
255
+ image = Image.asset('assets/images/xrp.png', height: 24, width: 24);
256
break;
257
default:
258
image = null;
lib/src/screens/address_book/contact_page.dart
+166
-184
@@ -1,5 +1,6 @@
1
import 'package:flutter/material.dart';
2
import 'package:flutter/cupertino.dart';
3
+import 'package:flutter_mobx/flutter_mobx.dart';
4
import 'package:provider/provider.dart';
5
import 'package:cake_wallet/generated/i18n.dart';
6
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
@@ -10,6 +11,9 @@ import 'package:cake_wallet/src/widgets/primary_button.dart';
11
import 'package:cake_wallet/src/widgets/address_text_field.dart';
12
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
13
import 'package:cake_wallet/palette.dart';
14
+import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
15
+import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
16
+import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
17
18
class ContactPage extends BasePage {
19
ContactPage({this.contact});
@@ -19,6 +23,9 @@ class ContactPage extends BasePage {
23
@override
24
String get title => S.current.contact;
25
26
+ @override
27
+ Color get backgroundColor => PaletteDark.historyPanel;
28
+
29
@override
30
Widget body(BuildContext context) => ContactForm(contact);
31
}
@@ -37,22 +44,31 @@ class ContactFormState extends State<ContactForm> {
44
final _contactNameController = TextEditingController();
45
final _currencyTypeController = TextEditingController();
46
final _addressController = TextEditingController();
47
+ final currencies = CryptoCurrency.all;
48
+ final downArrow = Image.asset(
49
+ 'assets/images/arrow_bottom_purple_icon.png',
50
+ color: PaletteDark.walletCardText,
51
+ height: 8);
52
41
- CryptoCurrency _selectectCrypto = CryptoCurrency.xmr;
53
+ CryptoCurrency _selectectCrypto;
54
55
@override
56
void initState() {
57
super.initState();
46
- if (widget.contact == null) {
47
- _currencyTypeController.text = _selectectCrypto.toString();
48
- } else {
58
+ if (widget.contact != null) {
59
_selectectCrypto = widget.contact.type;
60
_contactNameController.text = widget.contact.name;
61
_currencyTypeController.text = _selectectCrypto.toString();
62
_addressController.text = widget.contact.address;
63
+ WidgetsBinding.instance.addPostFrameCallback(afterLayout);
64
}
65
}
66
67
+ void afterLayout(dynamic _) {
68
+ final addressBookStore = Provider.of<AddressBookStore>(context);
69
+ addressBookStore.setDisabledStatus(false);
70
+ }
71
+
72
@override
73
void dispose() {
74
_contactNameController.dispose();
@@ -61,198 +77,164 @@ class ContactFormState extends State<ContactForm> {
77
super.dispose();
78
}
79
64
- Future<void> _setCurrencyType(BuildContext context) async {
65
- var currencyType = CryptoCurrency.all[0].toString();
66
- var selectedCurrency = CryptoCurrency.all[0];
67
-
68
- await showDialog<void>(
69
- context: context,
70
- builder: (BuildContext context) {
71
- return AlertDialog(
72
- title: Text(S.of(context).please_select),
73
- backgroundColor: Theme.of(context).backgroundColor,
74
- content: Container(
75
- height: 150.0,
76
- child: CupertinoPicker(
77
- backgroundColor: Theme.of(context).backgroundColor,
78
- itemExtent: 45.0,
79
- onSelectedItemChanged: (int index) {
80
- selectedCurrency = CryptoCurrency.all[index];
81
- currencyType = CryptoCurrency.all[index].toString();
82
- },
83
- children:
84
- List.generate(CryptoCurrency.all.length, (int index) {
85
- return Center(
86
- child: Text(
87
- CryptoCurrency.all[index].toString(),
88
- style: TextStyle(
89
- color: Theme.of(context)
90
- .primaryTextTheme
91
- .caption
92
- .color),
93
- ),
94
- );
95
- })),
96
- ),
97
- actions: <Widget>[
98
- FlatButton(
99
- onPressed: () {
100
- Navigator.pop(context);
101
- },
102
- child: Text(S.of(context).cancel)),
103
- FlatButton(
104
- onPressed: () {
105
- _selectectCrypto = selectedCurrency;
106
- _currencyTypeController.text = currencyType;
107
- Navigator.of(context).pop();
108
- },
109
- child: Text(S.of(context).ok))
110
- ],
111
- );
112
- });
80
+ void onHandleControllers(AddressBookStore addressBookStore) {
81
+ if (_contactNameController.text.isNotEmpty &&
82
+ _addressController.text.isNotEmpty &&
83
+ _currencyTypeController.text.isNotEmpty) {
84
+ addressBookStore.setDisabledStatus(false);
85
+ } else {
86
+ addressBookStore.setDisabledStatus(true);
87
+ }
88
}
89
90
@override
91
Widget build(BuildContext context) {
92
final addressBookStore = Provider.of<AddressBookStore>(context);
93
119
- return ScrollableWithBottomSection(
120
- content: Form(
121
- key: _formKey,
122
- child: Column(
123
- mainAxisSize: MainAxisSize.min,
124
- children: <Widget>[
125
- TextFormField(
126
- style: TextStyle(
127
- fontSize: 14.0,
128
- color: Theme.of(context).primaryTextTheme.headline.color),
129
- decoration: InputDecoration(
130
- hintStyle: TextStyle(color: Theme.of(context).hintColor),
131
- hintText: S.of(context).contact_name,
132
- focusedBorder: UnderlineInputBorder(
133
- borderSide:
134
- BorderSide(color: Palette.cakeGreen, width: 2.0)),
135
- enabledBorder: UnderlineInputBorder(
136
- borderSide: BorderSide(
137
- color: Theme.of(context).focusColor, width: 1.0))),
138
- controller: _contactNameController,
139
- validator: (value) {
140
- addressBookStore.validateContactName(value);
141
- return addressBookStore.errorMessage;
142
- },
143
- ),
144
- SizedBox(height: 14.0),
145
- Container(
146
- child: InkWell(
147
- onTap: () => _setCurrencyType(context),
148
- child: IgnorePointer(
149
- child: TextFormField(
150
- style: TextStyle(
151
- fontSize: 14.0,
152
- color: Theme.of(context)
153
- .primaryTextTheme
154
- .headline
155
- .color),
156
- decoration: InputDecoration(
157
- focusedBorder: UnderlineInputBorder(
158
- borderSide: BorderSide(
159
- color: Palette.cakeGreen, width: 2.0)),
160
- enabledBorder: UnderlineInputBorder(
161
- borderSide: BorderSide(
162
- color: Theme.of(context).focusColor,
163
- width: 1.0))),
164
- controller: _currencyTypeController,
94
+ _contactNameController.addListener(() {onHandleControllers(addressBookStore);});
95
+ _currencyTypeController.addListener(() {onHandleControllers(addressBookStore);});
96
+ _addressController.addListener(() {onHandleControllers(addressBookStore);});
97
+
98
+ return Container(
99
+ color: PaletteDark.historyPanel,
100
+ child: ScrollableWithBottomSection(
101
+ contentPadding: EdgeInsets.all(24),
102
+ content: Form(
103
+ key: _formKey,
104
+ child: Column(
105
+ mainAxisSize: MainAxisSize.min,
106
+ children: <Widget>[
107
+ BaseTextFormField(
108
+ controller: _contactNameController,
109
+ hintText: S.of(context).contact_name,
110
+ borderColor: PaletteDark.walletCardSubAddressField,
111
+ validator: (value) {
112
+ addressBookStore.validateContactName(value);
113
+ return addressBookStore.errorMessage;
114
+ }
115
+ ),
116
+ Padding(
117
+ padding: EdgeInsets.only(top: 20),
118
+ child: Container(
119
+ child: InkWell(
120
+ onTap: () => _presentPicker(context),
121
+ child: IgnorePointer(
122
+ child: BaseTextFormField(
123
+ controller: _currencyTypeController,
124
+ hintText: S.of(context).settings_currency,
125
+ borderColor: PaletteDark.walletCardSubAddressField,
126
+ suffixIcon: Row(
127
+ mainAxisSize: MainAxisSize.min,
128
+ mainAxisAlignment: MainAxisAlignment.end,
129
+ children: <Widget>[
130
+ downArrow
131
+ ],
132
+ ),
133
+ )
134
+ ),
135
),
136
),
137
),
168
- ),
169
- SizedBox(height: 14.0),
170
- AddressTextField(
171
- controller: _addressController,
172
- options: [AddressTextFieldOption.qrCode],
173
- validator: (value) {
174
- addressBookStore.validateAddress(value,
175
- cryptoCurrency: _selectectCrypto);
176
- return addressBookStore.errorMessage;
177
- },
178
- )
179
- ],
180
- ),
181
- ),
182
- bottomSection: Row(
183
- children: <Widget>[
184
- Expanded(
185
- child: PrimaryButton(
186
- onPressed: () {
187
- setState(() {
188
- _selectectCrypto = CryptoCurrency.xmr;
189
- _contactNameController.text = '';
190
- _currencyTypeController.text =
191
- _selectectCrypto.toString();
192
- _addressController.text = '';
193
- });
194
- },
195
- text: S.of(context).reset,
196
- color:
197
- Theme.of(context).accentTextTheme.button.backgroundColor,
198
- textColor:
199
- Theme.of(context).primaryTextTheme.button.color),
138
+ Padding(
139
+ padding: EdgeInsets.only(top: 20),
140
+ child: AddressTextField(
141
+ controller: _addressController,
142
+ options: [AddressTextFieldOption.qrCode],
143
+ validator: (value) {
144
+ addressBookStore.validateAddress(value,
145
+ cryptoCurrency: _selectectCrypto);
146
+ return addressBookStore.errorMessage;
147
+ },
148
+ ),
149
+ )
150
+ ],
151
),
201
- SizedBox(width: 20),
202
- Expanded(
152
+ ),
153
+ bottomSectionPadding: EdgeInsets.only(
154
+ left: 24,
155
+ right: 24,
156
+ bottom: 24
157
+ ),
158
+ bottomSection: Row(
159
+ children: <Widget>[
160
+ Expanded(
161
child: PrimaryButton(
204
- onPressed: () async {
205
- if (!_formKey.currentState.validate()) {
206
- return;
207
- }
208
-
209
- try {
210
- if (widget.contact == null) {
211
- final newContact = Contact(
212
- name: _contactNameController.text,
213
- address: _addressController.text,
214
- type: _selectectCrypto);
215
-
216
- await addressBookStore.add(contact: newContact);
217
- } else {
218
- widget.contact.name = _contactNameController.text;
219
- widget.contact.address = _addressController.text;
220
- widget.contact
221
- .updateCryptoCurrency(currency: _selectectCrypto);
162
+ onPressed: () {
163
+ setState(() {
164
+ _selectectCrypto = null;
165
+ _contactNameController.text = '';
166
+ _currencyTypeController.text = '';
167
+ _addressController.text = '';
168
+ });
169
+ },
170
+ text: S.of(context).reset,
171
+ color: Colors.red,
172
+ textColor: Colors.white
173
+ ),
174
+ ),
175
+ SizedBox(width: 20),
176
+ Expanded(
177
+ child: Observer(
178
+ builder: (_) => PrimaryButton(
179
+ onPressed: () async {
180
+ if (!_formKey.currentState.validate()) {
181
+ return;
182
+ }
183
223
- await addressBookStore.update(
224
- contact: widget.contact);
184
+ try {
185
+ if (widget.contact == null) {
186
+ final newContact = Contact(
187
+ name: _contactNameController.text,
188
+ address: _addressController.text,
189
+ type: _selectectCrypto);
190
+
191
+ await addressBookStore.add(contact: newContact);
192
+ } else {
193
+ widget.contact.name = _contactNameController.text;
194
+ widget.contact.address = _addressController.text;
195
+ widget.contact
196
+ .updateCryptoCurrency(currency: _selectectCrypto);
197
+
198
+ await addressBookStore.update(
199
+ contact: widget.contact);
200
+ }
201
+ Navigator.pop(context);
202
+ } catch (e) {
203
+ await showDialog<void>(
204
+ context: context,
205
+ builder: (BuildContext context) {
206
+ return AlertWithOneAction(
207
+ alertTitle: S.current.contact,
208
+ alertContent: e.toString(),
209
+ buttonText: S.of(context).ok,
210
+ buttonAction: () => Navigator.of(context).pop()
211
+ );
212
+ });
213
}
226
- Navigator.pop(context);
227
- } catch (e) {
228
- await showDialog<void>(
229
- context: context,
230
- builder: (BuildContext context) {
231
- return AlertDialog(
232
- title: Text(
233
- e.toString(),
234
- textAlign: TextAlign.center,
235
- ),
236
- actions: <Widget>[
237
- FlatButton(
238
- onPressed: () =>
239
- Navigator.of(context).pop(),
240
- child: Text(S.of(context).ok))
241
- ],
242
- );
243
- });
244
- }
245
- },
246
- text: S.of(context).save,
247
- color: Theme.of(context)
248
- .primaryTextTheme
249
- .button
250
- .backgroundColor,
251
- textColor: Theme.of(context)
252
- .primaryTextTheme
253
- .button
254
- .color))
255
- ],
256
- ));
214
+ },
215
+ text: S.of(context).save,
216
+ color: Colors.green,
217
+ textColor: Colors.white,
218
+ isDisabled: addressBookStore.isDisabledStatus,
219
+ )
220
+ )
221
+ )
222
+ ],
223
+ )),
224
+ );
225
+ }
226
+
227
+ void _presentPicker(BuildContext context) {
228
+ showDialog<void>(
229
+ builder: (_) => CurrencyPicker(
230
+ selectedAtIndex: currencies.indexOf(_selectectCrypto),
231
+ items: currencies,
232
+ title: S.of(context).please_select,
233
+ onItemSelected: (CryptoCurrency item) {
234
+ _selectectCrypto = item;
235
+ _currencyTypeController.text = _selectectCrypto.toString();
236
+ }
237
+ ),
238
+ context: context);
239
}
240
}
lib/src/stores/address_book/address_book_store.dart
+9
@@ -12,11 +12,15 @@ class AddressBookStore = AddressBookStoreBase with _$AddressBookStore;
12
abstract class AddressBookStoreBase with Store {
13
AddressBookStoreBase({@required this.contacts}) {
14
updateContactList();
15
+ isDisabledStatus = true;
16
}
17
18
@observable
19
List<Contact> contactList;
20
21
+ @observable
22
+ bool isDisabledStatus;
23
+
24
@observable
25
bool isValid;
26
@@ -37,6 +41,11 @@ abstract class AddressBookStoreBase with Store {
41
@action
42
Future delete({Contact contact}) async => await contact.delete();
43
44
+ @action
45
+ void setDisabledStatus(bool isDisabled) {
46
+ isDisabledStatus = isDisabled;
47
+ }
48
+
49
void validateContactName(String value) {
50
const pattern = '''^[^`,'"]{1,32}\$''';
51
final regExp = RegExp(pattern);
lib/src/widgets/base_text_form_field.dart
+3
@@ -17,6 +17,7 @@ class BaseTextFormField extends StatelessWidget {
17
this.borderColor = PaletteDark.menuList,
18
this.prefix,
19
this.suffix,
20
+ this.suffixIcon,
21
this.enabled = true,
22
this.validator
23
});
@@ -34,6 +35,7 @@ class BaseTextFormField extends StatelessWidget {
35
final Color borderColor;
36
final Widget prefix;
37
final Widget suffix;
38
+ final Widget suffixIcon;
39
final bool enabled;
40
final FormFieldValidator<String> validator;
41
@@ -55,6 +57,7 @@ class BaseTextFormField extends StatelessWidget {
57
decoration: InputDecoration(
58
prefix: prefix,
59
suffix: suffix,
60
+ suffixIcon: suffixIcon,
61
hintStyle: TextStyle(
62
color: hintColor,
63
fontSize: 16
lib/src/widgets/seed_widget.dart
+54
-46
@@ -13,7 +13,6 @@ import 'package:cake_wallet/src/domain/monero/mnemonics/russian.dart';
13
import 'package:cake_wallet/src/domain/monero/mnemonics/spanish.dart';
14
import 'package:cake_wallet/src/domain/common/mnemotic_item.dart';
15
import 'package:cake_wallet/generated/i18n.dart';
16
-import 'package:cake_wallet/src/widgets/top_panel.dart';
16
17
class SeedWidget extends StatefulWidget {
18
SeedWidget({Key key, this.onMnemoticChange, this.onFinish, this.seedLanguage}) : super(key: key) {
@@ -237,52 +236,61 @@ class SeedWidgetState extends State<SeedWidget> {
236
Flexible(
237
fit: FlexFit.tight,
238
flex: 1,
240
- child: TopPanel(
241
- color: PaletteDark.menuList,
242
- widget: SingleChildScrollView(
243
- child: Column(
244
- mainAxisAlignment: MainAxisAlignment.start,
245
- crossAxisAlignment: CrossAxisAlignment.start,
246
- children: <Widget>[
247
- Text(
248
- S.of(context).restore_active_seed,
249
- style: TextStyle(
250
- fontSize: 14,
251
- color: PaletteDark.walletCardText
252
- ),
253
- ),
254
- Padding(
255
- padding: EdgeInsets.only(top: 5),
256
- child: Wrap(
257
- children: items.map((item) {
258
- final isValid = item.isCorrect();
259
- final isSelected = selectedItem == item;
260
-
261
- return InkWell(
262
- onTap: () => onMnemoticTap(item),
263
- child: Container(
264
- decoration: BoxDecoration(
265
- color: isValid ? Colors.transparent : Palette.red),
266
- margin: EdgeInsets.only(right: 7, bottom: 8),
267
- child: Text(
268
- item.toString(),
269
- style: TextStyle(
270
- color:
271
- isValid ? Colors.white : Palette.lightGrey,
272
- fontSize: 16,
273
- fontWeight:
274
- isSelected ? FontWeight.w900 : FontWeight.w400,
275
- decoration: isSelected
276
- ? TextDecoration.underline
277
- : TextDecoration.none),
278
- )),
279
- );
280
- }).toList(),)
281
- )
282
- ],
239
+ child: Container(
240
+ width: double.infinity,
241
+ height: double.infinity,
242
+ padding: EdgeInsets.all(24),
243
+ decoration: BoxDecoration(
244
+ borderRadius: BorderRadius.only(
245
+ bottomLeft: Radius.circular(24),
246
+ bottomRight: Radius.circular(24)
247
),
284
- )
285
- )
248
+ color: PaletteDark.menuList
249
+ ),
250
+ child: SingleChildScrollView(
251
+ child: Column(
252
+ mainAxisAlignment: MainAxisAlignment.start,
253
+ crossAxisAlignment: CrossAxisAlignment.start,
254
+ children: <Widget>[
255
+ Text(
256
+ S.of(context).restore_active_seed,
257
+ style: TextStyle(
258
+ fontSize: 14,
259
+ color: PaletteDark.walletCardText
260
+ ),
261
+ ),
262
+ Padding(
263
+ padding: EdgeInsets.only(top: 5),
264
+ child: Wrap(
265
+ children: items.map((item) {
266
+ final isValid = item.isCorrect();
267
+ final isSelected = selectedItem == item;
268
+
269
+ return InkWell(
270
+ onTap: () => onMnemoticTap(item),
271
+ child: Container(
272
+ decoration: BoxDecoration(
273
+ color: isValid ? Colors.transparent : Palette.red),
274
+ margin: EdgeInsets.only(right: 7, bottom: 8),
275
+ child: Text(
276
+ item.toString(),
277
+ style: TextStyle(
278
+ color:
279
+ isValid ? Colors.white : Palette.lightGrey,
280
+ fontSize: 16,
281
+ fontWeight:
282
+ isSelected ? FontWeight.w900 : FontWeight.w400,
283
+ decoration: isSelected
284
+ ? TextDecoration.underline
285
+ : TextDecoration.none),
286
+ )),
287
+ );
288
+ }).toList(),)
289
+ )
290
+ ],
291
+ ),
292
+ ),
293
+ ),
294
),
295
Flexible(
296
fit: FlexFit.tight,