CWA-219 | created clear button and fiat currency picker; changed clear button in the send page; fiat and crypto balance aligned to baseline on wallet card; added placeholders to trade_history_panel and address_book_page; keyboard doesn't appear when buttons in the address text field are pressed

This commit is contained in:
Oleksandr Sobol
2020-06-02 21:03:05 +03:00
parent d9e0c260d4
commit bc88fa9758
22 changed files with 523 additions and 286 deletions
@@ -64,148 +64,163 @@ class AddressBookPage extends BasePage {
color: Theme.of(context).backgroundColor,
padding: EdgeInsets.only(top: 20.0, bottom: 20.0),
child: Observer(
builder: (_) => ListView.separated(
separatorBuilder: (_, __) => Container(
height: 1,
padding: EdgeInsets.only(left: 24),
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: Container(
builder: (_) {
final count = addressBookStore.contactList == null
? 0
: addressBookStore.contactList.length;
return count > 0
? ListView.separated(
separatorBuilder: (_, __) => Container(
height: 1,
color: Theme.of(context).dividerColor,
padding: EdgeInsets.only(left: 24),
color: Theme.of(context).accentTextTheme.title.backgroundColor,
child: Container(
height: 1,
color: Theme.of(context).dividerColor,
),
),
itemCount: count,
itemBuilder: (BuildContext context, int index) {
final contact = addressBookStore.contactList[index];
final image = _getCurrencyImage(contact.type);
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);
return;
}
final isCopied = await showNameAndAddressDialog(
context, contact.name, contact.address);
if (isCopied != null && isCopied) {
await Clipboard.setData(
ClipboardData(text: contact.address));
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(
S.of(context).copied_to_clipboard,
style: TextStyle(
color: Colors.white
),
),
backgroundColor: Colors.green,
duration: Duration(milliseconds: 1500),
),
);
}
},
child: Column(
children: <Widget>[
isDrawTop
? Container(
width: double.infinity,
height: 1,
color: Theme.of(context).dividerColor,
)
: Offstage(),
Container(
width: double.infinity,
color: Theme.of(context).accentTextTheme.title.backgroundColor,
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: Theme.of(context).primaryTextTheme.title.color
),
),
)
],
)
),
),
isDrawBottom
? Container(
width: double.infinity,
height: 1,
color: Theme.of(context).dividerColor,
)
: Offstage(),
],
),
);
return !isEditable
? content
: Slidable(
key: Key('${contact.key}'),
actionPane: SlidableDrawerActionPane(),
child: content,
secondaryActions: <Widget>[
IconSlideAction(
caption: S.of(context).edit,
color: Colors.blue,
icon: Icons.edit,
onTap: () async {
await Navigator.of(context).pushNamed(
Routes.addressBookAddContact,
arguments: contact);
await addressBookStore.updateContactList();
},
),
IconSlideAction(
caption: S.of(context).delete,
color: Colors.red,
icon: CupertinoIcons.delete,
onTap: () async {
await showAlertDialog(context)
.then((isDelete) async {
if (isDelete != null && isDelete) {
await addressBookStore.delete(
contact: contact);
await addressBookStore.updateContactList();
}
});
},
),
],
dismissal: SlidableDismissal(
child: SlidableDrawerDismissal(),
onDismissed: (actionType) async {
await addressBookStore.delete(contact: contact);
await addressBookStore.updateContactList();
},
onWillDismiss: (actionType) async {
return await showAlertDialog(context);
},
),
);
})
: Center(
child: Text(
S.of(context).placeholder_contacts,
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color.withOpacity(0.5),
fontSize: 12
),
),
itemCount: addressBookStore.contactList == null
? 0
: addressBookStore.contactList.length,
itemBuilder: (BuildContext context, int index) {
final contact = addressBookStore.contactList[index];
final image = _getCurrencyImage(contact.type);
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);
return;
}
final isCopied = await showNameAndAddressDialog(
context, contact.name, contact.address);
if (isCopied != null && isCopied) {
await Clipboard.setData(
ClipboardData(text: contact.address));
Scaffold.of(context).showSnackBar(
SnackBar(
content: Text(
S.of(context).copied_to_clipboard,
style: TextStyle(
color: Colors.white
),
),
backgroundColor: Colors.green,
duration: Duration(milliseconds: 1500),
),
);
}
},
child: Column(
children: <Widget>[
isDrawTop
? Container(
width: double.infinity,
height: 1,
color: Theme.of(context).dividerColor,
)
: Offstage(),
Container(
width: double.infinity,
color: Theme.of(context).accentTextTheme.title.backgroundColor,
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: Theme.of(context).primaryTextTheme.title.color
),
),
)
],
)
),
),
isDrawBottom
? Container(
width: double.infinity,
height: 1,
color: Theme.of(context).dividerColor,
)
: Offstage(),
],
),
);
return !isEditable
? content
: Slidable(
key: Key('${contact.key}'),
actionPane: SlidableDrawerActionPane(),
child: content,
secondaryActions: <Widget>[
IconSlideAction(
caption: S.of(context).edit,
color: Colors.blue,
icon: Icons.edit,
onTap: () async {
await Navigator.of(context).pushNamed(
Routes.addressBookAddContact,
arguments: contact);
await addressBookStore.updateContactList();
},
),
IconSlideAction(
caption: S.of(context).delete,
color: Colors.red,
icon: CupertinoIcons.delete,
onTap: () async {
await showAlertDialog(context)
.then((isDelete) async {
if (isDelete != null && isDelete) {
await addressBookStore.delete(
contact: contact);
await addressBookStore.updateContactList();
}
});
},
),
],
dismissal: SlidableDismissal(
child: SlidableDrawerDismissal(),
onDismissed: (actionType) async {
await addressBookStore.delete(contact: contact);
await addressBookStore.updateContactList();
},
onWillDismiss: (actionType) async {
return await showAlertDialog(context);
},
),
);
}),
);
},
));
}
@@ -14,6 +14,7 @@ import 'date_section_raw.dart';
import 'trade_row.dart';
import 'transaction_raw.dart';
import 'button_header.dart';
import 'package:cake_wallet/generated/i18n.dart';
class TradeHistoryPanel extends StatefulWidget {
@override
@@ -88,6 +89,18 @@ class TradeHistoryPanelState extends State<TradeHistoryPanel> {
height: freeSpaceHeight,
width: MediaQuery.of(context).size.width,
color: Theme.of(context).backgroundColor,
child: itemsCount > 1
? Offstage()
: Center(
child: Text(
S.of(context).placeholder_transactions,
textAlign: TextAlign.center,
style: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color.withOpacity(0.5),
fontSize: 12
),
),
),
);
}
@@ -279,6 +279,7 @@ class WalletCardState extends State<WalletCard> {
balance,
style: TextStyle(
fontSize: 28,
height: 1,
color: Theme.of(context).primaryTextTheme.title.color
),
)
@@ -288,6 +289,7 @@ class WalletCardState extends State<WalletCard> {
fiatBalance,
style: TextStyle(
fontSize: 14,
height: 2,
color: Theme.of(context).primaryTextTheme.title.color
),
)
+3 -14
View File
@@ -10,6 +10,7 @@ import 'package:cake_wallet/src/screens/base_page.dart';
import 'package:cake_wallet/src/stores/exchange_template/exchange_template_store.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/present_provider_picker.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/base_exchange_widget.dart';
import 'package:cake_wallet/src/widgets/clear_button.dart';
class ExchangePage extends BasePage {
@override
@@ -32,20 +33,8 @@ class ExchangePage extends BasePage {
Widget trailing(BuildContext context) {
final exchangeStore = Provider.of<ExchangeStore>(context);
return ButtonTheme(
minWidth: double.minPositive,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: FlatButton(
padding: EdgeInsets.all(0),
child: Text(
S.of(context).clear,
style: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontWeight: FontWeight.w500,
fontSize: 14),
),
onPressed: () => exchangeStore.reset()),
return ClearButton(
onPressed: () => exchangeStore.reset()
);
}
+3 -19
View File
@@ -29,6 +29,7 @@ import 'package:cake_wallet/src/screens/send/widgets/confirm_sending_alert.dart'
import 'package:cake_wallet/src/screens/send/widgets/sending_alert.dart';
import 'package:cake_wallet/src/widgets/template_tile.dart';
import 'package:cake_wallet/src/stores/send_template/send_template_store.dart';
import 'package:cake_wallet/src/widgets/clear_button.dart';
class SendPage extends BasePage {
@override
@@ -47,25 +48,8 @@ class SendPage extends BasePage {
Widget trailing(context) {
final sendStore = Provider.of<SendStore>(context);
return Container(
height: 32,
width: 82,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(16)),
color: Theme.of(context).accentTextTheme.title.color
),
child: ButtonTheme(
minWidth: double.minPositive,
child: FlatButton(
onPressed: () => sendStore.clear(),
child: Text(
S.of(context).clear,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 10.0,
color: Colors.blue),
)),
),
return ClearButton(
onPressed: () => sendStore.clear()
);
}
+8 -7
View File
@@ -24,6 +24,7 @@ import 'package:cake_wallet/src/screens/settings/widgets/settings_link_list_row.
import 'package:cake_wallet/src/screens/settings/widgets/settings_switch_list_row.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_text_list_row.dart';
import 'package:cake_wallet/src/screens/settings/widgets/settings_raw_widget_list_row.dart';
import 'package:cake_wallet/src/screens/settings/widgets/fiat_currency_picker.dart';
class SettingsPage extends BasePage {
@override
@@ -409,13 +410,13 @@ class SettingsFormState extends State<SettingsForm> {
final selectedItem = items.indexOf(settingsStore.fiatCurrency);
await showDialog<void>(
builder: (_) => Picker(
items: items,
selectedAtIndex: selectedItem,
title: S.of(context).please_select,
mainAxisAlignment: MainAxisAlignment.center,
onItemSelected: (FiatCurrency currency) async =>
await settingsStore.setCurrentFiatCurrency(currency: currency)),
builder: (_) => FiatCurrencyPicker(
selectedAtIndex: selectedItem,
items: items,
title: S.of(context).please_select,
onItemSelected: (currency) async =>
await settingsStore.setCurrentFiatCurrency(currency: currency)
),
context: context);
}
@@ -0,0 +1,111 @@
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/domain/common/fiat_currency.dart';
class FiatCurrencyPicker extends StatelessWidget {
FiatCurrencyPicker({
@required this.selectedAtIndex,
@required this.items,
@required this.title,
@required this.onItemSelected,
});
final int selectedAtIndex;
final List<FiatCurrency> items;
final String title;
final Function(FiatCurrency) onItemSelected;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => Navigator.of(context).pop(),
child: Container(
color: Colors.transparent,
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 3.0, sigmaY: 3.0),
child: Container(
decoration: BoxDecoration(color: PaletteDark.darkNightBlue.withOpacity(0.75)),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
padding: EdgeInsets.only(left: 24, right: 24),
child: Text(
title,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: Colors.white
),
),
),
Padding(
padding: EdgeInsets.only(top: 24),
child: GestureDetector(
onTap: () => null,
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(14)),
child: Container(
height: 400,
width: 300,
color: Theme.of(context).dividerColor,
child: GridView.count(
crossAxisCount: 3,
childAspectRatio: 1.25,
crossAxisSpacing: 1,
mainAxisSpacing: 1,
children: List.generate(items.length, (index) {
final item = items[index];
final isItemSelected = index == selectedAtIndex;
final color = isItemSelected
? Theme.of(context).accentTextTheme.subtitle.decorationColor
: Theme.of(context).primaryTextTheme.display1.color;
final textColor = isItemSelected
? Colors.blue
: Theme.of(context).primaryTextTheme.title.color;
return GestureDetector(
onTap: () {
if (onItemSelected == null) {
return;
}
Navigator.of(context).pop();
onItemSelected(item);
},
child: Container(
color: color,
child: Center(
child: Text(
item.toString(),
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
decoration: TextDecoration.none,
color: textColor
),
),
),
),
);
})
),
),
),
),
)
],
),
)
),
),
),
);
}
}
+107 -95
View File
@@ -38,103 +38,115 @@ class AddressTextField extends StatelessWidget {
@override
Widget build(BuildContext context) {
return TextFormField(
onFieldSubmitted: (_) => FocusScope.of(context).unfocus(),
enabled: isActive,
controller: controller,
focusNode: focusNode,
style: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.title.color
),
decoration: InputDecoration(
suffixIcon: SizedBox(
width: prefixIconWidth * options.length +
(spaceBetweenPrefixIcons * options.length),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 5),
if (this.options.contains(AddressTextFieldOption.qrCode)) ...[
Container(
width: prefixIconWidth,
height: prefixIconHeight,
padding: EdgeInsets.only(top: 0),
child: InkWell(
onTap: () async => _presentQRScanner(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset('assets/images/qr_code_icon.png')),
))
],
if (this
.options
.contains(AddressTextFieldOption.addressBook)) ...[
Container(
width: prefixIconWidth,
height: prefixIconHeight,
padding: EdgeInsets.only(top: 0),
child: InkWell(
onTap: () async => _presetAddressBookPicker(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/open_book.png')),
))
],
if (this
.options
.contains(AddressTextFieldOption.subaddressList)) ...[
Container(
width: prefixIconWidth,
height: prefixIconHeight,
padding: EdgeInsets.only(top: 0),
child: InkWell(
onTap: () async => _presetSubaddressListPicker(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/receive_icon_raw.png')),
))
],
],
return Stack(
children: <Widget>[
TextFormField(
onFieldSubmitted: (_) => FocusScope.of(context).unfocus(),
enabled: isActive,
controller: controller,
focusNode: focusNode,
style: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.title.color
),
),
hintStyle: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.caption.color
),
hintText: placeholder ?? S.current.widgets_address,
focusedBorder: isBorderExist
? UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))
: InputBorder.none,
disabledBorder: isBorderExist
? UnderlineInputBorder(
borderSide:
BorderSide(color: Theme.of(context).dividerColor, width: 1.0))
: InputBorder.none,
enabledBorder: isBorderExist
? UnderlineInputBorder(
borderSide:
decoration: InputDecoration(
suffixIcon: SizedBox(
width: prefixIconWidth * options.length +
(spaceBetweenPrefixIcons * options.length),
),
hintStyle: TextStyle(
fontSize: 16,
color: Theme.of(context).primaryTextTheme.caption.color
),
hintText: placeholder ?? S.current.widgets_address,
focusedBorder: isBorderExist
? UnderlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).dividerColor,
width: 1.0))
: InputBorder.none,
disabledBorder: isBorderExist
? UnderlineInputBorder(
borderSide:
BorderSide(color: Theme.of(context).dividerColor, width: 1.0))
: InputBorder.none,
),
validator: validator,
: InputBorder.none,
enabledBorder: isBorderExist
? UnderlineInputBorder(
borderSide:
BorderSide(color: Theme.of(context).dividerColor, width: 1.0))
: InputBorder.none,
),
validator: validator,
),
Positioned(
bottom: 10,
right: 0,
child: SizedBox(
width: prefixIconWidth * options.length +
(spaceBetweenPrefixIcons * options.length),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 5),
if (this.options.contains(AddressTextFieldOption.qrCode)) ...[
Container(
width: prefixIconWidth,
height: prefixIconHeight,
padding: EdgeInsets.only(top: 0),
child: InkWell(
onTap: () async => _presentQRScanner(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset('assets/images/qr_code_icon.png')),
))
],
if (this
.options
.contains(AddressTextFieldOption.addressBook)) ...[
Container(
width: prefixIconWidth,
height: prefixIconHeight,
padding: EdgeInsets.only(top: 0),
child: InkWell(
onTap: () async => _presetAddressBookPicker(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/open_book.png')),
))
],
if (this
.options
.contains(AddressTextFieldOption.subaddressList)) ...[
Container(
width: prefixIconWidth,
height: prefixIconHeight,
padding: EdgeInsets.only(top: 0),
child: InkWell(
onTap: () async => _presetSubaddressListPicker(context),
child: Container(
padding: EdgeInsets.all(8),
decoration: BoxDecoration(
color: buttonColor ?? Theme.of(context).accentTextTheme.title.color,
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Image.asset(
'assets/images/receive_icon_raw.png')),
))
],
],
),
)
)
],
);
}
+28
View File
@@ -0,0 +1,28 @@
import 'package:flutter/material.dart';
import 'package:cake_wallet/generated/i18n.dart';
class ClearButton extends StatelessWidget {
ClearButton({@required this.onPressed});
final VoidCallback onPressed;
@override
Widget build(BuildContext context) {
return ButtonTheme(
minWidth: double.minPositive,
highlightColor: Colors.transparent,
splashColor: Colors.transparent,
child: FlatButton(
padding: EdgeInsets.all(0),
child: Text(
S.of(context).clear,
style: TextStyle(
color: Theme.of(context).primaryTextTheme.caption.color,
fontWeight: FontWeight.w500,
fontSize: 14),
),
onPressed: onPressed),
);
}
}