CW-338: Currency picker UI when keyboard is showing (#854)
* fix: Currency picker UI when keyboard is showing * refactor: move picker logic into the common Picker widget - CurrencyPicker uses the common Picker widget in grid mode - SeedLanguagePicker uses the common Picker widget in grid mode - Added logic for keyboard showing UI into Picker widget - Added `softWrap: true` to the item text, so it doesn't overflow * fix: remove subPickerItemsList * fix: add final * fix: move function out of initState() * fix: keep build functions separate to remove boolean comparisons * fix: remove onItemSelected from already selected item * fix: change Expanded for Flexible widget
Rafael Saes committed
Apr 14, 2023 at 11:52 UTC
ff7a217d8417eee05f6942198bb23bef031d96a9
6 files changed
+427
-436
lib/src/screens/exchange/widgets/currency_picker.dart
+26
-136
@@ -1,13 +1,8 @@
1
-import 'package:cake_wallet/src/screens/exchange/widgets/currency_picker_item_widget.dart';
2
-import 'package:cake_wallet/src/screens/exchange/widgets/picker_item.dart';
3
-import 'package:cake_wallet/src/widgets/alert_close_button.dart';
4
-import 'package:cake_wallet/utils/responsive_layout_util.dart';
5
-import 'package:cw_core/currency.dart';
1
import 'package:flutter/material.dart';
2
+import 'package:cake_wallet/src/screens/exchange/widgets/picker_item.dart';
3
import 'package:cw_core/crypto_currency.dart';
8
-import 'package:cake_wallet/src/widgets/alert_background.dart';
9
-import 'package:cake_wallet/palette.dart';
10
-import 'currency_picker_widget.dart';
4
+import 'package:cake_wallet/src/widgets/picker.dart';
5
+import 'package:cw_core/currency.dart';
6
7
class CurrencyPicker extends StatefulWidget {
8
CurrencyPicker(
@@ -19,7 +14,7 @@ class CurrencyPicker extends StatefulWidget {
14
this.isMoneroWallet = false,
15
this.isConvertFrom = false});
16
22
- int selectedAtIndex;
17
+ final int selectedAtIndex;
18
final List<Currency> items;
19
final String? title;
20
final Function(Currency) onItemSelected;
@@ -35,144 +30,39 @@ class CurrencyPickerState extends State<CurrencyPicker> {
30
CurrencyPickerState(this.items)
31
: isSearchBarActive = false,
32
textFieldValue = '',
38
- subPickerItemsList = items,
39
- appBarTextStyle =
40
- TextStyle(fontSize: 20, fontFamily: 'Lato', backgroundColor: Colors.transparent, color: Colors.white),
41
- pickerItemsList = <PickerItem<Currency>>[];
33
+ appBarTextStyle = TextStyle(
34
+ fontSize: 20,
35
+ fontFamily: 'Lato',
36
+ backgroundColor: Colors.transparent,
37
+ color: Colors.white),
38
+ pickerItemsList = <PickerItem<CryptoCurrency>>[];
39
40
List<PickerItem<Currency>> pickerItemsList;
41
List<Currency> items;
42
bool isSearchBarActive;
43
String textFieldValue;
47
- List<Currency> subPickerItemsList;
44
TextStyle appBarTextStyle;
45
50
- void cleanSubPickerItemsList() => subPickerItemsList = items;
51
-
52
- void currencySearchBySubstring(String subString) {
53
- setState(() {
54
- if (subString.isNotEmpty) {
55
- subPickerItemsList = items
56
- .where((element) =>
57
- element.name.toLowerCase().contains(subString.toLowerCase()) ||
58
- (element.tag != null ? element.tag!.toLowerCase().contains(subString.toLowerCase()) : false) ||
59
- (element.fullName != null ? element.fullName!.toLowerCase().contains(subString.toLowerCase()) : false))
60
- .toList();
61
- return;
62
- }
63
- cleanSubPickerItemsList();
64
- });
46
+ bool currencySearchBySubstring(Currency currency, String subString) {
47
+ return currency.name.toLowerCase().contains(subString.toLowerCase()) ||
48
+ (currency.tag != null
49
+ ? currency.tag!.toLowerCase().contains(subString.toLowerCase())
50
+ : false) ||
51
+ (currency.fullName != null
52
+ ? currency.fullName!.toLowerCase().contains(subString.toLowerCase())
53
+ : false);
54
}
55
56
@override
57
Widget build(BuildContext context) {
69
- return AlertBackground(
70
- child: Column(
71
- mainAxisSize: MainAxisSize.min,
72
- mainAxisAlignment: MainAxisAlignment.center,
73
- children: <Widget>[
74
- if (widget.title?.isNotEmpty ?? false)
75
- Container(
76
- padding: EdgeInsets.symmetric(horizontal: 24),
77
- child: Text(
78
- widget.title!,
79
- textAlign: TextAlign.center,
80
- style: TextStyle(
81
- fontSize: 18,
82
- fontFamily: 'Lato',
83
- fontWeight: FontWeight.bold,
84
- decoration: TextDecoration.none,
85
- color: Colors.white,
86
- ),
87
- ),
88
- ),
89
- Padding(
90
- padding: EdgeInsets.only(left: 24, right: 24, top: 24),
91
- child: ClipRRect(
92
- borderRadius: BorderRadius.all(Radius.circular(30)),
93
- child: Container(
94
- color: Theme.of(context).accentTextTheme.headline6!.color!,
95
- child: ConstrainedBox(
96
- constraints: BoxConstraints(
97
- maxHeight: MediaQuery.of(context).size.height * 0.65,
98
- maxWidth: ResponsiveLayoutUtil.kPopupWidth
99
- ),
100
- child: Column(
101
- mainAxisSize: MainAxisSize.min,
102
- children: [
103
- if (widget.hintText != null)
104
- Padding(
105
- padding: const EdgeInsets.all(16),
106
- child: TextFormField(
107
- style: TextStyle(color: Palette.darkBlueCraiola),
108
- decoration: InputDecoration(
109
- hintText: widget.hintText,
110
- prefixIcon: Image.asset("assets/images/search_icon.png"),
111
- filled: true,
112
- fillColor: const Color(0xffF2F0FA),
113
- alignLabelWithHint: false,
114
- contentPadding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16),
115
- enabledBorder: OutlineInputBorder(
116
- borderRadius: BorderRadius.circular(14),
117
- borderSide: const BorderSide(
118
- color: Colors.transparent,
119
- )),
120
- focusedBorder: OutlineInputBorder(
121
- borderRadius: BorderRadius.circular(14),
122
- borderSide: const BorderSide(
123
- color: Colors.transparent,
124
- )),
125
- ),
126
- onChanged: (value) {
127
- this.textFieldValue = value;
128
- cleanSubPickerItemsList();
129
- currencySearchBySubstring(textFieldValue);
130
- },
131
- ),
132
- ),
133
- Divider(
134
- color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!,
135
- height: 1,
136
- ),
137
- if (widget.selectedAtIndex != -1)
138
- AspectRatio(
139
- aspectRatio: 6,
140
- child: PickerItemWidget(
141
- title: items[widget.selectedAtIndex].name,
142
- iconPath: items[widget.selectedAtIndex].iconPath,
143
- isSelected: true,
144
- tag: items[widget.selectedAtIndex].tag,
145
- ),
146
- ),
147
- Flexible(
148
- child: CurrencyPickerWidget(
149
- crossAxisCount: 2,
150
- selectedAtIndex: widget.selectedAtIndex,
151
- pickerItemsList: subPickerItemsList,
152
- pickListItem: (int index) {
153
- setState(() {
154
- widget.selectedAtIndex = index;
155
- });
156
- widget.onItemSelected(subPickerItemsList[index]);
157
- if (widget.isConvertFrom &&
158
- !widget.isMoneroWallet &&
159
- (subPickerItemsList[index] == CryptoCurrency.xmr)) {
160
- } else {
161
- Navigator.of(context).pop();
162
- }
163
- },
164
- ),
165
- ),
166
- ],
167
- ),
168
- ),
169
- ),
170
- ),
171
- ),
172
- SizedBox(height: ResponsiveLayoutUtil.kPopupSpaceHeight),
173
- AlertCloseButton(),
174
- ],
175
- ),
58
+ return Picker(
59
+ selectedAtIndex: widget.selectedAtIndex,
60
+ items: items,
61
+ isGridView: true,
62
+ title: widget.title,
63
+ hintText: widget.hintText,
64
+ matchingCriteria: currencySearchBySubstring,
65
+ onItemSelected: widget.onItemSelected,
66
);
67
}
68
}
lib/src/screens/restore/wallet_restore_from_seed_form.dart
+3
-9
@@ -113,16 +113,10 @@ class WalletRestoreFromSeedFormState extends State<WalletRestoreFromSeedForm> {
113
if (widget.displayLanguageSelector)
114
GestureDetector(
115
onTap: () async {
116
- final selected = await showPopUp<String>(
116
+ await showPopUp<void>(
117
context: context,
118
- builder: (BuildContext context) =>
119
- SeedLanguagePicker(selected: language));
120
-
121
- if (selected == null || selected.isEmpty) {
122
- return;
123
- }
124
-
125
- _changeLanguage(selected);
118
+ builder: (_) => SeedLanguagePicker(
119
+ selected: language, onItemSelected: _changeLanguage));
120
},
121
child: Container(
122
color: Colors.transparent,
lib/src/screens/seed_language/widgets/seed_language_picker.dart
+25
-126
@@ -1,9 +1,6 @@
1
-import 'dart:ui';
2
-import 'package:cake_wallet/src/widgets/alert_background.dart';
3
-import 'package:cake_wallet/src/widgets/alert_close_button.dart';
4
-import 'package:flutter/cupertino.dart';
1
import 'package:flutter/material.dart';
6
-import 'package:cake_wallet/palette.dart';
2
+import 'package:cake_wallet/src/widgets/picker.dart';
3
+import 'package:flutter/cupertino.dart';
4
import 'package:cake_wallet/generated/i18n.dart';
5
6
List<Image> flagImages = [
@@ -50,138 +47,40 @@ const List<String> seedLanguages = [
47
enum Places { topLeft, topRight, bottomLeft, bottomRight, inside }
48
49
class SeedLanguagePicker extends StatefulWidget {
53
- SeedLanguagePicker({
54
- Key? key,
55
- this.selected = defaultSeedLanguage})
50
+ SeedLanguagePicker(
51
+ {Key? key,
52
+ this.selected = defaultSeedLanguage,
53
+ required this.onItemSelected})
54
: super(key: key);
55
56
final String selected;
57
+ final Function(String) onItemSelected;
58
59
@override
61
- SeedLanguagePickerState createState() =>
62
- SeedLanguagePickerState(selected: selected);
60
+ SeedLanguagePickerState createState() => SeedLanguagePickerState(
61
+ selected: selected, onItemSelected: onItemSelected);
62
}
63
64
class SeedLanguagePickerState extends State<SeedLanguagePicker> {
66
- SeedLanguagePickerState({required this.selected});
65
+ SeedLanguagePickerState(
66
+ {required this.selected, required this.onItemSelected});
67
68
- final closeButton = Image.asset('assets/images/close.png');
69
- String selected;
68
+ final String selected;
69
+ final Function(String) onItemSelected;
70
71
@override
72
Widget build(BuildContext context) {
73
- return AlertBackground(
74
- child: Stack(
75
- alignment: Alignment.center,
76
- children: <Widget>[
77
- Column(
78
- mainAxisSize: MainAxisSize.min,
79
- children: <Widget>[
80
- Container(
81
- padding: EdgeInsets.only(left: 24, right: 24),
82
- child: Text(
83
- S.of(context).seed_choose,
84
- textAlign: TextAlign.center,
85
- style: TextStyle(
86
- fontSize: 18,
87
- fontWeight: FontWeight.bold,
88
- fontFamily: 'Lato',
89
- decoration: TextDecoration.none,
90
- color: Colors.white),
91
- ),
92
- ),
93
- Padding(
94
- padding: EdgeInsets.only(top: 24),
95
- child: ClipRRect(
96
- borderRadius: BorderRadius.all(Radius.circular(14)),
97
- child: Container(
98
- height: 300,
99
- width: 300,
100
- color:
101
- Theme.of(context).accentTextTheme!.headline6!.backgroundColor!,
102
- child: GridView.count(
103
- padding: EdgeInsets.all(0),
104
- shrinkWrap: true,
105
- crossAxisCount: 3,
106
- childAspectRatio: 4 / 3,
107
- physics: const NeverScrollableScrollPhysics(),
108
- crossAxisSpacing: 1,
109
- mainAxisSpacing: 1,
110
- children: List.generate(11, (index) {
111
- if (index == 10) {
112
- return gridTile(
113
- isCurrent: false,
114
- image: null,
115
- text: '',
116
- onTap: () {});
117
- }
118
-
119
- final code = languageCodes[index];
120
- final flag = flagImages[index];
121
- final isCurrent =
122
- index == seedLanguages.indexOf(selected);
123
-
124
- return gridTile(
125
- isCurrent: isCurrent,
126
- image: flag,
127
- text: code,
128
- onTap: () {
129
- selected = seedLanguages[index];
130
- Navigator.of(context).pop(selected);
131
- });
132
- }),
133
- ),
134
- ),
135
- ),
136
- )
137
- ],
138
- ),
139
- AlertCloseButton(image: closeButton)
140
- ],
141
- ));
142
- }
143
-
144
- Widget gridTile(
145
- {required bool isCurrent,
146
- required String text,
147
- required VoidCallback onTap,
148
- Image? image}) {
149
- final color = isCurrent
150
- ? Theme.of(context).textTheme!.bodyText1!.color!
151
- : Theme.of(context).accentTextTheme!.headline6!.color!;
152
- final textColor = isCurrent
153
- ? Palette.blueCraiola
154
- : Theme.of(context).primaryTextTheme!.headline6!.color!;
155
-
156
- return GestureDetector(
157
- onTap: onTap,
158
- child: Container(
159
- padding: EdgeInsets.all(10),
160
- color: color,
161
- child: Center(
162
- child: Row(
163
- mainAxisSize: MainAxisSize.min,
164
- mainAxisAlignment: MainAxisAlignment.center,
165
- crossAxisAlignment: CrossAxisAlignment.center,
166
- children: <Widget>[
167
- image ?? Offstage(),
168
- Padding(
169
- padding: image != null
170
- ? EdgeInsets.only(left: 10)
171
- : EdgeInsets.only(left: 0),
172
- child: Text(
173
- text,
174
- style: TextStyle(
175
- fontSize: 18,
176
- fontWeight: FontWeight.w600,
177
- fontFamily: 'Lato',
178
- decoration: TextDecoration.none,
179
- color: textColor),
180
- ),
181
- )
182
- ],
183
- ),
184
- ),
185
- ));
73
+ return Picker(
74
+ selectedAtIndex: seedLanguages.indexOf(selected),
75
+ items: seedLanguages,
76
+ images: flagImages,
77
+ isGridView: true,
78
+ title: S.of(context).seed_choose,
79
+ hintText: S.of(context).seed_choose,
80
+ matchingCriteria: (String language, String searchText) {
81
+ return language.toLowerCase().contains(searchText);
82
+ },
83
+ onItemSelected: onItemSelected,
84
+ );
85
}
86
}
lib/src/widgets/alert_close_button.dart
+6
-3
@@ -2,9 +2,10 @@ import 'package:cake_wallet/palette.dart';
2
import 'package:flutter/material.dart';
3
4
class AlertCloseButton extends StatelessWidget {
5
- AlertCloseButton({this.image});
5
+ AlertCloseButton({this.image, this.bottom});
6
7
final Image? image;
8
+ final double? bottom;
9
10
final closeButton = Image.asset(
11
'assets/images/close.png',
@@ -13,7 +14,9 @@ class AlertCloseButton extends StatelessWidget {
14
15
@override
16
Widget build(BuildContext context) {
16
- return GestureDetector(
17
+ return Positioned(
18
+ bottom: bottom ?? 60,
19
+ child: GestureDetector(
20
onTap: () => Navigator.of(context).pop(),
21
child: Container(
22
height: 42,
@@ -26,6 +29,6 @@ class AlertCloseButton extends StatelessWidget {
29
child: image ?? closeButton,
30
),
31
),
29
- );
32
+ ));
33
}
34
}
\ No newline at end of file
lib/src/widgets/picker.dart
+354
-149
@@ -4,6 +4,7 @@ import 'package:cake_wallet/utils/responsive_layout_util.dart';
4
import 'package:flutter/material.dart';
5
import 'package:cake_wallet/src/widgets/alert_background.dart';
6
import 'package:cake_wallet/src/widgets/alert_close_button.dart';
7
+import 'package:cw_core/currency.dart';
8
9
class Picker<Item> extends StatefulWidget {
10
Picker({
@@ -37,7 +38,8 @@ class Picker<Item> extends StatefulWidget {
38
final bool Function(Item, String)? matchingCriteria;
39
40
@override
40
- _PickerState<Item> createState() => _PickerState<Item>(items, images, onItemSelected);
41
+ _PickerState<Item> createState() =>
42
+ _PickerState<Item>(items, images, onItemSelected);
43
}
44
45
class _PickerState<Item> extends State<Picker<Item>> {
@@ -46,132 +48,235 @@ class _PickerState<Item> extends State<Picker<Item>> {
48
final Function(Item) onItemSelected;
49
List<Item> items;
50
List<Image> images;
51
+ List<Item> filteredItems = [];
52
+ List<Image> filteredImages = [];
53
54
final TextEditingController searchController = TextEditingController();
55
56
ScrollController controller = ScrollController();
57
58
+ void clearFilteredItemsList() {
59
+ filteredItems = List.from(
60
+ items,
61
+ growable: true,
62
+ );
63
+ filteredImages = List.from(
64
+ images,
65
+ growable: true,
66
+ );
67
+
68
+ if (widget.selectedAtIndex != -1) {
69
+ if (widget.selectedAtIndex < filteredItems.length) {
70
+ filteredItems.removeAt(widget.selectedAtIndex);
71
+ }
72
+
73
+ if (widget.selectedAtIndex < filteredImages.length) {
74
+ filteredImages.removeAt(widget.selectedAtIndex);
75
+ }
76
+ }
77
+ }
78
+
79
@override
80
void initState() {
81
super.initState();
82
83
+ clearFilteredItemsList();
84
+
85
searchController.addListener(() {
59
- items = [];
60
- images = [];
61
- for (int i=0;i<widget.items.length;i++) {
62
- if (widget.matchingCriteria?.call(widget.items[i], searchController.text) ?? true) {
63
- items.add(widget.items[i]);
64
- images.add(widget.images[i]);
65
- }
66
- }
67
- setState(() {});
86
+ clearFilteredItemsList();
87
+
88
+ setState(() {
89
+ filteredItems = List.from(items.where((element) {
90
+ if (widget.selectedAtIndex != items.indexOf(element) &&
91
+ (widget.matchingCriteria?.call(element, searchController.text) ??
92
+ true)) {
93
+ if (images.isNotEmpty) {
94
+ filteredImages.add(images[items.indexOf(element)]);
95
+ }
96
+ return true;
97
+ }
98
+
99
+ if (filteredImages.isNotEmpty) {
100
+ filteredImages.remove(images[items.indexOf(element)]);
101
+ }
102
+ return false;
103
+ }), growable: true);
104
+
105
+ return;
106
+ });
107
});
108
}
109
110
@override
111
Widget build(BuildContext context) {
112
+ final double padding = 24;
113
+
114
+ final mq = MediaQuery.of(context);
115
+ final bottom = mq.viewInsets.bottom;
116
+ final height = mq.size.height - bottom;
117
+ final screenCenter = height / 2;
118
+
119
+ double closeButtonBottom = 60;
120
+ double containerHeight = height * 0.65;
121
+ if (bottom > 0) {
122
+ // increase a bit or it gets too squished in the top
123
+ containerHeight = height * 0.75;
124
+
125
+ final containerCenter = containerHeight / 2;
126
+ final containerBottom = screenCenter - containerCenter;
127
+
128
+ final hasTitle = widget.title == null || widget.title!.isEmpty;
129
+
130
+ // position the close button right below the search container
131
+ closeButtonBottom = closeButtonBottom -
132
+ containerBottom +
133
+ (hasTitle ? padding : padding / 1.5);
134
+ }
135
+
136
return AlertBackground(
137
child: Column(
75
- mainAxisSize: MainAxisSize.min,
76
- mainAxisAlignment: MainAxisAlignment.center,
77
- children: <Widget>[
78
- if (widget.title?.isNotEmpty ?? false)
79
- Container(
80
- padding: EdgeInsets.symmetric(horizontal: 24),
81
- child: Text(
82
- widget.title!,
83
- textAlign: TextAlign.center,
84
- style: TextStyle(
85
- fontSize: 18,
86
- fontFamily: 'Lato',
87
- fontWeight: FontWeight.bold,
88
- decoration: TextDecoration.none,
89
- color: Colors.white,
90
- ),
91
- ),
92
- ),
93
- Padding(
94
- padding: EdgeInsets.only(left: 24, right: 24, top: 24),
95
- child: ClipRRect(
96
- borderRadius: BorderRadius.all(Radius.circular(30)),
97
- child: Container(
98
- color: Theme.of(context).accentTextTheme.headline6!.color!,
99
- child: ConstrainedBox(
100
- constraints: BoxConstraints(
101
- maxHeight: MediaQuery.of(context).size.height * 0.65,
102
- maxWidth: ResponsiveLayoutUtil.kPopupWidth,
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.headline6!.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.headline3!.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
- ),
138
+ children: [
139
+ Expanded(
140
+ flex: 1,
141
+ child: Stack(
142
+ alignment: Alignment.center,
143
+ children: <Widget>[
144
+ Column(
145
+ mainAxisSize: MainAxisSize.min,
146
+ mainAxisAlignment: MainAxisAlignment.center,
147
+ children: <Widget>[
148
+ if (widget.title?.isNotEmpty ?? false)
149
+ Container(
150
+ padding: EdgeInsets.symmetric(horizontal: padding),
151
+ child: Text(
152
+ widget.title!,
153
+ textAlign: TextAlign.center,
154
+ style: TextStyle(
155
+ fontSize: 18,
156
+ fontFamily: 'Lato',
157
+ fontWeight: FontWeight.bold,
158
+ decoration: TextDecoration.none,
159
+ color: Colors.white,
160
),
133
- Divider(
134
- color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!,
135
- height: 1,
161
),
137
- if (widget.selectedAtIndex != -1) buildSelectedItem(),
138
- Flexible(
139
- child: Stack(
140
- alignment: Alignment.center,
141
- children: <Widget>[
142
- items.length > 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.headline6!.color!,
160
- ),
162
+ ),
163
+ Padding(
164
+ padding: EdgeInsets.symmetric(horizontal: padding),
165
+ child: ClipRRect(
166
+ borderRadius: BorderRadius.all(Radius.circular(30)),
167
+ child: Container(
168
+ color: Theme.of(context)
169
+ .accentTextTheme
170
+ .headline6!
171
+ .color!,
172
+ child: ConstrainedBox(
173
+ constraints: BoxConstraints(
174
+ maxHeight: containerHeight,
175
+ maxWidth: ResponsiveLayoutUtil.kPopupWidth,
176
+ ),
177
+ child: Column(
178
+ mainAxisSize: MainAxisSize.min,
179
+ children: [
180
+ if (widget.hintText != null)
181
+ Padding(
182
+ padding: const EdgeInsets.all(16),
183
+ child: TextFormField(
184
+ controller: searchController,
185
+ style: TextStyle(
186
+ color: Theme.of(context)
187
+ .primaryTextTheme
188
+ .headline6!
189
+ .color!),
190
+ decoration: InputDecoration(
191
+ hintText: widget.hintText,
192
+ prefixIcon: Image.asset(
193
+ "assets/images/search_icon.png"),
194
+ filled: true,
195
+ fillColor: Theme.of(context)
196
+ .accentTextTheme
197
+ .headline3!
198
+ .color!,
199
+ alignLabelWithHint: false,
200
+ contentPadding:
201
+ const EdgeInsets.symmetric(
202
+ vertical: 4, horizontal: 16),
203
+ enabledBorder: OutlineInputBorder(
204
+ borderRadius:
205
+ BorderRadius.circular(14),
206
+ borderSide: const BorderSide(
207
+ color: Colors.transparent,
208
+ )),
209
+ focusedBorder: OutlineInputBorder(
210
+ borderRadius:
211
+ BorderRadius.circular(14),
212
+ borderSide: const BorderSide(
213
+ color: Colors.transparent,
214
+ )),
215
),
162
- )
163
- : Offstage(),
164
- ],
216
+ ),
217
+ ),
218
+ Divider(
219
+ color: Theme.of(context)
220
+ .accentTextTheme
221
+ .headline6!
222
+ .backgroundColor!,
223
+ height: 1,
224
+ ),
225
+ if (widget.selectedAtIndex != -1)
226
+ buildSelectedItem(widget.selectedAtIndex),
227
+ Flexible(
228
+ child: Stack(
229
+ alignment: Alignment.center,
230
+ children: <Widget>[
231
+ filteredItems.length > 3
232
+ ? Scrollbar(
233
+ controller: controller,
234
+ child: itemsList(),
235
+ )
236
+ : itemsList(),
237
+ (widget.description?.isNotEmpty ?? false)
238
+ ? Positioned(
239
+ bottom: padding,
240
+ left: padding,
241
+ right: padding,
242
+ child: Text(
243
+ widget.description!,
244
+ textAlign: TextAlign.center,
245
+ style: TextStyle(
246
+ fontSize: 12,
247
+ fontWeight: FontWeight.w500,
248
+ fontFamily: 'Lato',
249
+ decoration:
250
+ TextDecoration.none,
251
+ color: Theme.of(context)
252
+ .primaryTextTheme
253
+ .headline6!
254
+ .color!,
255
+ ),
256
+ ),
257
+ )
258
+ : Offstage(),
259
+ ],
260
+ ),
261
+ ),
262
+ ],
263
+ ),
264
),
265
),
167
- ],
168
- ),
266
+ ),
267
+ )
268
+ ],
269
),
170
- ),
270
+ SizedBox(height: ResponsiveLayoutUtil.kPopupSpaceHeight),
271
+ AlertCloseButton(bottom: closeButtonBottom),
272
+ ],
273
),
274
),
173
- SizedBox(height: ResponsiveLayoutUtil.kPopupSpaceHeight),
174
- AlertCloseButton(),
275
+ // gives the extra spacing using MediaQuery.viewInsets.bottom
276
+ // to simulate a keyboard area
277
+ SizedBox(
278
+ height: bottom,
279
+ )
280
],
281
),
282
);
@@ -185,7 +290,7 @@ class _PickerState<Item> extends State<Picker<Item>> {
290
padding: EdgeInsets.zero,
291
controller: controller,
292
shrinkWrap: true,
188
- itemCount: items.isEmpty ? 0 : items.length,
293
+ itemCount: filteredItems.isEmpty ? 0 : filteredItems.length,
294
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
295
crossAxisCount: 2,
296
crossAxisSpacing: 2,
@@ -199,34 +304,42 @@ class _PickerState<Item> extends State<Picker<Item>> {
304
shrinkWrap: true,
305
separatorBuilder: (context, index) => widget.isSeparated
306
? Divider(
202
- color: Theme.of(context).accentTextTheme.headline6!.backgroundColor!,
307
+ color: Theme.of(context)
308
+ .accentTextTheme
309
+ .headline6!
310
+ .backgroundColor!,
311
height: 1,
312
)
313
: const SizedBox(),
206
- itemCount: items.isEmpty ? 0 : items.length,
314
+ itemCount: filteredItems.isEmpty ? 0 : filteredItems.length,
315
itemBuilder: (context, index) => buildItem(index),
316
),
317
);
318
}
319
320
Widget buildItem(int index) {
213
- /// don't show selected item in the list view
214
- if (widget.items[widget.selectedAtIndex] == items[index] && !widget.isGridView) {
215
- return const SizedBox();
216
- }
321
+ final item = filteredItems[index];
322
+ final tag = item is Currency ? item.tag : null;
323
218
- final item = items[index];
219
- final image = images.isNotEmpty ? images[index] : null;
324
+ final icon = item is Currency && item.iconPath != null
325
+ ? Image.asset(
326
+ item.iconPath!,
327
+ height: 20.0,
328
+ width: 20.0,
329
+ )
330
+ : null;
331
+
332
+ final image = images.isNotEmpty ? filteredImages[index] : icon;
333
334
return GestureDetector(
335
onTap: () {
336
Navigator.of(context).pop();
224
- onItemSelected(item);
337
+ onItemSelected(item!);
338
},
339
child: Container(
340
height: 55,
341
color: Theme.of(context).accentTextTheme.headline6!.color!,
229
- padding: EdgeInsets.only(left: 24, right: 24),
342
+ padding: EdgeInsets.symmetric(horizontal: 24),
343
child: Row(
344
mainAxisSize: MainAxisSize.max,
345
mainAxisAlignment: widget.mainAxisAlignment,
@@ -236,15 +349,53 @@ class _PickerState<Item> extends State<Picker<Item>> {
349
Expanded(
350
child: Padding(
351
padding: EdgeInsets.only(left: image != null ? 12 : 0),
239
- child: Text(
240
- widget.displayItem?.call(item) ?? item.toString(),
241
- style: TextStyle(
242
- fontSize: 14,
243
- fontFamily: 'Lato',
244
- fontWeight: FontWeight.w600,
245
- color: Theme.of(context).primaryTextTheme.headline6!.color!,
246
- decoration: TextDecoration.none,
247
- ),
352
+ child: Row(
353
+ children: [
354
+ Flexible(
355
+ child: Text(
356
+ widget.displayItem?.call(item) ?? item.toString(),
357
+ softWrap: true,
358
+ style: TextStyle(
359
+ fontSize: 14,
360
+ fontFamily: 'Lato',
361
+ fontWeight: FontWeight.w600,
362
+ color: Theme.of(context)
363
+ .primaryTextTheme
364
+ .headline6!
365
+ .color!,
366
+ decoration: TextDecoration.none,
367
+ ),
368
+ ),
369
+ ),
370
+ if (tag != null)
371
+ Align(
372
+ alignment: Alignment.topCenter,
373
+ child: Container(
374
+ width: 35.0,
375
+ height: 18.0,
376
+ child: Center(
377
+ child: Text(
378
+ tag,
379
+ style: TextStyle(
380
+ fontSize: 7.0,
381
+ fontFamily: 'Lato',
382
+ color: Theme.of(context)
383
+ .textTheme
384
+ .bodyText2!
385
+ .color!),
386
+ ),
387
+ ),
388
+ decoration: BoxDecoration(
389
+ borderRadius: BorderRadius.circular(6.0),
390
+ //border: Border.all(color: ),
391
+ color: Theme.of(context)
392
+ .textTheme
393
+ .bodyText2!
394
+ .decorationColor!,
395
+ ),
396
+ ),
397
+ ),
398
+ ],
399
),
400
),
401
),
@@ -254,37 +405,91 @@ class _PickerState<Item> extends State<Picker<Item>> {
405
);
406
}
407
257
- Widget buildSelectedItem() {
258
- final item = widget.items[widget.selectedAtIndex];
259
- final image = images.isNotEmpty ? widget.images[widget.selectedAtIndex] : null;
408
+ Widget buildSelectedItem(int index) {
409
+ final item = items[index];
410
+ final tag = item is Currency ? item.tag : null;
411
261
- return Container(
262
- height: 55,
263
- color: Theme.of(context).accentTextTheme.headline6!.color!,
264
- padding: EdgeInsets.only(left: 24, right: 24),
265
- child: Row(
266
- mainAxisSize: MainAxisSize.max,
267
- mainAxisAlignment: widget.mainAxisAlignment,
268
- crossAxisAlignment: CrossAxisAlignment.center,
269
- children: <Widget>[
270
- image ?? Offstage(),
271
- Expanded(
272
- child: Padding(
273
- padding: EdgeInsets.only(left: image != null ? 12 : 0),
274
- child: Text(
275
- widget.displayItem?.call(item) ?? item.toString(),
276
- style: TextStyle(
277
- fontSize: 16,
278
- fontFamily: 'Lato',
279
- fontWeight: FontWeight.w700,
280
- color: Theme.of(context).primaryTextTheme.headline6!.color!,
281
- decoration: TextDecoration.none,
412
+ final icon = item is Currency && item.iconPath != null
413
+ ? Image.asset(
414
+ item.iconPath!,
415
+ height: 20.0,
416
+ width: 20.0,
417
+ )
418
+ : null;
419
+
420
+ final image = images.isNotEmpty ? images[index] : icon;
421
+
422
+ return GestureDetector(
423
+ onTap: () {
424
+ Navigator.of(context).pop();
425
+ },
426
+ child: Container(
427
+ height: 55,
428
+ color: Theme.of(context).accentTextTheme.headline6!.color!,
429
+ padding: EdgeInsets.symmetric(horizontal: 24),
430
+ child: Row(
431
+ mainAxisSize: MainAxisSize.max,
432
+ mainAxisAlignment: widget.mainAxisAlignment,
433
+ crossAxisAlignment: CrossAxisAlignment.center,
434
+ children: <Widget>[
435
+ image ?? Offstage(),
436
+ Expanded(
437
+ child: Padding(
438
+ padding: EdgeInsets.only(left: image != null ? 12 : 0),
439
+ child: Row(
440
+ children: [
441
+ Flexible(
442
+ child: Text(
443
+ widget.displayItem?.call(item) ?? item.toString(),
444
+ softWrap: true,
445
+ style: TextStyle(
446
+ fontSize: 16,
447
+ fontFamily: 'Lato',
448
+ fontWeight: FontWeight.w700,
449
+ color: Theme.of(context)
450
+ .primaryTextTheme
451
+ .headline6!
452
+ .color!,
453
+ decoration: TextDecoration.none,
454
+ ),
455
+ ),
456
+ ),
457
+ if (tag != null)
458
+ Align(
459
+ alignment: Alignment.topCenter,
460
+ child: Container(
461
+ width: 35.0,
462
+ height: 18.0,
463
+ child: Center(
464
+ child: Text(
465
+ tag,
466
+ style: TextStyle(
467
+ fontSize: 7.0,
468
+ fontFamily: 'Lato',
469
+ color: Theme.of(context)
470
+ .textTheme
471
+ .bodyText2!
472
+ .color!),
473
+ ),
474
+ ),
475
+ decoration: BoxDecoration(
476
+ borderRadius: BorderRadius.circular(6.0),
477
+ //border: Border.all(color: ),
478
+ color: Theme.of(context)
479
+ .textTheme
480
+ .bodyText2!
481
+ .decorationColor!,
482
+ ),
483
+ ),
484
+ ),
485
+ ],
486
),
487
),
488
),
285
- ),
286
- Icon(Icons.check_circle, color: Theme.of(context).accentTextTheme.bodyText1!.color!),
287
- ],
489
+ Icon(Icons.check_circle,
490
+ color: Theme.of(context).accentTextTheme.bodyText1!.color!),
491
+ ],
492
+ ),
493
),
494
);
495
}
lib/src/widgets/seed_language_selector.dart
+13
-13
@@ -5,7 +5,8 @@ import 'package:cake_wallet/src/screens/new_wallet/widgets/select_button.dart';
5
import 'package:cake_wallet/src/screens/seed_language/widgets/seed_language_picker.dart';
6
7
class SeedLanguageSelector extends StatefulWidget {
8
- SeedLanguageSelector({Key? key, required this.initialSelected}) : super(key: key);
8
+ SeedLanguageSelector({Key? key, required this.initialSelected})
9
+ : super(key: key);
10
11
final String initialSelected;
12
@@ -30,21 +31,20 @@ class SeedLanguageSelectorState extends State<SeedLanguageSelector> {
31
S.current.seed_language_italian,
32
];
33
String selected;
33
- final _pickerKey = GlobalKey<SeedLanguagePickerState>();
34
35
@override
36
Widget build(BuildContext context) {
37
return SelectButton(
38
- image: null,
39
- text: seedLocales[seedLanguages.indexOf(selected)],
40
- onTap: () async {
41
- final selected = await showPopUp<String>(
42
- context: context,
43
- builder: (BuildContext context) =>
44
- SeedLanguagePicker(key: _pickerKey, selected: this.selected));
45
- if (selected != null) {
46
- setState(() => this.selected = selected);
47
- }
48
- });
38
+ image: null,
39
+ text: seedLocales[seedLanguages.indexOf(selected)],
40
+ onTap: () async {
41
+ await showPopUp<String>(
42
+ context: context,
43
+ builder: (_) => SeedLanguagePicker(
44
+ selected: this.selected,
45
+ onItemSelected: (String selected) =>
46
+ setState(() => this.selected = selected)));
47
+ },
48
+ );
49
}
50
}