diff --git a/lib/models/add_wallet_list_entity/add_wallet_list_entity.dart b/lib/models/add_wallet_list_entity/add_wallet_list_entity.dart new file mode 100644 index 000000000..3dd24d7b1 --- /dev/null +++ b/lib/models/add_wallet_list_entity/add_wallet_list_entity.dart @@ -0,0 +1,8 @@ +import 'package:equatable/equatable.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; + +abstract class AddWalletListEntity extends Equatable { + Coin get coin; + String get name; + String get ticker; +} diff --git a/lib/models/add_wallet_list_entity/sub_classes/coin_entity.dart b/lib/models/add_wallet_list_entity/sub_classes/coin_entity.dart new file mode 100644 index 000000000..770a9d1cf --- /dev/null +++ b/lib/models/add_wallet_list_entity/sub_classes/coin_entity.dart @@ -0,0 +1,20 @@ +import 'package:stackwallet/models/add_wallet_list_entity/add_wallet_list_entity.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; + +class CoinEntity extends AddWalletListEntity { + CoinEntity(this._coin); + + final Coin _coin; + + @override + Coin get coin => _coin; + + @override + String get name => coin.prettyName; + + @override + String get ticker => coin.ticker; + + @override + List get props => [coin, name, ticker]; +} diff --git a/lib/models/add_wallet_list_entity/sub_classes/eth_token_entity.dart b/lib/models/add_wallet_list_entity/sub_classes/eth_token_entity.dart new file mode 100644 index 000000000..8392d5f82 --- /dev/null +++ b/lib/models/add_wallet_list_entity/sub_classes/eth_token_entity.dart @@ -0,0 +1,21 @@ +import 'package:stackwallet/models/add_wallet_list_entity/add_wallet_list_entity.dart'; +import 'package:stackwallet/models/ethereum/eth_token.dart'; +import 'package:stackwallet/utilities/enums/coin_enum.dart'; + +class EthTokenEntity extends AddWalletListEntity { + EthTokenEntity(this.token); + + final EthToken token; + + @override + Coin get coin => Coin.ethereum; + + @override + String get name => token.name; + + @override + String get ticker => token.symbol; + + @override + List get props => [coin, name, ticker, token.contractAddress]; +} diff --git a/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart b/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart index fd698679a..1047417a0 100644 --- a/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart +++ b/lib/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart @@ -3,10 +3,12 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/models/add_wallet_list_entity/add_wallet_list_entity.dart'; +import 'package:stackwallet/models/add_wallet_list_entity/sub_classes/coin_entity.dart'; +import 'package:stackwallet/models/add_wallet_list_entity/sub_classes/eth_token_entity.dart'; import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/add_wallet_text.dart'; -import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/mobile_coin_list.dart'; +import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/expanding_sub_list_item.dart'; import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/next_button.dart'; -import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/searchable_coin_list.dart'; import 'package:stackwallet/pages_desktop_specific/my_stack_view/exit_to_my_stack_button.dart'; import 'package:stackwallet/providers/global/prefs_provider.dart'; import 'package:stackwallet/utilities/assets.dart'; @@ -52,6 +54,26 @@ class _AddWalletViewState extends ConsumerState { final bool isDesktop = Util.isDesktop; + List filter( + String text, + List entities, + ) { + final _entities = [...entities]; + if (text.isNotEmpty) { + final lowercaseTerm = text.toLowerCase(); + _entities.retainWhere( + (e) => + e.ticker.toLowerCase().contains(lowercaseTerm) || + e.name.toLowerCase().contains(lowercaseTerm) || + e.coin.name.toLowerCase().contains(lowercaseTerm) || + (e is EthTokenEntity && + e.token.contractAddress.toLowerCase().contains(lowercaseTerm)), + ); + } + + return _entities; + } + @override void initState() { _searchFieldController = TextEditingController(); @@ -186,10 +208,21 @@ class _AddWalletViewState extends ConsumerState { ), ), Expanded( - child: SearchableCoinList( - entities: coinEntities, - isDesktop: true, - searchTerm: _searchTerm, + child: SingleChildScrollView( + child: Column( + children: [ + ExpandingSubListItem( + title: "Coins", + entities: filter(_searchTerm, coinEntities), + initialState: ExpandableState.expanded, + ), + ExpandingSubListItem( + title: "Tokens", + entities: filter(_searchTerm, tokenEntities), + initialState: ExpandableState.collapsed, + ), + ], + ), ), ), ], @@ -238,18 +271,73 @@ class _AddWalletViewState extends ConsumerState { const SizedBox( height: 16, ), + ClipRRect( + borderRadius: BorderRadius.circular( + Constants.size.circularBorderRadius, + ), + child: TextField( + autofocus: isDesktop, + autocorrect: !isDesktop, + enableSuggestions: !isDesktop, + controller: _searchFieldController, + focusNode: _searchFocusNode, + onChanged: (value) => setState(() => _searchTerm = value), + style: STextStyles.field(context), + decoration: standardInputDecoration( + "Search", + _searchFocusNode, + context, + desktopMed: isDesktop, + ).copyWith( + prefixIcon: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 16, + ), + child: SvgPicture.asset( + Assets.svg.search, + width: 16, + height: 16, + ), + ), + suffixIcon: _searchFieldController.text.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(right: 0), + child: UnconstrainedBox( + child: Row( + children: [ + TextFieldIconButton( + child: const XIcon(), + onTap: () async { + setState(() { + _searchFieldController.text = ""; + _searchTerm = ""; + }); + }, + ), + ], + ), + ), + ) + : null, + ), + ), + ), + const SizedBox( + height: 10, + ), Expanded( child: SingleChildScrollView( child: Column( children: [ ExpandingSubListItem( title: "Coins", - entities: coinEntities, + entities: filter(_searchTerm, coinEntities), initialState: ExpandableState.expanded, ), ExpandingSubListItem( title: "Tokens", - entities: tokenEntities, + entities: filter(_searchTerm, tokenEntities), initialState: ExpandableState.collapsed, ), ], @@ -271,91 +359,3 @@ class _AddWalletViewState extends ConsumerState { } } } - -class ExpandingSubListItem extends StatefulWidget { - const ExpandingSubListItem({ - Key? key, - required this.title, - required this.entities, - required this.initialState, - }) : super(key: key); - - final String title; - final List entities; - final ExpandableState initialState; - - @override - State createState() => _ExpandingSubListItemState(); -} - -class _ExpandingSubListItemState extends State { - final isDesktop = Util.isDesktop; - - late final ExpandableController _controller; - - late bool _expandedState; - - @override - void initState() { - _expandedState = widget.initialState == ExpandableState.expanded; - _controller = ExpandableController(); - WidgetsBinding.instance.addPostFrameCallback((_) { - if (_expandedState) { - _controller.toggle?.call(); - } - }); - super.initState(); - } - - @override - Widget build(BuildContext context) { - return Expandable( - controller: _controller, - onExpandChanged: (state) { - setState(() { - _expandedState = state == ExpandableState.expanded; - }); - }, - header: Container( - color: Colors.transparent, - child: Padding( - padding: const EdgeInsets.only( - top: 8.0, - bottom: 8.0, - right: 10, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - widget.title, - style: isDesktop - ? STextStyles.desktopTextExtraExtraSmall(context).copyWith( - color: Theme.of(context) - .extension()! - .textDark3, - ) - : STextStyles.smallMed12(context), - textAlign: TextAlign.left, - ), - SvgPicture.asset( - _expandedState ? Assets.svg.chevronUp : Assets.svg.chevronDown, - width: 12, - height: 6, - color: Theme.of(context) - .extension()! - .textFieldActiveSearchIconRight, - ), - ], - ), - ), - ), - body: SingleChildScrollView( - primary: false, - child: MobileCoinList( - entities: widget.entities, - ), - ), - ); - } -} diff --git a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/add_wallet_entity_list.dart b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/add_wallet_entity_list.dart new file mode 100644 index 000000000..590a8c8ce --- /dev/null +++ b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/add_wallet_entity_list.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:stackwallet/models/add_wallet_list_entity/add_wallet_list_entity.dart'; +import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/coin_select_item.dart'; + +class AddWalletEntityList extends StatelessWidget { + const AddWalletEntityList({ + Key? key, + required this.entities, + }) : super(key: key); + + final List entities; + + @override + Widget build(BuildContext context) { + return ListView.builder( + shrinkWrap: true, + primary: false, + itemCount: entities.length, + itemBuilder: (ctx, index) { + return Padding( + padding: const EdgeInsets.all(4), + child: CoinSelectItem( + entity: entities[index], + ), + ); + }, + ); + } +} diff --git a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/coin_select_item.dart b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/coin_select_item.dart index 0ccd713db..1a7ca829f 100644 --- a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/coin_select_item.dart +++ b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/coin_select_item.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; -import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/mobile_coin_list.dart'; +import 'package:stackwallet/models/add_wallet_list_entity/add_wallet_list_entity.dart'; import 'package:stackwallet/providers/providers.dart'; import 'package:stackwallet/utilities/assets.dart'; import 'package:stackwallet/utilities/constants.dart'; diff --git a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/expanding_sub_list_item.dart b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/expanding_sub_list_item.dart new file mode 100644 index 000000000..3097f1e75 --- /dev/null +++ b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/expanding_sub_list_item.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:stackwallet/models/add_wallet_list_entity/add_wallet_list_entity.dart'; +import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/add_wallet_entity_list.dart'; +import 'package:stackwallet/utilities/assets.dart'; +import 'package:stackwallet/utilities/text_styles.dart'; +import 'package:stackwallet/utilities/theme/stack_colors.dart'; +import 'package:stackwallet/utilities/util.dart'; +import 'package:stackwallet/widgets/expandable.dart'; + +class ExpandingSubListItem extends StatefulWidget { + const ExpandingSubListItem({ + Key? key, + required this.title, + required this.entities, + required this.initialState, + }) : super(key: key); + + final String title; + final List entities; + final ExpandableState initialState; + + @override + State createState() => _ExpandingSubListItemState(); +} + +class _ExpandingSubListItemState extends State { + final isDesktop = Util.isDesktop; + + late final ExpandableController _controller; + + late bool _expandedState; + + @override + void initState() { + _expandedState = widget.initialState == ExpandableState.expanded; + _controller = ExpandableController(); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (_expandedState) { + _controller.toggle?.call(); + } + }); + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Expandable( + controller: _controller, + onExpandChanged: (state) { + setState(() { + _expandedState = state == ExpandableState.expanded; + }); + }, + header: Container( + color: Colors.transparent, + child: Padding( + padding: const EdgeInsets.only( + top: 8.0, + bottom: 8.0, + right: 10, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.title, + style: isDesktop + ? STextStyles.desktopTextExtraExtraSmall(context).copyWith( + color: Theme.of(context) + .extension()! + .textDark3, + ) + : STextStyles.smallMed12(context), + textAlign: TextAlign.left, + ), + SvgPicture.asset( + _expandedState ? Assets.svg.chevronUp : Assets.svg.chevronDown, + width: 12, + height: 6, + color: Theme.of(context) + .extension()! + .textFieldActiveSearchIconRight, + ), + ], + ), + ), + ), + body: SingleChildScrollView( + primary: false, + child: AddWalletEntityList( + entities: widget.entities, + ), + ), + ); + } +} diff --git a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/mobile_coin_list.dart b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/mobile_coin_list.dart deleted file mode 100644 index 573d9d75f..000000000 --- a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/mobile_coin_list.dart +++ /dev/null @@ -1,73 +0,0 @@ -import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; -import 'package:stackwallet/models/ethereum/eth_token.dart'; -import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/coin_select_item.dart'; -import 'package:stackwallet/utilities/enums/coin_enum.dart'; - -class MobileCoinList extends StatelessWidget { - const MobileCoinList({ - Key? key, - required this.entities, - }) : super(key: key); - - final List entities; - - @override - Widget build(BuildContext context) { - return ListView.builder( - shrinkWrap: true, - itemCount: entities.length, - primary: false, - itemBuilder: (ctx, index) { - return Padding( - padding: const EdgeInsets.all(4), - child: CoinSelectItem( - entity: entities[index], - ), - ); - }, - ); - } -} - -abstract class AddWalletListEntity extends Equatable { - Coin get coin; - String get name; - String get ticker; -} - -class EthTokenEntity extends AddWalletListEntity { - EthTokenEntity(this.token); - - final EthToken token; - - @override - Coin get coin => Coin.ethereum; - - @override - String get name => token.name; - - @override - String get ticker => token.symbol; - - @override - List get props => [coin, name, ticker, token.contractAddress]; -} - -class CoinEntity extends AddWalletListEntity { - CoinEntity(this._coin); - - final Coin _coin; - - @override - Coin get coin => _coin; - - @override - String get name => coin.prettyName; - - @override - String get ticker => coin.ticker; - - @override - List get props => [coin, name, ticker]; -} diff --git a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/searchable_coin_list.dart b/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/searchable_coin_list.dart deleted file mode 100644 index 0bf73f9f3..000000000 --- a/lib/pages/add_wallet_views/add_wallet_view/sub_widgets/searchable_coin_list.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/coin_select_item.dart'; -import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/mobile_coin_list.dart'; -import 'package:stackwallet/providers/global/prefs_provider.dart'; -import 'package:stackwallet/utilities/enums/coin_enum.dart'; - -class SearchableCoinList extends ConsumerWidget { - const SearchableCoinList({ - Key? key, - required this.entities, - required this.isDesktop, - required this.searchTerm, - }) : super(key: key); - - final List entities; - final bool isDesktop; - final String searchTerm; - - List filterCoins(String text, bool showTestNetCoins) { - final _entities = [...entities]; - if (text.isNotEmpty) { - final lowercaseTerm = text.toLowerCase(); - _entities.retainWhere( - (e) => - e.ticker.toLowerCase().contains(lowercaseTerm) || - e.name.toLowerCase().contains(lowercaseTerm) || - e.coin.name.toLowerCase().contains(lowercaseTerm) || - (e is EthTokenEntity && - e.token.contractAddress.toLowerCase().contains(lowercaseTerm)), - ); - } - if (!showTestNetCoins) { - _entities.removeWhere( - (e) => e.name.endsWith("TestNet") || e == Coin.bitcoincashTestnet); - } - - return _entities; - } - - @override - Widget build(BuildContext context, WidgetRef ref) { - bool showTestNet = ref.watch( - prefsChangeNotifierProvider.select((value) => value.showTestNetCoins), - ); - - final _entities = filterCoins(searchTerm, showTestNet); - - return ListView.builder( - itemCount: _entities.length, - itemBuilder: (ctx, index) { - return Padding( - padding: const EdgeInsets.all(4), - child: CoinSelectItem( - entity: _entities[index], - ), - ); - }, - ); - } -} diff --git a/lib/pages/add_wallet_views/create_or_restore_wallet_view/create_or_restore_wallet_view.dart b/lib/pages/add_wallet_views/create_or_restore_wallet_view/create_or_restore_wallet_view.dart index bf1db760c..32c4b239e 100644 --- a/lib/pages/add_wallet_views/create_or_restore_wallet_view/create_or_restore_wallet_view.dart +++ b/lib/pages/add_wallet_views/create_or_restore_wallet_view/create_or_restore_wallet_view.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/mobile_coin_list.dart'; +import 'package:stackwallet/models/add_wallet_list_entity/add_wallet_list_entity.dart'; import 'package:stackwallet/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/coin_image.dart'; import 'package:stackwallet/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_or_restore_wallet_subtitle.dart'; import 'package:stackwallet/pages/add_wallet_views/create_or_restore_wallet_view/sub_widgets/create_or_restore_wallet_title.dart'; diff --git a/lib/providers/ui/add_wallet_selected_coin_provider.dart b/lib/providers/ui/add_wallet_selected_coin_provider.dart index acbad9c7b..6acf51db8 100644 --- a/lib/providers/ui/add_wallet_selected_coin_provider.dart +++ b/lib/providers/ui/add_wallet_selected_coin_provider.dart @@ -1,5 +1,5 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/mobile_coin_list.dart'; +import 'package:stackwallet/models/add_wallet_list_entity/add_wallet_list_entity.dart'; final addWalletSelectedEntityStateProvider = StateProvider.autoDispose((_) => null); diff --git a/lib/route_generator.dart b/lib/route_generator.dart index ea2cd76c1..140147f9c 100644 --- a/lib/route_generator.dart +++ b/lib/route_generator.dart @@ -2,6 +2,7 @@ import 'package:decimal/decimal.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:stackwallet/models/add_wallet_list_entity/add_wallet_list_entity.dart'; import 'package:stackwallet/models/buy/response_objects/quote.dart'; import 'package:stackwallet/models/contact_address_entry.dart'; import 'package:stackwallet/models/ethereum/eth_token.dart'; @@ -12,7 +13,6 @@ import 'package:stackwallet/models/isar/models/isar_models.dart'; import 'package:stackwallet/models/paynym/paynym_account_lite.dart'; import 'package:stackwallet/models/send_view_auto_fill_data.dart'; import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/add_wallet_view.dart'; -import 'package:stackwallet/pages/add_wallet_views/add_wallet_view/sub_widgets/mobile_coin_list.dart'; import 'package:stackwallet/pages/add_wallet_views/create_or_restore_wallet_view/create_or_restore_wallet_view.dart'; import 'package:stackwallet/pages/add_wallet_views/name_your_wallet_view/name_your_wallet_view.dart'; import 'package:stackwallet/pages/add_wallet_views/new_wallet_recovery_phrase_view/new_wallet_recovery_phrase_view.dart';