Redesign exchange currency picker (#402)

This commit is contained in:
Omar Hatem 2022-07-01 12:17:09 +02:00 committed by GitHub
parent 02533d68a0
commit 3bd9301be1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 259 additions and 306 deletions

View file

@ -150,6 +150,7 @@ class ContactPage extends BasePage {
contactViewModel.currencies.indexOf(contactViewModel.currency), contactViewModel.currencies.indexOf(contactViewModel.currency),
items: contactViewModel.currencies, items: contactViewModel.currencies,
title: S.of(context).please_select, title: S.of(context).please_select,
hintText: S.of(context).search_currency,
onItemSelected: (CryptoCurrency item) => onItemSelected: (CryptoCurrency item) =>
contactViewModel.currency = item), contactViewModel.currency = item),
context: context); context: context);

View file

@ -1,8 +1,8 @@
import 'dart:ui'; import 'dart:ui';
import 'package:cake_wallet/generated/i18n.dart'; import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker_item_widget.dart';
import 'package:cake_wallet/palette.dart';
import 'package:cake_wallet/src/screens/exchange/widgets/currency_utils.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/screens/exchange/widgets/picker_item.dart';
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
import 'package:flutter/cupertino.dart'; import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cw_core/crypto_currency.dart'; import 'package:cw_core/crypto_currency.dart';
@ -13,8 +13,9 @@ class CurrencyPicker extends StatefulWidget {
CurrencyPicker( CurrencyPicker(
{@required this.selectedAtIndex, {@required this.selectedAtIndex,
@required this.items, @required this.items,
@required this.title,
@required this.onItemSelected, @required this.onItemSelected,
this.title,
this.hintText,
this.isMoneroWallet = false, this.isMoneroWallet = false,
this.isConvertFrom = false}); this.isConvertFrom = false});
@ -24,6 +25,7 @@ class CurrencyPicker extends StatefulWidget {
final Function(CryptoCurrency) onItemSelected; final Function(CryptoCurrency) onItemSelected;
final bool isMoneroWallet; final bool isMoneroWallet;
final bool isConvertFrom; final bool isConvertFrom;
final String hintText;
@override @override
CurrencyPickerState createState() => CurrencyPickerState(items); CurrencyPickerState createState() => CurrencyPickerState(items);
@ -34,11 +36,8 @@ class CurrencyPickerState extends State<CurrencyPicker> {
: isSearchBarActive = false, : isSearchBarActive = false,
textFieldValue = '', textFieldValue = '',
subPickerItemsList = [], subPickerItemsList = [],
appBarTextStyle = TextStyle( appBarTextStyle =
fontSize: 20, TextStyle(fontSize: 20, fontFamily: 'Lato', backgroundColor: Colors.transparent, color: Colors.white);
fontFamily: 'Lato',
backgroundColor: Colors.transparent,
color: Colors.white);
@override @override
void initState() { void initState() {
@ -61,13 +60,10 @@ class CurrencyPickerState extends State<CurrencyPicker> {
TextStyle appBarTextStyle; TextStyle appBarTextStyle;
void cleanSubPickerItemsList() { void cleanSubPickerItemsList() {
subPickerItemsList = pickerItemsList subPickerItemsList = pickerItemsList.where((element) => items.contains(element.original)).toList();
.where((element) => items.contains(element.original))
.toList();
} }
void currencySearchBySubstring( void currencySearchBySubstring(String subString, List<PickerItem<CryptoCurrency>> list) {
String subString, List<PickerItem<CryptoCurrency>> list) {
setState(() { setState(() {
if (subString.isNotEmpty) { if (subString.isNotEmpty) {
subPickerItemsList = subPickerItemsList subPickerItemsList = subPickerItemsList
@ -84,118 +80,104 @@ class CurrencyPickerState extends State<CurrencyPicker> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return AlertBackground( return AlertBackground(
child: SafeArea( child: Stack(
child: Scaffold( alignment: Alignment.center,
resizeToAvoidBottomInset: false,
backgroundColor: Colors.transparent,
body: Column(
children: [ children: [
Padding( Column(
padding: EdgeInsets.symmetric(horizontal: 26.0, vertical: 0), mainAxisSize: MainAxisSize.min,
child: Row( children: <Widget>[
mainAxisAlignment: MainAxisAlignment.spaceBetween, if (widget.title?.isNotEmpty ?? false)
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( Container(
width: 100.0, padding: EdgeInsets.symmetric(horizontal: 24),
child: CupertinoTextField( child: Text(
autofocus: true, widget.title,
placeholder: textAlign: TextAlign.center,
S.of(context).search + '...', style: TextStyle(
placeholderStyle: appBarTextStyle, fontSize: 18,
decoration: BoxDecoration( fontFamily: 'Lato',
color: Colors.transparent), fontWeight: FontWeight.bold,
cursorColor: Colors.white, decoration: TextDecoration.none,
cursorHeight: 23.0, color: Colors.white,
style: appBarTextStyle, ),
),
),
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) { onChanged: (value) {
this.textFieldValue = value; this.textFieldValue = value;
cleanSubPickerItemsList(); cleanSubPickerItemsList();
currencySearchBySubstring( currencySearchBySubstring(textFieldValue, subPickerItemsList);
textFieldValue,
subPickerItemsList);
}),
)
],
),
)
: Text(
widget.title,
style: appBarTextStyle,
),
IconButton(
splashRadius: 23,
icon: Icon(Icons.search, color: Colors.white),
onPressed: () {
setState(() {
isSearchBarActive = true;
});
}, },
)
]),
), ),
Expanded( ),
flex: 12, Divider(
child: Padding( color: Theme.of(context).accentTextTheme.title.backgroundColor,
padding: const EdgeInsets.symmetric( height: 1,
horizontal: 26.0, vertical: 26.0), ),
child: Container( 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( child: CurrencyPickerWidget(
crossAxisCount: 2, crossAxisCount: 2,
selectedAtIndex: widget.selectedAtIndex, selectedAtIndex: widget.selectedAtIndex,
itemsCount: subPickerItemsList.length,
pickerItemsList: subPickerItemsList, pickerItemsList: subPickerItemsList,
pickListItem: (int index) { pickListItem: (int index) {
setState(() { setState(() {
widget.selectedAtIndex = index; widget.selectedAtIndex = index;
}); });
widget widget.onItemSelected(subPickerItemsList[index].original);
.onItemSelected(subPickerItemsList[index].original);
if (widget.isConvertFrom && if (widget.isConvertFrom &&
!widget.isMoneroWallet && !widget.isMoneroWallet &&
(subPickerItemsList[index].original == (subPickerItemsList[index].original == CryptoCurrency.xmr)) {
CryptoCurrency.xmr)) {
} else { } else {
Navigator.of(context).pop(); Navigator.of(context).pop();
} }
}, },
), ),
), ),
), ],
),
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,
), ),
), ),
), ),
@ -203,7 +185,8 @@ class CurrencyPickerState extends State<CurrencyPicker> {
), ),
], ],
), ),
), AlertCloseButton(),
],
), ),
); );
} }

View file

@ -2,8 +2,7 @@ import 'package:flutter/material.dart';
import 'package:cake_wallet/palette.dart'; import 'package:cake_wallet/palette.dart';
class PickerItemWidget extends StatelessWidget { class PickerItemWidget extends StatelessWidget {
const PickerItemWidget( const PickerItemWidget({this.iconPath, this.title, this.isSelected = false, this.tag, this.onTap});
{this.iconPath, this.title, this.isSelected, this.tag, this.onTap});
final String iconPath; final String iconPath;
final String title; final String title;
@ -16,43 +15,34 @@ class PickerItemWidget extends StatelessWidget {
return GestureDetector( return GestureDetector(
onTap: onTap, onTap: onTap,
child: Container( child: Container(
color: isSelected color: Theme.of(context).accentTextTheme.headline6.color,
? Theme.of(context).textTheme.bodyText1.color
: Theme.of(context).accentTextTheme.headline6.color,
child: Center(
child: Padding( child: Padding(
padding: const EdgeInsets.all(8.0), padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 24),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Expanded( Container(
child: Container(
child: Image.asset( child: Image.asset(
iconPath, iconPath,
height: 32.0, height: 20.0,
width: 32.0, width: 20.0,
),
), ),
), ),
const SizedBox(width: 6),
Expanded( Expanded(
child: Stack( child: Row(
clipBehavior: Clip.none,
alignment: Alignment.centerLeft,
children: [ children: [
Text( Text(
title, title,
style: TextStyle( style: TextStyle(
color: isSelected color: isSelected ? Palette.blueCraiola : Theme.of(context).primaryTextTheme.title.color,
? Palette.blueCraiola fontSize: isSelected ? 16 : 14.0,
: Theme.of(context).primaryTextTheme.title.color,
fontSize: 18.0,
fontFamily: 'Lato', fontFamily: 'Lato',
fontWeight: FontWeight.w600,
), ),
), ),
tag != null if (tag != null)
? Positioned( Align(
top: -20.0, alignment: Alignment.topCenter,
right: 7.0,
child: Container( child: Container(
width: 35.0, width: 35.0,
height: 18.0, height: 18.0,
@ -60,31 +50,22 @@ class PickerItemWidget extends StatelessWidget {
child: Text( child: Text(
tag, tag,
style: TextStyle( style: TextStyle(
fontSize: 7.0, fontSize: 7.0, fontFamily: 'Lato', color: Theme.of(context).textTheme.body1.color),
fontFamily: 'Lato',
color: Theme.of(context)
.textTheme
.body1
.color),
), ),
), ),
decoration: BoxDecoration( decoration: BoxDecoration(
borderRadius: BorderRadius.circular(6.0), borderRadius: BorderRadius.circular(6.0),
//border: Border.all(color: ), //border: Border.all(color: ),
color: Theme.of(context) color: Theme.of(context).textTheme.body1.decorationColor,
.textTheme
.body1
.decorationColor,
), ),
), ),
)
: Container(),
],
),
), ),
], ],
), ),
), ),
if (isSelected) Icon(Icons.check_circle, color: Theme.of(context).accentTextTheme.body2.color)
],
),
), ),
), ),
); );

View file

@ -4,42 +4,35 @@ import 'picker_item.dart';
import 'currency_picker_item_widget.dart'; import 'currency_picker_item_widget.dart';
class CurrencyPickerWidget extends StatelessWidget { class CurrencyPickerWidget extends StatelessWidget {
const CurrencyPickerWidget({ CurrencyPickerWidget({
@required this.crossAxisCount, @required this.crossAxisCount,
@required this.selectedAtIndex, @required this.selectedAtIndex,
@required this.itemsCount,
@required this.pickerItemsList, @required this.pickerItemsList,
@required this.pickListItem, @required this.pickListItem,
}); });
final int crossAxisCount; final int crossAxisCount;
final int selectedAtIndex; final int selectedAtIndex;
final int itemsCount;
final Function pickListItem; final Function pickListItem;
final List<PickerItem<CryptoCurrency>> pickerItemsList; final List<PickerItem<CryptoCurrency>> pickerItemsList;
final ScrollController _scrollController = ScrollController();
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
return Container( return Container(
decoration: BoxDecoration(
color: Theme.of(context).accentTextTheme.headline6.backgroundColor, color: Theme.of(context).accentTextTheme.headline6.backgroundColor,
borderRadius: BorderRadius.circular(14.0),
),
child: ClipRRect(
borderRadius: BorderRadius.circular(14.0),
child: Scrollbar( child: Scrollbar(
showTrackOnHover: true, controller: _scrollController,
isAlwaysShown: true,
thickness: 6.0,
radius: Radius.circular(3),
child: GridView.builder( child: GridView.builder(
controller: _scrollController,
padding: EdgeInsets.zero,
shrinkWrap: true,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount, crossAxisCount: crossAxisCount,
crossAxisSpacing: 1, crossAxisSpacing: 2,
mainAxisExtent: constraints.maxHeight / 8, childAspectRatio: 3,
mainAxisSpacing: 1), ),
itemCount: pickerItemsList.length, itemCount: pickerItemsList.length,
itemBuilder: (BuildContext ctx, index) { itemBuilder: (BuildContext ctx, index) {
return PickerItemWidget( return PickerItemWidget(
@ -48,14 +41,10 @@ class CurrencyPickerWidget extends StatelessWidget {
}, },
title: pickerItemsList[index].title, title: pickerItemsList[index].title,
iconPath: pickerItemsList[index].iconPath, iconPath: pickerItemsList[index].iconPath,
isSelected: index == selectedAtIndex,
tag: pickerItemsList[index].tag, tag: pickerItemsList[index].tag,
); );
}), }),
), ),
),
);
},
); );
} }
} }

View file

@ -445,7 +445,7 @@ class ExchangeCardState extends State<ExchangeCard> {
builder: (_) => CurrencyPicker( builder: (_) => CurrencyPicker(
selectedAtIndex: widget.currencies.indexOf(_selectedCurrency), selectedAtIndex: widget.currencies.indexOf(_selectedCurrency),
items: widget.currencies, items: widget.currencies,
title: S.of(context).change_currency, hintText: S.of(context).search_currency,
isMoneroWallet: _isMoneroWallet, isMoneroWallet: _isMoneroWallet,
isConvertFrom: widget.hasRefundAddress, isConvertFrom: widget.hasRefundAddress,
onItemSelected: (CryptoCurrency item) => onItemSelected: (CryptoCurrency item) =>

View file

@ -1,10 +1,16 @@
import 'package:cake_wallet/palette.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class AlertCloseButton extends StatelessWidget { class AlertCloseButton extends StatelessWidget {
AlertCloseButton({@required this.image}); AlertCloseButton({this.image});
final Image image; final Image image;
final closeButton = Image.asset(
'assets/images/close.png',
color: Palette.darkBlueCraiola,
);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Positioned( return Positioned(
@ -19,7 +25,7 @@ class AlertCloseButton extends StatelessWidget {
shape: BoxShape.circle shape: BoxShape.circle
), ),
child: Center( child: Center(
child: image, child: image ?? closeButton,
), ),
), ),
) )

View file

@ -3,7 +3,6 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:cake_wallet/src/widgets/alert_background.dart'; import 'package:cake_wallet/src/widgets/alert_background.dart';
import 'package:cake_wallet/src/widgets/alert_close_button.dart'; import 'package:cake_wallet/src/widgets/alert_close_button.dart';
import 'package:cake_wallet/palette.dart';
class Picker<Item extends Object> extends StatefulWidget { class Picker<Item extends Object> extends StatefulWidget {
Picker({ Picker({
@ -49,10 +48,6 @@ class PickerState<Item> extends State<Picker> {
final TextEditingController searchController = TextEditingController(); final TextEditingController searchController = TextEditingController();
final closeButton = Image.asset(
'assets/images/close.png',
color: Palette.darkBlueCraiola,
);
ScrollController controller = ScrollController(); ScrollController controller = ScrollController();
@override @override
@ -98,8 +93,6 @@ class PickerState<Item> extends State<Picker> {
), ),
Padding( Padding(
padding: EdgeInsets.only(left: 24, right: 24, top: 24), padding: EdgeInsets.only(left: 24, right: 24, top: 24),
child: GestureDetector(
onTap: () => null,
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(30)), borderRadius: BorderRadius.all(Radius.circular(30)),
child: Container( child: Container(
@ -176,11 +169,10 @@ class PickerState<Item> extends State<Picker> {
), ),
), ),
), ),
),
) )
], ],
), ),
AlertCloseButton(image: closeButton) AlertCloseButton(),
], ],
), ),
); );
@ -193,6 +185,7 @@ class PickerState<Item> extends State<Picker> {
? GridView.builder( ? GridView.builder(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
controller: controller, controller: controller,
shrinkWrap: true,
itemCount: items == null || items.isEmpty ? 0 : items.length, itemCount: items == null || items.isEmpty ? 0 : items.length,
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2, crossAxisCount: 2,