From 3bd9301be1c4b906d88b973bec00943584514716 Mon Sep 17 00:00:00 2001 From: Omar Hatem Date: Fri, 1 Jul 2022 12:17:09 +0200 Subject: [PATCH] Redesign exchange currency picker (#402) --- lib/src/screens/contact/contact_page.dart | 1 + .../exchange/widgets/currency_picker.dart | 229 ++++++++---------- .../widgets/currency_picker_item_widget.dart | 113 ++++----- .../widgets/currency_picker_widget.dart | 65 +++-- .../exchange/widgets/exchange_card.dart | 2 +- lib/src/widgets/alert_close_button.dart | 10 +- lib/src/widgets/picker.dart | 145 ++++++----- 7 files changed, 259 insertions(+), 306 deletions(-) diff --git a/lib/src/screens/contact/contact_page.dart b/lib/src/screens/contact/contact_page.dart index 6ac236352..234cdb673 100644 --- a/lib/src/screens/contact/contact_page.dart +++ b/lib/src/screens/contact/contact_page.dart @@ -150,6 +150,7 @@ class ContactPage extends BasePage { contactViewModel.currencies.indexOf(contactViewModel.currency), items: contactViewModel.currencies, title: S.of(context).please_select, + hintText: S.of(context).search_currency, onItemSelected: (CryptoCurrency item) => contactViewModel.currency = item), context: context); diff --git a/lib/src/screens/exchange/widgets/currency_picker.dart b/lib/src/screens/exchange/widgets/currency_picker.dart index 4cbda9b99..5e4a31831 100644 --- a/lib/src/screens/exchange/widgets/currency_picker.dart +++ b/lib/src/screens/exchange/widgets/currency_picker.dart @@ -1,8 +1,8 @@ import 'dart:ui'; -import 'package:cake_wallet/generated/i18n.dart'; -import 'package:cake_wallet/palette.dart'; +import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker_item_widget.dart'; import 'package:cake_wallet/src/screens/exchange/widgets/currency_utils.dart'; import 'package:cake_wallet/src/screens/exchange/widgets/picker_item.dart'; +import 'package:cake_wallet/src/widgets/alert_close_button.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cw_core/crypto_currency.dart'; @@ -13,8 +13,9 @@ class CurrencyPicker extends StatefulWidget { CurrencyPicker( {@required this.selectedAtIndex, @required this.items, - @required this.title, @required this.onItemSelected, + this.title, + this.hintText, this.isMoneroWallet = false, this.isConvertFrom = false}); @@ -24,6 +25,7 @@ class CurrencyPicker extends StatefulWidget { final Function(CryptoCurrency) onItemSelected; final bool isMoneroWallet; final bool isConvertFrom; + final String hintText; @override CurrencyPickerState createState() => CurrencyPickerState(items); @@ -34,11 +36,8 @@ class CurrencyPickerState extends State { : isSearchBarActive = false, textFieldValue = '', subPickerItemsList = [], - appBarTextStyle = TextStyle( - fontSize: 20, - fontFamily: 'Lato', - backgroundColor: Colors.transparent, - color: Colors.white); + appBarTextStyle = + TextStyle(fontSize: 20, fontFamily: 'Lato', backgroundColor: Colors.transparent, color: Colors.white); @override void initState() { @@ -61,13 +60,10 @@ class CurrencyPickerState extends State { TextStyle appBarTextStyle; void cleanSubPickerItemsList() { - subPickerItemsList = pickerItemsList - .where((element) => items.contains(element.original)) - .toList(); + subPickerItemsList = pickerItemsList.where((element) => items.contains(element.original)).toList(); } - void currencySearchBySubstring( - String subString, List> list) { + void currencySearchBySubstring(String subString, List> list) { setState(() { if (subString.isNotEmpty) { subPickerItemsList = subPickerItemsList @@ -84,118 +80,104 @@ class CurrencyPickerState extends State { @override Widget build(BuildContext context) { return AlertBackground( - child: SafeArea( - child: Scaffold( - resizeToAvoidBottomInset: false, - backgroundColor: Colors.transparent, - body: Column( - children: [ - Padding( - padding: EdgeInsets.symmetric(horizontal: 26.0, vertical: 0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - isSearchBarActive - ? Expanded( - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - InkWell( - child: Text( - S.of(context).cancel, - style: appBarTextStyle, - ), - onTap: () { - setState(() { - isSearchBarActive = false; - textFieldValue = ''; - cleanSubPickerItemsList(); - }); - }), - Container( - width: 100.0, - child: CupertinoTextField( - autofocus: true, - placeholder: - S.of(context).search + '...', - placeholderStyle: appBarTextStyle, - decoration: BoxDecoration( - color: Colors.transparent), - cursorColor: Colors.white, - cursorHeight: 23.0, - style: appBarTextStyle, - onChanged: (value) { - this.textFieldValue = value; - cleanSubPickerItemsList(); - currencySearchBySubstring( - textFieldValue, - subPickerItemsList); - }), - ) - ], - ), - ) - : Text( - widget.title, - style: appBarTextStyle, - ), - IconButton( - splashRadius: 23, - icon: Icon(Icons.search, color: Colors.white), - onPressed: () { - setState(() { - isSearchBarActive = true; - }); - }, - ) - ]), - ), - Expanded( - flex: 12, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 26.0, vertical: 26.0), - child: Container( - child: CurrencyPickerWidget( - crossAxisCount: 2, - selectedAtIndex: widget.selectedAtIndex, - itemsCount: subPickerItemsList.length, - pickerItemsList: subPickerItemsList, - pickListItem: (int index) { - setState(() { - widget.selectedAtIndex = index; - }); - widget - .onItemSelected(subPickerItemsList[index].original); - if (widget.isConvertFrom && - !widget.isMoneroWallet && - (subPickerItemsList[index].original == - CryptoCurrency.xmr)) { - } else { - Navigator.of(context).pop(); - } - }, + child: Stack( + alignment: Alignment.center, + children: [ + Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.title?.isNotEmpty ?? false) + Container( + padding: EdgeInsets.symmetric(horizontal: 24), + child: Text( + widget.title, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 18, + fontFamily: 'Lato', + fontWeight: FontWeight.bold, + decoration: TextDecoration.none, + color: Colors.white, ), ), ), - ), - Expanded( - flex: 2, - child: Container( - width: 42.0, - alignment: Alignment.topCenter, - child: FittedBox( - child: FloatingActionButton( - elevation: 0, - backgroundColor: Colors.white, - onPressed: () { - Navigator.of(context).pop(); - }, - child: Icon( - Icons.close_outlined, - color: Palette.darkBlueCraiola, - size: 30.0, + Padding( + padding: EdgeInsets.only(left: 24, right: 24, top: 24), + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(30)), + child: Container( + color: Theme.of(context).accentTextTheme.title.color, + child: ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height * 0.65, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.hintText != null) + Padding( + padding: const EdgeInsets.all(16), + child: TextFormField( + style: TextStyle(color: Theme.of(context).primaryTextTheme.title.color), + decoration: InputDecoration( + hintText: widget.hintText, + prefixIcon: Image.asset("assets/images/search_icon.png"), + filled: true, + fillColor: const Color(0xffF2F0FA), + alignLabelWithHint: false, + contentPadding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: const BorderSide( + color: Colors.transparent, + )), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: const BorderSide( + color: Colors.transparent, + )), + ), + onChanged: (value) { + this.textFieldValue = value; + cleanSubPickerItemsList(); + currencySearchBySubstring(textFieldValue, subPickerItemsList); + }, + ), + ), + Divider( + color: Theme.of(context).accentTextTheme.title.backgroundColor, + height: 1, + ), + if (widget.selectedAtIndex != -1) + AspectRatio( + aspectRatio: 6, + child: PickerItemWidget( + title: pickerItemsList[widget.selectedAtIndex].title, + iconPath: pickerItemsList[widget.selectedAtIndex].iconPath, + isSelected: true, + tag: pickerItemsList[widget.selectedAtIndex].tag, + ), + ), + Flexible( + child: CurrencyPickerWidget( + crossAxisCount: 2, + selectedAtIndex: widget.selectedAtIndex, + pickerItemsList: subPickerItemsList, + pickListItem: (int index) { + setState(() { + widget.selectedAtIndex = index; + }); + widget.onItemSelected(subPickerItemsList[index].original); + if (widget.isConvertFrom && + !widget.isMoneroWallet && + (subPickerItemsList[index].original == CryptoCurrency.xmr)) { + } else { + Navigator.of(context).pop(); + } + }, + ), + ), + ], ), ), ), @@ -203,7 +185,8 @@ class CurrencyPickerState extends State { ), ], ), - ), + AlertCloseButton(), + ], ), ); } diff --git a/lib/src/screens/exchange/widgets/currency_picker_item_widget.dart b/lib/src/screens/exchange/widgets/currency_picker_item_widget.dart index d38c5bd45..b710494fd 100644 --- a/lib/src/screens/exchange/widgets/currency_picker_item_widget.dart +++ b/lib/src/screens/exchange/widgets/currency_picker_item_widget.dart @@ -2,8 +2,7 @@ import 'package:flutter/material.dart'; import 'package:cake_wallet/palette.dart'; class PickerItemWidget extends StatelessWidget { - const PickerItemWidget( - {this.iconPath, this.title, this.isSelected, this.tag, this.onTap}); + const PickerItemWidget({this.iconPath, this.title, this.isSelected = false, this.tag, this.onTap}); final String iconPath; final String title; @@ -16,74 +15,56 @@ class PickerItemWidget extends StatelessWidget { return GestureDetector( onTap: onTap, child: Container( - color: isSelected - ? Theme.of(context).textTheme.bodyText1.color - : Theme.of(context).accentTextTheme.headline6.color, - child: Center( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: Container( - child: Image.asset( - iconPath, - height: 32.0, - width: 32.0, - ), - ), + color: Theme.of(context).accentTextTheme.headline6.color, + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 24), + child: Row( + children: [ + Container( + child: Image.asset( + iconPath, + height: 20.0, + width: 20.0, ), - Expanded( - child: Stack( - clipBehavior: Clip.none, - alignment: Alignment.centerLeft, - children: [ - Text( - title, - style: TextStyle( - color: isSelected - ? Palette.blueCraiola - : Theme.of(context).primaryTextTheme.title.color, - fontSize: 18.0, - fontFamily: 'Lato', + ), + const SizedBox(width: 6), + Expanded( + child: Row( + children: [ + Text( + title, + style: TextStyle( + color: isSelected ? Palette.blueCraiola : Theme.of(context).primaryTextTheme.title.color, + fontSize: isSelected ? 16 : 14.0, + fontFamily: 'Lato', + fontWeight: FontWeight.w600, + ), + ), + if (tag != null) + Align( + alignment: Alignment.topCenter, + child: Container( + width: 35.0, + height: 18.0, + child: Center( + child: Text( + tag, + style: TextStyle( + fontSize: 7.0, fontFamily: 'Lato', color: Theme.of(context).textTheme.body1.color), + ), + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(6.0), + //border: Border.all(color: ), + color: Theme.of(context).textTheme.body1.decorationColor, + ), ), ), - tag != null - ? Positioned( - top: -20.0, - right: 7.0, - child: Container( - width: 35.0, - height: 18.0, - child: Center( - child: Text( - tag, - style: TextStyle( - fontSize: 7.0, - fontFamily: 'Lato', - color: Theme.of(context) - .textTheme - .body1 - .color), - ), - ), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(6.0), - //border: Border.all(color: ), - color: Theme.of(context) - .textTheme - .body1 - .decorationColor, - ), - ), - ) - : Container(), - ], - ), + ], ), - ], - ), + ), + if (isSelected) Icon(Icons.check_circle, color: Theme.of(context).accentTextTheme.body2.color) + ], ), ), ), diff --git a/lib/src/screens/exchange/widgets/currency_picker_widget.dart b/lib/src/screens/exchange/widgets/currency_picker_widget.dart index 46029a44a..ec0a11356 100644 --- a/lib/src/screens/exchange/widgets/currency_picker_widget.dart +++ b/lib/src/screens/exchange/widgets/currency_picker_widget.dart @@ -4,58 +4,47 @@ import 'picker_item.dart'; import 'currency_picker_item_widget.dart'; class CurrencyPickerWidget extends StatelessWidget { - const CurrencyPickerWidget({ + CurrencyPickerWidget({ @required this.crossAxisCount, @required this.selectedAtIndex, - @required this.itemsCount, @required this.pickerItemsList, @required this.pickListItem, }); final int crossAxisCount; final int selectedAtIndex; - final int itemsCount; final Function pickListItem; final List> pickerItemsList; + final ScrollController _scrollController = ScrollController(); + @override Widget build(BuildContext context) { - return LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - return Container( - decoration: BoxDecoration( - color: Theme.of(context).accentTextTheme.headline6.backgroundColor, - borderRadius: BorderRadius.circular(14.0), - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(14.0), - child: Scrollbar( - showTrackOnHover: true, - isAlwaysShown: true, - thickness: 6.0, - radius: Radius.circular(3), - child: GridView.builder( - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: crossAxisCount, - crossAxisSpacing: 1, - mainAxisExtent: constraints.maxHeight / 8, - mainAxisSpacing: 1), - itemCount: pickerItemsList.length, - itemBuilder: (BuildContext ctx, index) { - return PickerItemWidget( - onTap: () { - pickListItem(index); - }, - title: pickerItemsList[index].title, - iconPath: pickerItemsList[index].iconPath, - isSelected: index == selectedAtIndex, - tag: pickerItemsList[index].tag, - ); - }), + return Container( + color: Theme.of(context).accentTextTheme.headline6.backgroundColor, + child: Scrollbar( + controller: _scrollController, + child: GridView.builder( + controller: _scrollController, + padding: EdgeInsets.zero, + shrinkWrap: true, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: crossAxisCount, + crossAxisSpacing: 2, + childAspectRatio: 3, ), - ), - ); - }, + itemCount: pickerItemsList.length, + itemBuilder: (BuildContext ctx, index) { + return PickerItemWidget( + onTap: () { + pickListItem(index); + }, + title: pickerItemsList[index].title, + iconPath: pickerItemsList[index].iconPath, + tag: pickerItemsList[index].tag, + ); + }), + ), ); } } diff --git a/lib/src/screens/exchange/widgets/exchange_card.dart b/lib/src/screens/exchange/widgets/exchange_card.dart index ec57323ac..384fc8d23 100644 --- a/lib/src/screens/exchange/widgets/exchange_card.dart +++ b/lib/src/screens/exchange/widgets/exchange_card.dart @@ -445,7 +445,7 @@ class ExchangeCardState extends State { builder: (_) => CurrencyPicker( selectedAtIndex: widget.currencies.indexOf(_selectedCurrency), items: widget.currencies, - title: S.of(context).change_currency, + hintText: S.of(context).search_currency, isMoneroWallet: _isMoneroWallet, isConvertFrom: widget.hasRefundAddress, onItemSelected: (CryptoCurrency item) => diff --git a/lib/src/widgets/alert_close_button.dart b/lib/src/widgets/alert_close_button.dart index 9349eb5b7..e1b6863be 100644 --- a/lib/src/widgets/alert_close_button.dart +++ b/lib/src/widgets/alert_close_button.dart @@ -1,10 +1,16 @@ +import 'package:cake_wallet/palette.dart'; import 'package:flutter/material.dart'; class AlertCloseButton extends StatelessWidget { - AlertCloseButton({@required this.image}); + AlertCloseButton({this.image}); final Image image; + final closeButton = Image.asset( + 'assets/images/close.png', + color: Palette.darkBlueCraiola, + ); + @override Widget build(BuildContext context) { return Positioned( @@ -19,7 +25,7 @@ class AlertCloseButton extends StatelessWidget { shape: BoxShape.circle ), child: Center( - child: image, + child: image ?? closeButton, ), ), ) diff --git a/lib/src/widgets/picker.dart b/lib/src/widgets/picker.dart index b5cad676b..0029c8191 100644 --- a/lib/src/widgets/picker.dart +++ b/lib/src/widgets/picker.dart @@ -3,7 +3,6 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:cake_wallet/src/widgets/alert_background.dart'; import 'package:cake_wallet/src/widgets/alert_close_button.dart'; -import 'package:cake_wallet/palette.dart'; class Picker extends StatefulWidget { Picker({ @@ -49,10 +48,6 @@ class PickerState extends State { final TextEditingController searchController = TextEditingController(); - final closeButton = Image.asset( - 'assets/images/close.png', - color: Palette.darkBlueCraiola, - ); ScrollController controller = ScrollController(); @override @@ -98,81 +93,78 @@ class PickerState extends State { ), Padding( padding: EdgeInsets.only(left: 24, right: 24, top: 24), - child: GestureDetector( - onTap: () => null, - child: ClipRRect( - borderRadius: BorderRadius.all(Radius.circular(30)), - child: Container( - color: Theme.of(context).accentTextTheme.title.color, - child: ConstrainedBox( - constraints: BoxConstraints( - maxHeight: MediaQuery.of(context).size.height * 0.65, - ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - if (widget.hintText != null) - Padding( - padding: const EdgeInsets.all(16), - child: TextFormField( - controller: searchController, - style: TextStyle(color: Theme.of(context).primaryTextTheme.title.color), - decoration: InputDecoration( - hintText: widget.hintText, - prefixIcon: Image.asset("assets/images/search_icon.png"), - filled: true, - fillColor: Theme.of(context).accentTextTheme.display2.color, - alignLabelWithHint: false, - contentPadding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(14), - borderSide: const BorderSide( - color: Colors.transparent, - )), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(14), - borderSide: const BorderSide( - color: Colors.transparent, - )), - ), + child: ClipRRect( + borderRadius: BorderRadius.all(Radius.circular(30)), + child: Container( + color: Theme.of(context).accentTextTheme.title.color, + child: ConstrainedBox( + constraints: BoxConstraints( + maxHeight: MediaQuery.of(context).size.height * 0.65, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + if (widget.hintText != null) + Padding( + padding: const EdgeInsets.all(16), + child: TextFormField( + controller: searchController, + style: TextStyle(color: Theme.of(context).primaryTextTheme.title.color), + decoration: InputDecoration( + hintText: widget.hintText, + prefixIcon: Image.asset("assets/images/search_icon.png"), + filled: true, + fillColor: Theme.of(context).accentTextTheme.display2.color, + alignLabelWithHint: false, + contentPadding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: const BorderSide( + color: Colors.transparent, + )), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(14), + borderSide: const BorderSide( + color: Colors.transparent, + )), ), ), - Divider( - color: Theme.of(context).accentTextTheme.title.backgroundColor, - height: 1, ), - if (widget.selectedAtIndex != -1) buildSelectedItem(), - Flexible( - child: Stack( - alignment: Alignment.center, - children: [ - (items?.length ?? 0) > 3 ? Scrollbar( - controller: controller, - child: itemsList(), - ) : itemsList(), - (widget.description?.isNotEmpty ?? false) - ? Positioned( - bottom: 24, - left: 24, - right: 24, - child: Text( - widget.description, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 12, - fontWeight: FontWeight.w500, - fontFamily: 'Lato', - decoration: TextDecoration.none, - color: Theme.of(context).primaryTextTheme.title.color, - ), + Divider( + color: Theme.of(context).accentTextTheme.title.backgroundColor, + height: 1, + ), + if (widget.selectedAtIndex != -1) buildSelectedItem(), + Flexible( + child: Stack( + alignment: Alignment.center, + children: [ + (items?.length ?? 0) > 3 ? Scrollbar( + controller: controller, + child: itemsList(), + ) : itemsList(), + (widget.description?.isNotEmpty ?? false) + ? Positioned( + bottom: 24, + left: 24, + right: 24, + child: Text( + widget.description, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + fontFamily: 'Lato', + decoration: TextDecoration.none, + color: Theme.of(context).primaryTextTheme.title.color, ), - ) - : Offstage(), - ], - ), + ), + ) + : Offstage(), + ], ), - ], - ), + ), + ], ), ), ), @@ -180,7 +172,7 @@ class PickerState extends State { ) ], ), - AlertCloseButton(image: closeButton) + AlertCloseButton(), ], ), ); @@ -193,6 +185,7 @@ class PickerState extends State { ? GridView.builder( padding: EdgeInsets.zero, controller: controller, + shrinkWrap: true, itemCount: items == null || items.isEmpty ? 0 : items.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2,