1
+import 'package:cake_wallet/generated/i18n.dart';
2
+import 'package:cake_wallet/new-ui/widgets/currency_picker/chain_chip_strip.dart';
3
+import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_args.dart';
4
+import 'package:cake_wallet/new-ui/widgets/currency_picker/picker_recents_loader.dart';
5
+import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_list_container.dart';
6
+import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_row.dart';
7
+import 'package:cake_wallet/new-ui/widgets/currency_picker/currency_picker_search_field.dart';
8
+import 'package:cake_wallet/new-ui/widgets/currency_picker/picker_section_header.dart';
9
+import 'package:cake_wallet/new-ui/widgets/currency_picker/pill_grid.dart';
10
+import 'package:cake_wallet/new-ui/widgets/currency_picker/select_network_page.dart';
11
+import 'package:cake_wallet/src/widgets/cake_image_widget.dart';
12
+import 'package:cake_wallet/wallet_types.g.dart';
13
+import 'package:cw_core/crypto_currency.dart';
14
+import 'package:cw_core/currency_for_wallet_type.dart';
15
+import 'package:cw_core/currency_groups.dart';
16
+import 'package:cw_core/utils/print_verbose.dart';
17
+import 'package:cw_core/wallet_type.dart';
18
+import 'package:flutter/material.dart';
19
+
20
+class MultiNetworkCurrencyPicker extends StatefulWidget {
21
+ const MultiNetworkCurrencyPicker({super.key, required this.args});
22
+
23
+ final CurrencyPickerArgs args;
24
+
25
+ @override
26
+ State<MultiNetworkCurrencyPicker> createState() => _MultiNetworkCurrencyPickerState();
27
+}
28
+
29
+class _MultiNetworkCurrencyPickerState extends State<MultiNetworkCurrencyPicker> {
30
+ bool _recentsLoaded = false;
31
+ WalletType? _selectedNetwork;
32
+ List<CryptoCurrency> _recents = const [];
33
+ final TextEditingController _searchController = TextEditingController();
34
+
35
+ bool get _isSearching => _searchController.text.trim().isNotEmpty;
36
+
37
+ late final List<WalletType> _networks = _computeNetworks();
38
+
39
+ List<WalletType> _computeNetworks() {
40
+ final counts = <WalletType, int>{};
41
+ for (final currency in widget.args.items) {
42
+ final walletType = cryptoCurrencyOrTokenToWalletType(currency);
43
+ if (walletType == null) continue;
44
+ counts[walletType] = (counts[walletType] ?? 0) + 1;
45
+ }
46
+ return counts.entries.where((e) => e.value >= 2).map((e) => e.key).toList(growable: false);
47
+ }
48
+
49
+ Set<CryptoCurrency> get _natives =>
50
+ {for (final walletType in availableWalletTypes) walletTypeToCryptoCurrency(walletType)};
51
+
52
+ @override
53
+ void initState() {
54
+ super.initState();
55
+ _searchController.addListener(() => setState(() {}));
56
+ _loadRecents();
57
+ }
58
+
59
+ @override
60
+ void dispose() {
61
+ _searchController.dispose();
62
+ super.dispose();
63
+ }
64
+
65
+ Future<void> _loadRecents() async {
66
+ try {
67
+ final recents = await PickerRecentsLoader.load(
68
+ source: widget.args.recentsSource,
69
+ visibleItems: widget.args.items,
70
+ );
71
+ if (!mounted) return;
72
+ setState(() {
73
+ _recents = recents;
74
+ _recentsLoaded = true;
75
+ });
76
+ } catch (e, s) {
77
+ printV('load picker recents failed: $e\n$s');
78
+ if (!mounted) return;
79
+ setState(() => _recentsLoaded = true);
80
+ }
81
+ }
82
+
83
+ bool _matchesNetwork(CryptoCurrency c) {
84
+ if (_selectedNetwork == null) return true;
85
+ return cryptoCurrencyOrTokenToWalletType(c) == _selectedNetwork;
86
+ }
87
+
88
+ List<CryptoCurrency> get _visibleItems {
89
+ final query = _searchController.text.trim();
90
+ return widget.args.items
91
+ .where(_hasSupportedChain)
92
+ .where((c) => currencyMatchesQuery(c, query) && _matchesNetwork(c))
93
+ .toList(growable: false);
94
+ }
95
+
96
+ bool _hasSupportedChain(CryptoCurrency c) {
97
+ if (c.tag == null) return true;
98
+ return cryptoCurrencyOrTokenToWalletType(c) != null;
99
+ }
100
+
101
+ void _selectCurrency(CryptoCurrency currency) {
102
+ widget.args.onSelected(currency);
103
+ Navigator.of(context).maybePop();
104
+ }
105
+
106
+ void _onStablecoinPillTapped(CryptoCurrency tapped) {
107
+ var variants = widget.args.items
108
+ .where((c) =>
109
+ isTrustedStablecoin(c) &&
110
+ c.title.toUpperCase() == tapped.title.toUpperCase() &&
111
+ cryptoCurrencyOrTokenToWalletType(c) != null)
112
+ .toList();
113
+
114
+ if (_selectedNetwork != null) {
115
+ final filtered = variants
116
+ .where((c) => cryptoCurrencyOrTokenToWalletType(c) == _selectedNetwork)
117
+ .toList(growable: false);
118
+ if (filtered.isNotEmpty) variants = filtered;
119
+ }
120
+
121
+ if (variants.length <= 1) {
122
+ _selectCurrency(variants.isNotEmpty ? variants.first : tapped);
123
+ return;
124
+ }
125
+
126
+ Navigator.of(context).push(
127
+ PageRouteBuilder<void>(
128
+ opaque: true,
129
+ transitionDuration: const Duration(milliseconds: 250),
130
+ reverseTransitionDuration: const Duration(milliseconds: 250),
131
+ pageBuilder: (_, __, ___) => SelectNetworkPage(
132
+ assetTitle: tapped.title,
133
+ assetFullName: tapped.fullName,
134
+ assetIconPath: tapped.iconPath,
135
+ variants: variants,
136
+ onSelected: widget.args.onSelected,
137
+ ),
138
+ transitionsBuilder: (_, animation, __, child) => SlideTransition(
139
+ position: animation.drive(
140
+ Tween<Offset>(begin: const Offset(0, 1), end: Offset.zero)
141
+ .chain(CurveTween(curve: Curves.easeInOut)),
142
+ ),
143
+ child: child,
144
+ ),
145
+ ),
146
+ );
147
+ }
148
+
149
+ @override
150
+ Widget build(BuildContext context) {
151
+ return Column(
152
+ mainAxisSize: MainAxisSize.max,
153
+ children: [
154
+ if (_networks.length > 1)
155
+ ChainChipStrip(
156
+ walletTypes: _networks,
157
+ selected: _selectedNetwork,
158
+ onSelected: (network) => setState(() => _selectedNetwork = network),
159
+ ),
160
+ Expanded(
161
+ child: _MultiNetworkPickerBody(
162
+ items: _visibleItems,
163
+ isSearching: _isSearching,
164
+ recents: _recents,
165
+ recentsLoaded: _recentsLoaded,
166
+ natives: _natives,
167
+ selected: widget.args.selected,
168
+ symbolResolver: widget.args.symbolResolver,
169
+ onSelect: _selectCurrency,
170
+ onStablecoinTap: _onStablecoinPillTapped,
171
+ ),
172
+ ),
173
+ CurrencyPickerSearchField(
174
+ controller: _searchController,
175
+ hintText: S.of(context).search,
176
+ ),
177
+ const SizedBox(height: 24),
178
+ ],
179
+ );
180
+ }
181
+}
182
+
183
+class _MultiNetworkPickerBody extends StatefulWidget {
184
+ const _MultiNetworkPickerBody({
185
+ required this.items,
186
+ required this.isSearching,
187
+ required this.recents,
188
+ required this.recentsLoaded,
189
+ required this.natives,
190
+ required this.selected,
191
+ required this.symbolResolver,
192
+ required this.onSelect,
193
+ required this.onStablecoinTap,
194
+ });
195
+
196
+ final bool isSearching;
197
+ final bool recentsLoaded;
198
+ final CryptoCurrency? selected;
199
+ final List<CryptoCurrency> items;
200
+ final List<CryptoCurrency> recents;
201
+ final Set<CryptoCurrency> natives;
202
+ final void Function(CryptoCurrency) onSelect;
203
+ final String Function(CryptoCurrency) symbolResolver;
204
+ final void Function(CryptoCurrency) onStablecoinTap;
205
+
206
+ @override
207
+ State<_MultiNetworkPickerBody> createState() => _MultiNetworkPickerBodyState();
208
+}
209
+
210
+class _MultiNetworkPickerBodyState extends State<_MultiNetworkPickerBody> {
211
+ static const int _previewCount = 3;
212
+
213
+ final ScrollController _scrollController = ScrollController();
214
+ bool _moreCryptosSectionExpanded = false;
215
+ bool _xstocksSectionExpanded = false;
216
+
217
+ @override
218
+ void initState() {
219
+ super.initState();
220
+ final selected = widget.selected;
221
+ if (selected == null) return;
222
+
223
+ final moreCryptos = _computeMoreCryptosSection(widget.items);
224
+ final xstocks = _computeXstocksSection(widget.items);
225
+
226
+ if (moreCryptos.indexOf(selected) >= _previewCount) {
227
+ _moreCryptosSectionExpanded = true;
228
+ }
229
+
230
+ if (xstocks.indexOf(selected) >= _previewCount) {
231
+ _xstocksSectionExpanded = true;
232
+ }
233
+ }
234
+
235
+ @override
236
+ void dispose() {
237
+ _scrollController.dispose();
238
+ super.dispose();
239
+ }
240
+
241
+ List<CryptoCurrency> _computeMoreCryptosSection(List<CryptoCurrency> from) {
242
+ final list = from
243
+ .where((c) =>
244
+ !widget.natives.contains(c) &&
245
+ cryptoCurrencyOrTokenToWalletType(c) == null &&
246
+ c.tag == null &&
247
+ !c.groups.contains(CurrencyGroups.stablecoin) &&
248
+ !c.groups.contains(CurrencyGroups.tokenizedStock))
249
+ .toList();
250
+
251
+ list.sort((a, b) => (a.fullName ?? a.title).toLowerCase().compareTo(
252
+ (b.fullName ?? b.title).toLowerCase(),
253
+ ));
254
+
255
+ return list;
256
+ }
257
+
258
+ List<CryptoCurrency> _computeXstocksSection(List<CryptoCurrency> from) {
259
+ return from
260
+ .where((c) => c.groups.contains(CurrencyGroups.tokenizedStock))
261
+ .toList(growable: false);
262
+ }
263
+
264
+ @override
265
+ Widget build(BuildContext context) {
266
+ final items = widget.items;
267
+ final isSearching = widget.isSearching;
268
+ final recents = widget.recents;
269
+ final recentsLoaded = widget.recentsLoaded;
270
+ final natives = widget.natives;
271
+ final selected = widget.selected;
272
+ final symbolResolver = widget.symbolResolver;
273
+ final onSelect = widget.onSelect;
274
+ final onStablecoinTap = widget.onStablecoinTap;
275
+
276
+ if (items.isEmpty) {
277
+ return Center(
278
+ child: Padding(
279
+ padding: const EdgeInsets.all(24),
280
+ child: Text(
281
+ S.of(context).picker_no_matches,
282
+ textAlign: TextAlign.center,
283
+ style: Theme.of(context).textTheme.bodyMedium?.copyWith(
284
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
285
+ ),
286
+ ),
287
+ ),
288
+ );
289
+ }
290
+
291
+ if (isSearching) {
292
+ return NotificationListener<ScrollNotification>(
293
+ onNotification: (_) => true,
294
+ child: ListView(
295
+ controller: _scrollController,
296
+ primary: false,
297
+ physics: const ClampingScrollPhysics(),
298
+ padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
299
+ children: [
300
+ CurrencyPickerListContainer(
301
+ rows: [
302
+ for (final item in items)
303
+ CurrencyPickerRow(
304
+ currency: item,
305
+ isSelected: selected != null && selected == item,
306
+ chainPillLabel: _chainPillLabelFor(item),
307
+ chainBadgePath: _chainBadgePathFor(item),
308
+ trailing: _SymbolTrailing(
309
+ currency: item,
310
+ symbolResolver: symbolResolver,
311
+ ),
312
+ onTap: () => onSelect(item),
313
+ ),
314
+ ],
315
+ ),
316
+ ],
317
+ ),
318
+ );
319
+ }
320
+
321
+ final visibleRecents = recents.where(items.contains).toList(growable: false);
322
+
323
+ final seenStablecoinTitles = <String>{};
324
+ final stablecoins = items
325
+ .where((c) => isTrustedStablecoin(c) && seenStablecoinTitles.add(c.title.toUpperCase()))
326
+ .toList(growable: false);
327
+
328
+ final cryptocurrencies = items.where(natives.contains).toList(growable: false);
329
+ final moreCryptos = _computeMoreCryptosSection(items);
330
+ final xstocks = _computeXstocksSection(items);
331
+ final allAssets = [...items]..sort((a, b) => (a.fullName ?? a.title).toLowerCase().compareTo(
332
+ (b.fullName ?? b.title).toLowerCase(),
333
+ ));
334
+
335
+ final section = _selectedSection(
336
+ recents: visibleRecents,
337
+ stablecoins: stablecoins,
338
+ cryptocurrencies: cryptocurrencies,
339
+ moreCryptos: moreCryptos,
340
+ xstocks: xstocks,
341
+ );
342
+
343
+ final moreCryptosVisible = _moreCryptosSectionExpanded
344
+ ? moreCryptos
345
+ : moreCryptos.take(_previewCount).toList(growable: false);
346
+ final xstocksVisible =
347
+ _xstocksSectionExpanded ? xstocks : xstocks.take(_previewCount).toList(growable: false);
348
+
349
+ return NotificationListener<ScrollNotification>(
350
+ onNotification: (_) => true,
351
+ child: ListView(
352
+ controller: _scrollController,
353
+ primary: false,
354
+ physics: const ClampingScrollPhysics(),
355
+ padding: const EdgeInsets.fromLTRB(16, 8, 16, 16),
356
+ children: [
357
+ if (recentsLoaded && visibleRecents.isNotEmpty)
358
+ _PickerSection(
359
+ title: S.of(context).picker_section_recents,
360
+ child: _RecentsRow(
361
+ items: visibleRecents,
362
+ selected: section == _SelSection.recents ? selected : null,
363
+ symbolResolver: symbolResolver,
364
+ onTap: onSelect,
365
+ ),
366
+ ),
367
+ if (stablecoins.isNotEmpty)
368
+ _PickerSection(
369
+ title: S.of(context).picker_section_stablecoins,
370
+ child: PillGrid(
371
+ items: stablecoins,
372
+ selected: section == _SelSection.stablecoins ? selected : null,
373
+ onTap: onStablecoinTap,
374
+ symbolResolver: symbolResolver,
375
+ ),
376
+ ),
377
+ if (cryptocurrencies.isNotEmpty)
378
+ _PickerSection(
379
+ title: S.of(context).picker_section_cryptocurrencies,
380
+ child: CurrencyPickerListContainer(
381
+ rows: [
382
+ for (final item in cryptocurrencies)
383
+ CurrencyPickerRow(
384
+ currency: item,
385
+ isSelected: section == _SelSection.cryptocurrencies && selected == item,
386
+ chainPillLabel: _chainPillLabelFor(item),
387
+ chainBadgePath: _chainBadgePathFor(item),
388
+ trailing: _SymbolTrailing(
389
+ currency: item,
390
+ symbolResolver: symbolResolver,
391
+ ),
392
+ onTap: () => onSelect(item),
393
+ ),
394
+ ],
395
+ ),
396
+ ),
397
+ if (moreCryptos.isNotEmpty)
398
+ _PickerSection(
399
+ title: S.of(context).picker_section_more_cryptocurrencies,
400
+ child: CurrencyPickerListContainer(
401
+ rows: [
402
+ for (final item in moreCryptosVisible)
403
+ CurrencyPickerRow(
404
+ currency: item,
405
+ isSelected: section == _SelSection.moreCryptocurrencies && selected == item,
406
+ trailing: _SymbolTrailing(
407
+ currency: item,
408
+ symbolResolver: symbolResolver,
409
+ ),
410
+ onTap: () => onSelect(item),
411
+ ),
412
+ if (moreCryptos.length > _previewCount)
413
+ _SeeAllRow(
414
+ expanded: _moreCryptosSectionExpanded,
415
+ onTap: () => setState(
416
+ () => _moreCryptosSectionExpanded = !_moreCryptosSectionExpanded),
417
+ ),
418
+ ],
419
+ ),
420
+ ),
421
+ if (xstocks.isNotEmpty)
422
+ _PickerSection(
423
+ title: S.of(context).picker_section_tokenized_stocks,
424
+ child: CurrencyPickerListContainer(
425
+ rows: [
426
+ for (final item in xstocksVisible)
427
+ CurrencyPickerRow(
428
+ currency: item,
429
+ isSelected: section == _SelSection.xstocks && selected == item,
430
+ trailing: _SymbolTrailing(
431
+ currency: item,
432
+ symbolResolver: symbolResolver,
433
+ ),
434
+ onTap: () => onSelect(item),
435
+ chainPillLabel: _chainPillLabelFor(item),
436
+ chainBadgePath: _chainBadgePathFor(item),
437
+ ),
438
+ if (xstocks.length > _previewCount)
439
+ _SeeAllRow(
440
+ expanded: _xstocksSectionExpanded,
441
+ onTap: () =>
442
+ setState(() => _xstocksSectionExpanded = !_xstocksSectionExpanded),
443
+ ),
444
+ ],
445
+ ),
446
+ ),
447
+ if (allAssets.isNotEmpty)
448
+ _PickerSection(
449
+ title: S.of(context).picker_section_all_assets,
450
+ child: CurrencyPickerListContainer(
451
+ rows: [
452
+ for (final item in allAssets)
453
+ CurrencyPickerRow(
454
+ currency: item,
455
+ isSelected: section == _SelSection.allAssets && selected == item,
456
+ chainPillLabel: _chainPillLabelFor(item),
457
+ chainBadgePath: _chainBadgePathFor(item),
458
+ trailing: _SymbolTrailing(
459
+ currency: item,
460
+ symbolResolver: symbolResolver,
461
+ ),
462
+ onTap: () => onSelect(item),
463
+ ),
464
+ ],
465
+ ),
466
+ ),
467
+ ],
468
+ ),
469
+ );
470
+ }
471
+
472
+ String? _chainPillLabelFor(CryptoCurrency c) {
473
+ if (c == CryptoCurrency.btcln) return _shortChainLabel(c);
474
+ if (_isL2NativeEth(c)) return _shortChainLabel(c);
475
+ if (widget.natives.contains(c)) return null;
476
+ if (cryptoCurrencyOrTokenToWalletType(c) == null) return null;
477
+ return _shortChainLabel(c);
478
+ }
479
+
480
+ String _shortChainLabel(CryptoCurrency c) {
481
+ if (cryptoCurrencyOrTokenToWalletType(c) == WalletType.bsc) return 'BSC';
482
+ return chainNameForCurrency(c);
483
+ }
484
+
485
+ String? _chainBadgePathFor(CryptoCurrency c) {
486
+ if (_isL2NativeEth(c)) return c.chainIconPath;
487
+ if (widget.natives.contains(c)) return null;
488
+ final wt = cryptoCurrencyOrTokenToWalletType(c);
489
+ if (wt == null) return null;
490
+ return c.chainIconPath ?? walletTypeToCryptoCurrency(wt).chainIconPath;
491
+ }
492
+
493
+ bool _isL2NativeEth(CryptoCurrency c) =>
494
+ c == CryptoCurrency.arbEth || c == CryptoCurrency.baseEth;
495
+
496
+ _SelSection? _selectedSection({
497
+ required List<CryptoCurrency> recents,
498
+ required List<CryptoCurrency> stablecoins,
499
+ required List<CryptoCurrency> cryptocurrencies,
500
+ required List<CryptoCurrency> moreCryptos,
501
+ required List<CryptoCurrency> xstocks,
502
+ }) {
503
+ final s = widget.selected;
504
+ if (s == null) return null;
505
+ if (recents.contains(s)) return _SelSection.recents;
506
+ if (stablecoins.any((c) => c.title.toUpperCase() == s.title.toUpperCase())) {
507
+ return _SelSection.stablecoins;
508
+ }
509
+ if (cryptocurrencies.contains(s)) return _SelSection.cryptocurrencies;
510
+ if (moreCryptos.contains(s)) return _SelSection.moreCryptocurrencies;
511
+ if (xstocks.contains(s)) return _SelSection.xstocks;
512
+ return _SelSection.allAssets;
513
+ }
514
+}
515
+
516
+class _PickerSection extends StatelessWidget {
517
+ const _PickerSection({required this.title, required this.child});
518
+
519
+ final String title;
520
+ final Widget child;
521
+
522
+ @override
523
+ Widget build(BuildContext context) {
524
+ return Column(
525
+ crossAxisAlignment: CrossAxisAlignment.start,
526
+ children: [
527
+ PickerSectionHeader(title: title),
528
+ child,
529
+ const SizedBox(height: 16),
530
+ ],
531
+ );
532
+ }
533
+}
534
+
535
+class _SymbolTrailing extends StatelessWidget {
536
+ const _SymbolTrailing({
537
+ required this.currency,
538
+ required this.symbolResolver,
539
+ });
540
+
541
+ final CryptoCurrency currency;
542
+ final String Function(CryptoCurrency) symbolResolver;
543
+
544
+ @override
545
+ Widget build(BuildContext context) {
546
+ return Text(
547
+ symbolResolver(currency),
548
+ style: Theme.of(context).textTheme.bodyMedium?.copyWith(
549
+ fontWeight: FontWeight.w500,
550
+ color: Theme.of(context).colorScheme.onSurfaceVariant,
551
+ ),
552
+ );
553
+ }
554
+}
555
+
556
+class _SeeAllRow extends StatelessWidget {
557
+ const _SeeAllRow({
558
+ required this.expanded,
559
+ required this.onTap,
560
+ });
561
+
562
+ final bool expanded;
563
+ final VoidCallback onTap;
564
+
565
+ @override
566
+ Widget build(BuildContext context) {
567
+ final colors = Theme.of(context).colorScheme;
568
+ final label = expanded ? S.of(context).picker_show_less : S.of(context).picker_see_all;
569
+ return InkWell(
570
+ onTap: onTap,
571
+ child: Padding(
572
+ padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
573
+ child: Row(
574
+ children: [
575
+ Expanded(
576
+ child: Text(
577
+ label,
578
+ style: Theme.of(context).textTheme.bodyMedium?.copyWith(
579
+ fontWeight: FontWeight.w500,
580
+ color: colors.primary,
581
+ ),
582
+ ),
583
+ ),
584
+ ],
585
+ ),
586
+ ),
587
+ );
588
+ }
589
+}
590
+
591
+class _RecentsRow extends StatelessWidget {
592
+ const _RecentsRow({
593
+ required this.items,
594
+ required this.selected,
595
+ required this.symbolResolver,
596
+ required this.onTap,
597
+ });
598
+
599
+ final List<CryptoCurrency> items;
600
+ final CryptoCurrency? selected;
601
+ final String Function(CryptoCurrency) symbolResolver;
602
+ final void Function(CryptoCurrency) onTap;
603
+
604
+ @override
605
+ Widget build(BuildContext context) {
606
+ return SizedBox(
607
+ height: 44,
608
+ child: ListView.separated(
609
+ scrollDirection: Axis.horizontal,
610
+ itemCount: items.length,
611
+ separatorBuilder: (_, __) => const SizedBox(width: 8),
612
+ itemBuilder: (_, i) {
613
+ final item = items[i];
614
+ return _RecentPill(
615
+ currency: item,
616
+ label: symbolResolver(item),
617
+ isSelected: selected != null && selected == item,
618
+ onTap: () => onTap(item),
619
+ );
620
+ },
621
+ ),
622
+ );
623
+ }
624
+}
625
+
626
+class _RecentPill extends StatelessWidget {
627
+ const _RecentPill({
628
+ required this.currency,
629
+ required this.label,
630
+ required this.isSelected,
631
+ required this.onTap,
632
+ });
633
+
634
+ final CryptoCurrency currency;
635
+ final String label;
636
+ final bool isSelected;
637
+ final VoidCallback onTap;
638
+
639
+ @override
640
+ Widget build(BuildContext context) {
641
+ final colors = Theme.of(context).colorScheme;
642
+ return InkWell(
643
+ onTap: onTap,
644
+ borderRadius: BorderRadius.circular(80),
645
+ child: Container(
646
+ padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
647
+ decoration: BoxDecoration(
648
+ color: colors.surfaceContainer,
649
+ borderRadius: BorderRadius.circular(80),
650
+ border: isSelected ? Border.all(color: colors.primary, width: 1.5) : null,
651
+ ),
652
+ child: Row(
653
+ mainAxisSize: MainAxisSize.min,
654
+ children: [
655
+ CakeImageWidget(
656
+ imageUrl: currency.iconPath,
657
+ width: 24,
658
+ height: 24,
659
+ fit: BoxFit.cover,
660
+ ),
661
+ const SizedBox(width: 8),
662
+ Text(
663
+ label,
664
+ style: Theme.of(context).textTheme.bodyMedium?.copyWith(
665
+ fontWeight: FontWeight.w600,
666
+ ),
667
+ ),
668
+ ],
669
+ ),
670
+ ),
671
+ );
672
+ }
673
+}
674
+
675
+enum _SelSection {
676
+ recents,
677
+ stablecoins,
678
+ cryptocurrencies,
679
+ moreCryptocurrencies,
680
+ xstocks,
681
+ allAssets,
682
+}