Merge pull request #61 from cake-tech/CWA-215-new-address-book-screen

Cwa 215 new address book screen
This commit is contained in:
Oleksandr Sobol
2020-05-25 18:31:38 +03:00
committed by GitHub
32 changed files with 411 additions and 339 deletions
@@ -10,6 +10,7 @@ import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
import 'package:cake_wallet/src/stores/address_book/address_book_store.dart';
import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/widgets/alert_with_two_actions.dart';
class AddressBookPage extends BasePage {
AddressBookPage({this.isEditable = true});
@@ -17,14 +18,11 @@ class AddressBookPage extends BasePage {
final bool isEditable;
@override
bool get isModalBackButton => true;
Color get backgroundColor => PaletteDark.historyPanel;
@override
String get title => S.current.address_book;
@override
AppBarStyle get appBarStyle => AppBarStyle.withShadow;
@override
Widget trailing(BuildContext context) {
if (!isEditable) {
@@ -34,17 +32,17 @@ class AddressBookPage extends BasePage {
final addressBookStore = Provider.of<AddressBookStore>(context);
return Container(
width: 28.0,
height: 28.0,
width: 32.0,
height: 32.0,
decoration: BoxDecoration(
shape: BoxShape.circle, color: Theme.of(context).selectedRowColor),
shape: BoxShape.circle, color: PaletteDark.menuList),
child: Stack(
alignment: Alignment.center,
children: <Widget>[
Icon(Icons.add, color: Palette.violet, size: 22.0),
Icon(Icons.add, color: Colors.white, size: 22.0),
ButtonTheme(
minWidth: 28.0,
height: 28.0,
minWidth: 32.0,
height: 32.0,
child: FlatButton(
shape: CircleBorder(),
onPressed: () async {
@@ -63,20 +61,30 @@ class AddressBookPage extends BasePage {
final addressBookStore = Provider.of<AddressBookStore>(context);
return Container(
color: PaletteDark.historyPanel,
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: Observer(
builder: (_) => ListView.separated(
separatorBuilder: (_, __) => Divider(
color: Theme.of(context).dividerTheme.color,
height: 1.0,
),
separatorBuilder: (_, __) => Container(
height: 1,
padding: EdgeInsets.only(left: 24),
color: PaletteDark.menuList,
child: Container(
height: 1,
color: PaletteDark.mainBackgroundColor,
),
),
itemCount: addressBookStore.contactList == null
? 0
: addressBookStore.contactList.length,
itemBuilder: (BuildContext context, int index) {
final contact = addressBookStore.contactList[index];
final image = _getCurrencyImage(contact.type);
final content = ListTile(
final isDrawTop = index == 0 ? true : false;
final isDrawBottom = index == addressBookStore.contactList.length - 1 ? true : false;
final content = GestureDetector(
onTap: () async {
if (!isEditable) {
Navigator.of(context).pop(contact);
@@ -86,39 +94,69 @@ class AddressBookPage extends BasePage {
final isCopied = await showNameAndAddressDialog(
context, contact.name, contact.address);
if (isCopied) {
if (isCopied != null && isCopied) {
await Clipboard.setData(
ClipboardData(text: contact.address));
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text('Copied to Clipboard'),
content: Text(
S.of(context).copied_to_clipboard,
style: TextStyle(
color: Colors.white
),
),
backgroundColor: Colors.green,
duration: Duration(milliseconds: 1500),
),
);
}
},
leading: Container(
height: 25.0,
width: 48.0,
alignment: Alignment.center,
decoration: BoxDecoration(
color: _getCurrencyBackgroundColor(contact.type),
borderRadius: BorderRadius.circular(6.0),
),
child: Text(
contact.type.toString(),
style: TextStyle(
fontSize: 11.0,
color: _getCurrencyTextColor(contact.type),
child: Column(
children: <Widget>[
isDrawTop
? Container(
width: double.infinity,
height: 1,
color: PaletteDark.mainBackgroundColor,
)
: Offstage(),
Container(
width: double.infinity,
color: PaletteDark.menuList,
child: Padding(
padding: const EdgeInsets.only(left: 24, top: 16, bottom: 16, right: 24),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
image != null
? image
: Offstage(),
Padding(
padding: image != null
? EdgeInsets.only(left: 12)
: EdgeInsets.only(left: 0),
child: Text(
contact.name,
style: TextStyle(
fontSize: 14,
color: Colors.white
),
),
)
],
)
),
),
),
),
title: Text(
contact.name,
style: TextStyle(
fontSize: 16.0,
color: Theme.of(context).primaryTextTheme.title.color),
isDrawBottom
? Container(
width: double.infinity,
height: 1,
color: PaletteDark.mainBackgroundColor,
)
: Offstage(),
],
),
);
@@ -130,7 +168,7 @@ class AddressBookPage extends BasePage {
child: content,
secondaryActions: <Widget>[
IconSlideAction(
caption: 'Edit',
caption: S.of(context).edit,
color: Colors.blue,
icon: Icons.edit,
onTap: () async {
@@ -141,7 +179,7 @@ class AddressBookPage extends BasePage {
},
),
IconSlideAction(
caption: 'Delete',
caption: S.of(context).delete,
color: Colors.red,
icon: CupertinoIcons.delete,
onTap: () async {
@@ -171,95 +209,68 @@ class AddressBookPage extends BasePage {
));
}
Color _getCurrencyBackgroundColor(CryptoCurrency currency) {
Color color;
Image _getCurrencyImage(CryptoCurrency currency) {
Image image;
switch (currency) {
case CryptoCurrency.xmr:
color = Palette.cakeGreenWithOpacity;
image = Image.asset('assets/images/monero.png', height: 24, width: 24);
break;
case CryptoCurrency.ada:
color = Colors.blue[200];
image = Image.asset('assets/images/ada.png', height: 24, width: 24);
break;
case CryptoCurrency.bch:
color = Colors.orangeAccent;
image = Image.asset('assets/images/bch.png', height: 24, width: 24);
break;
case CryptoCurrency.bnb:
color = Colors.blue;
image = Image.asset('assets/images/bnb.png', height: 24, width: 24);
break;
case CryptoCurrency.btc:
color = Colors.orange;
image = Image.asset('assets/images/bitcoin.png', height: 24, width: 24);
break;
case CryptoCurrency.dash:
color = Colors.blue;
image = Image.asset('assets/images/dash.png', height: 24, width: 24);
break;
case CryptoCurrency.eos:
color = Colors.orangeAccent;
image = Image.asset('assets/images/eos.png', height: 24, width: 24);
break;
case CryptoCurrency.eth:
color = Colors.black;
image = Image.asset('assets/images/eth.png', height: 24, width: 24);
break;
case CryptoCurrency.ltc:
color = Colors.blue[200];
image = Image.asset('assets/images/litecoin.png', height: 24, width: 24);
break;
case CryptoCurrency.nano:
color = Colors.orange;
image = Image.asset('assets/images/nano.png', height: 24, width: 24);
break;
case CryptoCurrency.trx:
color = Colors.black;
image = Image.asset('assets/images/trx.png', height: 24, width: 24);
break;
case CryptoCurrency.usdt:
color = Colors.blue[200];
image = Image.asset('assets/images/usdt.png', height: 24, width: 24);
break;
case CryptoCurrency.xlm:
color = color = Colors.blue;
image = Image.asset('assets/images/xlm.png', height: 24, width: 24);
break;
case CryptoCurrency.xrp:
color = Colors.orangeAccent;
image = Image.asset('assets/images/xrp.png', height: 24, width: 24);
break;
default:
color = Colors.white;
image = null;
}
return color;
}
Color _getCurrencyTextColor(CryptoCurrency currency) {
Color color;
switch (currency) {
case CryptoCurrency.xmr:
color = Palette.cakeGreen;
break;
case CryptoCurrency.ltc:
case CryptoCurrency.ada:
case CryptoCurrency.usdt:
color = Palette.lightBlue;
break;
default:
color = Colors.white;
}
return color;
return image;
}
Future<bool> showAlertDialog(BuildContext context) async {
return await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text(
'Remove contact',
textAlign: TextAlign.center,
),
content: const Text(
'Are you sure that you want to remove selected contact?',
textAlign: TextAlign.center,
),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop( false),
child: const Text('Cancel')),
FlatButton(
onPressed: () => Navigator.of(context).pop(true),
child: const Text('Remove')),
],
return AlertWithTwoActions(
alertTitle: S.of(context).address_remove_contact,
alertContent: S.of(context).address_remove_content,
leftButtonText: S.of(context).remove,
rightButtonText: S.of(context).cancel,
actionLeftButton: () => Navigator.of(context).pop(true),
actionRightButton: () => Navigator.of(context).pop(false)
);
});
}
@@ -269,24 +280,13 @@ class AddressBookPage extends BasePage {
return await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
name,
textAlign: TextAlign.center,
style: TextStyle(fontWeight: FontWeight.bold),
),
content: Text(
address,
textAlign: TextAlign.center,
),
actions: <Widget>[
FlatButton(
onPressed: () => Navigator.of(context).pop(false),
child: Text('Cancel')),
FlatButton(
onPressed: () => Navigator.of(context).pop(true),
child: Text('Copy'))
],
return AlertWithTwoActions(
alertTitle: name,
alertContent: address,
leftButtonText: S.of(context).copy,
rightButtonText: S.of(context).cancel,
actionLeftButton: () => Navigator.of(context).pop(true),
actionRightButton: () => Navigator.of(context).pop(false)
);
});
}
+166 -184
View File
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import 'package:provider/provider.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/domain/common/crypto_currency.dart';
@@ -10,6 +11,9 @@ import 'package:cake_wallet/src/widgets/primary_button.dart';
import 'package:cake_wallet/src/widgets/address_text_field.dart';
import 'package:cake_wallet/src/widgets/scollable_with_bottom_section.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/widgets/base_text_form_field.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker.dart';
import 'package:cake_wallet/src/widgets/alert_with_one_action.dart';
class ContactPage extends BasePage {
ContactPage({this.contact});
@@ -19,6 +23,9 @@ class ContactPage extends BasePage {
@override
String get title => S.current.contact;
@override
Color get backgroundColor => PaletteDark.historyPanel;
@override
Widget body(BuildContext context) => ContactForm(contact);
}
@@ -37,22 +44,31 @@ class ContactFormState extends State<ContactForm> {
final _contactNameController = TextEditingController();
final _currencyTypeController = TextEditingController();
final _addressController = TextEditingController();
final currencies = CryptoCurrency.all;
final downArrow = Image.asset(
'assets/images/arrow_bottom_purple_icon.png',
color: PaletteDark.walletCardText,
height: 8);
CryptoCurrency _selectectCrypto = CryptoCurrency.xmr;
CryptoCurrency _selectectCrypto;
@override
void initState() {
super.initState();
if (widget.contact == null) {
_currencyTypeController.text = _selectectCrypto.toString();
} else {
if (widget.contact != null) {
_selectectCrypto = widget.contact.type;
_contactNameController.text = widget.contact.name;
_currencyTypeController.text = _selectectCrypto.toString();
_addressController.text = widget.contact.address;
WidgetsBinding.instance.addPostFrameCallback(afterLayout);
}
}
void afterLayout(dynamic _) {
final addressBookStore = Provider.of<AddressBookStore>(context);
addressBookStore.setDisabledStatus(false);
}
@override
void dispose() {
_contactNameController.dispose();
@@ -61,198 +77,164 @@ class ContactFormState extends State<ContactForm> {
super.dispose();
}
Future<void> _setCurrencyType(BuildContext context) async {
var currencyType = CryptoCurrency.all[0].toString();
var selectedCurrency = CryptoCurrency.all[0];
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(S.of(context).please_select),
backgroundColor: Theme.of(context).backgroundColor,
content: Container(
height: 150.0,
child: CupertinoPicker(
backgroundColor: Theme.of(context).backgroundColor,
itemExtent: 45.0,
onSelectedItemChanged: (int index) {
selectedCurrency = CryptoCurrency.all[index];
currencyType = CryptoCurrency.all[index].toString();
},
children:
List.generate(CryptoCurrency.all.length, (int index) {
return Center(
child: Text(
CryptoCurrency.all[index].toString(),
style: TextStyle(
color: Theme.of(context)
.primaryTextTheme
.caption
.color),
),
);
})),
),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(S.of(context).cancel)),
FlatButton(
onPressed: () {
_selectectCrypto = selectedCurrency;
_currencyTypeController.text = currencyType;
Navigator.of(context).pop();
},
child: Text(S.of(context).ok))
],
);
});
void onHandleControllers(AddressBookStore addressBookStore) {
if (_contactNameController.text.isNotEmpty &&
_addressController.text.isNotEmpty &&
_currencyTypeController.text.isNotEmpty) {
addressBookStore.setDisabledStatus(false);
} else {
addressBookStore.setDisabledStatus(true);
}
}
@override
Widget build(BuildContext context) {
final addressBookStore = Provider.of<AddressBookStore>(context);
return ScrollableWithBottomSection(
content: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
TextFormField(
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context).primaryTextTheme.headline.color),
decoration: InputDecoration(
hintStyle: TextStyle(color: Theme.of(context).hintColor),
hintText: S.of(context).contact_name,
focusedBorder: UnderlineInputBorder(
borderSide:
BorderSide(color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor, width: 1.0))),
controller: _contactNameController,
validator: (value) {
addressBookStore.validateContactName(value);
return addressBookStore.errorMessage;
},
),
SizedBox(height: 14.0),
Container(
child: InkWell(
onTap: () => _setCurrencyType(context),
child: IgnorePointer(
child: TextFormField(
style: TextStyle(
fontSize: 14.0,
color: Theme.of(context)
.primaryTextTheme
.headline
.color),
decoration: InputDecoration(
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Palette.cakeGreen, width: 2.0)),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).focusColor,
width: 1.0))),
controller: _currencyTypeController,
_contactNameController.addListener(() {onHandleControllers(addressBookStore);});
_currencyTypeController.addListener(() {onHandleControllers(addressBookStore);});
_addressController.addListener(() {onHandleControllers(addressBookStore);});
return Container(
color: PaletteDark.historyPanel,
child: ScrollableWithBottomSection(
contentPadding: EdgeInsets.all(24),
content: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
BaseTextFormField(
controller: _contactNameController,
hintText: S.of(context).contact_name,
borderColor: PaletteDark.walletCardSubAddressField,
validator: (value) {
addressBookStore.validateContactName(value);
return addressBookStore.errorMessage;
}
),
Padding(
padding: EdgeInsets.only(top: 20),
child: Container(
child: InkWell(
onTap: () => _presentPicker(context),
child: IgnorePointer(
child: BaseTextFormField(
controller: _currencyTypeController,
hintText: S.of(context).settings_currency,
borderColor: PaletteDark.walletCardSubAddressField,
suffixIcon: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
downArrow
],
),
)
),
),
),
),
Padding(
padding: EdgeInsets.only(top: 20),
child: AddressTextField(
controller: _addressController,
options: [AddressTextFieldOption.qrCode],
validator: (value) {
addressBookStore.validateAddress(value,
cryptoCurrency: _selectectCrypto);
return addressBookStore.errorMessage;
},
),
)
],
),
),
bottomSectionPadding: EdgeInsets.only(
left: 24,
right: 24,
bottom: 24
),
bottomSection: Row(
children: <Widget>[
Expanded(
child: PrimaryButton(
onPressed: () {
setState(() {
_selectectCrypto = null;
_contactNameController.text = '';
_currencyTypeController.text = '';
_addressController.text = '';
});
},
text: S.of(context).reset,
color: Colors.red,
textColor: Colors.white
),
),
SizedBox(height: 14.0),
AddressTextField(
controller: _addressController,
options: [AddressTextFieldOption.qrCode],
validator: (value) {
addressBookStore.validateAddress(value,
cryptoCurrency: _selectectCrypto);
return addressBookStore.errorMessage;
},
SizedBox(width: 20),
Expanded(
child: Observer(
builder: (_) => PrimaryButton(
onPressed: () async {
if (!_formKey.currentState.validate()) {
return;
}
try {
if (widget.contact == null) {
final newContact = Contact(
name: _contactNameController.text,
address: _addressController.text,
type: _selectectCrypto);
await addressBookStore.add(contact: newContact);
} else {
widget.contact.name = _contactNameController.text;
widget.contact.address = _addressController.text;
widget.contact
.updateCryptoCurrency(currency: _selectectCrypto);
await addressBookStore.update(
contact: widget.contact);
}
Navigator.pop(context);
} catch (e) {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertWithOneAction(
alertTitle: S.current.contact,
alertContent: e.toString(),
buttonText: S.of(context).ok,
buttonAction: () => Navigator.of(context).pop()
);
});
}
},
text: S.of(context).save,
color: Colors.green,
textColor: Colors.white,
isDisabled: addressBookStore.isDisabledStatus,
)
)
)
],
),
)),
);
}
void _presentPicker(BuildContext context) {
showDialog<void>(
builder: (_) => CurrencyPicker(
selectedAtIndex: currencies.indexOf(_selectectCrypto),
items: currencies,
title: S.of(context).please_select,
onItemSelected: (CryptoCurrency item) {
_selectectCrypto = item;
_currencyTypeController.text = _selectectCrypto.toString();
}
),
bottomSection: Row(
children: <Widget>[
Expanded(
child: PrimaryButton(
onPressed: () {
setState(() {
_selectectCrypto = CryptoCurrency.xmr;
_contactNameController.text = '';
_currencyTypeController.text =
_selectectCrypto.toString();
_addressController.text = '';
});
},
text: S.of(context).reset,
color:
Theme.of(context).accentTextTheme.button.backgroundColor,
textColor:
Theme.of(context).primaryTextTheme.button.color),
),
SizedBox(width: 20),
Expanded(
child: PrimaryButton(
onPressed: () async {
if (!_formKey.currentState.validate()) {
return;
}
try {
if (widget.contact == null) {
final newContact = Contact(
name: _contactNameController.text,
address: _addressController.text,
type: _selectectCrypto);
await addressBookStore.add(contact: newContact);
} else {
widget.contact.name = _contactNameController.text;
widget.contact.address = _addressController.text;
widget.contact
.updateCryptoCurrency(currency: _selectectCrypto);
await addressBookStore.update(
contact: widget.contact);
}
Navigator.pop(context);
} catch (e) {
await showDialog<void>(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
e.toString(),
textAlign: TextAlign.center,
),
actions: <Widget>[
FlatButton(
onPressed: () =>
Navigator.of(context).pop(),
child: Text(S.of(context).ok))
],
);
});
}
},
text: S.of(context).save,
color: Theme.of(context)
.primaryTextTheme
.button
.backgroundColor,
textColor: Theme.of(context)
.primaryTextTheme
.button
.color))
],
));
context: context);
}
}
@@ -12,11 +12,15 @@ class AddressBookStore = AddressBookStoreBase with _$AddressBookStore;
abstract class AddressBookStoreBase with Store {
AddressBookStoreBase({@required this.contacts}) {
updateContactList();
isDisabledStatus = true;
}
@observable
List<Contact> contactList;
@observable
bool isDisabledStatus;
@observable
bool isValid;
@@ -37,6 +41,11 @@ abstract class AddressBookStoreBase with Store {
@action
Future delete({Contact contact}) async => await contact.delete();
@action
void setDisabledStatus(bool isDisabled) {
isDisabledStatus = isDisabled;
}
void validateContactName(String value) {
const pattern = '''^[^`,'"]{1,32}\$''';
final regExp = RegExp(pattern);
@@ -17,6 +17,7 @@ class BaseTextFormField extends StatelessWidget {
this.borderColor = PaletteDark.menuList,
this.prefix,
this.suffix,
this.suffixIcon,
this.enabled = true,
this.validator
});
@@ -34,6 +35,7 @@ class BaseTextFormField extends StatelessWidget {
final Color borderColor;
final Widget prefix;
final Widget suffix;
final Widget suffixIcon;
final bool enabled;
final FormFieldValidator<String> validator;
@@ -55,6 +57,7 @@ class BaseTextFormField extends StatelessWidget {
decoration: InputDecoration(
prefix: prefix,
suffix: suffix,
suffixIcon: suffixIcon,
hintStyle: TextStyle(
color: hintColor,
fontSize: 16
+54 -46
View File
@@ -13,7 +13,6 @@ import 'package:cake_wallet/src/domain/monero/mnemonics/russian.dart';
import 'package:cake_wallet/src/domain/monero/mnemonics/spanish.dart';
import 'package:cake_wallet/src/domain/common/mnemotic_item.dart';
import 'package:cake_wallet/generated/i18n.dart';
import 'package:cake_wallet/src/widgets/top_panel.dart';
class SeedWidget extends StatefulWidget {
SeedWidget({Key key, this.onMnemoticChange, this.onFinish, this.seedLanguage}) : super(key: key) {
@@ -237,52 +236,61 @@ class SeedWidgetState extends State<SeedWidget> {
Flexible(
fit: FlexFit.tight,
flex: 1,
child: TopPanel(
color: PaletteDark.menuList,
widget: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).restore_active_seed,
style: TextStyle(
fontSize: 14,
color: PaletteDark.walletCardText
),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Wrap(
children: items.map((item) {
final isValid = item.isCorrect();
final isSelected = selectedItem == item;
return InkWell(
onTap: () => onMnemoticTap(item),
child: Container(
decoration: BoxDecoration(
color: isValid ? Colors.transparent : Palette.red),
margin: EdgeInsets.only(right: 7, bottom: 8),
child: Text(
item.toString(),
style: TextStyle(
color:
isValid ? Colors.white : Palette.lightGrey,
fontSize: 16,
fontWeight:
isSelected ? FontWeight.w900 : FontWeight.w400,
decoration: isSelected
? TextDecoration.underline
: TextDecoration.none),
)),
);
}).toList(),)
)
],
child: Container(
width: double.infinity,
height: double.infinity,
padding: EdgeInsets.all(24),
decoration: BoxDecoration(
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(24),
bottomRight: Radius.circular(24)
),
)
)
color: PaletteDark.menuList
),
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
S.of(context).restore_active_seed,
style: TextStyle(
fontSize: 14,
color: PaletteDark.walletCardText
),
),
Padding(
padding: EdgeInsets.only(top: 5),
child: Wrap(
children: items.map((item) {
final isValid = item.isCorrect();
final isSelected = selectedItem == item;
return InkWell(
onTap: () => onMnemoticTap(item),
child: Container(
decoration: BoxDecoration(
color: isValid ? Colors.transparent : Palette.red),
margin: EdgeInsets.only(right: 7, bottom: 8),
child: Text(
item.toString(),
style: TextStyle(
color:
isValid ? Colors.white : Palette.lightGrey,
fontSize: 16,
fontWeight:
isSelected ? FontWeight.w900 : FontWeight.w400,
decoration: isSelected
? TextDecoration.underline
: TextDecoration.none),
)),
);
}).toList(),)
)
],
),
),
),
),
Flexible(
fit: FlexFit.tight,