Redesign exchange currency picker (#402)

Omar Hatem committed Jul 1, 2022 at 12:17 UTC 3bd9301be1c4b906d88b973bec00943584514716
7 files changed +259 -306
lib/src/screens/contact/contact_page.dart
+1
@@ -150,6 +150,7 @@ class ContactPage extends BasePage {
150 contactViewModel.currencies.indexOf(contactViewModel.currency),
151 items: contactViewModel.currencies,
152 title: S.of(context).please_select,
153 + hintText: S.of(context).search_currency,
154 onItemSelected: (CryptoCurrency item) =>
155 contactViewModel.currency = item),
156 context: context);
lib/src/screens/exchange/widgets/currency_picker.dart
+106 -123
@@ -1,8 +1,8 @@
1 import 'dart:ui';
2 -import 'package:cake_wallet/generated/i18n.dart';
3 -import 'package:cake_wallet/palette.dart';
2 +import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker_item_widget.dart';
3 import 'package:cake_wallet/src/screens/exchange/widgets/currency_utils.dart';
4 import 'package:cake_wallet/src/screens/exchange/widgets/picker_item.dart';
5 +import 'package:cake_wallet/src/widgets/alert_close_button.dart';
6 import 'package:flutter/cupertino.dart';
7 import 'package:flutter/material.dart';
8 import 'package:cw_core/crypto_currency.dart';
@@ -13,8 +13,9 @@ class CurrencyPicker extends StatefulWidget {
13 CurrencyPicker(
14 {@required this.selectedAtIndex,
15 @required this.items,
16 - @required this.title,
16 @required this.onItemSelected,
17 + this.title,
18 + this.hintText,
19 this.isMoneroWallet = false,
20 this.isConvertFrom = false});
21
@@ -24,6 +25,7 @@ class CurrencyPicker extends StatefulWidget {
25 final Function(CryptoCurrency) onItemSelected;
26 final bool isMoneroWallet;
27 final bool isConvertFrom;
28 + final String hintText;
29
30 @override
31 CurrencyPickerState createState() => CurrencyPickerState(items);
@@ -34,11 +36,8 @@ class CurrencyPickerState extends State<CurrencyPicker> {
36 : isSearchBarActive = false,
37 textFieldValue = '',
38 subPickerItemsList = [],
37 - appBarTextStyle = TextStyle(
38 - fontSize: 20,
39 - fontFamily: 'Lato',
40 - backgroundColor: Colors.transparent,
41 - color: Colors.white);
39 + appBarTextStyle =
40 + TextStyle(fontSize: 20, fontFamily: 'Lato', backgroundColor: Colors.transparent, color: Colors.white);
41
42 @override
43 void initState() {
@@ -61,13 +60,10 @@ class CurrencyPickerState extends State<CurrencyPicker> {
60 TextStyle appBarTextStyle;
61
62 void cleanSubPickerItemsList() {
64 - subPickerItemsList = pickerItemsList
65 - .where((element) => items.contains(element.original))
66 - .toList();
63 + subPickerItemsList = pickerItemsList.where((element) => items.contains(element.original)).toList();
64 }
65
69 - void currencySearchBySubstring(
70 - String subString, List<PickerItem<CryptoCurrency>> list) {
66 + void currencySearchBySubstring(String subString, List<PickerItem<CryptoCurrency>> list) {
67 setState(() {
68 if (subString.isNotEmpty) {
69 subPickerItemsList = subPickerItemsList
@@ -84,118 +80,104 @@ class CurrencyPickerState extends State<CurrencyPicker> {
80 @override
81 Widget build(BuildContext context) {
82 return AlertBackground(
87 - child: SafeArea(
88 - child: Scaffold(
89 - resizeToAvoidBottomInset: false,
90 - backgroundColor: Colors.transparent,
91 - body: Column(
92 - children: [
93 - Padding(
94 - padding: EdgeInsets.symmetric(horizontal: 26.0, vertical: 0),
95 - child: Row(
96 - mainAxisAlignment: MainAxisAlignment.spaceBetween,
97 - children: [
98 - isSearchBarActive
99 - ? Expanded(
100 - child: Row(
101 - mainAxisAlignment:
102 - MainAxisAlignment.spaceBetween,
103 - children: [
104 - InkWell(
105 - child: Text(
106 - S.of(context).cancel,
107 - style: appBarTextStyle,
108 - ),
109 - onTap: () {
110 - setState(() {
111 - isSearchBarActive = false;
112 - textFieldValue = '';
113 - cleanSubPickerItemsList();
114 - });
115 - }),
116 - Container(
117 - width: 100.0,
118 - child: CupertinoTextField(
119 - autofocus: true,
120 - placeholder:
121 - S.of(context).search + '...',
122 - placeholderStyle: appBarTextStyle,
123 - decoration: BoxDecoration(
124 - color: Colors.transparent),
125 - cursorColor: Colors.white,
126 - cursorHeight: 23.0,
127 - style: appBarTextStyle,
128 - onChanged: (value) {
129 - this.textFieldValue = value;
130 - cleanSubPickerItemsList();
131 - currencySearchBySubstring(
132 - textFieldValue,
133 - subPickerItemsList);
134 - }),
135 - )
136 - ],
137 - ),
138 - )
139 - : Text(
140 - widget.title,
141 - style: appBarTextStyle,
142 - ),
143 - IconButton(
144 - splashRadius: 23,
145 - icon: Icon(Icons.search, color: Colors.white),
146 - onPressed: () {
147 - setState(() {
148 - isSearchBarActive = true;
149 - });
150 - },
151 - )
152 - ]),
153 - ),
154 - Expanded(
155 - flex: 12,
156 - child: Padding(
157 - padding: const EdgeInsets.symmetric(
158 - horizontal: 26.0, vertical: 26.0),
159 - child: Container(
160 - child: CurrencyPickerWidget(
161 - crossAxisCount: 2,
162 - selectedAtIndex: widget.selectedAtIndex,
163 - itemsCount: subPickerItemsList.length,
164 - pickerItemsList: subPickerItemsList,
165 - pickListItem: (int index) {
166 - setState(() {
167 - widget.selectedAtIndex = index;
168 - });
169 - widget
170 - .onItemSelected(subPickerItemsList[index].original);
171 - if (widget.isConvertFrom &&
172 - !widget.isMoneroWallet &&
173 - (subPickerItemsList[index].original ==
174 - CryptoCurrency.xmr)) {
175 - } else {
176 - Navigator.of(context).pop();
177 - }
178 - },
83 + child: Stack(
84 + alignment: Alignment.center,
85 + children: [
86 + Column(
87 + mainAxisSize: MainAxisSize.min,
88 + children: <Widget>[
89 + if (widget.title?.isNotEmpty ?? false)
90 + Container(
91 + padding: EdgeInsets.symmetric(horizontal: 24),
92 + child: Text(
93 + widget.title,
94 + textAlign: TextAlign.center,
95 + style: TextStyle(
96 + fontSize: 18,
97 + fontFamily: 'Lato',
98 + fontWeight: FontWeight.bold,
99 + decoration: TextDecoration.none,
100 + color: Colors.white,
101 ),
102 ),
103 ),
182 - ),
183 - Expanded(
184 - flex: 2,
185 - child: Container(
186 - width: 42.0,
187 - alignment: Alignment.topCenter,
188 - child: FittedBox(
189 - child: FloatingActionButton(
190 - elevation: 0,
191 - backgroundColor: Colors.white,
192 - onPressed: () {
193 - Navigator.of(context).pop();
194 - },
195 - child: Icon(
196 - Icons.close_outlined,
197 - color: Palette.darkBlueCraiola,
198 - size: 30.0,
104 + Padding(
105 + padding: EdgeInsets.only(left: 24, right: 24, top: 24),
106 + child: ClipRRect(
107 + borderRadius: BorderRadius.all(Radius.circular(30)),
108 + child: Container(
109 + color: Theme.of(context).accentTextTheme.title.color,
110 + child: ConstrainedBox(
111 + constraints: BoxConstraints(
112 + maxHeight: MediaQuery.of(context).size.height * 0.65,
113 + ),
114 + child: Column(
115 + mainAxisSize: MainAxisSize.min,
116 + children: [
117 + if (widget.hintText != null)
118 + Padding(
119 + padding: const EdgeInsets.all(16),
120 + child: TextFormField(
121 + style: TextStyle(color: Theme.of(context).primaryTextTheme.title.color),
122 + decoration: InputDecoration(
123 + hintText: widget.hintText,
124 + prefixIcon: Image.asset("assets/images/search_icon.png"),
125 + filled: true,
126 + fillColor: const Color(0xffF2F0FA),
127 + alignLabelWithHint: false,
128 + contentPadding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16),
129 + enabledBorder: OutlineInputBorder(
130 + borderRadius: BorderRadius.circular(14),
131 + borderSide: const BorderSide(
132 + color: Colors.transparent,
133 + )),
134 + focusedBorder: OutlineInputBorder(
135 + borderRadius: BorderRadius.circular(14),
136 + borderSide: const BorderSide(
137 + color: Colors.transparent,
138 + )),
139 + ),
140 + onChanged: (value) {
141 + this.textFieldValue = value;
142 + cleanSubPickerItemsList();
143 + currencySearchBySubstring(textFieldValue, subPickerItemsList);
144 + },
145 + ),
146 + ),
147 + Divider(
148 + color: Theme.of(context).accentTextTheme.title.backgroundColor,
149 + height: 1,
150 + ),
151 + if (widget.selectedAtIndex != -1)
152 + AspectRatio(
153 + aspectRatio: 6,
154 + child: PickerItemWidget(
155 + title: pickerItemsList[widget.selectedAtIndex].title,
156 + iconPath: pickerItemsList[widget.selectedAtIndex].iconPath,
157 + isSelected: true,
158 + tag: pickerItemsList[widget.selectedAtIndex].tag,
159 + ),
160 + ),
161 + Flexible(
162 + child: CurrencyPickerWidget(
163 + crossAxisCount: 2,
164 + selectedAtIndex: widget.selectedAtIndex,
165 + pickerItemsList: subPickerItemsList,
166 + pickListItem: (int index) {
167 + setState(() {
168 + widget.selectedAtIndex = index;
169 + });
170 + widget.onItemSelected(subPickerItemsList[index].original);
171 + if (widget.isConvertFrom &&
172 + !widget.isMoneroWallet &&
173 + (subPickerItemsList[index].original == CryptoCurrency.xmr)) {
174 + } else {
175 + Navigator.of(context).pop();
176 + }
177 + },
178 + ),
179 + ),
180 + ],
181 ),
182 ),
183 ),
@@ -203,7 +185,8 @@ class CurrencyPickerState extends State<CurrencyPicker> {
185 ),
186 ],
187 ),
206 - ),
188 + AlertCloseButton(),
189 + ],
190 ),
191 );
192 }
lib/src/screens/exchange/widgets/currency_picker_item_widget.dart
+47 -66
@@ -2,8 +2,7 @@ import 'package:flutter/material.dart';
2 import 'package:cake_wallet/palette.dart';
3
4 class PickerItemWidget extends StatelessWidget {
5 - const PickerItemWidget(
6 - {this.iconPath, this.title, this.isSelected, this.tag, this.onTap});
5 + const PickerItemWidget({this.iconPath, this.title, this.isSelected = false, this.tag, this.onTap});
6
7 final String iconPath;
8 final String title;
@@ -16,74 +15,56 @@ class PickerItemWidget extends StatelessWidget {
15 return GestureDetector(
16 onTap: onTap,
17 child: Container(
19 - color: isSelected
20 - ? Theme.of(context).textTheme.bodyText1.color
21 - : Theme.of(context).accentTextTheme.headline6.color,
22 - child: Center(
23 - child: Padding(
24 - padding: const EdgeInsets.all(8.0),
25 - child: Row(
26 - mainAxisAlignment: MainAxisAlignment.center,
27 - children: [
28 - Expanded(
29 - child: Container(
30 - child: Image.asset(
31 - iconPath,
32 - height: 32.0,
33 - width: 32.0,
34 - ),
35 - ),
18 + color: Theme.of(context).accentTextTheme.headline6.color,
19 + child: Padding(
20 + padding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 24),
21 + child: Row(
22 + children: [
23 + Container(
24 + child: Image.asset(
25 + iconPath,
26 + height: 20.0,
27 + width: 20.0,
28 ),
37 - Expanded(
38 - child: Stack(
39 - clipBehavior: Clip.none,
40 - alignment: Alignment.centerLeft,
41 - children: [
42 - Text(
43 - title,
44 - style: TextStyle(
45 - color: isSelected
46 - ? Palette.blueCraiola
47 - : Theme.of(context).primaryTextTheme.title.color,
48 - fontSize: 18.0,
49 - fontFamily: 'Lato',
29 + ),
30 + const SizedBox(width: 6),
31 + Expanded(
32 + child: Row(
33 + children: [
34 + Text(
35 + title,
36 + style: TextStyle(
37 + color: isSelected ? Palette.blueCraiola : Theme.of(context).primaryTextTheme.title.color,
38 + fontSize: isSelected ? 16 : 14.0,
39 + fontFamily: 'Lato',
40 + fontWeight: FontWeight.w600,
41 + ),
42 + ),
43 + if (tag != null)
44 + Align(
45 + alignment: Alignment.topCenter,
46 + child: Container(
47 + width: 35.0,
48 + height: 18.0,
49 + child: Center(
50 + child: Text(
51 + tag,
52 + style: TextStyle(
53 + fontSize: 7.0, fontFamily: 'Lato', color: Theme.of(context).textTheme.body1.color),
54 + ),
55 + ),
56 + decoration: BoxDecoration(
57 + borderRadius: BorderRadius.circular(6.0),
58 + //border: Border.all(color: ),
59 + color: Theme.of(context).textTheme.body1.decorationColor,
60 + ),
61 ),
62 ),
52 - tag != null
53 - ? Positioned(
54 - top: -20.0,
55 - right: 7.0,
56 - child: Container(
57 - width: 35.0,
58 - height: 18.0,
59 - child: Center(
60 - child: Text(
61 - tag,
62 - style: TextStyle(
63 - fontSize: 7.0,
64 - fontFamily: 'Lato',
65 - color: Theme.of(context)
66 - .textTheme
67 - .body1
68 - .color),
69 - ),
70 - ),
71 - decoration: BoxDecoration(
72 - borderRadius: BorderRadius.circular(6.0),
73 - //border: Border.all(color: ),
74 - color: Theme.of(context)
75 - .textTheme
76 - .body1
77 - .decorationColor,
78 - ),
79 - ),
80 - )
81 - : Container(),
82 - ],
83 - ),
63 + ],
64 ),
85 - ],
86 - ),
65 + ),
66 + if (isSelected) Icon(Icons.check_circle, color: Theme.of(context).accentTextTheme.body2.color)
67 + ],
68 ),
69 ),
70 ),
lib/src/screens/exchange/widgets/currency_picker_widget.dart
+27 -38
@@ -4,58 +4,47 @@ import 'picker_item.dart';
4 import 'currency_picker_item_widget.dart';
5
6 class CurrencyPickerWidget extends StatelessWidget {
7 - const CurrencyPickerWidget({
7 + CurrencyPickerWidget({
8 @required this.crossAxisCount,
9 @required this.selectedAtIndex,
10 - @required this.itemsCount,
10 @required this.pickerItemsList,
11 @required this.pickListItem,
12 });
13
14 final int crossAxisCount;
15 final int selectedAtIndex;
17 - final int itemsCount;
16 final Function pickListItem;
17 final List<PickerItem<CryptoCurrency>> pickerItemsList;
18
19 + final ScrollController _scrollController = ScrollController();
20 +
21 @override
22 Widget build(BuildContext context) {
23 - return LayoutBuilder(
24 - builder: (BuildContext context, BoxConstraints constraints) {
25 - return Container(
26 - decoration: BoxDecoration(
27 - color: Theme.of(context).accentTextTheme.headline6.backgroundColor,
28 - borderRadius: BorderRadius.circular(14.0),
29 - ),
30 - child: ClipRRect(
31 - borderRadius: BorderRadius.circular(14.0),
32 - child: Scrollbar(
33 - showTrackOnHover: true,
34 - isAlwaysShown: true,
35 - thickness: 6.0,
36 - radius: Radius.circular(3),
37 - child: GridView.builder(
38 - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
39 - crossAxisCount: crossAxisCount,
40 - crossAxisSpacing: 1,
41 - mainAxisExtent: constraints.maxHeight / 8,
42 - mainAxisSpacing: 1),
43 - itemCount: pickerItemsList.length,
44 - itemBuilder: (BuildContext ctx, index) {
45 - return PickerItemWidget(
46 - onTap: () {
47 - pickListItem(index);
48 - },
49 - title: pickerItemsList[index].title,
50 - iconPath: pickerItemsList[index].iconPath,
51 - isSelected: index == selectedAtIndex,
52 - tag: pickerItemsList[index].tag,
53 - );
54 - }),
23 + return Container(
24 + color: Theme.of(context).accentTextTheme.headline6.backgroundColor,
25 + child: Scrollbar(
26 + controller: _scrollController,
27 + child: GridView.builder(
28 + controller: _scrollController,
29 + padding: EdgeInsets.zero,
30 + shrinkWrap: true,
31 + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
32 + crossAxisCount: crossAxisCount,
33 + crossAxisSpacing: 2,
34 + childAspectRatio: 3,
35 ),
56 - ),
57 - );
58 - },
36 + itemCount: pickerItemsList.length,
37 + itemBuilder: (BuildContext ctx, index) {
38 + return PickerItemWidget(
39 + onTap: () {
40 + pickListItem(index);
41 + },
42 + title: pickerItemsList[index].title,
43 + iconPath: pickerItemsList[index].iconPath,
44 + tag: pickerItemsList[index].tag,
45 + );
46 + }),
47 + ),
48 );
49 }
50 }
lib/src/screens/exchange/widgets/exchange_card.dart
+1 -1
@@ -445,7 +445,7 @@ class ExchangeCardState extends State<ExchangeCard> {
445 builder: (_) => CurrencyPicker(
446 selectedAtIndex: widget.currencies.indexOf(_selectedCurrency),
447 items: widget.currencies,
448 - title: S.of(context).change_currency,
448 + hintText: S.of(context).search_currency,
449 isMoneroWallet: _isMoneroWallet,
450 isConvertFrom: widget.hasRefundAddress,
451 onItemSelected: (CryptoCurrency item) =>
lib/src/widgets/alert_close_button.dart
+8 -2
@@ -1,10 +1,16 @@
1 +import 'package:cake_wallet/palette.dart';
2 import 'package:flutter/material.dart';
3
4 class AlertCloseButton extends StatelessWidget {
4 - AlertCloseButton({@required this.image});
5 + AlertCloseButton({this.image});
6
7 final Image image;
8
9 + final closeButton = Image.asset(
10 + 'assets/images/close.png',
11 + color: Palette.darkBlueCraiola,
12 + );
13 +
14 @override
15 Widget build(BuildContext context) {
16 return Positioned(
@@ -19,7 +25,7 @@ class AlertCloseButton extends StatelessWidget {
25 shape: BoxShape.circle
26 ),
27 child: Center(
22 - child: image,
28 + child: image ?? closeButton,
29 ),
30 ),
31 )
lib/src/widgets/picker.dart
+69 -76
@@ -3,7 +3,6 @@ import 'package:flutter/cupertino.dart';
3 import 'package:flutter/material.dart';
4 import 'package:cake_wallet/src/widgets/alert_background.dart';
5 import 'package:cake_wallet/src/widgets/alert_close_button.dart';
6 -import 'package:cake_wallet/palette.dart';
6
7 class Picker<Item extends Object> extends StatefulWidget {
8 Picker({
@@ -49,10 +48,6 @@ class PickerState<Item> extends State<Picker> {
48
49 final TextEditingController searchController = TextEditingController();
50
52 - final closeButton = Image.asset(
53 - 'assets/images/close.png',
54 - color: Palette.darkBlueCraiola,
55 - );
51 ScrollController controller = ScrollController();
52
53 @override
@@ -98,81 +93,78 @@ class PickerState<Item> extends State<Picker> {
93 ),
94 Padding(
95 padding: EdgeInsets.only(left: 24, right: 24, top: 24),
101 - child: GestureDetector(
102 - onTap: () => null,
103 - child: ClipRRect(
104 - borderRadius: BorderRadius.all(Radius.circular(30)),
105 - child: Container(
106 - color: Theme.of(context).accentTextTheme.title.color,
107 - child: ConstrainedBox(
108 - constraints: BoxConstraints(
109 - maxHeight: MediaQuery.of(context).size.height * 0.65,
110 - ),
111 - child: Column(
112 - mainAxisSize: MainAxisSize.min,
113 - children: [
114 - if (widget.hintText != null)
115 - Padding(
116 - padding: const EdgeInsets.all(16),
117 - child: TextFormField(
118 - controller: searchController,
119 - style: TextStyle(color: Theme.of(context).primaryTextTheme.title.color),
120 - decoration: InputDecoration(
121 - hintText: widget.hintText,
122 - prefixIcon: Image.asset("assets/images/search_icon.png"),
123 - filled: true,
124 - fillColor: Theme.of(context).accentTextTheme.display2.color,
125 - alignLabelWithHint: false,
126 - contentPadding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16),
127 - enabledBorder: OutlineInputBorder(
128 - borderRadius: BorderRadius.circular(14),
129 - borderSide: const BorderSide(
130 - color: Colors.transparent,
131 - )),
132 - focusedBorder: OutlineInputBorder(
133 - borderRadius: BorderRadius.circular(14),
134 - borderSide: const BorderSide(
135 - color: Colors.transparent,
136 - )),
137 - ),
96 + child: ClipRRect(
97 + borderRadius: BorderRadius.all(Radius.circular(30)),
98 + child: Container(
99 + color: Theme.of(context).accentTextTheme.title.color,
100 + child: ConstrainedBox(
101 + constraints: BoxConstraints(
102 + maxHeight: MediaQuery.of(context).size.height * 0.65,
103 + ),
104 + child: Column(
105 + mainAxisSize: MainAxisSize.min,
106 + children: [
107 + if (widget.hintText != null)
108 + Padding(
109 + padding: const EdgeInsets.all(16),
110 + child: TextFormField(
111 + controller: searchController,
112 + style: TextStyle(color: Theme.of(context).primaryTextTheme.title.color),
113 + decoration: InputDecoration(
114 + hintText: widget.hintText,
115 + prefixIcon: Image.asset("assets/images/search_icon.png"),
116 + filled: true,
117 + fillColor: Theme.of(context).accentTextTheme.display2.color,
118 + alignLabelWithHint: false,
119 + contentPadding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16),
120 + enabledBorder: OutlineInputBorder(
121 + borderRadius: BorderRadius.circular(14),
122 + borderSide: const BorderSide(
123 + color: Colors.transparent,
124 + )),
125 + focusedBorder: OutlineInputBorder(
126 + borderRadius: BorderRadius.circular(14),
127 + borderSide: const BorderSide(
128 + color: Colors.transparent,
129 + )),
130 ),
131 ),
140 - Divider(
141 - color: Theme.of(context).accentTextTheme.title.backgroundColor,
142 - height: 1,
132 ),
144 - if (widget.selectedAtIndex != -1) buildSelectedItem(),
145 - Flexible(
146 - child: Stack(
147 - alignment: Alignment.center,
148 - children: <Widget>[
149 - (items?.length ?? 0) > 3 ? Scrollbar(
150 - controller: controller,
151 - child: itemsList(),
152 - ) : itemsList(),
153 - (widget.description?.isNotEmpty ?? false)
154 - ? Positioned(
155 - bottom: 24,
156 - left: 24,
157 - right: 24,
158 - child: Text(
159 - widget.description,
160 - textAlign: TextAlign.center,
161 - style: TextStyle(
162 - fontSize: 12,
163 - fontWeight: FontWeight.w500,
164 - fontFamily: 'Lato',
165 - decoration: TextDecoration.none,
166 - color: Theme.of(context).primaryTextTheme.title.color,
167 - ),
133 + Divider(
134 + color: Theme.of(context).accentTextTheme.title.backgroundColor,
135 + height: 1,
136 + ),
137 + if (widget.selectedAtIndex != -1) buildSelectedItem(),
138 + Flexible(
139 + child: Stack(
140 + alignment: Alignment.center,
141 + children: <Widget>[
142 + (items?.length ?? 0) > 3 ? Scrollbar(
143 + controller: controller,
144 + child: itemsList(),
145 + ) : itemsList(),
146 + (widget.description?.isNotEmpty ?? false)
147 + ? Positioned(
148 + bottom: 24,
149 + left: 24,
150 + right: 24,
151 + child: Text(
152 + widget.description,
153 + textAlign: TextAlign.center,
154 + style: TextStyle(
155 + fontSize: 12,
156 + fontWeight: FontWeight.w500,
157 + fontFamily: 'Lato',
158 + decoration: TextDecoration.none,
159 + color: Theme.of(context).primaryTextTheme.title.color,
160 ),
169 - )
170 - : Offstage(),
171 - ],
172 - ),
161 + ),
162 + )
163 + : Offstage(),
164 + ],
165 ),
174 - ],
175 - ),
166 + ),
167 + ],
168 ),
169 ),
170 ),
@@ -180,7 +172,7 @@ class PickerState<Item> extends State<Picker> {
172 )
173 ],
174 ),
183 - AlertCloseButton(image: closeButton)
175 + AlertCloseButton(),
176 ],
177 ),
178 );
@@ -193,6 +185,7 @@ class PickerState<Item> extends State<Picker> {
185 ? GridView.builder(
186 padding: EdgeInsets.zero,
187 controller: controller,
188 + shrinkWrap: true,
189 itemCount: items == null || items.isEmpty ? 0 : items.length,
190 gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
191 crossAxisCount: 2,