feat: Add UI signifying the currently selected wallet indicator (#1704)

* feat: Add UI signifying the currently selected wallet indicator * fix: Product Fix - Add more padding to child wallets list item tile in grouped wallets, fix sort by dragging for general single wallets, and also groups, add animation to trailing arrow icon when tile is expanded or not * Update wallet group image and change maxLines --------- Co-authored-by: tuxpizza <tuxsudo@tux.pizza>

David Adegoke committed Sep 27, 2024 at 23:36 UTC b2850c203f319a37d7888465a46b83d951383156
10 files changed +144 -26
assets/images/wallet_group.png
Binary files a/assets/images/wallet_group.png and /dev/null differ
assets/images/wallet_group_bright.png
Binary files /dev/null and b/assets/images/wallet_group_bright.png differ
assets/images/wallet_group_dark.png
Binary files /dev/null and b/assets/images/wallet_group_dark.png differ
assets/images/wallet_group_light.png
Binary files /dev/null and b/assets/images/wallet_group_light.png differ
lib/src/screens/new_wallet/wallet_group_description_page.dart
+14 -5
@@ -3,10 +3,11 @@ import 'package:cake_wallet/src/widgets/primary_button.dart';
3 import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
4 import 'package:cw_core/wallet_type.dart';
5 import 'package:flutter/material.dart';
6 -
6 +import 'package:cake_wallet/themes/theme_base.dart';
7 import 'package:cake_wallet/generated/i18n.dart';
8 import 'package:cake_wallet/routes.dart';
9 import 'package:cake_wallet/src/screens/base_page.dart';
10 +import 'package:flutter_svg/svg.dart';
11
12 class WalletGroupDescriptionPage extends BasePage {
13 WalletGroupDescriptionPage({required this.selectedWalletType});
@@ -16,17 +17,25 @@ class WalletGroupDescriptionPage extends BasePage {
17 @override
18 String get title => S.current.wallet_group;
19
20 +
21 @override
22 Widget body(BuildContext context) {
23 +
24 + final lightImage = 'assets/images/wallet_group_light.png';
25 + final darkImage = 'assets/images/wallet_group_dark.png';
26 + final brightImage = 'assets/images/wallet_group_bright.png';
27 +
28 + final image = currentTheme.type == ThemeType.light ? lightImage : darkImage;
29 +
30 return Container(
31 alignment: Alignment.center,
32 padding: EdgeInsets.all(24),
33 child: Column(
34 children: [
26 - Image.asset(
27 - 'assets/images/wallet_group.png',
28 - scale: 0.8,
29 - ),
35 + Image.asset(
36 + image,
37 + height: 200
38 + ),
39 SizedBox(height: 32),
40 Expanded(
41 child: Text.rich(
lib/src/screens/new_wallet/wallet_group_display_page.dart
+1
@@ -58,6 +58,7 @@ class WalletGroupsDisplayBody extends StatelessWidget {
58 final groupName =
59 group.groupName ?? '${S.of(context).wallet_group} ${index + 1}';
60 return GroupedWalletExpansionTile(
61 + shouldShowCurrentWalletPointer: false,
62 leadingWidget:
63 Icon(Icons.account_balance_wallet_outlined, size: 28),
64 borderRadius: BorderRadius.all(Radius.circular(16)),
lib/src/screens/new_wallet/widgets/grouped_wallet_expansion_tile.dart
+41 -6
@@ -12,6 +12,7 @@ class GroupedWalletExpansionTile extends StatelessWidget {
12 this.childWallets = const [],
13 this.onTitleTapped,
14 this.onChildItemTapped = _defaultVoidCallback,
15 + this.onExpansionChanged,
16 this.leadingWidget,
17 this.trailingWidget,
18 this.childTrailingWidget,
@@ -22,13 +23,18 @@ class GroupedWalletExpansionTile extends StatelessWidget {
23 this.borderRadius,
24 this.margin,
25 this.tileKey,
26 + this.isCurrentlySelectedWallet = false,
27 + this.shouldShowCurrentWalletPointer = false,
28 }) : super(key: tileKey);
29
30 final Key? tileKey;
31 final bool isSelected;
32 + final bool isCurrentlySelectedWallet;
33 + final bool shouldShowCurrentWalletPointer;
34
35 final VoidCallback? onTitleTapped;
36 final void Function(WalletListItem item) onChildItemTapped;
37 + final void Function(bool)? onExpansionChanged;
38
39 final String title;
40 final Widget? leadingWidget;
@@ -70,8 +76,10 @@ class GroupedWalletExpansionTile extends StatelessWidget {
76 splashFactory: NoSplash.splashFactory,
77 ),
78 child: ExpansionTile(
79 + onExpansionChanged: onExpansionChanged,
80 key: tileKey,
74 - tilePadding: EdgeInsets.symmetric(vertical: 1, horizontal: 16),
81 + tilePadding:
82 + EdgeInsets.symmetric(vertical: 1, horizontal: !isCurrentlySelectedWallet ? 16 : 0),
83 iconColor: effectiveArrowColor,
84 collapsedIconColor: effectiveArrowColor,
85 leading: leadingWidget,
@@ -90,19 +98,46 @@ class GroupedWalletExpansionTile extends StatelessWidget {
98 ),
99 children: childWallets.map(
100 (item) {
101 + final currentColor = item.isCurrent
102 + ? Theme.of(context)
103 + .extension<WalletListTheme>()!
104 + .createNewWalletButtonBackgroundColor
105 + : Theme.of(context).colorScheme.background;
106 final walletTypeToCrypto = walletTypeToCryptoCurrency(item.type);
107 return ListTile(
108 + contentPadding: EdgeInsets.zero,
109 key: ValueKey(item.name),
110 trailing: childTrailingWidget?.call(item),
111 onTap: () => onChildItemTapped(item),
98 - leading: Image.asset(
99 - walletTypeToCrypto.iconPath!,
100 - width: 32,
101 - height: 32,
112 + leading: SizedBox(
113 + width: 60,
114 + child: Row(
115 + children: [
116 + item.isCurrent && shouldShowCurrentWalletPointer
117 + ? Container(
118 + height: 35,
119 + width: 6,
120 + decoration: BoxDecoration(
121 + borderRadius: BorderRadius.only(
122 + topRight: Radius.circular(16),
123 + bottomRight: Radius.circular(16),
124 + ),
125 + color: currentColor,
126 + ),
127 + )
128 + : SizedBox(width: 6),
129 + SizedBox(width: 16),
130 + Image.asset(
131 + walletTypeToCrypto.iconPath!,
132 + width: 32,
133 + height: 32,
134 + ),
135 + ],
136 + ),
137 ),
138 title: Text(
139 item.name,
105 - maxLines: 1,
140 + maxLines: 2,
141 style: TextStyle(
142 fontSize: 18,
143 fontWeight: FontWeight.w500,
lib/src/screens/wallet_list/edit_wallet_button_widget.dart
+3 -1
@@ -7,11 +7,13 @@ class EditWalletButtonWidget extends StatelessWidget {
7 required this.width,
8 required this.onTap,
9 this.isGroup = false,
10 + this.isExpanded = false,
11 super.key,
12 });
13
14 final bool isGroup;
15 final double width;
16 + final bool isExpanded;
17 final VoidCallback onTap;
18
19 @override
@@ -42,7 +44,7 @@ class EditWalletButtonWidget extends StatelessWidget {
44 if (isGroup) ...{
45 SizedBox(width: 6),
46 Icon(
45 - Icons.keyboard_arrow_down,
47 + isExpanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
48 size: 24,
49 color: Theme.of(context).extension<FilterTheme>()!.titlesColor,
50 ),
lib/src/screens/wallet_list/wallet_list_page.dart
+55 -11
@@ -11,6 +11,7 @@ import 'package:cake_wallet/themes/extensions/cake_text_theme.dart';
11 import 'package:cake_wallet/src/screens/auth/auth_page.dart';
12 import 'package:cake_wallet/core/auth_service.dart';
13 import 'package:cake_wallet/themes/extensions/filter_theme.dart';
14 +import 'package:cake_wallet/themes/extensions/wallet_list_theme.dart';
15 import 'package:cake_wallet/utils/responsive_layout_util.dart';
16 import 'package:cake_wallet/utils/show_bar.dart';
17 import 'package:cake_wallet/utils/show_pop_up.dart';
@@ -156,7 +157,18 @@ class WalletListBodyState extends State<WalletListBody> {
157 final group = widget.walletListViewModel.multiWalletGroups[index];
158 final groupName = group.groupName ??
159 '${S.current.wallet_group} ${index + 1}';
160 +
161 + widget.walletListViewModel.updateTileState(
162 + index,
163 + widget.walletListViewModel.expansionTileStateTrack[index] ?? false,
164 + );
165 +
166 return GroupedWalletExpansionTile(
167 + onExpansionChanged: (value) {
168 + widget.walletListViewModel.updateTileState(index, value);
169 + setState(() {});
170 + },
171 + shouldShowCurrentWalletPointer: true,
172 borderRadius: BorderRadius.all(Radius.circular(16)),
173 margin: EdgeInsets.only(left: 20, right: 20, bottom: 12),
174 title: groupName,
@@ -168,6 +180,8 @@ class WalletListBodyState extends State<WalletListBody> {
180 trailingWidget: EditWalletButtonWidget(
181 width: 74,
182 isGroup: true,
183 + isExpanded:
184 + widget.walletListViewModel.expansionTileStateTrack[index]!,
185 onTap: () {
186 final wallet = widget.walletListViewModel
187 .convertWalletInfoToWalletListItem(group.wallets.first);
@@ -193,13 +207,16 @@ class WalletListBodyState extends State<WalletListBody> {
207 childTrailingWidget: (item) {
208 return item.isCurrent
209 ? SizedBox.shrink()
196 - : EditWalletButtonWidget(
197 - width: 44,
198 - onTap: () => Navigator.of(context).pushNamed(
199 - Routes.walletEdit,
200 - arguments: WalletEditPageArguments(
201 - walletListViewModel: widget.walletListViewModel,
202 - editingWallet: item,
210 + : Padding(
211 + padding: const EdgeInsets.only(right: 16),
212 + child: EditWalletButtonWidget(
213 + width: 44,
214 + onTap: () => Navigator.of(context).pushNamed(
215 + Routes.walletEdit,
216 + arguments: WalletEditPageArguments(
217 + walletListViewModel: widget.walletListViewModel,
218 + editingWallet: item,
219 + ),
220 ),
221 ),
222 );
@@ -232,13 +249,40 @@ class WalletListBodyState extends State<WalletListBody> {
249 updateFunction: widget.walletListViewModel.reorderAccordingToWalletList,
250 itemBuilder: (context, index) {
251 final wallet = widget.walletListViewModel.singleWalletsList[index];
252 + final currentColor = wallet.isCurrent
253 + ? Theme.of(context)
254 + .extension<WalletListTheme>()!
255 + .createNewWalletButtonBackgroundColor
256 + : Theme.of(context).colorScheme.background;
257
258 return GroupedWalletExpansionTile(
259 tileKey: ValueKey('single_wallets_expansion_tile_widget_$index'),
238 - leadingWidget: Image.asset(
239 - walletTypeToCryptoCurrency(wallet.type).iconPath!,
240 - width: 32,
241 - height: 32,
260 + isCurrentlySelectedWallet: wallet.isCurrent,
261 + leadingWidget: SizedBox(
262 + width: 60,
263 + child: Row(
264 + children: [
265 + wallet.isCurrent
266 + ? Container(
267 + height: 35,
268 + width: 6,
269 + margin: EdgeInsets.only(right: 16),
270 + decoration: BoxDecoration(
271 + borderRadius: BorderRadius.only(
272 + topRight: Radius.circular(16),
273 + bottomRight: Radius.circular(16),
274 + ),
275 + color: currentColor,
276 + ),
277 + )
278 + : SizedBox(width: 6),
279 + Image.asset(
280 + walletTypeToCryptoCurrency(wallet.type).iconPath!,
281 + width: 32,
282 + height: 32,
283 + ),
284 + ],
285 + ),
286 ),
287 title: wallet.name,
288 isSelected: false,
lib/view_model/wallet_list/wallet_list_view_model.dart
+30 -3
@@ -22,7 +22,8 @@ abstract class WalletListViewModelBase with Store {
22 this._walletManager,
23 ) : wallets = ObservableList<WalletListItem>(),
24 multiWalletGroups = ObservableList<WalletGroup>(),
25 - singleWalletsList = ObservableList<WalletListItem>() {
25 + singleWalletsList = ObservableList<WalletListItem>(),
26 + expansionTileStateTrack = ObservableMap<int, bool>() {
27 setOrderType(_appStore.settingsStore.walletListOrder);
28 reaction((_) => _appStore.wallet, (_) => updateList());
29 updateList();
@@ -40,6 +41,18 @@ abstract class WalletListViewModelBase with Store {
41 @observable
42 ObservableList<WalletListItem> singleWalletsList;
43
44 + @observable
45 + ObservableMap<int, bool> expansionTileStateTrack;
46 +
47 + @action
48 + void updateTileState(int index, bool isExpanded) {
49 + if (expansionTileStateTrack.containsKey(index)) {
50 + expansionTileStateTrack.update(index, (value) => isExpanded);
51 + } else {
52 + expansionTileStateTrack.addEntries({index: isExpanded}.entries);
53 + }
54 + }
55 +
56 @computed
57 bool get shouldRequireTOTP2FAForAccessingWallet =>
58 _appStore.settingsStore.shouldRequireTOTP2FAForAccessingWallet;
@@ -100,8 +113,8 @@ abstract class WalletListViewModelBase with Store {
113 // delete all wallets from walletInfoSource:
114 await _walletInfoSource.clear();
115
103 - // add wallets from wallets list in order of wallets list, by name:
104 - for (WalletListItem wallet in wallets) {
116 + // Reorder single wallets using the singleWalletsList
117 + for (WalletListItem wallet in singleWalletsList) {
118 for (int i = 0; i < walletInfoSourceCopy.length; i++) {
119 if (walletInfoSourceCopy[i].name == wallet.name) {
120 await _walletInfoSource.add(walletInfoSourceCopy[i]);
@@ -111,6 +124,20 @@ abstract class WalletListViewModelBase with Store {
124 }
125 }
126
127 + // Reorder wallets within multi-wallet groups
128 + for (WalletGroup group in multiWalletGroups) {
129 + for (WalletInfo walletInfo in group.wallets) {
130 + for (int i = 0; i < walletInfoSourceCopy.length; i++) {
131 + if (walletInfoSourceCopy[i].name == walletInfo.name) {
132 + await _walletInfoSource.add(walletInfoSourceCopy[i]);
133 + walletInfoSourceCopy.removeAt(i);
134 + break;
135 + }
136 + }
137 + }
138 + }
139 +
140 + // Rebuild the list of wallets and groups
141 updateList();
142 }
143