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 }